c++ - MSVC++ 可变宏扩展

标签 c++ visual-c++ c-preprocessor variadic-macros

所以我得到了一个在 GCC 中运行良好但在 Microsoft 的 C++ 编译器中运行不佳的宏。我希望有人知道解决方法,或者可以向我解释为什么会这样。

我确定这个宏不是完全“标准”的,但它确实可以帮助我。

这是宏的功能示例:

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

下面是我可能如何使用这个宏:

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 一样扩展它吗?也许多加几对括号?

像这样的宏确实可以帮助我替换一堆“胶水”代码,但由于这个问题,我无法将它移到我的 VS 项目中。任何帮助将不胜感激!

谢谢。

最佳答案

我知道这个问题已经有两年多了,但我想我会尝试为那些仍然像我一样偶然发现这个问题的人提供更完善的答案。

Jeff Walden 的答案有效,但您必须为每个想要具有可变参数的 FOO 宏声明 FOO_CHOOSE_HELPER/1/2 。我开发了一个抽象层来解决这个问题。请考虑以下事项:

#define GLUE(x, y) x y

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

使用这种架构,您可以这样定义可变参数宏:

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

根据 Jeff 的回答,您必须按如下方式定义宏:

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

这没什么大不了的,但是我希望我的代码尽可能简洁。如果您使用多个可变参数宏,它还可以成倍地帮助减少代码重复和可能导致的复杂情况。据我所知,这种方法也是可移植的。我已经在许多最常见的编译器上对其进行了测试,它们产生了相同的结果。

使用示例:

int foo()
{
    ASSERT(one); // singleArgumentExpansion(one)
    ASSERT(two, "foopy"); // twoArgumentExpansion(two, "foopy")

    ERROR("Only print a title");
    ERROR("Error title", "Extended error description");
}

关于c++ - MSVC++ 可变宏扩展,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38149968/

相关文章:

c++ - 使用汇编代码从内存复制到 C++ 中的寄存器

c++ - 链接器、库和目录信息

c++ - 将日期转换为 const char*

c++ - 具有 Windows 7 外观的消息框

c++ - C++中CURL下载的文件打不开

c++ - 调整属性表的错误

C风格: Macros or preprocessor?

c - 这两段代码的工作

c++ - 如何使用 boost::asio 发送原始二进制数据

c++ - OpenCV vs MATLAB(图像处理的形状测量功能)