【问题标题】:Macro concatenation using compiler define使用编译器定义的宏连接
【发布时间】:2013-04-08 22:05:01
【问题描述】:

这应该很简单,但我很难弄清楚。我有 PROJECT_NAME 作为编译器 (g++) -D 定义,我想将它与其他一些文本连接起来以形成命名空间名称。我目前的做法是这样的:

#define VERSION_NAMESPACE PROJECT_NAME ## Versioning

对于我当前的项目,我希望 VERSION_NAMESPACE 是 Syren_DLLVersioning。相反,我得到一个编译器错误:

error: 'PROJECT_NAMEVersioning' has not been declared

但根据g++ 调用,PROJECT_NAME 的定义正确:

ccache g++ ... -DPROJECT_NAME=Syren_DLL ...

为什么PROJECT_NAME 在连接发生之前没有被替换?

【问题讨论】:

    标签: c++ c c-preprocessor


    【解决方案1】:

    宏名称出现在## 运算符旁边时不会展开,因此您需要更多的间接层:

    #define P_VERSION2(foo)   foo ## Versioning
    #define P_VERSION(foo)    P_VERSION2(foo)
    #define VERSION_NAMESPACE P_VERSION(PROJECT_NAME)
    

    这样PROJECT_NAME 被扩展为P_VERSION 的参数,然后连接到P_VERSION2。

    在第 16.3.3 节 [cpp.concat] 第 3 段中指定

    对于类似对象和类似函数的宏调用,在重新检查替换列表以替换更多宏名称之前,替换列表(不是来自参数)中的每个 ## 预处理标记实例都会被删除,并且前面的预处理标记与下面的预处理标记连接。

    与## 预处理标记相邻的预处理标记在替换列表上完成宏替换之前连接。因此,PROJECT_NAME 必须通过另一个(类似函数的)宏才能被替换并与Versioning 连接。

    但在 16.3.1 [cpp.subst] 的第 1 段中,标准指定(强调由我添加)

    在确定了调用类函数宏的参数后,将进行参数替换。替换列表中的参数,除非前面有 # 或 ## 预处理标记或后跟 ## 预处理标记(见下文),在所有宏之后被相应的参数替换其中包含的内容已扩展。在被替换之前,每个参数的预处理标记都被完全宏替换,就好像它们形成了预处理文件的其余部分一样;没有其他可用的预处理令牌。

    如果与## 预处理标记相邻,则宏参数不会受到进一步的宏扩展。因此,接收PROJECT_NAME作为参数的类函数宏不能直接将其参数与Versioning连接,而是要扩展PROJECT_NAME,它必须调用另一个类函数宏,最后进行连接。

    因此,在上面调用ccache g++ ... -DPROJECT_NAME=Syren_DLL ... 时,PROJECT_NAME 被替换为Syren_DLL,当P_VERSION(PROJECT_NAME) 被扩展时,导致P_VERSION2(Syren_DLL),然后导致Syren_DLL 和Versioning 的串联.

    【讨论】:

    • 我不确定这是否严格合法(它可能是编译器特定的行为)...请您在这方面设置标准吗?
    • 糟糕,我没有使用足够的预处理器。原版缺少一层间接性。感谢您让我查找它。
    • 幸运的是我放弃了问,我绝对不会猜到这个。谢谢!
    猜你喜欢
    • 2021-02-07
    • 2015-10-23
    • 1970-01-01
    • 2015-08-02
    • 1970-01-01
    • 2011-03-03
    • 2015-09-23
    • 1970-01-01
    相关资源
    最近更新 更多