Here's one scalable approach. First, some general utility macros:
#define EVAL(...) __VA_ARGS__
#define VARCOUNT(...)
EVAL(VARCOUNT_I(__VA_ARGS__,9,8,7,6,5,4,3,2,1,))
#define VARCOUNT_I(_,_9,_8,_7,_6,_5,_4,_3,_2,X_,...) X_
#define GLUE(X,Y) GLUE_I(X,Y)
#define GLUE_I(X,Y) X##Y
#define FIRST(...) EVAL(FIRST_I(__VA_ARGS__,))
#define FIRST_I(X,...) X
#define TUPLE_TAIL(...) EVAL(TUPLE_TAIL_I(__VA_ARGS__))
#define TUPLE_TAIL_I(X,...) (__VA_ARGS__)
#define TRANSFORM(NAME_, ARGS_) (GLUE(TRANSFORM_,VARCOUNT ARGS_)(NAME_, ARGS_))
#define TRANSFORM_1(NAME_, ARGS_) NAME_ ARGS_
#define TRANSFORM_2(NAME_, ARGS_) NAME_(FIRST ARGS_),TRANSFORM_1(NAME_,TUPLE_TAIL ARGS_)
#define TRANSFORM_3(NAME_, ARGS_) NAME_(FIRST ARGS_),TRANSFORM_2(NAME_,TUPLE_TAIL ARGS_)
#define TRANSFORM_4(NAME_, ARGS_) NAME_(FIRST ARGS_),TRANSFORM_3(NAME_,TUPLE_TAIL ARGS_)
#define TRANSFORM_5(NAME_, ARGS_) NAME_(FIRST ARGS_),TRANSFORM_4(NAME_,TUPLE_TAIL ARGS_)
#define TRANSFORM_6(NAME_, ARGS_) NAME_(FIRST ARGS_),TRANSFORM_5(NAME_,TUPLE_TAIL ARGS_)
#define TRANSFORM_7(NAME_, ARGS_) NAME_(FIRST ARGS_),TRANSFORM_6(NAME_,TUPLE_TAIL ARGS_)
#define TRANSFORM_8(NAME_, ARGS_) NAME_(FIRST ARGS_),TRANSFORM_7(NAME_,TUPLE_TAIL ARGS_)
#define TRANSFORM_9(NAME_, ARGS_) NAME_(FIRST ARGS_),TRANSFORM_8(NAME_,TUPLE_TAIL ARGS_)
Semantically, VARCOUNT
counts arguments; GLUE
is a typical indirect paster; FIRST
extracts the first argument; EVAL
expands to its arguments (with intent to evaluate), and TUPLE_TAIL
returns the tail of a tuple (i.e., it discards the first argument).
TRANSFORM
here is the main idea; TRANSFORM(FOO,(X,Y,Z))
takes a tuple (X,Y,Z)
to (FOO(X),FOO(Y),FOO(Z))
.
This in place, here's the special purpose code:
#define Z_ARG(X) GLUE(X,Arg)
#define MAKE_INITIALIZER(...) { __VA_ARGS__ }
#define FIELD_DECLARATION(FNAME_, ...)
GLUE(field_, FNAME_) = EVAL(MAKE_INITIALIZER TRANSFORM(Z_ARG, (__VA_ARGS__)));
Given the above, this should be readable, but just to explain... Z_ARG
pastes Arg
to an item; MAKE_INITIALIZER
transforms a preprocessor tuple to an initialization list; and FIELD_DECLARATION
is your macro. Note that EVAL
wraps the MAKE_INITIALIZER
/transformed tuple so it will actually call that macro.
Note: Moved EVAL
to the top and used it in a few more places, such that this will work in MSVC as well.
Demonstration, original code
Demonstration, current code