MSVC++ variadic macro expansion
c++, c-preprocessor, variadic-macros, visual-c++
Solution
Coincidentally, I happened to run into this problem just today, and after enough effort I think I've found a solution for my own purposes. The bug is MSVC treats `__VA_ARGS__` as a single token in argument lists. But you can work around this by not using it directly within a macro call argument list. This comment suggests the start of an answer to your problems:
#define VA_NARGS(...) VA_NUM_ARGS_IMPL_((__VA_ARGS__, 5,4,3,2,1))
#define VA_NARGS_IMPL_(tuple) VA_NUM_ARGS_IMPL tuple
#define VA_NARGS_IMPL(_1,_2,_3,_4,_5,N,...) N
But then I suspect you'll likely run into the issue of making sure that gets fully expanded to the actual "N" you want, and not to `VA_NARGS_IMPL (arg1, arg2, 5, 4, 3, 2, 1)`, say. I found that my code (which looked like yours) had to change to expand `MAC##code` all as one unit, and then that had to be separately combined with the argument list. Here's the code that I found worked for me:
#define ASSERT_HELPER1(expr) singleArgumentExpansion(expr)
#define ASSERT_HELPER2(expr, explain) \
twoArgumentExpansion(expr, explain)
/*
* Count the number of arguments passed to ASSERT, very carefully
* tiptoeing around an MSVC bug where it improperly expands __VA_ARGS__ as a
* single token in argument lists. See these URLs for details:
*
* http://connect.microsoft.com/VisualStudio/feedback/details/380090/variadic-macro-replacement
* http://cplusplus.co.il/2010/07/17/variadic-macro-to-count-number-of-arguments/#comment-644
*/
#define COUNT_ASSERT_ARGS_IMPL2(_1, _2, count, ...) \
count
#define COUNT_ASSERT_ARGS_IMPL(args) \
COUNT_ASSERT_ARGS_IMPL2 args
#define COUNT_ASSERT_ARGS(...) \
COUNT_ASSERT_ARGS_IMPL((__VA_ARGS__, 2, 1, 0))
/* Pick the right helper macro to invoke. */
#define ASSERT_CHOOSE_HELPER2(count) ASSERT_HELPER##count
#define ASSERT_CHOOSE_HELPER1(count) ASSERT_CHOOSE_HELPER2(count)
#define ASSERT_CHOOSE_HELPER(count) ASSERT_CHOOSE_HELPER1(count)
/* The actual macro. */
#define ASSERT_GLUE(x, y) x y
#define ASSERT(...) \
ASSERT_GLUE(ASSERT_CHOOSE_HELPER(COUNT_ASSERT_ARGS(__VA_ARGS__)), \
(__VA_ARGS__))
int foo()
{
ASSERT(one); // singleArgumentExpansion(one)
ASSERT(two, "foopy"); // twoArgumentExpansion(two, "foopy")
}
My mind is too much mush after a few hours solving my own issues to then go and completely solve yours, I'm sorry to say. :-) But I think this is enough to get you to something that works, with a little work.
Problem
So I've got a macro that works nicely in GCC, but not in Microsoft's C++ Compiler. I'm hoping somebody might know of a workaround, or perhaps can explain to me why it behaves this way. I'm sure this macro isn't exactly "standard", but it would really help me out. Here is a functional example of the macro: ``` #define VA_NARGS_IMPL(_1, _2, _3, _4, _5, N, ...) N #define VA_NARGS(...) VA_NARGS_IMPL(__VA_ARGS__, 5, 4, 3, 2, 1) #define FULLY_EXPANDED(count, ...) \ MAC ## count (__VA_ARGS__) #define SEMI_EXPANDED(count, ...) FULLY_EXPANDED(count, __VA_ARGS__) #define EXPAND_THESE(...) SEMI_EXPANDED(VA_NARGS(__VA_ARGS__), __VA_ARGS__) #define ACTUAL_MACRO(x) parent->GetProperty<x>(); #define MAC1(a) ACTUAL_MACRO(a) #define MAC2(a,b) MAC1(a) ACTUAL_MACRO(b) #define MAC3(a,b,c) MAC2(a,b) ACTUAL_MACRO(c) #define MAC4(a,b,c,d) MAC3(a,b,c) ACTUAL_MACRO(d) #define MAC5(a,b,c,d,e) MAC4(a,b,c,d) ACTUAL_MACRO(e) ``` Here is how I might use this macro: ``` struct MyStructure { void Foo() { EXPAND_THESE(Property1, Property2, Property3, Property4) } Base * parent; } ``` Here's how GCC expands the above: ``` struct MyStructure { void Foo() { parent->GetProperty<Property1>(); parent->GetProperty<Property2>(); parent->GetProperty<Property3>(); parent->GetProperty<Property4>(); } Base * parent; } ``` But Microsoft for some reason expands all my __VA_ARGS__ as one argument: ``` struct MyStructure { void Foo() { parent->GetProperty<Property1, Property2, Property3, Property4>(); } Base * parent; } ``` Does anybody know why this is? Is there some trick I can pull to get Microsoft to expand this like GCC? Maybe toss in a couple extra pairs of parentheses? Macros like this could really help me out in replacing a bunch of "glue" code, but because of this problem, I can't move it into my VS project. Any help would be greatly appreciated! Thanks.