VS2022中部分预处理器宏重载失效原因咨询
可变参数宏生成变量名失效问题排查
需求与实现
我在VS2022中编写了一组可变参数宏,参考《Overloading Macro on Number of Arguments》的技巧,实现三个功能:
- 生成带作用域的类型本身
- 生成作用域类型的字符串字面量
- 将作用域中的
::替换为_以生成变量名
宏定义如下:
#define GET_ARGS(_1, _2, _3, _4, _5, ARGS, ...) ARGS // 生成带作用域的类型 #define SCOPE1(_1) _1 #define SCOPE2(_1, ...) _1##::## SCOPE1(__VA_ARGS__) #define SCOPE3(_1, ...) _1##::## SCOPE2(__VA_ARGS__) #define SCOPE4(_1, ...) _1##::## SCOPE3(__VA_ARGS__) #define SCOPE5(_1, ...) _1##::## SCOPE4(__VA_ARGS__) #define GET_SCOPE(...) GET_ARGS(__VA_ARGS__, SCOPE5, SCOPE4, SCOPE3, SCOPE2, SCOPE1)(__VA_ARGS__) // 生成作用域类型的字符串字面量 #define STR1(_1) #_1 #define STR2(_1, ...) #_1 "::" STR1(__VA_ARGS__) #define STR3(_1, ...) #_1 "::" STR2(__VA_ARGS__) #define STR4(_1, ...) #_1 "::" STR3(__VA_ARGS__) #define STR5(_1, ...) #_1 "::" STR4(__VA_ARGS__) #define GET_STR(...) GET_ARGS(__VA_ARGS__, STR5, STR4, STR3, STR2, STR1)(__VA_ARGS__) // 生成替换::为_的变量名 #define VAR1(_1) _1 #define VAR2(_1, ...) _1##_## VAR1(__VA_ARGS__) #define VAR3(_1, ...) _1##_## VAR2(__VA_ARGS__) #define VAR4(_1, ...) _1##_## VAR3(__VA_ARGS__) #define VAR5(_1, ...) _1##_## VAR4(__VA_ARGS__) #define GET_VAR(...) GET_ARGS(__VA_ARGS__, VAR5, VAR4, VAR3, VAR2, VAR1)(__VA_ARGS__)
测试代码
namespace ns1 { namespace ns2 { struct cls {}; } } GET_SCOPE(int) i; GET_SCOPE(ns1, ns2, cls) v; const char* sv = GET_STR(ns1, ns2, cls); int GET_VAR(ns1, ns2, cls);
问题现象
前三个测试案例正常展开,得到预期结果:
int i; ns1::ns2::cls v; const char* sv = "ns1" "::" "ns2" "::" "cls";
但最后一个GET_VAR(ns1, ns2, cls)被错误解析为:
int ns1_VAR2(ns2, cls);
已启用/Zc:preprocessor确保预处理器合规,替换_为其他字符也无法解决问题。
问题原因
问题出在VAR系列宏的##连接符使用方式上:
预处理阶段,##连接符会抑制其左右宏参数的预展开。以VAR2(_1, ...)为例,_1##_## VAR1(__VA_ARGS__)中的VAR1(__VA_ARGS__)不会先被展开,而是直接将VAR1作为标识符的一部分与前面的_1_连接,最终导致后续的宏被当作函数声明的一部分,而非继续展开。
而SCOPE和STR系列宏不受影响的原因:
- SCOPE宏中
_1##::## SCOPE1(...)的写法存在冗余(::是独立的预处理token,无需##连接),但VS预处理器处理时,::的存在让预处理器识别到这是作用域符号,从而正常展开后续的SCOPE1宏; - STR宏中没有使用
##连接符,STR1(__VA_ARGS__)会被正常优先展开。
解决方法
给VAR系列宏增加一层中间展开宏,确保嵌套的宏先被展开后再执行##连接:
#define GET_ARGS(_1, _2, _3, _4, _5, ARGS, ...) ARGS // 生成带作用域的类型(优化冗余的##) #define SCOPE1(_1) _1 #define SCOPE2(_1, ...) _1::SCOPE1(__VA_ARGS__) #define SCOPE3(_1, ...) _1::SCOPE2(__VA_ARGS__) #define SCOPE4(_1, ...) _1::SCOPE3(__VA_ARGS__) #define SCOPE5(_1, ...) _1::SCOPE4(__VA_ARGS__) #define GET_SCOPE(...) GET_ARGS(__VA_ARGS__, SCOPE5, SCOPE4, SCOPE3, SCOPE2, SCOPE1)(__VA_ARGS__) // 生成作用域类型的字符串字面量 #define STR1(_1) #_1 #define STR2(_1, ...) #_1 "::" STR1(__VA_ARGS__) #define STR3(_1, ...) #_1 "::" STR2(__VA_ARGS__) #define STR4(_1, ...) #_1 "::" STR3(__VA_ARGS__) #define STR5(_1, ...) #_1 "::" STR4(__VA_ARGS__) #define GET_STR(...) GET_ARGS(__VA_ARGS__, STR5, STR4, STR3, STR2, STR1)(__VA_ARGS__) // 生成替换::为_的变量名(增加中间层确保先展开嵌套宏) #define VAR1(_1) _1 #define VAR2_HELPER(a, b) a##_##b #define VAR2(_1, ...) VAR2_HELPER(_1, VAR1(__VA_ARGS__)) #define VAR3_HELPER(a, b) a##_##b #define VAR3(_1, ...) VAR3_HELPER(_1, VAR2(__VA_ARGS__)) #define VAR4_HELPER(a, b) a##_##b #define VAR4(_1, ...) VAR4_HELPER(_1, VAR3(__VA_ARGS__)) #define VAR5_HELPER(a, b) a##_##b #define VAR5(_1, ...) VAR5_HELPER(_1, VAR4(__VA_ARGS__)) #define GET_VAR(...) GET_ARGS(__VA_ARGS__, VAR5, VAR4, VAR3, VAR2, VAR1)(__VA_ARGS__)
修改后,GET_VAR(ns1, ns2, cls)会正确展开为:
int ns1_ns2_cls;
内容的提问来源于stack exchange,提问作者MSinger
相关产品推荐
相关产品推荐

