I am trying to define a C++ struct with variable members by a macro as part of an introspection metadata system. Given that I am creating a struct definition I cannot use compile-time templates (right?). Instead I create a variadic macro that should accept tuples of (type, name) but I have had problems in writing a macro that would expand and join the tuple with a symbol to achieve something like
JOIN(.,(a,A)) -> a.A
or
JOIN(&,(b,B)) -> b&B
.
I am working in Visual Studio 2017 and part of my confusion may be the inconsistency in the VA_ARGS expansion in MSVC and GCC: MSVC++ variadic macro expansion
My approach has been to write an unpacking macro that simply strips the parenthesis from the tuple and a join macro that would join the arguments by the desired string:
#define UNPACK(a, b) a, b
#define JOINAB(a, b, S) a S b
#define JOINTUPLE(S, ab) JOINAB(UNPACK ab, S)
But this does not work as it seems the macro is not evaluated in the right order. I have then tried to explicitly expand the arguments, e.g. #define EXPAND(args) args
, but to no luck.
I have finally found a workaround by embedding the argument parenthesis in the unpacking, thereby 'forcing' an order of evaluation:
#define EXPAND(...) __VA_ARGS__
#define UNPACK(a, b) (a, b
#define JOINAB(a, b, S) a S b
#define JOINTUPLE(S, ab) EXPAND(JOINAB UNPACK ab, S))
which works, but seems extremely hacky...
My questions are
- Is there a proper way to achieve the evaluation of the unpacked result?
- Why do I need the EXPAND? Without it the expression
JOINTUPLE(:,(a,B))
resolves toJOIN (a, B, :)
but why is this not further processed toa : B
? - Could there be a way to solve it with token-pasting operator?
S
would only exist in 3 variants.
EXPAND
does the trick, although I can't explain in detail how exactly it falls together. – Quentinstruct
means exactly the same asclass
. – TonyKEXPAND
which had the same effect on execution order. An explanation would make a better answer. – Paamand