Using a macro as an argument in an x-macro definition

818 Views Asked by At

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?

2

There are 2 best solutions below

1
On BEST ANSWER

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();
3
On

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;}