如何解决带有模板和继承的 Google 模拟
我目前正在尝试使模板和继承与 GMock 表现良好。我有一种感觉,我想要做的是两种对立的意识形态,我应该只使用一个接口,但由于可能的虚拟调用开销,我想避免使用接口(也许我过早地进行了优化)
无论如何,这是我正在尝试做的一个例子
class ConcreteObj {
public:
// Called a lot and so don't want to hit possible virtual overhead
void performant_function();
};
class MockObj {
public:
MOCK_METHOD(void,performant_function,(),());
};
class ITest {
public:
template<typename T>
void test_function(T& );
};
class ConcreteTest : public ITest {
public:
template<typename T>
void test_function(T& ) {};
template<>
void test_function<ConcreteObj>(ConcreteObj& obj) {
// Do something with concrete object
obj.performant_function();
}
template<>
void test_function<MockObj>(MockObj& obj) {
// Do something with mock object
}
}
然后我想做的事情如下
ConcreteTest concrete_test;
ITest* test = &concrete_test;
// In production
ConcreteObj concrete_obj;
test.test_function(concrete_obj);
// In test
MockObj mock_obj;
test.test_function(mock_obj);
然后会通过 ConcreteTest
接口调用 ITest
,但是如果没有对 ConcreteTest
进行某种类型的强制转换,上面的内容显然不起作用,因为您不能拥有虚拟模板化函数.
如果有人对我如何执行以下操作有任何想法,我将不胜感激,尽管我可能会辞职使用纯虚拟接口并处理可能出现的 vtable 开销,如果存在 {{1} IObj
和 ConcreteObj
继承自的 } 接口。
解决方法
为了避免运行时多态,可以使用模板,如下:
class ConcreteObj {
public:
// Called a lot and so don't want to hit possible virtual overhead
void performant_function();
};
class MockObj {
public:
MOCK_METHOD(void,performant_function,(),());
};
class ITest {
public:
virtual ~ITest() = default;
virtual void test_function() = 0;
};
template <typename T>
// requires (T t){ t.performant_function(); } // C++20
class ConcreteTest : public ITest {
T t;
public:
explicit ConcreteTest(T& t) : t(t) {}
void test_function()
{
for (int i = 0; i != big_number; ++i) {
t.performant_function();
}
// ...
}
};
然后
// In production
ConcreteObj concrete_obj;
ConcreteTest<ConcreteObj> concrete_test{concrete_obj};
ITest* test = &concrete_test;
test->test_function();
// In test
MockObj mock_obj;
// EXPECT_CALL(..)
ConcreteTest<MockObj > concrete_test{mock_obj};
ITest* test = &concrete_test;
test->test_function();
,
如果您考虑为 ConcreteObj
指定的要求,您会要求两件事:
-
performant_function()
方法的每次调用开销绝对最小。 - 能够根据上下文(即运行测试与生产时)交换不同的实现。
只有一种方法可以保证同时获得两者:模板。
您发布的代码没有提供很多上下文,所以很可能它不会那么简单,但它看起来很像这样:
class ConcreteTest : public ITest {
public:
template<typename T>
void test_function(T& obj) {
// Do something with obj
obj.performant_function();
};
};
// ...
ConcreteTest concrete_test;
ITest* test = &concrete_test;
// In production
ConcreteObj concrete_obj;
test->test_function(concrete_obj);
// In test
MockObj mock_obj;
test->test_function(mock_obj);
但是,您要问:
(可能是我过早优化了)
答案几乎是肯定的。编译器非常擅长优化东西。在您的场景中,您可以使用 -flto
进行编译并使用:
class IObj {
public:
virtual void performant_function() = 0;
};
class ConcreteObj final : public IObj {
public:
virtual void performant_function() = 0;
};
这很有可能在称为去虚拟化的优化过程中消除开销。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。