如何强制附加预处理器宏扫描

How to force additional preprocessor macro scan

本文关键字:处理器 扫描 预处理 何强制      更新时间:2023-10-16

我有以下代码:

#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的函子传递一些额外的参数。

您需要了解

  1. 预处理器在将参数代入宏的展开之前,将参数完全展开到每个类似函数的宏,除非它们是###预处理运算符的操作数(在这种情况下,它们根本没有展开)。

  2. 通过执行宏扩展修改输入预处理令牌序列后,预处理器会自动重新扫描结果,以便执行进一步的宏扩展。

在您的潜在客户示例中,给定

#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宏的解决方案,从而保留您现在拥有的顶级接口。