C预处理器如何实现宏仅展开一次?宏拼接结果不符合预期问题求解
问题原因
C 预处理器对类函数宏的展开遵循如下规则:如果宏参数没有被用于 # 字符串化或者 ## 拼接操作,参数会被完全递归展开后再替换到宏体中,最终再对宏体整体做展开。
你当前的代码展开流程为:
- 调用
MYCONCAT(FOO, 10)时,MYCONCAT的参数没有参与#/##操作,因此先完全展开参数:FOO展开为BAR,BAR继续展开为1。 - 展开后的参数替换到宏体得到
MYCONCAT2(1)(10)。 - 展开
MYCONCAT2(1)时,##直接拼接得到func_1,最终结果为func_1(10)。
解决方案
以下两种方案可以实现你要的 func_BAR(10) 效果:
方案1:无需修改现有FOO/BAR定义(依赖主流编译器扩展)
GCC、Clang、MSVC 均支持 push_macro/pop_macro 扩展,可以临时取消 BAR 的定义阻止其展开,拼接完成后再恢复,不影响其他代码使用 BAR:
#define BAR 1 // 来自我无法修改的外部头文件 #define FOO BAR // 拼接实现宏,直接做拼接操作,传入的参数不会被二次展开 #define MYCONCAT_IMPL(c, x) func_ ## c (x) #define MYCONCAT(c, x) \ _Pragma("push_macro(\"BAR\")") \ _Pragma("undef BAR") \ MYCONCAT_IMPL(c, x) \ _Pragma("pop_macro(\"BAR\")") // 调用后展开结果为 func_BAR(10) MYCONCAT(FOO, 10)
方案2:标准C实现(需修改FOO定义)
如果可以调整 FOO 的定义,将其改为惰性展开的带括号宏,就可以实现只展开一层的效果:
#define BAR 1 // 来自我无法修改的外部头文件 // 修改FOO为带括号的惰性展开形式 #define FOO() BAR #define MYCONCAT2(c) func_ ## c #define MYCONCAT(c, x) MYCONCAT2(c())(x) // 调用后展开结果为 func_BAR(10) MYCONCAT(FOO, 10)
原理:FOO 定义为需要触发才会展开的宏,在参数预展开阶段不会被自动展开,只有到拼接前才会展开一次为 BAR,而 BAR 作为 ## 操作的参数不会被二次展开为 1,因此可以得到预期结果。
内容的提问来源于stack exchange,提问作者sergej
相关产品推荐
相关产品推荐

