使用编译器定义的宏连接

cma*_*t85 4 c c++ c-preprocessor

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

#define VERSION_NAMESPACE PROJECT_NAME ## Versioning
Run Code Online (Sandbox Code Playgroud)

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

error: 'PROJECT_NAMEVersioning' has not been declared
Run Code Online (Sandbox Code Playgroud)

但根据g++电话,PROJECT_NAME正在正确定义:

ccache g++ ... -DPROJECT_NAME=Syren_DLL ...
Run Code Online (Sandbox Code Playgroud)

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

Dan*_*her 12

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

#define P_VERSION2(foo)   foo ## Versioning
#define P_VERSION(foo)    P_VERSION2(foo)
#define VERSION_NAMESPACE P_VERSION(PROJECT_NAME)
Run Code Online (Sandbox Code Playgroud)

所以它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_DLLP_VERSION(PROJECT_NAME)被膨胀,从而导致P_VERSION2(Syren_DLL)其然后导致的串联Syren_DLLVersioning.