使用编译器定义的宏连接

时间:2013-04-08 22:05:01

标签: c++ c c-preprocessor

这应该很简单,但我很难弄明白。我有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

1 个答案:

答案 0 :(得分: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段中,指定了

  

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

在替换列表上进行宏替换之前,将<{1}}预处理令牌旁边的预处理令牌连接起来。因此,##必须通过另一个(类似函数的)宏传递,以便替换它并与PROJECT_NAME连接。

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

  

在识别出类似函数的宏的调用参数之后,发生参数替换。替换列表中的参数除非前面有Versioning#预处理令牌或后跟##预处理令牌(见下文),否则将替换为在扩展了包含在其中的所有宏之后的相应参数。在被替换之前,每个参数的预处理标记都被完全宏替换,好像它们形成了预处理文件的其余部分;没有其他预处理令牌可供使用。

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

因此,在上文中,通过调用PROJECT_NAMEccache g++ ... -DPROJECT_NAME=Syren_DLL ...在展开PROJECT_NAME时被Syren_DLL替换,从而产生P_VERSION(PROJECT_NAME),然后导致P_VERSION2(Syren_DLL) Syren_DLLVersioning的串联。