Monday, September 15, 2008

Macros with a Variable Number of Arguments

Macros with a Variable Number of Arguments.
================================================

In ISO C99, a macro with a variable number of arguments can be defined.

example:
    #define debug(format, ...) fprintf (stderr, format, __VA_ARGS__)

this macro has two parameter:format and ..., as __VA_ARGS__ stands for ... . So the invocation of this macro shall have two arguments acording to ISO C.

So the statement below is invalid.
    debug ("A message");    //only one argument
and it should be:
    debug ("A message",);   //an empty argument

But under GUN C, it is valid.

In the above examples, the compiler would complain though since the expansion of the macro still has the extra comma after the format string. (a error)

To help solve this problem, CPP behaves specially for variable arguments used with the token paste operator, `##'.  If instead you write

    #define debug(format, ...) fprintf (stderr, format, ## __VA_ARGS__)

and if the variable arguments are omitted or empty, the `##' operator
causes the preprocessor to remove the comma before it.


And under GNU C, a more readabel way to refer variabel arguments.

    #define debug(format, args...) fprintf (stderr, format, args).


See gcc info section 5.14 'Macros with a Variable Number of Arguments' for more information.

test code:

#include <stdio.h>

#define DEVICE_NAME "test"
#define dprintk(fmt, ...) \
    do{ \
        if(1) printf(DEVICE_NAME ": " fmt, \
                 ## __VA_ARGS__); \
    }while(0)

#define dprintk1(fmt, ...) \
    do{ \
        if(1) printf(DEVICE_NAME ": " fmt, \
                 __VA_ARGS__); \
    }while(0)

/* 该宏也可以这样实现. 但其实这里回避了问题. 问题的引入是建立在可变参数部分为可以为空的基础上的,函数的可变参数就是这样的*/
#define dprintk2(...) \
    do{ \
        if(1) printf(DEVICE_NAME ": " __VA_ARGS__); \
    }while(0)


int main(void)
{
    dprintk("abc%d\n", 123);
    dprintk("abc\n");
#if 0
    dprintk1("abc%d\n", 123);
    dprintk1("abc\n");//test.c:29: error: expected expression before ')' token

#endif
    dprintk2("abc%d\n", 123);
    dprintk2("abc\n");

    return 0;
    
}