The following code will work:
#define EVAL(...) __VA_ARGS__
#define EVAL2(...) EVAL(__VA_ARGS__)
#define EMPTY()
#define DELAYED_CALL(F, ...) F EMPTY()(__VA_ARGS__)
#define BB(i,n) i +
#define AA(i,n) i + (MYLIST(BB) 0) +
#define MYLIST(XX) \
DELAYED_CALL(XX, 1, hello) \
DELAYED_CALL(XX, 2, world)
int foo = EVAL2(MYLIST(AA)) 0;
Output: int foo = 1 + (1 + 2 + 0) + 2 + (1 + 2 + 0) + 0;
Unfortunately, I don't have a deep understanding of why this works; I just tried a few tricks that tend to help in cases like this. But I can explain some of it.
Sometimes a macro is flagged as "not to be expanded further". The flag usually gets set when you start to expand it, and unset once the expansion is finished. This tends to prevent recursion.
When a macro expands into tokens that would normally be a function-like macro call, sometimes we've already passed the phase where they would be expanded.
We can bypass the first issue by delaying the expansion of the macro to a point where the flag won't cause any problems by adding a second macro to create the macro call when it is evaluated. That's what DELAYED_CALL
does. But in doing so, we run into the second issue, so we have to add some calls to EVAL
to cause the macro to be rescanned (The arguments to a function-like macro are always scanned, so passing a sequence of tokens to a function-like macro that just echos its arguments will cause a rescan).
Sometimes we need a couple of rescans to make everything work. EVAL2(X)
is just shorthand for EVAL(EVAL(X))
. Sometimes more evals will be necessary.
The below code makes it a bit clearer what's happening. Note how the MYLIST2 version needs one less EVAL; this is because AA calls MYLIST, and MYLIST2 is the one that has the offending flag set.
#define EVAL(...) __VA_ARGS__
#define EVAL2(...) EVAL(__VA_ARGS__)
#define EVAL3(...) EVAL2(__VA_ARGS__)
#define EMPTY()
#define DELAYED_CALL(F, ...) F EMPTY()(__VA_ARGS__)
#define BB(i,n) i +
#define AA(i,n) i + (MYLIST(BB) 0) +
#define MYLIST(XX) \
DELAYED_CALL(XX, 1, hello) \
DELAYED_CALL(XX, 2, world)
#define MYLIST2(XX) \
XX(1, hello) \
XX(2, world)
% MYLIST
int foo = MYLIST(AA) 0;
int foo = EVAL(MYLIST(AA)) 0;
int foo = EVAL2(MYLIST(AA)) 0;
% MYLIST2
int foo = MYLIST2(AA) 0;
int foo = EVAL(MYLIST2(AA)) 0;
int foo = EVAL2(MYLIST2(AA)) 0;
The output of this will be:
% MYLIST
int foo = AA (1, hello) AA (2, world) 0;
int foo = 1 + (BB (1, hello) BB (2, world) 0) + 2 + (BB (1, hello) BB (2, world) 0) + 0;
int foo = 1 + (1 + 2 + 0) + 2 + (1 + 2 + 0) + 0;
% MYLIST2
int foo = 1 + (BB (1, hello) BB (2, world) 0) + 2 + (BB (1, hello) BB (2, world) 0) + 0;
int foo = 1 + (1 + 2 + 0) + 2 + (1 + 2 + 0) + 0;
int foo = 1 + (1 + 2 + 0) + 2 + (1 + 2 + 0) + 0;
(The % signs aren't anything special. I just wanted comments that would show up in the output, and C-style comments would get removed during preprocessing.)
Further reading. The author of the article understands this much better than I do.