Macro macro macro

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

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

And possible use:

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

Here is what I have 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 that is illegal. And another problem with this is the stop condition recursion.

+10
c ++ c macros foreach
Jul 15 '11 at 12:45
source share
7 answers

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

 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); 
+6
Jul 15 '11 at 13:15
source share

Yes, in C recursive macros are possible using a fancy workaround. The ultimate goal is to create a MAP macro that works as follows:

 #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 method to emit what looks like a call macro, but not yet:

 #define MAP_OUT 

Imagine that we have the following macros:

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

A macro evaluation of A (blah) creates the output text:

 blah B (blah) 

The preprocessor does not see recursion, since calling B (blah) just the text at this point, and B is not even the name of the current macro. Filing this text back to the preprocessor expands the call, with the output:

 blah blah A (blah) 

Evaluating the result for the third time expands the macro A (blah) , transferring a recursive full circle. Recursion continues as long as the caller continues to feed the output text back to the preprocessor.

To perform these reevaluations, the following EVAL macro runs its arguments down the macro tree:

 #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 to, evaluating the input of Just 365 times. In other words, calling EVAL (A (blah)) produce 365 copies of the word blah , and then finally unassessed B (blah) . This provides the basic basis for recursion, at least within a certain stack depth.

End detection

The next task 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:

 #define MAP_END(...) 

Evaluating this macro does nothing to complete the recursion.

To actually choose between two macros, use the following MAP_NEXT macro to compare one list item with a special end-of-list marker () . The macro returns MAP_END if the element matches, or the next parameter if the element is something 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 an element next to the macro MAP_GET_END . If this makes a macro call, everything is moved by the slot to the MAP_NEXT0 parameter list, which changes the output. The MAP_OUT prevents the preprocessor from evaluating the final result.

Introducing everything together

Using these things, you can now implement useful versions of macros A and B from the above example:

 #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 element of the list x . Then they look at the next element of the peek list to see if they should continue or not.

The final step is to bind everything together in the top-level MAP macro:

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

This macro places marker () at the end of the list, as well as an extra 0 for ANSI matching (otherwise the last iteration would have an illegal 0-long list). Then it goes through EVAL and returns the result.

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

+39
Nov 19 '12 at 17:59
source share

Using PPNARG , I wrote a set of macros to apply a macro to each argument in a macro. I call it a variable 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 allows us to count the number of arguments. Then we add this 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 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 result of gcc -E in the 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 is the last motive of all this. But it was not very useful.

+7
Jul 17 '11 at 5:35
source share

In C ++, without extensions, you can go Boost.Preprocessor and its sequences:

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

Using BOOST_PP_SEQ_FOR_EACH() in a sequence, you can repeat it and easily generate code that prints them.

Unconfirmed direct 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) } 
+4
Jul 15 2018-11-17T00:
source share

An old question, but I thought I would attach the solution I came up with to use Boost.Preprocessor without the ugly syntax (a)(b) .

Title:

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

Using:

 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 std::endl comma or something if you want it all on one line.

+3
Feb 03 '17 at 4:47 on
source share

The preprocessor is not powerful enough to do such things. However, you do not need a preprocessor that is bad. If all you want to do is assign variable names and their values ​​in a convenient way. You can have two simple macros:

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

Then you could use your intended use:

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

Will print

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

There are many ways to make this more powerful, but it works, allows you to beautifully print non-integer values, and this is a pretty simple solution.

+2
Jul 15 '11 at 12:57
source share

You can use Boost.PP (after adding Boost , boost folder to the list of included directories) to get macros for this. Here is 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!"); } 

Exit:

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

The BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__) converts the list of argument variables into Boost, the traditional way of expressing multiple arguments as a single argument, which looks like this: (item1)(item2)(item3) .

Not sure why he starts numbering the arguments in two. The documentation simply describes the first parameter as "the next available repetition of BOOST_PP_FOR ".

Here is another example that defines enum with the ability to write it as a string in ostream , which also includes Boost 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; } 

Exit:

 RedGreen Blue 
0
Jan 19 '19 at 1:33
source share



All Articles