GMock 1.8.0中带volatile参数的Mock函数编译失败问题求助
我之前在嵌入式项目的单元测试中也碰到过这个GMock升级后的坑,结合你的C++98环境和硬件交互的需求,给你几个可行的解决方案,同时聊聊这个问题是否属于GMock的功能缺失:
问题根源
GMock 1.8.0依赖的GTest在打印指针类型时,默认代码里用了reinterpret_cast<const void*>(p)来转换指针,但C++98里这个转换无法自动移除volatile限定符,而1.7.0的版本可能没有这个打印逻辑或者处理方式不同,导致升级后触发编译错误。
可行解决方案
1. 注册自定义类型打印机(推荐)
GTest允许为特定类型注册自定义的打印函数,我们可以给volatile int*类型写一个专属打印机,绕过默认的指针处理逻辑:
#include <iostream> #include <gtest/gtest-printers.h> namespace testing { namespace internal { // 为volatile int*注册自定义打印函数 void PrintTo(const volatile int* p, std::ostream* os) { // 仅打印地址,用const_cast临时移除volatile是安全的(不会修改内存) *os << "volatile int* @ 0x" << std::hex << reinterpret_cast<uintptr_t>(const_cast<int*>(p)) << std::dec; } } // namespace internal } // namespace testing
把这段代码放在你的测试文件开头,GMock在处理这个参数时就会调用自定义的打印逻辑,不会触发原有的编译错误。
2. 封装Volatile参数为结构体
把volatile int*封装成一个简单的结构体,让GMock处理结构体类型而非直接的volatile指针:
// 封装volatile指针的结构体 struct VolatileIntPtr { volatile int* ptr; // 显式构造函数避免隐式转换 explicit VolatileIntPtr(volatile int* p) : ptr(p) {} }; // 修改Mock方法定义 MOCK_METHOD1(foo, int(VolatileIntPtr));
调用mock方法时,只需要传入VolatileIntPtr(your_volatile_ptr)即可,这样既保留了volatile语义,又避开了GMock的指针打印问题。
3. 临时修改GTest源码(应急方案)
如果项目周期紧张,也可以临时修改GTest的源码:找到gtest-printers.h中报错的那一行,把
*os << reinterpret_cast<const void*>(p);
修改为
template <typename T> struct remove_volatile { typedef T type; }; template <typename T> struct remove_volatile<volatile T> { typedef T type; }; *os << reinterpret_cast<const void*>(const_cast<typename remove_volatile<element_type>::type*>(p));
这个方法的缺点是会修改库源码,后续升级GMock时需要重复操作,仅适合临时应急。
是否属于GMock的功能缺失?
这个情况更应该被视为GMock/GTest的回归问题或者对volatile指针类型的支持遗漏:
- 1.7.0版本能正常编译,说明旧版本的逻辑可以处理volatile参数;
- 嵌入式场景中volatile指针是很常见的需求,GMock应该考虑到这种情况;
- 即使在C++98环境下,也可以通过模板特化等方式处理volatile限定符,而不是直接抛出编译错误。
你可以在GMock的官方Issue平台上提交这个问题,说明你的嵌入式场景需求,官方大概率会认可这是一个需要修复的问题,后续版本可能会补上对volatile指针的支持。
内容的提问来源于stack exchange,提问作者hannibal

