2

Consider the following user-style x-macro:

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

We can use this to expand a passed-in macro func repeatedly with the first four primes. For example:

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

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

So far, so good.

Let's say that I know want to create a related x-macro, which calls its argument not with the bare primes 2, 3, ... but with some token derived from it, such as the function names above. That is, I want this:

#define PRIMES_FOO_X(func) \
  func(foo2) \
  func(foo3) \
  func(foo5) \
  func(foo7) 

but without actually writing it all out (indeed, it would get out of sync the moment PRIMES_X changes.

What I want is a way define PRIMES_FOO_X in terms of PRIMES_X. I can almost get there, e.g.:

#define FOO_ADAPT(num) func(foo ## num)
#define PRIMES_FOO_X(f) PRIMES_X(FOO_ADAPT)

In this case, PRIMES_FOO_X expands to:

  func(foo2) \
  func(foo3) \
  func(foo5) \
  func(foo7)

... which looks right, but the func here isn't the passed arg, but just plain token func since FOO_ADAPT doesn't have an argument called func, only PRIMES_FOO_X(func) does (and it doesn't use it).

I can't figure out a way to make this work.

BeeOnRope
  • 60,350
  • 16
  • 207
  • 386
  • It might be helpful if you could give one or two use-cases showing the desired output of the `PRIMES_FOO_X` macro. – Joseph Quinsey Jul 21 '18 at 15:04
  • @JosephQuinsey well the desired `PRIMES_FOO_X` _expansion_ for `PRIMES_FOO_X` is shown at the end of my question: it should really behave exactly as if it was `#define PRIMES_FOO_X(func) \` followed by the text shown in my last code block. Of course, I don't want to write it out by hand but for it to have this effect but be based on the existing `PRIMES_X` macro (and so in particular changes to `PRIMES_X` will be reflected in `PRIMES_FOO_X`). – BeeOnRope Jul 22 '18 at 02:55

4 Answers4

1

Perhaps a simple-minded work-around would suffice.

Instead of passing an argument func to PRIMES_FOO_X, you can declare it beforehand. For example, in this code we use FOO_FUNC to hold func:

#define PRIMES_FOO_X PRIMES_X(FOO_ADAPT)
#define FOO_ADAPT(num) FOO_FUNC(foo ## num)

#define FOO_FUNC bar
PRIMES_FOO_X

#undef  FOO_FUNC
#define FOO_FUNC(x) x();
PRIMES_FOO_X

The result is:

bar(foo2) bar(foo3) bar(foo5) bar(foo7)

foo2(); foo3(); foo5(); foo7();
Joseph Quinsey
  • 9,553
  • 10
  • 54
  • 77
1

A key observation... given this:

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

PRIMES_X() expands to (2) (3) (5) (7), which in terms of CPP metaprogramming is a sequence data structure. This in mind, let's start going backwards. You want something like this:

#define PRIMES_FOO_X(func) \
  /* something that expands to: func(foo2) func(foo3) func(foo5) func(foo7) */

...and you want foo2, foo3, foo5, foo7 to come from PRIMES_X expansion. Obviously then 2 becomes foo2, 3 becomes foo3, etc; so let's assume such becomings happen according to a macro called FOOIDENT_OF. Then in PRIMES_FOO_X you need to call func on (FOOIDENT_OF(2)), etc; that is, you want something more precisely like this:

#define PRIMES_FOO_X(func) \
  /* something that expands to: \
   * func(FOOIDENT_OF(2)) func(FOOIDENT_OF(3)) \
   * func(FOOIDENT_OF(5)) func(FOOIDENT_OF(7)) */

Combining the two ideas, the elements we have are:

  • func, the operation to apply in the derived X-macro
  • FOOIDENT_OF, the operation that transforms each X-macro argument list to the new form
  • PRIMES_X(), a sequence of all of the argument lists

This is possible, and even a bit easy to do if we use boost preprocessor's sequence.

#include <boost/preprocessor/seq.hpp>

#define PAIR_ELEMENT_1(A,B) A
#define PAIR_ELEMENT_2(A,B) B

#define PAIR_XFORM_MACRO(r, data, elem) \
   PAIR_ELEMENT_1 data ( PAIR_ELEMENT_2 data (elem) )

#define PAIR_XFORM(PAIR_, SEQ_) \
   BOOST_PP_SEQ_FOR_EACH(PAIR_XFORM_MACRO, PAIR_, SEQ_)

Here I have a PAIR_XFORM that takes a 2-tuple ("pair") of macros, and applies both of them to each element of a sequence. IOW, PAIR_XFORM((func, FOOIDENT_OF), PRIMES_X()) generates our target. Now all we need is to gen up the new X-macro and make the inner transform macro:

#define FOOIDENT_OF(N) foo##N
#define PRIMES_FOO_X(func) PAIR_XFORM((func, FOOIDENT_OF), PRIMES_X())

Here is what it looks like on stacked-crooked.

H Walters
  • 2,634
  • 1
  • 11
  • 13
1

(Remark: This is my second answer to this question)

Inspired by H Walters' answer using Boost, I wanted to find a C-only solution. William Swanson's excellent answer to Foreach macro on macros arguments seems to provide one.

Taking the code from his answer, we can generate this solution:

// The first part here is taken from William Swanson's answer
// to https://stackoverflow.com/questions/6707148
#define EVAL0(...) __VA_ARGS__
#define EVAL1(...) EVAL0(EVAL0(EVAL0(__VA_ARGS__)))
#define EVAL2(...) EVAL1(EVAL1(EVAL1(__VA_ARGS__)))
#define EVAL3(...) EVAL2(EVAL2(EVAL2(__VA_ARGS__)))
#define EVAL4(...) EVAL3(EVAL3(EVAL3(__VA_ARGS__)))
#define EVAL(...)  EVAL4(EVAL4(EVAL4(__VA_ARGS__)))

#define MAP_OUT
#define MAP_END(...)
#define MAP_GET_END() 0, MAP_END
#define MAP_NEXT0(item, next, ...) next MAP_OUT
#define MAP_NEXT1(item, next) MAP_NEXT0(item, next, 0)
#define MAP_NEXT(item, next)  MAP_NEXT1(MAP_GET_END item, next)

#define MAP0(f, x, peek, ...) f(x) MAP_NEXT(peek, MAP1)(f, peek, __VA_ARGS__)
#define MAP1(f, x, peek, ...) f(x) MAP_NEXT(peek, MAP0)(f, peek, __VA_ARGS__)
#define MAP(f, ...) EVAL(MAP1(f, __VA_ARGS__ (), 0))

// This is the example given by the OP:
#define PRIMES_X(func) \
  func(2) \
  func(3) \
  func(5) \
  func(7)

#define FOO_LIST(num) foo ## num, // note comma
#define PRIMES_FOO_X(f) MAP(f, PRIMES_X(FOO_LIST))

#define XXX(x) bar(x)
#define YYY(x) x();
PRIMES_FOO_X(XXX)
PRIMES_FOO_X(YYY)

The result, using gcc -E -P ..., is:

bar(foo2) bar(foo3) bar(foo5) bar(foo7)
foo2(); foo3(); foo5(); foo7();

Notes:

  • In the definition of MAP, I had to delete the comma after __VA_ARGS__ to prevent an extra garbage value appearing at the end. But this breaks the macro for other uses. One would think that simply moving the comma in FOO_LIST would fix this, but it doesn't. (Todo: fix)

  • Any MAP-like or FOREACH-like solution which uses the ## concatenate operator is unlikely to work here, because any input list given by a macro is not expanded. (This was new to me:()


Edit: A second alternative solution using the same idea is based on code from https://esolangs.org/wiki/ELIP. The output is identical to the above. (And this shows that my comment about ## is not correct.)

// The first part here is based on esolangs.org/wiki/ELIP (CC0 public domain)
// (Note MAP here is their FOREACH)
#define XCAT(x,y) x ## y
#define CAT(x,y) XCAT(x,y)
#define EMPTY()
#define LPAREN (
#define RPAREN )
#define DEFER(x) x EMPTY()
#define EAT(...)
#define EXPAND(...) __VA_ARGS__
#define TRUE(x,...) x
#define FALSE(x,...) __VA_ARGS__
#define TRANSFORM(seq, ...) CAT(TRANSFORM1_A seq,0END)(EAT, __VA_ARGS__)
#define TRANSFORM1_A(...) (EXPAND, __VA_ARGS__)() TRANSFORM1_B
#define TRANSFORM1_B(...) (EXPAND, __VA_ARGS__)() TRANSFORM1_A
#define TRANSFORM1_A0END
#define TRANSFORM1_B0END
#define RPXFRM(m, ...) m(RPAREN RPXFRM_ID)
#define RPXFRM_ID() RPXFRM
#define INFUSE(seq, ...) INFUSE5(INFUSE1(TRANSFORM(seq), __VA_ARGS__))
#define INFUSE1(xfrm, ...) INFUSE2 xfrm, __VA_ARGS__ RPXFRM xfrm
#define INFUSE2(m, ...) m(INFUSE3 DEFER(XCAT)(LPA,REN)(__VA_ARGS__), INFUSE2_ID)
#define INFUSE2_ID() INFUSE2
#define INFUSE3(...) INFUSE4(__VA_ARGS__)
#define INFUSE4(x, rest, ...) (__VA_ARGS__, EXPAND x)() rest, __VA_ARGS__
#define INFUSE5(...) INFUSE6(__VA_ARGS__)
#define INFUSE6(...) INFUSE7(__VA_ARGS__)
#define INFUSE7(seq, ...) seq
#define MAP(macro, seq) EXPAND(MAP1 INFUSE(seq, TRUE, macro)(FALSE, EAT,))
#define MAP1(p, m, ...) m(__VA_ARGS__) p(MAP1_ID)
#define MAP1_ID() MAP1

// This is the example given by the OP:
#define PRIMES_X(func) \
  func(2) \
  func(3) \
  func(5) \
  func(7)

#define FOO_LIST(num) (foo ## num) // parentheses, no commas
#define PRIMES_FOO_X(f) MAP(f, PRIMES_X(FOO_LIST))

#define XXX(x) bar(x)
#define YYY(x) x();
PRIMES_FOO_X(XXX)
PRIMES_FOO_X(YYY)
Joseph Quinsey
  • 9,553
  • 10
  • 54
  • 77
0

Try the following solution:

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

#define DERIVE_TOKEN(num) (foo##num);
#define FOO_ADAPT(f) f DERIVE_TOKEN
#define PRIMES_FOO_X(f) PRIMES_X(FOO_ADAPT(f))

It expands

PRIMES_FOO_X(funct)

to

funct (foo2); funct (foo3); funct (foo5); funct (foo7);
Marian
  • 7,402
  • 2
  • 22
  • 34