Search code examples
cgccmacrosvariadic-macros

C variadic macro do not compile


I have some issue with this code i written. GCC does not like it :

#define _DEBUG_ADD(string, ...)                     \
do{                                                 \
if (EVALUATE_TYPE(string)){                         \
size_t  size = strlen(string) + BUFFER_SIZE_DEBUG;  \
char    *buffer = alloca(size);                     \
bzero(buffer, size);                                \
snprintf(buffer, size, string, __VA_ARGS__);        \
fwrite(buffer, strlen(buffer), 1, DEBUG_STREAM); }} \
while(0)

But gcc display this error :

../debug.h:33:42: error: expected expression before ')' token
                    snprintf(buffer, size, string, __VA_ARGS__);

I read the gcc doc about variadic macro and i am not doing it wrong.

Can someone point my mistake ? I am completly lost.

edit :

I use it that way

_DEBUG_ADD("Bbox found @ %f %f %f %f", box[0], box[1], box[2], box[3]);

Solution

  • If I take the code fragments you showed and fill in missing bits to get a complete, compilable source file, I do not get any errors:

    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    
    #define _DEBUG_ADD(string, ...)                     \
    do{                                                 \
    if (EVALUATE_TYPE(string)){                         \
    size_t  size = strlen(string) + BUFFER_SIZE_DEBUG;  \
    char    *buffer = alloca(size);                     \
    bzero(buffer, size);                                \
    snprintf(buffer, size, string, __VA_ARGS__);        \
    fwrite(buffer, strlen(buffer), 1, DEBUG_STREAM); }} \
    while(0)
    
    #define EVALUATE_TYPE(s) 1
    #define BUFFER_SIZE_DEBUG 128
    #define DEBUG_STREAM stderr
    
    void test(double box[4])
    {
      _DEBUG_ADD("Bbox found @ %f %f %f %f", box[0], box[1], box[2], box[3]);
    }
    

    -->

    $ gcc -fsyntax-only -Wall test.c
    $
    

    This is why we make such a fuss about minimal, complete, verifiable examples. We don't want to waste a lot of time barking up the wrong tree.

    However, in this case, I have a strong suspicion that your problem was actually triggered by code like this:

    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    
    #define _DEBUG_ADD(string, ...)                     \
    do{                                                 \
    if (EVALUATE_TYPE(string)){                         \
    size_t  size = strlen(string) + BUFFER_SIZE_DEBUG;  \
    char    *buffer = alloca(size);                     \
    bzero(buffer, size);                                \
    snprintf(buffer, size, string, __VA_ARGS__);        \
    fwrite(buffer, strlen(buffer), 1, DEBUG_STREAM); }} \
    while(0)
    
    #define EVALUATE_TYPE(s) 1
    #define BUFFER_SIZE_DEBUG 128
    #define DEBUG_STREAM stderr
    
    void test(void)
    {
      _DEBUG_ADD("got here 1");
    }
    

    which produces nearly the same error message you showed:

    $ gcc -fsyntax-only -Wall test.c
    test.c: In function ‘test’:
    test.c:11:43: error: expected expression before ‘)’ token
     snprintf(buffer, size, string, __VA_ARGS__);        \
                                               ^
    test.c:21:3: note: in expansion of macro ‘_DEBUG_ADD’
       _DEBUG_ADD("got here 1");
       ^~~~~~~~~~
    

    When you give _DEBUG_ADD no arguments after the format string, __VA_ARGS__ expands to nothing, so the "compiler proper" sees

    snprintf(buffer, size, string, );
    

    which is indeed a syntax error. This is what the GNU comma-deletion extension is for: if you put ## between , and __VA_ARGS__, the comma will be removed when __VA_ARGS__ expands to nothing.

    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    
    #define _DEBUG_ADD(string, ...)                     \
    do{                                                 \
    if (EVALUATE_TYPE(string)){                         \
    size_t  size = strlen(string) + BUFFER_SIZE_DEBUG;  \
    char    *buffer = alloca(size);                     \
    bzero(buffer, size);                                \
    snprintf(buffer, size, string, ##__VA_ARGS__);      \
    fwrite(buffer, strlen(buffer), 1, DEBUG_STREAM); }} \
    while(0)
    
    #define EVALUATE_TYPE(s) 1
    #define BUFFER_SIZE_DEBUG 128
    #define DEBUG_STREAM stderr
    
    void test(void)
    {
      _DEBUG_ADD("got here 1");
    }
    

    -->

    $ gcc -fsyntax-only -Wall test.c
    $
    

    Unfortunately, this extension is only available in GCC and Clang. I understand the C and C++ committees are talking about adding a comparable, but incompatible, feature Real Soon Now (see the comments on this question and its answers, and also C committee documents N2023 and N2153), but even if they do, it'll probably be a decade or so before that's ubiquitous enough to use.

    Incidentally, the name _DEBUG_ADD begins with an underscore. All names beginning with underscores are reserved for internal use by the C compiler and library in at least some contexts. Until you have a lot more experience in the language, you should not give anything in your code a name beginning with an underscore. (It is OK to use things with names that begin with underscores, like __VA_ARGS__ and _IONBF, but only if they are documented.)