c++ - 如何正确扩展宏?

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

我需要能够扩展宏来构建我用于我的应用程序的 typedef。该宏构建了一个简单的 typedef。我的问题是 __VA_ARGS__(即你会在调用的后面丢失参数吗?)如何在传递给大量宏时起作用,以及如何知道何时需要进行另一次扫描以强制获得我认为的正确结果这可能是创建高阶 DERIVED 宏时问题的根源。

#define DERIVED0()          rtti::impl::BaseTypedefList<rtti::impl::null>
#define DERIVED1(T1)        rtti::impl::BaseTypedefList<T1, DERIVED0()>
#define DERIVED2(T1, T2)    rtti::impl::BaseTypedefList<T1, DERIVED1(T2)>
#define BUILD(count, ...) DERIVED##count( __VA_ARGS__ )

// inside the classes
#define CLASS_BODY(count, ...) typedef BUILD(count, __VA_ARGS__)    BaseClassList;

// example usages
CLASS_BODY(0)                   // WORKS
CLASS_BODY(1, MeshRenderer)     // WORKS
CLASS_BODY(2, Renderer, Object) // ERROR

最佳答案

C 预处理器(MSVC++、MSVC)的 Microsoft Visual Studio 版本有一个特殊的实体概念,否则将是一系列多个标记被分块到单个标记中。这在扩展可变参数宏时特别有用; __VA_ARGS__ 始终作为单个标记展开,即使该展开包含逗号。此行为是 Microsoft 预处理器特有的。

特别是,在调用 CLASS_BODY(2, Renderer, Object) 期间,您正在调用:

 BUILD(2, Renderer, Object)

从技术上讲,Renderer, Object 是一个标记,但在这一点 这并不重要。在此处的参数识别期间,class2...Renderer, Object 匹配。在参数替换期间,这变得有些奇怪:

DERIVED##count( Renderer, Object )

这看起来无害,但奇怪的是 Renderer, Object 统称为 一个标记。那里的逗号不作为分隔符处理。稍后可以看到其中的含义......在我们浏览粘贴之后:

DERIVED2( Renderer, Object )

...然后 DERIVED2 被调用。此处,参数标识将 T1Renderer, Object 匹配。 T2 悬空;它不匹配。这会产生预处理器错误。

这里的一般经验法则是在替换列表中使用 __VA_ARGS__ 的任何地方应用扩展步骤,至少如果您依赖于将多个参数解析为多个预处理器标记(除非出于某种奇怪的原因,您实际上想要这种行为,但在这种情况下,您将被锁定在 MSVS 特性中)。 99% 的时间这种形式的间接寻址会起作用:

#define EVAL(...) __VA_ARGS__
#define BUILD(count, ...) EVAL(DERIVED##count( __VA_ARGS__ ))

有时你可能不得不做这样的事情:

#define CALL(X,Y) X Y
#define BUILD(count, ...) CALL(DERIVED##count,( __VA_ARGS__))

在这种特殊情况下都有效。

关于c++ - 如何正确扩展宏?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46881690/

相关文章:

c - C宏中两个相邻的井号是什么意思?

c - GCC __func__ 被评估为空字符串

c++ - CMake 在 C++ 项目中包含目录和环境变量

c++ - 用于插入 moSTLy 排序数据的数据结构,它将保持排序顺序

c++ - "unexpected in macro definition"

macros - 为什么 foo 与这个 Racket 宏中的 (foo) 具有相同的值?

c++ - 预处理器 : Meaning of "The definition also permits you to split an identifier at any position and get exactly two tokens"

c++ - 为什么在这里调用 move 构造函数?

c++ - 在类范围内声明一个与类属性同名的局部变量

c++ - 定义预处理器宏