Reputation: 199
When I was playing with a macro argument counter, I found it failed to work if I delete one level of indirection.
Original code:
#define COUNT_ARGS(...) \
COUNT_INDIR(__VA_ARGS__,COUNT_DOWN())
#define COUNT_INDIR(...) \
COUNT_HELPER(__VA_ARGS__)
#define COUNT_HELPER( \
_1, _2, _3, _4, _5, N,...) N
#define COUNT_DOWN() \
5, 4, 3, 2, 1, 0
COUNT_ARGS(1,2,3);
My edited version:
#define COUNT_ARGS(...) \
COUNT_HELPER(__VA_ARGS__,COUNT_DOWN())
#define COUNT_HELPER( \
_1, _2, _3, _4, _5, N,...) N
#define COUNT_DOWN() \
5, 4, 3, 2, 1, 0
COUNT_ARGS(1,2,3);
And I received the following message
error: macro "COUNT_HELPER" requires 7 arguments, but only 4 given
My guess is that COUNT_DOWN() is passed to COUNT_HELPER without being expanded. But I read from Argument Prescan that macro arguments are completely expanded before they are substituted into macro body. The contradictory behaviour left me confused.
Upvotes: 1
Views: 744
Reputation: 180968
The expansion of a function-like macro operates in discrete stages:
The arguments are identified from the argument list. There must be one argument for each named parameter in the macro's parameter list, and at least one more if the macro is variadic.
Each argument is completely macro-expanded.
The macro invocation is replaced by the macro's replacement text, with the expanded arguments substituted for the corresponding parameter names.
The expansion is rescanned for additional macro expansions.
(That ignores the effects of the #
and ##
operators, which are not in play here.)
I suspect your confusion arises from not appreciating that step (2) is completely separate from and subsequent to step (1). Once the macro arguments have been identified, their number and their correspondence to the macro's parameters is set. Expanding them does not modify that, no matter what the nature of the expanded text. Alternatively, perhaps you don't appreciate that macro invocations in a macro's replacement text are not expanded in the macro definition itself, but rather at step (4) of the process of expanding the macro.
Thus, given these (the original) definitions ...
#define COUNT_ARGS(...) \
COUNT_INDIR(__VA_ARGS__,COUNT_DOWN())
#define COUNT_INDIR(...) \
COUNT_HELPER(__VA_ARGS__)
#define COUNT_HELPER( \
_1, _2, _3, _4, _5, N,...) N
#define COUNT_DOWN() \
5, 4, 3, 2, 1, 0
... this is the sequence of expansions:
COUNT_ARGS(1,2,3);
COUNT_INDIR(1,2,3,COUNT_DOWN());
COUNT_HELPER(1,2,3,5,4,3,2,1,0);
3;
On the other hand, given your modified definitions ...
#define COUNT_ARGS(...) \
COUNT_HELPER(__VA_ARGS__,COUNT_DOWN())
#define COUNT_HELPER( \
_1, _2, _3, _4, _5, N,...) N
#define COUNT_DOWN() \
5, 4, 3, 2, 1, 0
... the expansions go like this:
COUNT_ARGS(1,2,3);
COUNT_HELPER(1,2,3,COUNT_DOWN());
error
Just as the error message tells you, too few arguments have been provided to COUNT_HELPER()
. That an expansion of COUNT_HELPER()
would produce a comma-delimited list is irrelevant at that point.
Causing COUNT_DOWN()
to be expanded to serve as multiple arguments to COUNT_HELPER()
is the whole purpose of the original's COUNT_INDIR()
macro.
Upvotes: 2