使用编译器定义的宏连接

Macro concatenation using compiler define

本文关键字:宏连 连接 定义 编译器      更新时间:2023-10-16

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

#define VERSION_NAMESPACE PROJECT_NAME ## Versioning

对于我目前的项目,我期望VERSION_NAMESPACESyren_DLLVersioning。相反,我得到一个编译器错误:

error: 'PROJECT_NAMEVersioning' has not been declared

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

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

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

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

#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 ...,当P_VERSION(PROJECT_NAME)展开时,PROJECT_NAMESyren_DLL取代,从而产生P_VERSION2(Syren_DLL),然后导致Syren_DLLVersioning的连接。