MSVC ++ variadicmacros扩展

所以我有一个在GCC中很好用的macros,但是在微软的C ++编译器中没有。 我希望有人可能知道一个解决方法,或者可以向我解释为什么这样做。

我相信这个macros并不完全是“标准的”,但它确实会帮助我。

这是macros的一个function性的例子:

#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) 

下面是我可以使用这个macros的方法:

 struct MyStructure { void Foo() { EXPAND_THESE(Property1, Property2, Property3, Property4) } Base * parent; } 

以下是GCC如何扩展上述内容:

 struct MyStructure { void Foo() { parent->GetProperty<Property1>(); parent->GetProperty<Property2>(); parent->GetProperty<Property3>(); parent->GetProperty<Property4>(); } Base * parent; } 

但是由于某种原因,微软把我所有的__VA_ARGS__扩展为一个参数:

 struct MyStructure { void Foo() { parent->GetProperty<Property1, Property2, Property3, Property4>(); } Base * parent; } 

有人知道这是为什么吗? 是否有一些技巧可以让微软像GCC一样扩展? 也许折腾一对额外的括号?

像这样的macros可以帮助我取代一堆“胶水”代码,但由于这个问题,我不能将它移动到我的VS项目。 任何帮助将不胜感激!

谢谢。

巧合的是,我今天碰巧遇到了这个问题,经过足够的努力,我觉得我已经find了一个解决scheme,为我自己的目的。 错误是MSVC将__VA_ARGS__视为参数列表中的单个标记。 但是你可以通过在macros调用参数列表中不直接使用它来解决这个问题。 这个评论意味着开始回答你的问题:

 #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 

但是,我怀疑你可能会遇到问题,确保完全扩展到你想要的实际“N”,而不是VA_NARGS_IMPL (arg1, arg2, 5, 4, 3, 2, 1) 。 我发现我的代码(看起来像你的代码)必须改为将MAC##code全部作为一个单元展开,然后必须单独与参数列表组合。 以下是我find的代码:

 #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) xy #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") } 

几个小时后,我的脑海里就解决了自己的问题,然后彻底解决了你的问题,我很抱歉地说。 :-)但是我认为这足以让你得到一些有效的工作。

我知道这个问题已经两年多了,但是我想我会试图给那些仍然绊倒在这个问题上的人提供一个更好的答案,就像我一样。

Jeff Walden的答案是有效的,但是你必须为每个你想要有可变参数的FOOmacros声明FOO_CHOOSE_HELPER / 1/2。 我开发了一个抽象层来解决这个问题。 考虑以下几点:

 #define GLUE(x, y) xy #define RETURN_ARG_COUNT(_1_, _2_, _3_, _4_, _5_, count, ...) count #define EXPAND_ARGS(args) RETURN_ARG_COUNT args #define COUNT_ARGS_MAX5(...) EXPAND_ARGS((__VA_ARGS__, 5, 4, 3, 2, 1, 0)) #define OVERLOAD_MACRO2(name, count) name##count #define OVERLOAD_MACRO1(name, count) OVERLOAD_MACRO2(name, count) #define OVERLOAD_MACRO(name, count) OVERLOAD_MACRO1(name, count) #define CALL_OVERLOAD(name, ...) GLUE(OVERLOAD_MACRO(name, COUNT_ARGS_MAX5(__VA_ARGS__)), (__VA_ARGS__)) 

有了这个架构,你可以像下面这样定义可变参数macros:

 #define ERROR1(title) printf("Error: %s\n", title) #define ERROR2(title, message)\ ERROR1(title);\ printf("Message: %s\n", message) #define ERROR(...) CALL_OVERLOAD(ERROR, __VA_ARGS__) #define ASSERT1(expr) singleArgumentExpansion(expr) #define ASSERT2(expr, explain) twoArgumentExpansion(expr, explain) #define ASSERT(...) CALL_OVERLOAD(ASSERT, __VA_ARGS__) 

用杰夫的答案,你将不得不定义macros如下:

 #define ERROR1(title) printf("Error: %s\n", title) #define ERROR2(title, message)\ ERROR1(title);\ printf("Message: %s\n", message) #define ERROR_CHOOSE_HELPER2(count) ERROR##count #define ERROR_CHOOSE_HELPER1(count) ERROR_CHOOSE_HELPER2(count) #define ERROR_CHOOSE_HELPER(count) ERROR_CHOOSE_HELPER1(count) #define ERROR(...) GLUE(ERROR_CHOOSE_HELPER(COUNT_ARGS_MAX5(__VA_ARGS__)),\ (__VA_ARGS__)) #define ASSERT1(expr) singleArgumentExpansion(expr) #define ASSERT2(expr, explain) twoArgumentExpansion(expr, explain) #define ASSERT_CHOOSE_HELPER2(count) ASSERT##count #define ASSERT_CHOOSE_HELPER1(count) ASSERT_CHOOSE_HELPER2(count) #define ASSERT_CHOOSE_HELPER(count) ASSERT_CHOOSE_HELPER1(count) #define ASSERT(...) GLUE(ASSERT_CHOOSE_HELPER(COUNT_ARGS_MAX5(__VA_ARGS__)),\ (__VA_ARGS__)) 

这不是什么大问题,但是我喜欢我的代码尽可能简洁。 如果您使用多个可变macros,它也会成倍地帮助您减less代码重复以及可能导致的复杂性。 据我所知,这个方法也是可移植的。 我已经在许多最常用的编译器上进行了testing,并且他们产生了相同的结果。

使用示例:

 int foo() { ASSERT(one); // singleArgumentExpansion(one) ASSERT(two, "foopy"); // twoArgumentExpansion(two, "foopy") ERROR("Only print a title"); ERROR("Error title", "Extended error description"); }