Foreach macro on macros arguments
Asked Answered
S

7

20

I wonder if it is possible to write a macro foreach on macros arguments. Here is what want to do:

#define PRINT(a) printf(#a": %d", a)
#define PRINT_ALL(...) ? ? ? THE PROBLEM ? ? ? 

And possible usage:

int a = 1, b = 3, d = 0;
PRINT_ALL(a,b,d);

Here is what I achieved so far

#define FIRST_ARG(arg,...) arg
#define AFTER_FIRST_ARG(arg,...) , ##__VA_ARGS__     
#define PRINT(a) printf(#a": %d", a)
#define PRINT_ALL PRINT(FIRST_ARG(__VA_ARGS__)); PRINT_ALL(AFTER_FIRST_ARG(__VA_ARGS__))

This is a recursive macro, which is illegal. And another problem with that is stop condition of recursion.

Snorkel answered 15/7, 2011 at 12:45 Comment(4)
## in AFTER_FIRST_ARG is a gnu compiler extension of CSnorkel
See #825139Durman
you can solve this problem in c++ using the comma operator too, in a nicer way.Tades
@Johannes: Not including the stringized variable names (or expressions, or ...) though.Bump
G
8

Since you are accepting that the preprocessor has VA_ARGS (in C99, but not in the current C++ standard) you can go with P99. It has exactly what you are asking for: P99_FOR. It works without the crude ()()() syntax from BOOST. The interface is just

P99_FOR(NAME, N, OP, FUNC,...) 

and you can use it with something like

#define P00_SEP(NAME, I, REC, RES) REC; RES
#define P00_VASSIGN(NAME, X, I) X = (NAME)[I]
#define MYASSIGN(NAME, ...) P99_FOR(NAME, P99_NARG(__VA_ARGS__), P00_SEP, P00_VASSIGN, __VA_ARGS__)

MYASSIGN(A, toto, tutu);
Grantley answered 15/7, 2011 at 13:15 Comment(4)
Oh, that crude ,,, syntax ;)Bump
@Georg :) While the definition of macros might sometimes look strange the usage of macros should just follow usual conventions of the language(s), I think.Grantley
Just kidding, couldn't resist to comment on the Boost pun :) I guess i got used to it though.Bump
The example is actually not so clear. Could you change it so as to perform the printf's OP asked for?Powel
C
70

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.

Crucifixion answered 19/11, 2012 at 17:59 Comment(8)
Very nice. But is it possible to pass some value along with VA_ARGS to target function? I managed to add 1 param, but when I try to pack a few params within that one, I stuck. coliru.stacked-crooked.com/a/8622a9a5fb7d2ba5Headmaster
For the record, for this to work with VS2015 in my setup I had to: #define MAP_NEXT1(item, next) EVAL0(MAP_NEXT0 (item, next, 0))Bigler
This has been very useful for me; I have a related (follow-up) question here.Powel
This works wonderful, except when you get an error inside the macro expansion and you have to scroll up forever to skip all the infos backtracking it through all the EVAL expansions.Tingey
Where did the number 365 come from?Gisborne
365 is because we have 5 levels, where each level expands its input 3 times. So, 1+(3*(3*(3*(3*(3*(1)+1)+1)+1)+1)+1) = 365. There is a chart at the bottom the github readme showing other possible combinations.Crucifixion
Very nicely done. I prefer this solution to the P99 lib in the accepted answer, because it's a simple and short header with no dependencies.Amor
How can I make this work with no values like that: MAP(f) so that the output is just nothing?Yahiya
L
14

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.


Edit: many years later...

If we take a step back and reimagine the goal "apply a macro to each argument of a macro", this ia almost the same thing as an X-Macro. And I think an X-Macro can be made to do roughly the same job with a slight difference in syntax.

#define EACH_THING(X) \
  X(Thing1) \
  X(Thing2) \
  X(OtherThing) \
/**/

Then you can write a macro that deals with each thing individually and by invoking the EACH_* with the name of the macro to use.

#define BareWord_comma(X) X ,
#define String_comma(X) #X ,

enum{ EACH_THING( BareWord_comma ) NUM_THINGS };
char*names[]={ EACH_THING( String_comma ) NULL };

