cc-preprocessorx-macros

Creating a related x-macro from an existing one


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.


Solution

  • 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:

    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.