我有以下代码:
#define FOO_BAR x
#define FOO(x) FOO_BAR
我确实希望 FOO(2)
扩展为 2
,但我得到的是 x
。我尝试使用 EXPAND
宏来强制进行额外扫描:
#define FOO_BAR x
#define EXPAND(x) x
#define FOO(x) EXPAND(FOO_BAR)
请注意,这是故意的,FOO_BAR
不接受 x
作为参数。基本上,我不能将 x 传递给 FOO_BAR
。
但是效果不是很好。有什么想法吗?
我希望它适用于任何编译器(MSVC、gcc、clang)。
我到底想完成什么
我的最终目标是为 OpenGL 创建类型安全的枚举。所以,我需要做从我的安全枚举到不安全枚举的映射。所以我有类似的东西:
enum class my_enum {
foo,
bar
}
GLenum my_enum2gl(my_enum e) {
switch (e) {
case my_enum::foo: return GL_FOO;
case my_enum::bar: return GL_BAR;
}
return GL_NONE;
}
因为我很懒,我做了一些preprocessor magic .并将其实现为:
#define PP_IMPL_ENUM_VALUE(enum_pair) __PP_EVAL(__PP_IMPL_ENUM_VALUE enum_pair)
#define __PP_IMPL_ENUM_VALUE(cpp_enum, gl_enum) cpp_enum,
#define PP_IMPL_CONVERT(enum_pair) __PP_EVAL(__PP_IMPL_CONVERT enum_pair)
#define __PP_IMPL_CONVERT(cpp_enum, gl_enum) case name::cpp_enum: return gl_enum;
#define DEF_STATE_ENUM(name, ...) \
enum name { \
PP_FOR_EACH(PP_IMPL_ENUM_VALUE, ##__VA_ARGS__) \
}; \
namespace detail { \
GLenum name ## 2gl(name e) { \
switch(e) { \
__PP_EVAL(PP_FOR_EACH(PP_IMPL_CONVERT, ##__VA_ARGS__)) \
default: \
assert(!"Unknown value"); \
return GL_NONE; \
} \
} \
}
DEF_STATE_ENUM(my_enum,
(foo, GL_FOO),
(bar, GL_BAR)
)
问题是 __PP_IMPL_CONVERT
使用了未展开的 name
。将 x
传递给 FOO_BAR
意味着我将一些额外的参数传递给 PP_FOR_EACH
的仿函数。
最佳答案
你需要明白
预处理器在将参数代入宏的扩展之前完全扩展每个类函数宏的参数,除非它们是
#
或##
的操作数> 预处理运算符(在这种情况下它们根本不会扩展)。通过执行宏扩展修改输入预处理 token 序列后,预处理器自动重新扫描结果以执行进一步的宏扩展。
然后,在您的主要示例中,给定
#define FOO_BAR x #define FOO(x) FOO_BAR
和一个宏调用
FOO(2)
,预处理器首先宏扩展参数 2
,保持不变,然后用扩展替换宏调用。由于展开实际上并没有首先使用参数,因此初始结果是
FOO_BAR
预处理器然后重新扫描它,将 FOO_BAR
识别为类对象宏的标识符,并用它的扩展替换它,产生
x
,如您所见。这是符合标准的 C 预处理器的正常和预期行为,据我所知,C++ 对其预处理器具有等效规范。
插入EXPAND()
宏调用没有帮助,因为问题不在于扩展宏失败,而是宏扩展的时间和上下文。最终,当宏 FOO(x)
的替换文本不使用宏参数 x
时,与该参数关联的实际参数具有对扩容结果没有影响。
我无法完全解决您的实际代码,因为它主要依赖于您未提供其定义的宏 PP_FOR_EACH()
。据推测,该宏的名称传达了要点,但正如您所见,细节很重要。但是,如果您实际上了解您的 PP_FOR_EACH
宏是如何工作的,那么我敢打赌您可以想出一个接受额外前导参数的变体,通过它您可以传达(相同的)每个扩展的名称
。
或者,这是 X Macros 需要解决的问题被发明。我看到评论中已经提出了替代方案。您甚至可以——小心地——构建一个内部使用 X 宏的解决方案,以保留您现在拥有的顶级接口(interface)。
关于c++ - 如何强制执行额外的预处理器宏扫描,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46731209/