Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is a repeated macro invocation via token concatenation unspecified behavior?

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.

Can anyone argue that the interpretations made by gcc and clang are required by something in the standard?

Is anyone aware of an implementation that interprets this situation differently?

like image 785
Kyle Avatar asked Oct 29 '22 23:10

Kyle


1 Answers

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.

like image 58
Jens Gustedt Avatar answered Nov 17 '22 08:11

Jens Gustedt