Typically, when I need to do debug output in Windows, I use the following C code segment:
#ifdef _DEBUG
#define DBGPRINT( kwszDebugFormatString, ... ) \
{ \
wprintf_s( L"[%s:%d] ", __FUNCTIONW__, __LINE__ ); \
wprintf_s( kwszDebugFormatString, __VA_ARGS__ ); \
}
#else
#define DBGPRINT( kwszDebugFormatString, ...) ;;
#endif
I'd like to recode this to use OutputDebugString
which doesn't accept a format string. I consider statically allocating a small array on the stack (e.g., WCHAR wszBuf[100] = {0};
) to be somewhat crude since it might consume significantly more or less memory than what is allocated and either truncate output or waste memory. I wrote the following code that addresses all of these concerns, but concerns me because the macro is a bit large.
#ifdef _DEBUG
#define DBGPRINT( kwszDebugFormatString, ... ) \
{ \
INT iLineNumber = __LINE__; \
FILE *fileNul = NULL; \
INT cbFormatString = 0; \
PWCHAR wszDebugString = NULL; \
size_t st_Offset = 0; \
\
/* Determine the number of characters in the format string by writing to NUL. */\
fopen_s( &fileNul, "nul", "w" ); \
cbFormatString = fwprintf_s( fileNul, L"[%s:%d]", __FUNCTIONW__, iLineNumber ) * sizeof( WCHAR ); \
cbFormatString += fwprintf_s( fileNul, kwszDebugFormatString, __VA_ARGS__ ) * sizeof( WCHAR ) + 2; \
\
/* Depending on the size of the format string, allocate space on the stack or the heap. */ \
wszDebugString = (PWCHAR)_malloca( cbFormatString ); \
\
/* Populate the buffer with the contents of the format string. */ \
StringCbPrintfW( wszDebugString, cbFormatString, L"[%s:%d]", __FUNCTIONW__, iLineNumber ); \
StringCbLengthW( wszDebugString, cbFormatString, &st_Offset ); \
StringCbPrintfW( &wszDebugString[st_Offset / sizeof(WCHAR)], cbFormatString - st_Offset, kwszDebugFormatString, __VA_ARGS__ ); \
\
OutputDebugStringW( wszDebugString ); \
\
_freea( wszDebugString ); \
fclose( fileNul ); \
}
#else
#define DBGPRINT( kwszDebugFormatString, ... ) ;;
#endif
A few notes:
My question is, quite simply, would this macro be considered bad practice for one reason or another (particularly for size or inefficiency)? If so, what alternatives should I be considering?
In case anyone else is wondering, I used the suggestions from the comments and the selected answer and came up with the following code. Thank you so much to everyone who commented or answered - feel free to add more if you have a clever way of doing this that you want to share!
#ifdef _DEBUG
#define DBGPRINT(kwszDebugFormatString, ...) _DBGPRINT(__FUNCTIONW__, __LINE__, kwszDebugFormatString, __VA_ARGS__)
VOID _DBGPRINT( LPCWSTR kwszFunction, INT iLineNumber, LPCWSTR kwszDebugFormatString, ... ) \
{
INT cbFormatString = 0;
va_list args;
PWCHAR wszDebugString = NULL;
size_t st_Offset = 0;
va_start( args, kwszDebugFormatString );
cbFormatString = _scwprintf( L"[%s:%d] ", kwszFunction, iLineNumber ) * sizeof( WCHAR );
cbFormatString += _vscwprintf( kwszDebugFormatString, args ) * sizeof( WCHAR ) + 2;
/* Depending on the size of the format string, allocate space on the stack or the heap. */
wszDebugString = (PWCHAR)_malloca( cbFormatString );
/* Populate the buffer with the contents of the format string. */
StringCbPrintfW( wszDebugString, cbFormatString, L"[%s:%d] ", kwszFunction, iLineNumber );
StringCbLengthW( wszDebugString, cbFormatString, &st_Offset );
StringCbVPrintfW( &wszDebugString[st_Offset / sizeof(WCHAR)], cbFormatString - st_Offset, kwszDebugFormatString, args );
OutputDebugStringW( wszDebugString );
_freea( wszDebugString );
va_end( args );
}
#else
#define DBGPRINT( kwszDebugFormatString, ... ) ;;
#endif
It would be much simpler and less error prone if you'd put all the code into a normal varargs function and then called that in your macro, similar to this:
void dbgprint(const wchar_t *func, int line, const wchar_t *fmt, ...) {
// Fomat the string, maybe with vsprintf, log it, etc.
}
#define DBGPRINT(fmt, ...) dbgprint(__WFUNCTION__, __LINE__, fmt, __VA_ARGS__)