You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 15:13:18