Foreach macro on macros arguments

Yes, recursive macros are possible in C using a fancy workaround. The end goal is to create a MAP macro which works like this:

#define PRINT(a) printf(#a": %d", a)
MAP(PRINT, a, b, c) /* Apply PRINT to a, b, and c */

Basic Recursion

First, we need a technique for emitting something that looks like a macro call, but isn't yet:

#define MAP_OUT

Imagine we have the following macros:

#define A(x) x B MAP_OUT (x)
#define B(x) x A MAP_OUT (x)

Evaluating the macro A (blah) produces the output text:

blah B (blah)

The preprocessor doesn't see any recursion, since the B (blah) call is just plain text at this point, and B isn't even the name of the current macro. Feeding this text back into the preprocessor expands the call, producing the output:

blah blah A (blah)

Evaluating the output a third time expands the A (blah) macro, carrying the recursion full-circle. The recursion continues as long as the caller continues to feed the output text back into the preprocessor.

To perform these repeated evaluations, the following EVAL macro passes its arguments down a tree of macro calls:

#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__)))

Each level multiplies the effort of the level before, evaluating the input 365 times in total. In other words, calling EVAL (A (blah)) would produce 365 copies of the word blah, followed by a final un-evaluated B (blah). This provides the basic framework for recursion, at least within a certain stack depth.

End Detection

The next challenge is to stop the recursion when it reaches the end of the list.

The basic idea is to emit the following macro name instead of the normal recursive macro when the time comes to quit:

#define MAP_END(...)

Evaluating this macro does nothing, which ends the recursion.

To actually select between the two macros, the following MAP_NEXT macro compares a single list item against the special end-of-list marker (). The macro returns MAP_END if the item matches, or the next parameter if the item is anything else:

#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)

This macro works by placing the item next to the MAP_GET_END macro. If doing that forms a macro call, everything moves over by a slot in the MAP_NEXT0 parameter list, changing the output. The MAP_OUT trick prevents the preprocessor from evaluating the final result.

Putting it All Together

With these pieces in place, it is now possible to implement useful versions of the A and B macros from the example above:

#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__)

These macros apply the operation f to the current list item x. They then examine the next list item, peek, to see if they should continue or not.

The final step is to tie everything together in a top-level MAP macro:

#define MAP(f, ...) EVAL (MAP1 (f, __VA_ARGS__, (), 0))

This macro places a () marker on the end of the list, as well as an extra 0 for ANSI compliance (otherwise, the last iteration would have an illegal 0-length list). It then passes the whole thing through EVAL and returns the result.

I have uploaded this code as a library on github for your convenience.


Using PPNARG, I wrote a set of macros to apply a macro to each argument in a macro. I call it a variadic X-macro.

/*
 * The PP_NARG macro evaluates to the number of arguments that have been
 * passed to it.
 *
 * Laurent Deniau, "__VA_NARG__," 17 January 2006, <comp.std.c> (29 November 2007).
 */
#define PP_NARG(...)    PP_NARG_(__VA_ARGS__,PP_RSEQ_N())
#define PP_NARG_(...)   PP_ARG_N(__VA_ARGS__)

#define PP_ARG_N( \
        _1, _2, _3, _4, _5, _6, _7, _8, _9,_10,  \
        _11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \
        _21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \
        _31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \
        _41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \
        _51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \
        _61,_62,_63,N,...) N

#define PP_RSEQ_N() \
        63,62,61,60,                   \
        59,58,57,56,55,54,53,52,51,50, \
        49,48,47,46,45,44,43,42,41,40, \
        39,38,37,36,35,34,33,32,31,30, \
        29,28,27,26,25,24,23,22,21,20, \
        19,18,17,16,15,14,13,12,11,10, \
        9,8,7,6,5,4,3,2,1,0

PPNARG lets us get a count of how many arguments there are. Then we append that number to the macro name and call it with the original arguments.

/* need extra level to force extra eval */
#define Paste(a,b) a ## b
#define XPASTE(a,b) Paste(a,b)


