Macros with Variable Argument Lists

On June 21, 2010, in Code Monkey, by Tom

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.

Tagged with:  

One Response to Macros with Variable Argument Lists

  1. Cesar says:

    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

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>