Reputation: 1271
I was wondering if it is possible to construct A C99 macro that can either consume this syntax
MAGIC(a,b,(c,d),(e,(f,g))) // Expands to {{a}, {b}, {{c, d}}, {{e, {f,g}}}}
Or this more functional syntax
MAGIC( (a)(b)(c,d)(e,(f,g)) ) // Again should expands to {{a}, {b}, {{c, d}}, {{e, {f,g}}}}
If need be, I can assume a maximum nesting depth w.r.t. parenthesis of, say four levels.
I have played around with the solution presented here. But so far I haven't come vary far. I am trying to prevent having to create these generation like macro's to simulate recursion / iteration. But it is probably not possible to get around that.
Upvotes: 0
Views: 192
Reputation: 2674
Part of what makes this difficult for the C preprocessor is the fact that it requires recursing into parenthetical groupings. This in practice means that either we need to apply delays/evaluations at the outer levels or we need different macros at the inner ones. There's also an asymmetry in the specification at the recursion level which, while not hard per se, has to be accounted for.
If need be, I can assume a maximum nesting depth w.r.t. parenthesis of, say four levels.
I'll commit to this... and support four levels.
Definitions (using boost preprocessor compatible terms): A tuple is a preprocessing data structure comprised of a single parenthetical grouping whose elements are comma delimited. So (a,(b,c),d)
is a tuple with three elements... a
, (b,c)
, and d
. A sequence is a preprocessing data structure comprised of a series of contiguous elements each of which is surrounded by a parenthetical group. So the same elements listed above are represented by the sequence (a)((b,c))(d)
.
To process tuples using variadics, we need a counter, a glue macro, and a macro for each of the tuple sizes we want to support. If say we support tuples from 1 to 10 elements, and want to change it to support tuples from 1 to 20, we may have to change the counter and add 10 more macros. But if we support 4 levels of recursion using tuples alone, we need 4 times this many macros; that makes scaling more burdensome.
You show two forms of macros; both use tuples, the latter just uses a sequence at the top level. For consistency I'll just pick the first form. But for scalability, I'll add tuple processing just to convert a tuple to a sequence, showing 9 element tuple support... then, to support n element tuples, you only need to change the count and add n-9 macros.
We start with a basic glue macro, a count, and a sequence converter at a single level (PARN
):
#define GLUE(A,B) GLUE_I(A,B)
#define GLUE_I(A,B) A##B
#define PARN(...) GLUE(PARN_, COUNT (__VA_ARGS__)) (__VA_ARGS__)
#define PARN_9(A,B,C,D,E,F,G,H,I) (A)(B)(C)(D)(E)(F)(G)(H)(I)
...
#define PARN_2(A,B) (A)(B)
#define PARN_1(A) (A)
We want to either iterate another level or just process an element depending on if it's parenthetical. To do that I'll apply a pattern matcher based on an "indirect second macro". The idea is that you put a pattern in argument 1 and set up macros such that, if a pattern matches, it shifts tokens into argument 2; otherwise, your pattern just produces a bunch of tokens in argument 1 that get ignored. Here's the pattern matcher construct with a parenthetical detector (note that my second expands to empty if you pass it 1 argument):
#define SECOND(...) SECOND_I(__VA_ARGS__,,)
#define SECOND_I(A,B,...) B
#define CALLDETECT(...) ,
Given the link you provided you should already know how to apply sequences. I have a specific macro for sequence application:
#define PASTE_E(...) PASTE_E_I(__VA_ARGS__)
#define PASTE_E_I(...) __VA_ARGS__ ## E
...typical "simple" sequence processing toggles between two macros, call them A
and B
, until they hit the terminal... so they go A,B,A,B,...,E. But we want to leave a trail of comma delimited results with no extra commas. So we'll prepend the comma in each of these macros, but we'll start with another A that doesn't prepend; i.e., we're going to go A,B,C,B,C,...,E. We'll need four of these sets to (a) avoid blue paint, and (b) process differently at the top level. Here's the initial set:
#define BRACP0(X) {SECOND(CALLDETECT X MAGIC1)X}
#define BRAC0_A(X) BRACP0(X)BRAC0_B
#define BRAC0_B(X) ,BRACP0(X)BRAC0_C
#define BRAC0_C(X) ,BRACP0(X)BRAC0_B
#define BRAC0_BE
#define BRAC0_CE
This will chain the outer level MAGIC
to an inner level MAGIC1
; this set specifically adds an additional {}
pair per spec (BRACP0
). The rest of the sets look similar except for that {}
. But for the last level we want to call a simple bracify instead of the "next magic" (this too is scalable; you need one of these sets per level recursion depth supported). The bracify and level 4's "BRACP" just look like this:
#define BRACIFY(...) {__VA_ARGS__}
#define BRACP4(X) SECOND(CALLDETECT X BRACIFY)X
I have an "unwrap" per magic macro level:
#define MAGIC(...) {PASTE_E(MAGIC_U(BRAC0_A PARN(__VA_ARGS__)))}
#define MAGIC_U(...) __VA_ARGS__
#define MAGIC1(...) {PASTE_E(MAGIC1_U(BRAC1_A PARN(__VA_ARGS__)))}
#define MAGIC1_U(...) __VA_ARGS__
...
#define MAGIC4(...) {PASTE_E(MAGIC4_U(BRAC3_A PARN(__VA_ARGS__)))}
#define MAGIC4_U(...) __VA_ARGS__
Link to coliru.stacked-crooked demo.
I interpreted C99 as meaning standard here and this question as just an "is-it-possible"; as such, I haven't bothered to convert this to work with MSVS's preprocessor. It probably won't work with that as-is. Let me know if that bugs you at all.
Upvotes: 1