如何创建可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
相关产品推荐
相关产品推荐

