I am trying to identify a problem because of an unusual usage of variadic macros. Here is the hypothetic macro:
#define va(c, d, ...) c(d, __VA_ARGS__)
#define var(a, b, ...) va(__VA_ARGS__, a, b)
var(2, 3, printf, “%d %d %d\n”, 1);
For gcc, the preprocessor will output
printf("%d %d %d\n", 1, 2, 3)
but for VS 2008, the output is
printf, “%d %d %d\n”, 1(2, 3);
I suspect the difference is caused by the different treatment to VA_ARGS, for gcc, it will first expand the expression to va(printf, "%d %d %d\n", 1, 2, 3), and treat 1, 2, 3 as the VA_ARGS for macro va. But for VS 2008, it will first treat b as VA_ARGS for macro va, and then do the expansion.
Which one is correct interpretation for C99 variadic macro? or my usage falls into an undefined behavior?
There is an easy way to deal with this problem:
#define exp(...) __VA_ARGS__
#define va(c, d, ...) c(d, __VA_ARGS__)
#define var(a, b, ...) exp(va(__VA_ARGS__, a, b))
var(2, 3, printf, “%d %d %d\n”, 1);
This will do the trick on VS 2008 and it won't affect gcc
Look into the ISO/IEC 9899:1999, chapter 6.10.3.1. It states that:
After the arguments for the invocation of a function-like macro have been identified, argument substitution takes place. A parameter in the replacement list, unless preceded by a # or ## preprocessing token or followed by a ## preprocessing token (see below), is replaced by the corresponding argument after all macros contained therein have been expanded. Before being substituted, each argument’s preprocessing tokens are completely macro replaced as if they formed the rest of the preprocessing file; no other preprocessing tokens are available.
So in va the first argument c has one preprocessing token being __VA_ARGS__
, which, according to this paragraph, must be macro replaced before being substituted to c (which still does not give the answer as to which compiler is right)
But later:
An identifier
__VA_ARGS__
that occurs in the replacement list shall be treated as if it were a parameter, and the variable arguments shall form the preprocessing tokens used to replace it.
According to the first fragment, first the arguments to var are identified. They are: 2
, 3
, printf
, “%d %d %d\n”
and 1
. Now argument substitution takes place. This means that parameters from var's replacement list are taken and replaced. However, the second fragment states, that identifier __VA_ARGS__
is to be treated as a parameter, so it must be substituted to printf, “%d %d %d\n”, 1
. Now there are no macros inside parameters, so no further substitution takes place, resulting in the expansion of var(2, 3, printf, “%d %d %d\n”, 1);
into va(printf, “%d %d %d\n”, 1, 2, 3);
. Because va is a macro, it is also expanded, giving the result of printf(“%d %d %d\n”, 1, 2, 3);
.
Now if you take VS 2008's reasoning, how can it identify the arguments to va, if one of them is __VA_ARGS__
from var, and can contain many arguments? Well, it treats __VA_ARGS__
as an argument to va, which in my opition is wrong, as, according to fragment one, argument substitution takes place only after the arguments for invocation have been identified.
So it seems to me, that in var the preprocessor has to first identify the arguments to the invocation of the macro va, and then start expanding va. And this means that probably gcc is right here.
It is also shown in C preprocessor and concatenation how processing tokens are macro-replaced in order, until there are no identifiers which can be further expanded as macros, or the preprocessor spots a recursion and terminates expansion.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With