Home > Software engineering >  c complains about __VA_ARGS__
c complains about __VA_ARGS__

Time:07-30

The following code has been compiled with gcc-5.4.0 with no issues:

% gcc -W -Wall a.c
...

#include <stdio.h>
#include <stdarg.h>

static int debug_flag;
static void debug(const char *fmt, ...)
{
   va_list ap;

   va_start(ap, fmt);
   vfprintf(stderr, fmt, ap);
   va_end(ap);
}

#define DEBUG(...)               \
   do {                 \
      if (debug_flag) {       \
         debug("DEBUG:"__VA_ARGS__);  \
      }              \
   } while(0)

int main(void)
{
   int dummy = 10;
   debug_flag = 1;
   DEBUG("debug msg dummy=%d\n", dummy);
   return 0;
}

However compiling this with g has interesting effects:

% g   -W -Wall -std=c  11 a.c
a.c: In function ‘int main()’:
a.c:18:10: error: unable to find string literal operator ‘operator""__VA_ARGS__’ with ‘const char [8]’, ‘long unsigned int’ arguments
    debug("DEBUG: "__VA_ARGS__); \

% g   -W -Wall -std=c  0x
<same error>

% g   -W -Wall -std=c  03
<no errors>

Changing debug("DEBUG:"__VA_ARGS__); to debug("DEBUG:" __VA_ARGS__); i.e. space before __VA_ARGS__ enables to compile with all three -std= options.

What is the reason for such behaviour? Thanks.

CodePudding user response:

Since C 11 there is support for user-defined literals, which are literals, including string literals, immediately (without whitespace) followed by an identifier. A user-defined literal is considered a single preprocessor token. See https://en.cppreference.com/w/cpp/language/user_literal for details on their purpose.

Therefore "DEBUG:"__VA_ARGS__ is a single preprocessor token and it has no special meaning in a macro definition. The correct behavior is to simply place it unchanged into the macro expansion, where it then fails to compile as no user-defined literal operator for a __VA_ARG__ suffix was declared.

So GCC is correct to reject it as C 11 code.

This is one of the backwards-incompatible changes between C 03 and C 11 listed in the appendix of the C 11 standard draft N3337: https://timsong-cpp.github.io/cppwp/n3337/diff.cpp03.lex

Before C 11 the string literal (up to the closing ") would be its own preprocessor token and the following identifier a second preprocessor token, even without whitespace between them.

So GCC is also correct to accept it in C 03 mode. (-std=c 0x is the same as -std=c 11, C 0x was the placeholder name for C 11 when it was still in drafting)

It is also an incompatibility with C (in all revisions up to now) since C doesn't support user-defined literals either and considers the two parts of "DEBUG:"__VA_ARGS__ as two preprocessor tokens as well.

Therefore it is correct for GCC to accept it as C code as well (which is how the gcc command interprets .c files in contrast to g which treats them as C ).

To fix this add a whitespace between "DEBUG:" and __VA_ARGS__ as you suggested. That should make it compatible with all C and C revisions.

  • Related