使用编译器定义的宏连接
这应该很简单,但我很难弄明白。 我有PROJECT_NAME
作为编译器( g++
)- -D
define,我想将它与其他一些文本连接起来以形成命名空间名称。 我目前的做法是这样的:
#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
没有被替换?
当宏名称出现在##
运算符旁边时,它不会展开,因此您需要更多层间接:
#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_NAME
被Syren_DLL
替换,导致P_VERSION2(Syren_DLL)
,然后导致Syren_DLL
和Versioning
的连接。