The C11 standard admits vagueness with regard to at least one situation that can arise in macro expansion, when a function like macro expands to its unenvoked name, and is invoked by the next preprocessing token. The example given in the standard is this.
#define f(a) a*g
#define g(a) f(a)
// may produce either 2*f(9) or 2*9*g
f(2)(9)
That example does not clarify what happens when a macro, M, is expanded, and all or part of the result contributes via token concatenation to a second preprocessing token, M, which is invoked.
Question: Is such an invocation blocked?
Here is an example of such an invocation. This issue tends to only come up when using a fairly complicated set of macros, so this example is contrived for the sake of simplicity.
// arity gives the arity of its args as a decimal integer (good up to 4 args)
#define arity(...) arity_help(__VA_ARGS__,4,3,2,1,)
#define arity_help(_1,_2,_3,_4,_5,...) _5
// define 'test' to mimic 'arity' by calling it twice
#define test(...) test_help_A( arity(__VA_ARGS__) )
#define test_help_A(k) test_help_B(k)
#define test_help_B(k) test_help_##k
#define test_help_1 arity(1)
#define test_help_2 arity(1,2)
#define test_help_3 arity(1,2,3)
#define test_help_4 arity(1,2,3,4)
// does this expand to '1' or 'arity(1)'?
test(X)
test(X)
expands to test_help_A( arity(X) )
, which invokes test_help_A
on rescanning, which expands its arg before substitution, and so is identical to test_help_A(1)
, which produces test_help_B(1)
, which produces test_help_1
. This much is clear.
So, the question comes in here. test_help_1
is produced using a character, 1
, that came from an expansion of arity
. So can the expansion of test_help_1
invoke arity again? My versions of gcc and clang each think so.
I think that gcc's and clang's interpretation are correct. The two expansions of arity
are not in the same call path. The first descends from the expansion of test_help_A
's argument, the second from the expansion of test_help_A
itself.
The idea of these rules is to guarantee that there can't be infinite recursion, which is guaranteed, here. There is progress in the evaluation of the macro between the two calls.
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