3

Consider the following user-style x-macro:

#define PRIMES_X(func) \
  func(2) \
  func(3) \
  func(5)

We can use this to call a passed-in macro func repeatedly with first three primes. For example:

#define MAKE_FUNC(num) void foo ## num();
PRIMES_X(MAKE_FUNC)

Would declare the void-returning functions foo2(), foo3() and foo5().

So far, so good. Now let's say I want to use a macro in the definition of the x-macro itself, as an argument, as follows:

#define MAX_PRIME 5
#define PRIMES_X(func) \
  func(2) \
  func(3) \
  func(MAX_PRIME)

It doesn't work, because MAKE_FUNC will now try to declare void fooMAX_PRIME(), as (I suppose) the token concatenation happens without expanding MAX_PRIME.

Can I fix this so that it declares foo5() as before?

BeeOnRope
  • 60,350
  • 16
  • 207
  • 386
  • On a side note out of curiosity, are you going to use the x_macro with `#undef`s? – Yunnosch May 18 '18 at 06:05
  • @Yunnosch - no, I don't currently undef things like `MAKE_FUNC`. One of the benefits of the user-form. – BeeOnRope May 18 '18 at 06:07
  • Exactly. May I ask where you picked up that undef-free version of x_macros? I ask because some time ago I claimed the invention here at StackOverflow, asking for references to any "previous art". So I am very interested in your source, even if it means that I have to stop claiming it. – Yunnosch May 18 '18 at 06:10
  • Here's [the link](http://www.drdobbs.com/cpp/the-x-macro/228700289). The article is not by Andrei but see the comment at the bottom: _Of course, you may already be using a macro or variable named X, and X is hardcoded in the macro body. Andrei Alexandrescu suggests the following improvement where the X macro is itself a parameter..._, but there isn't a reference trail beyond that. The comment thread is also interesting, people pointing out that x-macros get lost from time to time. I consider them at least semi-mainstream now that they have a wikipedia page ... @Yunnosch – BeeOnRope May 18 '18 at 06:14
  • Thanks, I will study it later. – Yunnosch May 18 '18 at 06:14

2 Answers2

2

You can insert another level of macro-expansion (PRIMES_X2 below).

#define MAKE_FUNC(num) void foo ## num();
#define MAX_PRIME 5
#define PRIMES_X(func) PRIMES_X2(func, MAX_PRIME)
#define PRIMES_X2(func, maxPrimePar) \
  func(2) \
  func(3) \
  func(maxPrimePar)

PRIMES_X(MAKE_FUNC)

Output with gcc -E:

void foo2(); void foo3(); void foo5();
Yunnosch
  • 26,130
  • 9
  • 42
  • 54
  • Thanks, it worked perfectly for the [real use case in my project](https://github.com/travisdowns/uarch-bench/commit/f28703d1c53c5dcc476d2ec3bf2d39cd100b5edf#diff-65670f3dc051237b53613ab20060709bR30). – BeeOnRope May 18 '18 at 06:12
1

The answer by Yunnosch is fine, but to spin the X macro insanity a bit further, you could also do this with a macro call inside the list, rather than a wrapper macro outside it. The advantage of this is that you can pass "variables" from the list to the called macro.

I suppose this could have some use - suppose for example that you wish to use the X macro to declare functions of different types?

Example:

#define MAX_PRIME 5

#define CREATE_FUNC(func, ret_type, param) func(ret_type, param)

#define PRIMES_X(func)                 \
  CREATE_FUNC(func, int,    2)         \
  CREATE_FUNC(func, void,   3)         \
  CREATE_FUNC(func, double, MAX_PRIME) \

#define MAKE_FUNC(ret_type, num) ret_type foo ## num(void);
  PRIMES_X(MAKE_FUNC)
#undef MAKE_FUNC

Debug code to check that the functions did indeed get the expected prototypes:

int main(void)
{
  (void)foo2();
  foo3();
  (void)foo5();
}

int foo2 (void){ return 0;}
void foo3 (void){}
double foo5 (void){ return 0.0;}
Lundin
  • 195,001
  • 40
  • 254
  • 396
  • Thanks, this technique works too! I don't quite follow the benefit relative to the other approach though - with the other approach, you could just as easily pass the `ret_type` argument by modifying `PRIMES_X2` to call `func(int, 2) \ func(void, 3)...` right? – BeeOnRope May 19 '18 at 03:49
  • @BeeOnRope The benefit of this version is that the type is part of the X macro list and not the caller code. The whole purpose of X macros is to centralize all maintenance to one single list - this version makes it possible to do so by letting the X macro list determine the types used. Seems handy, although I would of course if possible avoid macros that create functions in the first place. – Lundin May 21 '18 at 07:01
  • Yup, I get that, but can't you do the exact same thing with the form of the other answer? Specifically, add the types into the `PRIMES_X2` list. I don't see how this approach allows this while the other doesn't. – BeeOnRope May 21 '18 at 16:10