Here the list of things isn't the argument list to a macro, but a sequence of macro invocations in the body of a macro. The important parts are all here, though: separating the list of things from the transformation to apply to each one.

Leflore answered 17/7, 2011 at 5:35 Comment(4)
I'm still tickled by my youthful/arrogant "copyright notice". :)Leflore
By virtue of posting to StackOverflow, this code is public domain, correct? ... it seems there's an automatic Creative Commons license, according to meta.stackexchange.com/questions/12527/…Farmyard
p.s, in my youthful/arrogant days, I used wtfpl.net/about. LawyerCats get all bent out of shape over that b/c it doesn't explicitly say you can copy the code.Farmyard
Technically, it's CC by SA. Almost the same as PD but requiring attribution.Leflore
G
8

Since you are accepting that the preprocessor has VA_ARGS (in C99, but not in the current C++ standard) you can go with P99. It has exactly what you are asking for: P99_FOR. It works without the crude ()()() syntax from BOOST. The interface is just

P99_FOR(NAME, N, OP, FUNC,...) 

and you can use it with something like

#define P00_SEP(NAME, I, REC, RES) REC; RES
#define P00_VASSIGN(NAME, X, I) X = (NAME)[I]
#define MYASSIGN(NAME, ...) P99_FOR(NAME, P99_NARG(__VA_ARGS__), P00_SEP, P00_VASSIGN, __VA_ARGS__)

MYASSIGN(A, toto, tutu);
Grantley answered 15/7, 2011 at 13:15 Comment(4)
Oh, that crude ,,, syntax ;)Bump
@Georg :) While the definition of macros might sometimes look strange the usage of macros should just follow usual conventions of the language(s), I think.Grantley
Just kidding, couldn't resist to comment on the Boost pun :) I guess i got used to it though.Bump
The example is actually not so clear. Could you change it so as to perform the printf's OP asked for?Powel
B
4

In C++ without extensions you could go for Boost.Preprocessor and it's sequences:

PRINT_ALL((a)(b)(c));

By using BOOST_PP_SEQ_FOR_EACH() on the sequence you can iterate it and easily generate code that prints them.

Untested straight-forward sample:

#define DO_PRINT(elem) std::cout << BOOST_PP_STRINGIZE(elem) << "=" << (elem) << "\n";
#define PRINT_ALL(seq) { BOOST_PP_SEQ_FOR_EACH(DO_PRINT, _, seq) }
Bump answered 15/7, 2011 at 13:4 Comment(2)
GCC decides that DO_PRINT both exists and doesn't, and either way it's not happy: macro "DO_PRINT" passed 3 arguments, but takes just 1 ... error: ‘DO_PRINT’ was not declared in this scopeHilel
I found that BOOST_PP_SEQ_FOR_EACH(macro, data, seq) calls macro(index, data, seq[index-2]) where index is an index that starts at 2 for some reason (tested with gcc 8.1.0) and seq[index-2] is an element of the sequence. The documentation just describes the first parameter as "the next available BOOST_PP_FOR repetition".Hilel
A
3

Old question, but I thought I'd tack on a solution I came up with to use Boost.Preprocessor without the ugly (a)(b) syntax.

Header:

#include <iostream>
#include <boost\preprocessor.hpp>

#define _PPSTUFF_OUTVAR1(_var) BOOST_PP_STRINGIZE(_var) " = " << (_var) << std::endl
#define _PPSTUFF_OUTVAR2(r, d, _var) << _PPSTUFF_OUTVAR1(_var) 
#define _PPSTUFF_OUTVAR_SEQ(vseq) _PPSTUFF_OUTVAR1(BOOST_PP_SEQ_HEAD(vseq)) \
        BOOST_PP_SEQ_FOR_EACH(_PPSTUFF_OUTVAR2,,BOOST_PP_SEQ_TAIL(vseq)) 
#define OUTVAR(...) _PPSTUFF_OUTVAR_SEQ(BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__))

Usage:

int a = 3;
char b[] = "foo";

std::cout << OUTVAR(a);

// Expands to: 
//
// std::cout << "a" " = " << (a ) << std::endl  ;
//
// Output:
//
// a = 3

