宏名称出现在运算符旁边时不会展开##
,因此您需要更多的间接层:
#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 ...
,PROJECT_NAME
被替换为Syren_DLL
whenP_VERSION(PROJECT_NAME)
被扩展,导致P_VERSION2(Syren_DLL)
then 导致Syren_DLL
and的连接Versioning
。