C variadic macro do not compile

1.2k views Asked by At

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]);
1

There are 1 answers

4
zwol On BEST ANSWER

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.)