c++ - 如何强制执行额外的预处理器宏扫描

标签 c++ c c-preprocessor

我有以下代码:

#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 的仿函数。

最佳答案

你需要明白

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

  2. 通过执行宏扩展修改输入预处理 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/

相关文章:

c++ - 在自动化服务部署时设置用户服务运行的最佳实践是什么?

c++ - 为什么为具有引用成员变量的类生成默认复制构造函数?

c++ - 如何在 Mac OS X 上监视目录中的更改?

c - X-NUCLEO_NFC05A1有读取NFC-A(ISO14443A)标签的功能吗?

c++ - 如何获取字符串的一部分

c++ - 使用 typedef 和 #define 的不同原型(prototype)

c++ - 如何在多个文件中定义一个外部函数并指定使用哪个定义?

c++ - 使用 KNN 分类器进行数字识别前的预处理

c - 如何从具有 int 值的文本文件创建一组数组?

c++ - 使用 CMake 获取预处理器定义值