#define Quote(x) #x
#define String(x) Quote(x)
#define SeparateImpl(first, second) first + second
#define Separate(pair) SeparateImpl(pair)
#define comma ,
Macro invocation proceeds as follows:
- Argument substitution (a.s), where if a parameter is mentioned in the replacement list and said parameter does not participate in a paste or stringification, it is fully expanded and said mentions of the parameter in the replacement list are substituted with the result.
- Stringification
- Pastes
- Rescan and further replacement (r.a.f.r.), where the resulting replacement list is rescanned, during which the macro's name is marked as invalid for expansion ("painted blue").
Here's how each case should expand:
Quote(1 comma 2)
a.s. no action (only mention of parameter is stringification). Stringification applies. Result: "1 comma 2"
.
String(1 comma 2)
a.s. applies; yielding Quote(1 , 2)
. During r.a.f.r., Quote
identified as a macro, but the argument count doesn't match. This is invalid. But see below.
SeparateImpl(1 comma 2)
Invalid macro call. The macro is being invoked with one argument, but it should have 2. Note that comma
being defined as a macro is irrelevant; at the level of macro invocation you're just looking at the tokens.
Separate(1 comma 2)
a.s. applies; yielding SeparateImpl(1 , 2)
. During r.a.f.r., SeparateImpl
is invoked... that invocation's a.s. applies, yielding 1 + 2
.
I tried this in VS2019.
I could tell from a glance it was VS something before 2020, where the walls tells me they're finally going to work on preprocessor compliance. VS in particular seems to have this strange state in which tokens with commas in them nevertheless are treated as single arguments (it's as if argument identification occurs before expansion but continues to apply or something); so in this case, 1 , 2
would be that strange thing in your String(1 comma 2)
call; i.e., Quote
is being called with 1 , 2
but in that case it's actually one argument.