使用编译器定义的宏连接

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

 #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的连接。