std::cout << OUTVAR(a, b);

// Expands to: 
//
// std::cout << "a" " = " << (a ) << std::endl << "b" " = " << (b) << std::endl  ;
//
// Output:
//
// a = 3
// b = foo

Nice and clean.

Of course you can replace the std::endl with a comma or something if you want it all on one line.

Aphyllous answered 3/2, 2017 at 4:47 Comment(0)
H
3

You can use Boost.PP (after adding Boost's boost folder to your list of include directories) to get macros for this. Here's an example (tested with GCC 8.1.0):

#include <iostream>
#include <limits.h>
#include <boost/preprocessor.hpp>

#define WRITER(number,middle,elem) std::cout << \
    number << BOOST_PP_STRINGIZE(middle) << elem << "\n";
#define PRINT_ALL(...) \
    BOOST_PP_SEQ_FOR_EACH(WRITER, =>, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__))

int main (int argc, char *argv[])
{
    PRINT_ALL(INT_MAX, 123, "Hello, world!");
}

Output:

2=>2147483647
3=>123
4=>Hello, world!

The BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__) part converts the variable-argument list to Boost's traditional way of expressing multiple arguments as a single argument, which looks like this: (item1)(item2)(item3).

Not sure why it starts numbering the arguments at two. The documentation just describes the first parameter as "the next available BOOST_PP_FOR repetition".

Here's another example that defines an enum with the ability to write it as a string to an ostream, which also enables Boost's lexical_cast<string>:

#define ENUM_WITH_TO_STRING(ENUMTYPE, ...)                   \
    enum ENUMTYPE {                                          \
        __VA_ARGS__                                          \
    };                                                       \
    inline const char* to_string(ENUMTYPE value) {           \
        switch (value) {                                     \
            BOOST_PP_SEQ_FOR_EACH(_ENUM_TO_STRING_CASE, _,   \
               BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__))        \
            default: return nullptr;                         \
        }                                                    \
    }                                                        \
    inline std::ostream& operator<<(std::ostream& os, ENUMTYPE v)\
        { return os << to_string(v); }
#define _ENUM_TO_STRING_CASE(_,__,elem)                      \
    case elem: return BOOST_PP_STRINGIZE(elem);

ENUM_WITH_TO_STRING(Color, Red, Green, Blue)

int main (int argc, char *argv[])
{
    std::cout << Red << Green << std::endl;
    std::cout << boost::lexical_cast<string>(Blue) << std::endl;
}

Output:

RedGreen
Blue
Hilel answered 19/1, 2019 at 1:33 Comment(2)
Can you clarify--does BOOST_PP_SEQ_FOR_EACH and BOOST_PP_VARIADIC_TO_SEQ work only with a non-standard pre-processor?Willaims
@SwissFrank I don't know for sure, but my guess is that BOOST_PP_SEQ_FOR_EACH, BOOST_PP_VARIADIC_TO_SEQ, ... and __VA_ARGS__ are pretty standard. Boost is known for its compatibility with many compilers.Hilel
S
2

The preprocessor is not powerful enough to do stuff like this. However, you don't really need the preprocessor that badly. If all you want to do is to dump variable names and their values in a convenient manner. You could have two simple macros:

#define PRINT(x) \
{ \
    std::ostringstream stream; \
    stream << x; \
    std::cout << stream.str() << std::endl; \
}

#define VAR(v) #v << ": " << v << ", "

You could then almost use your intended usage:

int a = 1, b = 3, d = 0;
PRINT(VAR(a) << VAR(b) << VAR(d))

This prints

a: 1, b: 3, d: 0,

There are a lot of ways to make this more powerful, but this works, allows you to print non-integer values nicely and it's a rather simple solution.

Squeal answered 15/7, 2011 at 12:57 Comment(2)
The PP is powerful enough, e.g. by using Boost.PP sequences in C++.Bump
Printing variable names is just a part of what I want to do. I was rather thinking about general idea if it possible to create such macro. It might be useful for different reasons: creating number of let say exception classes, or creating number of variables and then initialize them. There is plenty of possibilities.Snorkel

© 2022 - 2024 — McMap. All rights reserved.