如何强制附加预处理器宏扫描
How to force additional preprocessor macro scan
我有以下代码:
#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;
}
由于我很懒,我做了一些预处理器魔术。并将其实现为:
#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
的函子传递一些额外的参数。
您需要了解
-
预处理器在将参数代入宏的展开之前,将参数完全展开到每个类似函数的宏,除非它们是
#
或##
预处理运算符的操作数(在这种情况下,它们根本没有展开)。 -
通过执行宏扩展修改输入预处理令牌序列后,预处理器会自动重新扫描结果,以便执行进一步的宏扩展。
在您的潜在客户示例中,给定
#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
宏的实际工作方式,那么我敢打赌,您可以想出一个接受额外前导参数的变体,通过该变体,您可以向每个扩展传递(相同的)name
。
或者,这就是X宏被发明的问题。我看到已经在评论中提出了这一备选方案。您甚至可以谨慎地构建一个在内部使用X宏的解决方案,从而保留您现在拥有的顶级接口。
- #定义c-预处理器常量..我做错了什么
- 预处理器:插入结构名称中的前一个行号
- 如何在c++中实现处理器调度模拟器
- C/C++预处理器是否可以检测一些编译器选项
- 要与"if constexpr"一起使用的编译时消息(在预处理器之后)
- 检查不带转换的扫描格式
- 在clang++预处理器中确定gcc工具链版本
- 不同/较旧的处理器运行c++代码的方式是否不同
- 用于交叉编译和CMake的预处理器宏的单元测试
- 有没有办法在从编译器获取参数时避免预处理器宏?
- 如何比较两个同名的预处理器宏?
- 从预处理器获取 Windows 版本(C++ Win32)
- 如何摆脱为条件编译定义预处理器宏的需要?
- C 预处理器 - 现有定义的预置路径
- VS2015 预处理器定义与点
- 不带预处理器的调用方法/文件的文件名/行号
- 获取文件数据预处理器宏
- 有没有办法在 c++ 中拥有条件预处理器
- 如何在编译时定义C++预处理器指令的值?
- 如何强制附加预处理器宏扫描