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.
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.
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.
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);
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) }
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.
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
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.
Related
Ultimately, what I want is this: first, have a list of variable names declared as a C preprocessor macro; say, in test_cpp.c:
#define VARLIST \
var_one, \
var_two, \
var_three, \
var_four
These would eventually be actual variable names in code - but, of course, the preprocessor does not know (or even has a concept of) that at this time.
To make sure the macro has been parsed correctly, I use this command (awk to get rid of the preamble defines in the gcc -E preprocessor output):
$ gcc -E -dD test_cpp.c | awk 'BEGIN{prn=0} /# 1 "test_cpp.c"/ {prn=1} prn==1 {print}'
# 1 "test_cpp.c"
#define VARLIST var_one, var_two, var_three, var_four
So far, so good.
Now: second, I'd like to use this list - that is, I'd like to (pre)process it - and prepend and append characters to each element (token) of the VARLIST, so that I end up with the equivalent of the following macros:
#define VARLIST_QUOTED "var_one", "var_two", "var_three", "var_four"
#define VARLIST_PTR &var_one, &var_two, &var_three, &var_four
... which I could ultimately use in code as, say:
char varnames[][16] = { VARLIST_QUOTED };
( ... which then would end up like this in compiled code, inspected in debugger:
(gdb) p varnames
$1 = {"var_one\000\000\000\000\000\000\000\000",
"var_two\000\000\000\000\000\000\000\000",
"var_three\000\000\000\000\000\000",
"var_four\000\000\000\000\000\000\000"}
)
I'm guessing, at this time the preprocessor wouldn't know & is intended to be an "address-of" operator, although I think it has special handling for double quotes.
In any case, I think that such "lists" in the preprocessor are handled via Variadic Macros (The C Preprocessor), where there is an identifier __VA_ARGS__. Unfortunately, I do not understand this very well: I tried the first thing that came to mind - again, test_cpp.c:
#define VARLIST \
var_one, \
var_two, \
var_three, \
var_four
#define do_prepend(...) &##__VA_ARGS__
#define VARLIST_PTR do_prepend(VARLIST)
void* vars_ptr[] = { VARLIST_PTR };
Then if I run the preprocessor, I get this:
$ gcc -E -dD test_cpp.c | awk 'BEGIN{prn=0} /# 1 "test_cpp.c"/ {prn=1} prn==1 {print}' | sed '/^$/d;G'
test_cpp.c:8:25: error: pasting "&" and "VARLIST" does not give a valid preprocessing token
8 | #define do_prepend(...) &##__VA_ARGS__
| ^
test_cpp.c:9:21: note: in expansion of macro 'do_prepend'
9 | #define VARLIST_PTR do_prepend(VARLIST)
| ^~~~~~~~~~
test_cpp.c:11:22: note: in expansion of macro 'VARLIST_PTR'
11 | void* vars_ptr[] = { VARLIST_PTR };
| ^~~~~~~~~~~
# 1 "test_cpp.c"
#define VARLIST var_one, var_two, var_three, var_four
#define do_prepend(...) & ##__VA_ARGS__
#define VARLIST_PTR do_prepend(VARLIST)
void* vars_ptr[] = { &var_one, var_two, var_three, var_four };
It does show an error - but ultimately, the preprocessor did prepend a single ampersand & to the first variable in the array vars_ptr, as I wanted it to ...
The question is, then: can it prepend an ampersand & to all the entries in the list VARLIST without errors (and likewise, can it both prepend and append a double quote " to all the entries in the list VARLIST without errors) - and if so, how?
Sounds like a job for X macros:
#include <stdio.h>
#define VARS \
X(var_one) \
X(var_two) \
X(var_three) \
X(var_four)
// Define all the variables as ints (just for the example)
#define X(V) int V=0;
VARS
#undef X
// Define the array of variable pointers
#define X(V) &V,
void* vars_ptr[] = { VARS };
#undef X
// Define the array of variable name strings
#define X(V) #V,
const char *var_names[] = { VARS };
#undef X
// Set a few variable values and print out the name/value of all variables
int main()
{
var_one = 9;
var_two = 2;
for(unsigned i = 0; i < sizeof(var_names)/sizeof(var_names[0]); i++)
{
printf("%s=%d\n", var_names[i], *(int *)vars_ptr[i]);
}
return 0;
}
Ok, seems I found an answer, mostly thanks to Is it possible to iterate over arguments in variadic macros? (see also Expand a macro in a macro); this is test_cpp.c:
#define VARLIST \
var_one, \
var_two, \
var_three, \
var_four
// Make a FOREACH macro
#define FE_0(WHAT)
#define FE_1(WHAT, X) WHAT(X)
#define FE_2(WHAT, X, ...) WHAT(X)FE_1(WHAT, __VA_ARGS__)
#define FE_3(WHAT, X, ...) WHAT(X)FE_2(WHAT, __VA_ARGS__)
#define FE_4(WHAT, X, ...) WHAT(X)FE_3(WHAT, __VA_ARGS__)
#define FE_5(WHAT, X, ...) WHAT(X)FE_4(WHAT, __VA_ARGS__)
//... repeat as needed
#define GET_MACRO(_0,_1,_2,_3,_4,_5,NAME,...) NAME
#define FOR_EACH(action,...) \
GET_MACRO(_0,__VA_ARGS__,FE_5,FE_4,FE_3,FE_2,FE_1,FE_0)(action,__VA_ARGS__)
#define XSTR(x) STR(x)
#define STR(x) #x
// original: https://stackoverflow.com/a/11994395
//#define QUALIFIER(X) X::
//#define QUALIFIED(NAME,...) FOR_EACH(QUALIFIER,__VA_ARGS__)NAME
#define POINTERER(X) &X,
#define POINTERIFIED(NAME,...) FOR_EACH(POINTERER,__VA_ARGS__)NAME
// leave first argument (from original QUALIFIED) empty here!
#define vptrs POINTERIFIED(,VARLIST)
void* vars_ptr[] = { vptrs };
//#define DQUOTE " // no need for DQUOTE;
// if we just use the number/hash sign, X gets automatically quoted with double quotes!
// (don't forget the comma at the end!)
#define DQUOTERER(X) #X,
#define DQUOTERIFIED(NAME,...) FOR_EACH(DQUOTERER,__VA_ARGS__)NAME
// leave first argument (from original QUALIFIED) empty here!
#define vnames DQUOTERIFIED(,VARLIST)
char vars_names[][16] = { vnames };
... and this is the output of the preprocessor:
$ gcc -E -dD test_cpp.c | awk 'BEGIN{prn=0} /# 1 "test_cpp.c"/ {prn=1} prn==1 {print}' | sed '/^$/d;G'
# 1 "test_cpp.c"
#define VARLIST var_one, var_two, var_three, var_four
#define FE_0(WHAT)
#define FE_1(WHAT,X) WHAT(X)
#define FE_2(WHAT,X,...) WHAT(X)FE_1(WHAT, __VA_ARGS__)
#define FE_3(WHAT,X,...) WHAT(X)FE_2(WHAT, __VA_ARGS__)
#define FE_4(WHAT,X,...) WHAT(X)FE_3(WHAT, __VA_ARGS__)
#define FE_5(WHAT,X,...) WHAT(X)FE_4(WHAT, __VA_ARGS__)
#define GET_MACRO(_0,_1,_2,_3,_4,_5,NAME,...) NAME
#define FOR_EACH(action,...) GET_MACRO(_0,__VA_ARGS__,FE_5,FE_4,FE_3,FE_2,FE_1,FE_0)(action,__VA_ARGS__)
#define XSTR(x) STR(x)
#define STR(x) #x
#define POINTERER(X) &X,
#define POINTERIFIED(NAME,...) FOR_EACH(POINTERER,__VA_ARGS__)NAME
#define vptrs POINTERIFIED(,VARLIST)
void* vars_ptr[] = { &var_one,&var_two,&var_three,&var_four, };
#define DQUOTERER(X) #X,
#define DQUOTERIFIED(NAME,...) FOR_EACH(DQUOTERER,__VA_ARGS__)NAME
#define vnames DQUOTERIFIED(,VARLIST)
char vars_names[][16] = { "var_one","var_two","var_three","var_four", };
So, ultimately, I got my output as desired - and no preprocessor errors/warnings, as far as I can tell:
void* vars_ptr[] = { &var_one,&var_two,&var_three,&var_four, };
char vars_names[][16] = { "var_one","var_two","var_three","var_four", };
As already mentioned, you are more or less literally describing the purpose of "X macros".
An alternative, arguably somewhat more readable way of writing them is to first declare a macro list, then pass a macro to that list. As in
"here is a function-like macro, run it with all the arguments listed", rather than
"here is the function-like macro X, run macro X with all the arguments listed".
This allows you to give macros meaningful names, optionally group all macros belonging to the list somewhere, and it eliminates the need to #undef.
#include <stdio.h>
// note the absence of commas
#define VARLIST(X) \
X(var_one) \
X(var_two) \
X(var_three) \
X(var_four) \
int main (void)
{
char varnames[][16] =
{
#define VARLIST_QUOTED(name) #name, /* "stringification operator" */
VARLIST(VARLIST_QUOTED) /* no semicolon or comma here */
};
#define VARLIST_DECL_VARS(name) int name; /* declare a bunch of int */
VARLIST(VARLIST_DECL_VARS)
int* const pointers[] =
{
#define VARLIST_PTR(name) &name, /* declare an array of pointers to previous ints */
VARLIST(VARLIST_PTR)
};
var_two = 123;
printf("%s has value %d and address %p\n",
varnames[1],
var_two,
pointers[1]);
}
As I was searching for a way to do reflection in C, I found this answer https://stackoverflow.com/a/31908340/6784916.
In his answer, he refers to the metaresc library and he shows an example how to use it:
TYPEDEF_STRUCT (point_t,
double x,
double y
);
int main (int argc, char * argv[])
{
point_t point = {
.x = M_PI,
.y = M_E,
};
...
}
TYPEDEF_STRUCT is defined on line 237 of https://github.com/alexanderchuranov/Metaresc/blob/master/src/metaresc.h
I tried extracting the source of the macro but I'm not sure if I missed something because it's so complex.
#define TYPEDEF_STRUCT(...) P00_TYPEDEF (STRUCT, __VA_ARGS__)
#ifndef MR_MODE
#define MR_MODE_UNDEFINED
#define MR_MODE PROTO
#endif
#include <mr_protos.h>
#ifdef MR_MODE_UNDEFINED
#undef MR_MODE_UNDEFINED
#undef MR_MODE
#endif
#define MR_IS_MR_MODE_EQ_MR_MODE 0
#define P00_TYPEDEF(...) \
MR_IF_ELSE (MR_PASTE2 (MR_IS_MR_MODE_EQ_, MR_MODE)) \
(P00_TYPEDEF_MODE (MR_MODE, __VA_ARGS__)) \
(P00_TYPEDEF_MODE (PROTO, __VA_ARGS__) P00_TYPEDEF_MODE (DESC, __VA_ARGS__))
#define MR_IGNORE(...)
#define MR_IDENT(...) __VA_ARGS__
#define MR_IF_ELSE_CASE_0(...) __VA_ARGS__ MR_IGNORE
#define MR_IF_ELSE_CASE_1(...) MR_IDENT
#define MR_IF_ELSE(...) MR_PASTE2 (MR_IF_ELSE_CASE_, MR_IS_EQ_0 (__VA_ARGS__))
#define MR_PASTE2(...) MR_PASTE2_ (__VA_ARGS__)
#define MR_PASTE2_(_0, _1) _0 ## _1
#define MR_IS_0_EQ_0 ,
#define MR_IS_EQ_0_CASE_011 ,
#define MR_GET_SECOND(_0, ...) __VA_ARGS__
#define MR_IS_EQ_0(...) MR_IS_EQ_0_ (__VA_ARGS__) /* evaluate arguments */
#define MR_IS_EQ_0_(...) MR_IS_EQ_0__ ((__VA_ARGS__), (MR_PASTE2 (MR_IS_0_EQ_, __VA_ARGS__)))
#define MR_IS_EQ_0__(ARGS, ARGS_EQ_0) \
MR_HAS_COMMA (MR_PASTE4 (MR_IS_EQ_0_CASE_, \
/* test if there is just one argument, eventually a zero */ \
MR_HAS_COMMA ARGS, \
/* test if MR_IS_0_EQ_ together with the argument adds a comma */ \
MR_HAS_COMMA ARGS_EQ_0, \
/* test that there is nothing after comma */ \
MR_IS_EMPTY (MR_GET_SECOND ARGS_EQ_0)))
#define P00_TYPEDEF_MODE(P00_MODE, P00_TYPE, ...) \
P00_TYPEDEF_MODE_ (P00_MODE, P00_TYPE, \
ATTRIBUTES (P00_GET_ATTRIBUTES (__VA_ARGS__)), \
P00_GET_NON_ATTRIBUTES (__VA_ARGS__))
#define P00_TYPEDEF_MODE_(...) P00_TYPEDEF_MODE__ (__VA_ARGS__)
#define P00_TYPEDEF_MODE__(P00_MODE, P00_TYPE, ATTR_META_RES, ...) \
#define P00_GET_ATTRIBUTES(...) MR_FOREACH (P00_EXTRACT_ATTRIBUTES, __VA_ARGS__)
#define P00_GET_NON_ATTRIBUTES(...) MR_FOREACH (P00_EXTRACT_NON_ATTRIBUTES, __VA_ARGS__)
#define MR_FOREACH(X, ...) MR_PASTE2 (MR_FOREACH, MR_NARG (__VA_ARGS__)) (X, __VA_ARGS__)
#define MR_FOR(NAME, N, OP, FUNC, ...) MR_PASTE2 (MR_FOR, N) (NAME, OP, FUNC, __VA_ARGS__)
#define P00_TYPEDEF_ATTR_STRUCT TYPEDEF_ATTR
#define P00_TYPEDEF_ATTR_UNION TYPEDEF_ATTR
#define P00_TYPEDEF_ATTR_ENUM TYPEDEF_ATTR
#define P00_TYPEDEF_ATTR_CHAR_ARRAY(P00_MODE, P00_TYPE, ATTR_META_RES, P00_TYPE_NAME, SIZE, ...) MR_PASTE2 (MR_TYPEDEF_CHAR_ARRAY_, P00_MODE) (P00_TYPE_NAME, SIZE, MR_PASTE2 (P00_REMOVE_, ATTR_META_RES), __VA_ARGS__)
#define P00_TYPEDEF_ATTR_FUNC(P00_MODE, P00_TYPE, ATTR_META_RES, RET_TYPE, P00_TYPE_NAME, ARGS, ...) MR_PASTE2 (MR_TYPEDEF_FUNC_, P00_MODE) (RET_TYPE, P00_TYPE_NAME, ARGS, MR_PASTE2 (P00_REMOVE_, ATTR_META_RES), __VA_ARGS__)
#define P00_UNFOLD(PREFIX, P00_TYPE, P00_MODE, ...) MR_PASTE4 (PREFIX, P00_TYPE, _, P00_MODE) (__VA_ARGS__)
#define TYPEDEF_ATTR(P00_MODE, P00_TYPE, ATTR_META_RES, P00_TYPE_NAME, ...) \
P00_UNFOLD (MR_TYPEDEF_, P00_TYPE, P00_MODE, P00_TYPE_NAME, MR_PASTE2 (P00_GET_FIRST_, ATTR_META_RES)) \
MR_FOR ((P00_MODE, P00_TYPE_NAME), MR_NARG (__VA_ARGS__), MR_SER, MR_PASTE3 (P00_, P00_TYPE, _HANDLER), __VA_ARGS__) \
P00_UNFOLD (MR_END_, P00_TYPE, P00_MODE, P00_TYPE_NAME, MR_PASTE2 (P00_GET_OTHER_, ATTR_META_RES))
# define P00_IS_ATTRIBUTES_EQ_ATTRIBUTES(...) 0 /* help macro for ATTRIBUTES test IF clause */
#define P00_REMOVE_ATTRIBUTES(...) __VA_ARGS__
#define P00_GET_FIRST_ATTRIBUTES(FIRST, ...) FIRST /* extract typedef attributes */
#define P00_GET_OTHER_ATTRIBUTES(FIRST, ...) __VA_ARGS__ /* extract typedef meta information */
All I want to know is how can a macro call such as
TYPEDEF_STRUCT (point_t,
double x,
double y
);
expand to that
typedef struct point_t {
double x;
double y;
} point_t;
I'm not sure why do you want to decouple headers from the rest of the library. Probably first 700 lines of metaresc.h are used in TYPEDEF_STRUCT, so you will need most of this file. Even though it will not help you, just because result of the macro is typedef + meta-data required for serialization. Serialization functions are implemented in the library, so there is no sense to decouple meta-data generation from the code that uses this meta-data.
You could run example through preprocessor and evaluate outcome. This will give you and idea what do I mean as meta-data.
If you are really interested in understanding of TYPEDEF_STRUCT macro implementation details, then get ready for 100+ layers of nested macros. As a first exercises you could start here https://gustedt.wordpress.com/2010/06/08/detect-empty-macro-arguments/ and continue with other macro tricks from https://p99.gforge.inria.fr/
I'd like to know how to apply a unary function (or another macro) to variadic arguments of a macro, like
int f(int a);
#define apply(args...) <the magic>
apply(a, b, c)
which unrolls
f(a)
f(b)
f(c)
Note that the number of arguments is unknown.
The code below is working for what you've asked for with up to 1024 arguments and without using additional stuff like boost. It defines an EVAL(...) and also a MAP(m, first, ...) macro to do recursion and to use for each iteration the macro m with the next parameter first.
With the use of that, your apply(...) looks like: #define apply(...) EVAL(MAP(apply_, __VA_ARGS__)).
It is mostly copied from C Pre-Processor Magic. It is also great explained there. You can also download these helper macros like EVAL(...) at this git repository, there are also a lot of explanation in the actual code. It is variadic so it takes the number of arguments you want.
But I changed the FIRST and the SECOND macro as it uses a Gnu extension like it is in the source I've copied it from. This is said in the comments below by #HWalters:
Specifically, 6.10.3p4: "Otherwise [the identifier-list ends in a ...] there shall be more arguments in the invocation than there are parameters in the macro definition (excluding the ...)".
Main function part:
int main()
{
int a, b, c;
apply(a, b, c) /* Expands to: f(a); f(b); f(c); */
return 0;
}
Macro definitions:
#define FIRST_(a, ...) a
#define SECOND_(a, b, ...) b
#define FIRST(...) FIRST_(__VA_ARGS__,)
#define SECOND(...) SECOND_(__VA_ARGS__,)
#define EMPTY()
#define EVAL(...) EVAL1024(__VA_ARGS__)
#define EVAL1024(...) EVAL512(EVAL512(__VA_ARGS__))
#define EVAL512(...) EVAL256(EVAL256(__VA_ARGS__))
#define EVAL256(...) EVAL128(EVAL128(__VA_ARGS__))
#define EVAL128(...) EVAL64(EVAL64(__VA_ARGS__))
#define EVAL64(...) EVAL32(EVAL32(__VA_ARGS__))
#define EVAL32(...) EVAL16(EVAL16(__VA_ARGS__))
#define EVAL16(...) EVAL8(EVAL8(__VA_ARGS__))
#define EVAL8(...) EVAL4(EVAL4(__VA_ARGS__))
#define EVAL4(...) EVAL2(EVAL2(__VA_ARGS__))
#define EVAL2(...) EVAL1(EVAL1(__VA_ARGS__))
#define EVAL1(...) __VA_ARGS__
#define DEFER1(m) m EMPTY()
#define DEFER2(m) m EMPTY EMPTY()()
#define IS_PROBE(...) SECOND(__VA_ARGS__, 0)
#define PROBE() ~, 1
#define CAT(a,b) a ## b
#define NOT(x) IS_PROBE(CAT(_NOT_, x))
#define _NOT_0 PROBE()
#define BOOL(x) NOT(NOT(x))
#define IF_ELSE(condition) _IF_ELSE(BOOL(condition))
#define _IF_ELSE(condition) CAT(_IF_, condition)
#define _IF_1(...) __VA_ARGS__ _IF_1_ELSE
#define _IF_0(...) _IF_0_ELSE
#define _IF_1_ELSE(...)
#define _IF_0_ELSE(...) __VA_ARGS__
#define HAS_ARGS(...) BOOL(FIRST(_END_OF_ARGUMENTS_ __VA_ARGS__)())
#define _END_OF_ARGUMENTS_() 0
#define MAP(m, first, ...) \
m(first) \
IF_ELSE(HAS_ARGS(__VA_ARGS__))( \
DEFER2(_MAP)()(m, __VA_ARGS__) \
)( \
/* Do nothing, just terminate */ \
)
#define _MAP() MAP
#define apply_(x) f(x);
#define apply(...) EVAL(MAP(apply_, __VA_ARGS__))
To test macro expansion it is useful to use gcc with the command line argument -E:
$ gcc -E srcFile.c
because your're getting concrete error messages and understand what's going on.
Everything is possible in C if you throw enough ugly macros at it. For example, you can have an ugly function-like macro:
#include <stdio.h>
int f (int a)
{
printf("%d\n", a);
}
#define SIZEOF(arr) (sizeof(arr) / sizeof(*arr))
#define apply(...) \
{ \
int arr[] = {__VA_ARGS__}; \
for(size_t i=0; i<SIZEOF(arr); i++) \
{ \
f(arr[i]); \
} \
}
int main (void)
{
apply(1, 2, 3);
}
Notice that 1) This would be much better off as a variadic function, and 2) it would be even better if you get rid of the variadic nonsense entirely and simply make a function such as
int f (size_t n, int array[n])
I am trying to write a macro which counts the number of specific elements in a sequence. E.g. for the sequence (A)(B)(A) I want to get the count 2 for A.
Now taking the approach from https://stackoverflow.com/a/12540675/2525536 I end up with this code:
#define CAT(x, ...) CAT1(x, __VA_ARGS__)
#define CAT1(x, ...) CAT2(x, __VA_ARGS__)
#define CAT2(x, ...) x ## __VA_ARGS__
#define EXPAND(...) __VA_ARGS__
#define EAT(...)
#define DEFER(...) __VA_ARGS__ EAT()
#define OBSTRUCT(...) __VA_ARGS__ DEFER(EAT)()
#define SIZE(seq) CAT(SIZE_, SIZE_0 seq)
#define SIZE_0(...) SIZE_1
#define SIZE_1(...) SIZE_2
#define SIZE_2(...) SIZE_3
#define SIZE_3(...) SIZE_4
#define SIZE_SIZE_0 0
#define SIZE_SIZE_1 1
#define SIZE_SIZE_2 2
#define SIZE_SIZE_3 3
#define SIZE_SIZE_4 4
#define GET_FIRST(x) GET_FIRST2(GET_FIRST1 x)
#define GET_FIRST1(x) x, EAT()
#define GET_FIRST2(x) GET_FIRST3(x)
#define GET_FIRST3(x, ...) x
#define POP_FIRST(x) EAT x
#define EVAL(...) EVAL1(EVAL1(__VA_ARGS__))
#define EVAL1(...) EVAL2(EVAL2(__VA_ARGS__))
#define EVAL2(...) EVAL3(EVAL3(__VA_ARGS__))
#define EVAL3(...) EVAL4(EVAL4(__VA_ARGS__))
#define EVAL4(...) __VA_ARGS__
#define CHECK_N(x, n, ...) n
#define CHECK(...) CHECK_N(__VA_ARGS__, 0,)
#define CHECK_PROBE(x) x, 1,
#define NOT(x) CHECK(CAT(NOT_, x))
#define NOT_0 ~, 1,
#define COMPL(b) CAT(COMPL_, b)
#define COMPL_0 1
#define COMPL_1 0
#define BOOL(x) COMPL(NOT(x))
#define IIF(c) CAT(IIF_, c)
#define IIF_0(t, ...) __VA_ARGS__
#define IIF_1(t, ...) t
#define IF(c) IIF(BOOL(c))
#define WHEN(c) IF(c)(EXPAND, EAT)
#define INC(x) CAT(INC_, x)
#define INC_0 1
#define INC_1 2
#define INC_2 3
#define INC_3 4
#define INC_4 5
#define DEC(x) CAT(DEC_, x)
#define DEC_0 0
#define DEC_1 0
#define DEC_2 1
#define DEC_3 2
#define DEC_4 3
#define DEC_5 4
#define COUNT_IF(tpl, data, x) COUNT_IF1(0, SIZE(x), 0, tpl, data, x)
#define COUNT_IF1(i, n, count, tpl, data, x) \
IF(n) ( \
OBSTRUCT(CAT) ( \
COUNT_IF3_, \
tpl(i, data, GET_FIRST(x)) \
) ( \
OBSTRUCT(COUNT_IF2)() \
(INC(i), DEC(n), count, tpl, data, POP_FIRST(x)) /* call recursive */ \
) \
, count \
)
#define COUNT_IF2() COUNT_IF1
#define COUNT_IF3_0 EXPAND
#define COUNT_IF3_1 INC
#define A_EQUAL_A(...) 1
#define A_EQUAL_B(...) 0
#define COUNT_A(i, data, x) CAT(A_EQUAL_, x)(i)
EVAL(COUNT_IF(COUNT_A, ~, (A)))
EVAL(COUNT_IF(COUNT_A, ~, (B)))
EVAL(COUNT_IF(COUNT_A, ~, (A)(B)))
EVAL(COUNT_IF(COUNT_A, ~, (B)(A)))
EVAL(COUNT_IF(COUNT_A, ~, (A)(A)))
EVAL(COUNT_IF(COUNT_A, ~, (B)(B)))
EVAL(COUNT_IF(COUNT_A, ~, (A)(B)(A)))
This works already quite well for the first 4 examples but ends up with wrong macro name expandation for the others (where INC or EXPAND needs to be expanded more than once).
The reason for the is probably because macros are marked blue in C, which is why this works https://stackoverflow.com/a/11640759/2525536.
But I cannot find a workaround for this.
Anyone an idea?
As I already received some comments about using the C preprocessor for this task is not an appropiate way of handling this issue I still intent to go this way not only to minimize code dependencies but also to make the best out of what C offers in my code (which may not be obvious here).
Having this said I found out why the macro was not expanding correctly and also a way to make the preprocessor do this task correctly.
The issue was that the macro generated a construct like INC(EXPAND(INC(0))) from outside (first element) to inside (last element). The preprocessor tried to solve this while always only knowing all values until the current level/element. This turned into something like INC(COUNT_IF1(...)) on which point the preprocessor started to substitute INC for example.
The correct way of handling this was to make the preprocessor extend the macro from inside to outside.
Simplifying the code a little bit gives this solution:
#define CAT(x, ...) CAT1(x, __VA_ARGS__)
#define CAT1(x, ...) CAT2(x, __VA_ARGS__)
#define CAT2(x, ...) x ## __VA_ARGS__
#define EXPAND(...) __VA_ARGS__
#define EAT(...)
#define DEFER(...) __VA_ARGS__ EAT()
#define OBSTRUCT(...) __VA_ARGS__ DEFER(EAT)()
#define SIZE(seq) CAT(SIZE_, SIZE_0 seq)
#define SIZE_0(...) SIZE_1
#define SIZE_1(...) SIZE_2
#define SIZE_2(...) SIZE_3
#define SIZE_3(...) SIZE_4
#define SIZE_SIZE_0 0
#define SIZE_SIZE_1 1
#define SIZE_SIZE_2 2
#define SIZE_SIZE_3 3
#define SIZE_SIZE_4 4
#define GET_FIRST(x) GET_FIRST2(GET_FIRST1 x)
#define GET_FIRST1(x) x, EAT()
#define GET_FIRST2(x) GET_FIRST3(x)
#define GET_FIRST3(x, ...) x
#define POP_FIRST(x) EAT x
#define EVAL(...) EVAL1(EVAL1(__VA_ARGS__))
#define EVAL1(...) EVAL2(EVAL2(__VA_ARGS__))
#define EVAL2(...) EVAL3(EVAL3(__VA_ARGS__))
#define EVAL3(...) EVAL4(EVAL4(__VA_ARGS__))
#define EVAL4(...) __VA_ARGS__
#define CHECK_N(x, n, ...) n
#define CHECK(...) CHECK_N(__VA_ARGS__, 0,)
#define CHECK_PROBE(x) x, 1,
#define NOT(x) CHECK(CAT(NOT_, x))
#define NOT_0 ~, 1,
#define COMPL(b) CAT(COMPL_, b)
#define COMPL_0 1
#define COMPL_1 0
#define BOOL(x) COMPL(NOT(x))
#define IIF(c) CAT(IIF_, c)
#define IIF_0(t, ...) __VA_ARGS__
#define IIF_1(t, ...) t
#define IF(c) IIF(BOOL(c))
#define WHEN(c) IF(c)(EXPAND, EAT)
#define INC(x) CAT(INC_, x)
#define INC_0 1
#define INC_1 2
#define INC_2 3
#define INC_3 4
#define INC_4 5
#define DEC(x) CAT(DEC_, x)
#define DEC_0 0
#define DEC_1 0
#define DEC_2 1
#define DEC_3 2
#define DEC_4 3
#define DEC_5 4
#define IS_PAREN(x) CHECK(IS_PARENT1 x)
#define IS_PARENT1(...) CHECK_PROBE(~)
#define IS_COMPARABLE(x) IS_PAREN(CAT(COMPARE_, x)(()))
#define BITAND(lhs, rhs) CAT(CAT(CAT(BITAND_, lhs), _), rhs)
#define BITAND_0_0 0
#define BITAND_0_1 0
#define BITAND_1_0 0
#define BITAND_1_1 1
#define NOT_EQUAL(lhs, rhs) \
IIF(BITAND(IS_COMPARABLE(lhs), IS_COMPARABLE(rhs)))( \
NOT_EQUAL_HELPER, \
1 NULL \
)(lhs, rhs)
#define NOT_EQUAL_HELPER(lhs, rhs) IS_PAREN( \
CAT(COMPARE_, lhs)(CAT(COMPARE_, rhs))(()) \
)
#define EQUAL(lhs, rhs) COMPL(NOT_EQUAL(lhs, rhs))
#define COUNT_IF(match, x) COUNT_IF1(SIZE(x), match, 0, x)
#define COUNT_IF1(n, match, count, x) \
IF(n) ( \
OBSTRUCT(COUNT_IF2)()( \
DEC(n), \
match, \
IF(match(GET_FIRST(x)))(INC, EXPAND)(count), POP_FIRST(x) \
) /* call recursive */ \
, count \
)
#define COUNT_IF2() COUNT_IF1
#define COMPARE_A(x) x
#define EQUAL_A(x) EQUAL(x, A)
EVAL(COUNT_IF(EQUAL_A, (A)))
EVAL(COUNT_IF(EQUAL_A, (B)))
EVAL(COUNT_IF(EQUAL_A, (A)(B)))
EVAL(COUNT_IF(EQUAL_A, (B)(A)))
EVAL(COUNT_IF(EQUAL_A, (A)(A)))
EVAL(COUNT_IF(EQUAL_A, (B)(B)))
EVAL(COUNT_IF(EQUAL_A, (A)(B)(A)(A)))
P.S.: An 11 lines macro still seems simple enough to me to handle in terms of complexity and maintainability.
I saw this mechanism to simulate macro overloading recently here .
This is the code used for dispatching:
#define macro_dispatcher(func, ...) \
macro_dispatcher_(func, VA_NUM_ARGS(__VA_ARGS__))
#define macro_dispatcher_(func, nargs) \
macro_dispatcher__(func, nargs)
#define macro_dispatcher__(func, nargs) \
func ## nargs
I don't understand how this works. Why does it need the third macro macro_dispatcher__ to concatenate the arguments? I have tried to eliminate the third macro and replace it with the second one, resulting this code:
#include <stdio.h>
#include "va_numargs.h"
#define macro_dispatcher(func, ...) \
macro_dispatcher_(func, __VA_NUM_ARGS__(__VA_ARGS__))
#define macro_dispatcher_(func, nargs) \
func ## nargs
#define max(...) macro_dispatcher(max, __VA_ARGS__) \
(__VA_ARGS__)
#define max1(a) a
#define max2(a, b) ((a) > (b) ? (a) : (b))
int main()
{
max(1);
max(1, 2);
return 0;
}
va_numargs.h:
#ifndef _VA_NARG_H
#define _VA_NARG_H
#define __VA_NUM_ARGS__(...) \
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
#endif
Which evaluates to this:
int main()
{
max__VA_NUM_ARGS__(1) (1);
max__VA_NUM_ARGS__(1, 2) (1, 2);
return 0;
}
What is happening here? Why isn't __VA_NUM_ARGS__(__VA_ARGS__) replaced with the acutal number of arguments?
The extra step is needed because the token concatenation operator (##) suppresses macro expansion of its operands. Here's a simple example that demonstrates the problem:
#define macro macro_expansion
#define concat(x, y) x ## y
concat(macro, macro)
You might expect the above to produce macro_expansionmacro_expansion, but what you get instead is macromacro. While expanding the right-hand side of concat(), the preprocessor notices that x and y (which are set to macro here) are used as operands to ##, and so does not expand them further.
To work around this, we can add another step:
#define macro macro_expansion
#define concat(x, y) concat_(x, y)
#define concat_(x, y) x ## y
concat(macro, macro)
Now x and y are no longer operands of '##' in the right-hand side of concat(), and are therefore expanded. This means that we get concat_(macro_expansion, macro_expansion), which in turn expands to macro_expansionmacro_expansion.
The stringification operator (#) also suppresses macro expansion by the way.
Here's the relevant part of the C11 spec. (section 6.10.3.1):
A parameter in the replacement list, unless preceded by a # or ## preprocessing token or followed by a ## preprocessing token (see below), is replaced by the corresponding argument after all macros contained therein have been expanded.