微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

带有模板和继承的 Google 模拟

如何解决带有模板和继承的 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} IObjConcreteObj 继承自的 } 接口。

解决方法

为了避免运行时多态,可以使用模板,如下:

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 举报,一经查实,本站将立刻删除。