0

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

  1. Is there a proper way to achieve the evaluation of the unpacked result?
  2. Why do I need the EXPAND? Without it the expression JOINTUPLE(:,(a,B)) resolves to JOIN (a, B, :) but why is this not further processed to a : B?
  3. Could there be a way to solve it with token-pasting operator? S would only exist in 3 variants.
Sisir
  • 4,584
  • 4
  • 26
  • 37
Paamand
  • 626
  • 1
  • 6
  • 17
  • Look into [Boost.Preprocessor](https://www.boost.org/doc/libs/1_71_0/libs/preprocessor/doc/index.html). – Angew is no longer proud of SO Oct 01 '19 at 13:22
  • 1
    One more `EXPAND` [does the trick](http://coliru.stacked-crooked.com/a/d8a896ad3563d403), although I can't explain in detail how exactly it falls together. – Quentin Oct 01 '19 at 13:27
  • Sure you can use compile-time templates. In C++, `struct` means exactly the same as `class`. – TonyK Oct 01 '19 at 14:20
  • @Quentin Spot on. You pulled the parenthesis inside the `EXPAND` which had the same effect on execution order. An explanation would make a better answer. – Paamand Oct 01 '19 at 14:21
  • @TonyK As stated, the idea is to create a struct/class with from a member definition list and register those in a meta-structure. Templates will not be able to add members to the class. – Paamand Oct 01 '19 at 14:23
  • @Paamand that's why it's only a comment -- I don't *have* an explanation ;) – Quentin Oct 01 '19 at 14:28
  • 1
    @Quentin I tested your suggestion (which works) and posted the full conclusion. However I keep the question open in hope of a better explanation. – Paamand Oct 01 '19 at 14:35

1 Answers1

0

Following the comment by Quentin, a solution is to include the argument parenthesis in the EXPAND macro:

#define EXPAND(...) __VA_ARGS__
#define UNPACK(a, b) a, b
#define JOINAB(a, b, S) a S b
#define JOINTUPLE(S, ab) EXPAND(JOINAB EXPAND((UNPACK ab, S)))

The full solution with variadic arguments follows:

#define JOIN1(S, aA) JOINTUPLE(S, aA)
#define JOIN2(S, aA, bB) JOINTUPLE(S, aA), JOINTUPLE(S, bB)
#define JOIN3(S, aA, bB, cC) JOINTUPLE(S, aA), JOINTUPLE(S, bB), JOINTUPLE(S, cC)
#define JOINN(_1, _2, _3, N, ...) JOIN##N
#define JOIN(S,...) _EXPAND(JOINN(__VA_ARGS__,3,2,1)(S,__VA_ARGS__))

which enables the following syntax:

JOIN(:, (a, A)) // Expands to "a : A"
JOIN(., (a, A), (b, B)) // Expands to "a . A, b . B"

An answer to why it is needed to manipulate the syntax with this EXPAND, if it is compiler specific/portable and weather it is good/bad practice would be appreciated.

Paamand
  • 626
  • 1
  • 6
  • 17