Google 使用模板和继承进行模拟
Google mock with templates and inheritance
我目前正在尝试让模板和继承与 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);
然后会通过 ITest
接口调用 ConcreteTest
,但是如果不对 ConcreteTest
进行某种类型的强制转换,上述方法显然无法工作,因为您不能拥有虚拟模板化函数。
如果有人对我如何执行以下操作有任何想法,我将不胜感激,尽管我可能会辞职使用纯虚拟接口并处理如果有 [= ConcreteObj
和 MockObj
继承自的 16=] 接口。
如果您考虑为 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);
然而,你问:
(perhaps I am optimizing prematurely)
答案几乎是肯定的。编译器真的很擅长优化东西。在您的场景中,您可以使用 -flto
进行编译并使用:
class IObj {
public:
virtual void performant_function() = 0;
};
class ConcreteObj final : public IObj {
public:
virtual void performant_function() = 0;
};
并且在称为 去虚拟化.
的优化过程中,这将很有可能消除开销
为了避免运行时多态,可以使用模板,如下:
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();
我目前正在尝试让模板和继承与 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);
然后会通过 ITest
接口调用 ConcreteTest
,但是如果不对 ConcreteTest
进行某种类型的强制转换,上述方法显然无法工作,因为您不能拥有虚拟模板化函数。
如果有人对我如何执行以下操作有任何想法,我将不胜感激,尽管我可能会辞职使用纯虚拟接口并处理如果有 [= ConcreteObj
和 MockObj
继承自的 16=] 接口。
如果您考虑为 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);
然而,你问:
(perhaps I am optimizing prematurely)
答案几乎是肯定的。编译器真的很擅长优化东西。在您的场景中,您可以使用 -flto
进行编译并使用:
class IObj {
public:
virtual void performant_function() = 0;
};
class ConcreteObj final : public IObj {
public:
virtual void performant_function() = 0;
};
并且在称为 去虚拟化.
的优化过程中,这将很有可能消除开销为了避免运行时多态,可以使用模板,如下:
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();