4

这应该很简单,但我正在努力弄清楚。我有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在连接发生之前不被替换?

4

1 回答 1

12

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

#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_NAMEVersioning

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

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

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

所以在上面,调用ccache g++ ... -DPROJECT_NAME=Syren_DLL ...,PROJECT_NAME被替换为Syren_DLLwhenP_VERSION(PROJECT_NAME)被扩展,导致P_VERSION2(Syren_DLL)then 导致Syren_DLLand的连接Versioning

于 2013-04-08T22:08:46.743 回答