/* APPLYXn variadic X-Macro by M Joshua Ryan      */
/* Free for all uses. Don't be a jerk.            */
/* I got bored after typing 15 of these.          */
/* You could keep going upto 64 (PPNARG's limit). */
#define APPLYX1(a)           X(a)
#define APPLYX2(a,b)         X(a) X(b)
#define APPLYX3(a,b,c)       X(a) X(b) X(c)
#define APPLYX4(a,b,c,d)     X(a) X(b) X(c) X(d)
#define APPLYX5(a,b,c,d,e)   X(a) X(b) X(c) X(d) X(e)
#define APPLYX6(a,b,c,d,e,f) X(a) X(b) X(c) X(d) X(e) X(f)
#define APPLYX7(a,b,c,d,e,f,g) \
    X(a) X(b) X(c) X(d) X(e) X(f) X(g)
#define APPLYX8(a,b,c,d,e,f,g,h) \
    X(a) X(b) X(c) X(d) X(e) X(f) X(g) X(h)
#define APPLYX9(a,b,c,d,e,f,g,h,i) \
    X(a) X(b) X(c) X(d) X(e) X(f) X(g) X(h) X(i)
#define APPLYX10(a,b,c,d,e,f,g,h,i,j) \
    X(a) X(b) X(c) X(d) X(e) X(f) X(g) X(h) X(i) X(j)
#define APPLYX11(a,b,c,d,e,f,g,h,i,j,k) \
    X(a) X(b) X(c) X(d) X(e) X(f) X(g) X(h) X(i) X(j) X(k)
#define APPLYX12(a,b,c,d,e,f,g,h,i,j,k,l) \
    X(a) X(b) X(c) X(d) X(e) X(f) X(g) X(h) X(i) X(j) X(k) X(l)
#define APPLYX13(a,b,c,d,e,f,g,h,i,j,k,l,m) \
    X(a) X(b) X(c) X(d) X(e) X(f) X(g) X(h) X(i) X(j) X(k) X(l) X(m)
#define APPLYX14(a,b,c,d,e,f,g,h,i,j,k,l,m,n) \
    X(a) X(b) X(c) X(d) X(e) X(f) X(g) X(h) X(i) X(j) X(k) X(l) X(m) X(n)
#define APPLYX15(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o) \
    X(a) X(b) X(c) X(d) X(e) X(f) X(g) X(h) X(i) X(j) X(k) X(l) X(m) X(n) X(o)
#define APPLYX_(M, ...) M(__VA_ARGS__)
#define APPLYXn(...) APPLYX_(XPASTE(APPLYX, PP_NARG(__VA_ARGS__)), __VA_ARGS__)

And here are some examples with the output from gcc -E in comments.

/* Example */
#define X(a) #a,
char *list[] = {
    APPLYXn(sugar,coffee,drink,smoke)
};
#undef X

/* Produces (gcc -E)
char *list[] = {
    "sugar", "coffee", "drink", "smoke",
};
 */


#define c1(a) case a:
#define c2(a,b)     c1(a) c1(b)
#define c3(a,b,c)   c1(a) c2(b,c)
#define c4(a,b,c,d) c1(a) c3(b,c,d)
#define c_(M, ...) M(__VA_ARGS__)
#define cases(...) c_(XPASTE(c, PP_NARG(__VA_ARGS__)), __VA_ARGS__)


//cases(3,4,5,6,7)
//produces
//case 3: case 4: case 5: case 6:


#define r_(a,b) range(a,b)
#define range(a,b) a,r_(a+1,b-1)
//range(3,4)

#define ps1(a) O ## a ();
#define ps2(a,b)     ps1(a) ps1(b)
#define ps3(a,b,c)   ps1(a) ps2(b,c)
#define ps4(a,b,c,d) ps1(a) ps3(b,c,d)
#define ps_(M, ...) M(__VA_ARGS__)
#define ps(...)     ps_(XPASTE(ps, PP_NARG(__VA_ARGS__)), __VA_ARGS__)

//ps(dup,add,sub)

This last was the motive for the whole thing. But it didn't turn out to be very useful.