2

I have information stored in a macro list like this:

#define MYLIST(XX) \
  XX(1, hello)     \
  XX(2, world)     \
  ...

Now I would like to use this macro twice in the same "call stack". Here is a silly example:

#define BB(i,n) i +
#define AA(i,n) i + (MYLIST(BB) 0) +
int foo = MYLIST(AA) 0;

However, this doesn't work since MYLIST does not expand the second time:

int foo = 1 + (MYLIST(BB) 0) + 2 + (MYLIST(BB) 0) + 0;

Is there a way to use MYLIST in the same "call stack" twice - or a workaround - using my existing list?

Julius
  • 1,155
  • 9
  • 19
  • It's unclear to me what you want because you have `BB` both as a macro with two parameters and as an argument to `MYLIST`. (It also doesn't help that you write `XX` as capitals which also make it look like a macro.) Could you be more clear? – meaning-matters Mar 03 '19 at 21:52
  • @meaning-matters XX is a macro function passed in as a parameter. It's quite common to do lists like that, so it can be re-used for multiple purposes. [Check out this answer.](https://stackoverflow.com/questions/4904255/how-can-i-generate-a-list-via-the-c-preprocessor-cpp) However, macro expansion doesn't seem to allow me to use the list twice in the same call stack. – Julius Mar 03 '19 at 22:02
  • The term *call stack* is improper here, you probably mean `MYLIST` does not get expanded recursively. It is indeed a feature that macros do not get reinterpreted in their own expansion. – chqrlie Mar 03 '19 at 22:07
  • @chqrlie Yeah, I didn't know how to call it to be honest. Still looking for a workaround, I don't really like the idea to declare the list twice.. – Julius Mar 03 '19 at 22:09
  • Declaring the list twice would be defeating the purpose... Can you post the actual use case? – chqrlie Mar 03 '19 at 22:15
  • @chqrlie I need to create a simple jump table, and it must contain an entry for all possible combinations in the list. – Julius Mar 03 '19 at 22:22
  • My advise would be to not write obscure macros like this. This is surely an "XY problem" where your design has gone haywire and you try to save it by making things even more obscure. I don't know what you are trying to do here, but it sounds like the wrong solution to an artificial problem you shouldn't be having. – Lundin Mar 04 '19 at 08:31

1 Answers1

3

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.

Ray
  • 1,706
  • 22
  • 30