In C/C++, macros that take a variable number of arguments (called variadic macros) can be very useful. Having printf-style macros just makes certain things easier to read and understand. Below I’ll describe how to do this in a way that works on Windows and Linux, as well as supports empty argument lists.
Example
Consider the following log method:
void WriteLog( const LOG_LEVEL level, const char* file, const int line, const char* format, ... );
Pretty straight-forward: it allows you to specify the log level (NOTICE, WARNING, ERROR, etc), a file name and line number, and a user-defined description in “printf” format. To log a warning message, you would type:
WriteLog( LL_WARNING, __FILE__, __LINE__, "[WARNING] Failed to import descriptor '%s:%i'!", _descriptor, _id );
The entry in the log file would look like:
Core.cpp:715 [WARNING] Failed to import descriptor 'timing-engine'!
Creating a Variadic Macro
Calling WriteLog directly works fine, but it’s pretty verbose and annoying to use. One way to make it simpler is to wrap WriteLog with a variadic macro:
#define LOG_WARNING( format, ... ) \\
WriteLog( LL_WARNING, __FILE__, __LINE__, "[WARNING] " format, ##__VA_ARGS__ );
Then the example above would become:
LOG_WARNING( "Failed to import descriptor '%s:%i'!", _descriptor, _id );
The macro automatically fills in the file and line number where the log message was generated, and the log level is specified by the macro name (LOG_WARNING). This is much more straight-forward and I believe it makes the code easier to read.
Notice that __VA_ARGS__ is used to get the variable argument list and pass it to WriteLog. During compilation, the preprocessor replaces __VA_ARGS__ with the comma-separated list of arguments. The ‘##‘ that prefixes __VA_ARGS__ is vital if you want your macro to work like you expect. Without it, you would be required to have at least one argument after the format string. ’##‘ has a special meaning in this case: it causes the preceding comma to be deleted if there are no variable arguments. Without this, the following line would generate a syntax error:
LOG_WARNING( "This would cause a syntax error" );
Prefixing __VA_ARGS__ with ‘##‘ allows the above code to work just fine.


This was exactly what I was looking for, Thanks!
Btw, I think you can, and may be better off with leaving off the terminating semicolon in your macro definition, right?
I may be wrong, just starting to learn effectively use macros, etc, but I think it will just add an extra semicolon, since your calling code will very likely have it already, and may cause probs in some situations… no?
Anyway, Thanks a Bagillion!
C
Reply