我需要能够扩展宏来构建我用于我的应用程序的 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
是一个标记,但在这一点 这并不重要。在此处的参数识别期间,class
与 2
和 ...
与 Renderer, Object
匹配。在参数替换期间,这变得有些奇怪:
DERIVED##count( Renderer, Object )
这看起来无害,但奇怪的是 Renderer, Object
统称为 一个标记。那里的逗号不作为分隔符处理。稍后可以看到其中的含义......在我们浏览粘贴之后:
DERIVED2( Renderer, Object )
...然后 DERIVED2
被调用。此处,参数标识将 T1
与 Renderer, 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/