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

如何创建可Mock自由函数的C++宏 免除手动追加参数名

问题核心

现有自由函数Mock宏需要手动在调用时追加一遍参数名列表,存在冗余,目标是仅通过返回值、函数名、带类型+参数名的形参列表三个入参,自动完成参数透传,实现和MOCK_METHOD一致的调用体验。

实现思路

本质需求是从传入的带括号形参列表(比如(foo_t foo, bar_t bar))中自动提取每个形参的参数名,拼成逗号分隔的实参列表,替换掉原来需要手动传入的__VA_ARGS__部分,具体实现分不同编译标准的场景:

C++20及以上场景(原生预处理器即可实现)

利用C++20预处理器支持的可变参数递归遍历、__VA_OPT__能力,写基础的形参提取辅助宏:

// 提取单个参数的形参名(默认参数写法为"类型 参数名")
#define EXTRACT_PARAM(...) _EXTRACT_LAST(__VA_ARGS__)
#define _EXTRACT_LAST(...) _GET_LAST(__VA_ARGS__)
#define _GET_LAST(first, ...) _GET_LAST_INNER(__VA_ARGS__)
#define _GET_LAST_INNER(last) last

// 遍历所有形参,把提取到的参数名拼成逗号分隔的列表
#define BUILD_PASS_ARGS(...) _BUILD_PASS_ARGS(__VA_ARGS__)
#define _BUILD_PASS_ARGS(first, ...) \
    EXTRACT_PARAM(first) \
    __VA_OPT__(,) _BUILD_PASS_ARGS(__VA_ARGS__)
// 单参数场景终止递归
#define _BUILD_PASS_ARGS_SINGLE(name) name

// 去掉包裹形参列表的外层括号
#define UNWRAP(...) __VA_ARGS__

基于上述辅助宏改写原有Mock宏:

#define MOCK_FREE_VOID(ret, name, args) \
ret name args { \
    Mockups::GetInstance()->name(UNWRAP(BUILD_PASS_ARGS args)) \
}

#define MOCK_FREE_NON_VOID(ret, name, args) \
ret name args { \
    return Mockups::GetInstance()->name(UNWRAP(BUILD_PASS_ARGS args)) \
}

改写后即可直接按照期望的简洁形式调用,完全消除手动追加参数名的冗余:

// 无冗余的目标调用形式
MOCK_FREE_VOID(void, fun_foo, (foo_t foo, bar_t bar))
MOCK_FREE_NON_VOID(bar_t, fun_bar, (foo_t foo, bar_t bar))

宏展开后的代码和原来手动传参的生成结果完全一致,不会改变原有逻辑。

低版本C++(C++11/14/17)兼容方案

低版本标准的预处理器不支持原生递归遍历可变参数能力,可通过两种方式实现同等效果:

  • 引入轻量的预处理工具库(如Boost.Preprocessor),直接调用库内置的形参遍历、提取能力,不需要自己手写递归宏,兼容性覆盖绝大多数老项目场景。
  • 针对项目内待Mock函数的最大参数数量,硬编码对应数量的分支宏。比如所有待Mock函数最多3个参数,就分别写0/1/2/3个参数的专用提取分支,完全不依赖高版本编译特性,适配无外部依赖的项目环境。
注意事项
  • 上述形参提取逻辑适配常规类型 参数名的写法,如果形参包含函数指针、数组、带嵌套逗号的复杂类型,需要针对性调整提取规则,避免将类型内部的逗号误判为参数分隔符导致编译错误。
  • 需保证Mockups单例类中的对应成员方法签名,和自由函数的签名完全一致,否则透传过程会触发类型不匹配的编译报错。

内容的提问来源于stack exchange,提问作者Twisteroor

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.26 06:06:04