这应该很简单,但我很难弄明白。我有PROJECT_NAME
作为编译器(g++
)-D
定义,我想将它与其他一些文本连接起来以形成命名空间名称。我目前的做法是:
#define VERSION_NAMESPACE PROJECT_NAME ## Versioning
对于我当前的项目,我希望VERSION_NAMESPACE
为Syren_DLLVersioning
。相反,我得到一个编译器错误:
error: 'PROJECT_NAMEVersioning' has not been declared
但根据g++
来电,正确定义了PROJECT_NAME
:
ccache g++ ... -DPROJECT_NAME=Syren_DLL ...
为什么在连接发生之前不会替换PROJECT_NAME
?
答案 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_NAME
,ccache g++ ... -DPROJECT_NAME=Syren_DLL ...
在展开PROJECT_NAME
时被Syren_DLL
替换,从而产生P_VERSION(PROJECT_NAME)
,然后导致P_VERSION2(Syren_DLL)
Syren_DLL
和Versioning
的串联。