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.