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 进行某种类型的强制转换,上述方法显然无法工作,因为您不能拥有虚拟模板化函数。

如果有人对我如何执行以下操作有任何想法,我将不胜感激,尽管我可能会辞职使用纯虚拟接口并处理如果有 [= ConcreteObjMockObj 继承自的 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();