为什么
MatcherBase
中的gtest-matchers.h中的googletest
类定义了VTable
,它的目的是什么?我推测它实现了类似于虚函数表的机制,但是为什么不直接使用虚函数呢?
VTable
在以下链接的第316行定义:
我尝试搜索并询问一些LLM,但没有得到好的答案。我想得到C++专家的专业解答。谢谢!
VTable
类确实用于实现动态调度,这是常规虚拟表所做的事情。代码本质上是这样的:
template <typename T>
class MatcherBase{
public:
bool MatchAndExplain(const T& x, MatchResultListener* listener) const {
return vtable_->match_and_explain(*this, x, listener);
}
template <typename P>
static auto MatchAndExplainImpl(const MatcherBase& m, const T& value,
MatchResultListener* listener)
-> decltype(P::Get(m).MatchAndExplain(value, listener->stream())) {
return P::Get(m).MatchAndExplain(value, listener->stream());
}
protected:
template <typename U>
explicit MatcherBase(const MatcherInterface<U>* impl)
: vtable_(nullptr), buffer_() {
Init(impl);
}
template <typename M, typename = typename std::remove_reference<
M>::type::is_gtest_matcher>
MatcherBase(M&& m) : vtable_(nullptr), buffer_() { // NOLINT
Init(std::forward<M>(m));
}
private:
struct VTable {
bool (*match_and_explain)(const MatcherBase&, const T&,
MatchResultListener*);
};
template <typename P>
const VTable* GetVTable() {
static constexpr VTable kVTable = {&MatchAndExplainImpl<P>};
return &kVTable;
}
template <typename M>
struct ValuePolicy {
static const M& Get(const MatcherBase& m) {
// When inlined along with Init, need to be explicit to avoid violating
// strict aliasing rules.
const M* ptr =
static_cast<const M*>(static_cast<const void*>(&m.buffer_));
return *ptr;
}
};
// Other ValuePolicy class templates...
template <typename M>
void Init(M&& m) {
using MM = typename std::decay<M>::type;
using Policy = ValuePolicy<MM>;
vtable_ = GetVTable<Policy>();
Policy::Init(*this, std::forward<M>(m));
}
const VTable* vtable_;
Buffer buffer_;
};
从上面我们可以看到
MatcherBase<T>::MatchAndExplain()
调用了相应的MatcherBase<T>::MatchAndExplainImpl<P>()
,其中P
是一个ValuePolicy
类,其类型由构造函数确定。这称为基于策略的模式。 ValuePolicy
确定如何通过 MatcherBase<T>::buffer_
将 ValuePolicy::Get()
解释为匹配器,对于上面列出的构造函数,它可以是 MatcherInterface<U>
(请注意,U
不一定与 T
相同),或其他一些实现了 MatchAndExplain
函数的类。特别是,该类(通常是用户定义的匹配器)不需要继承MatcherInterface<U>
类,它只需要实现相应的功能。 gmock 文档 中给出的自定义匹配器示例如下所示,
请注意,它不会继承任何东西。
class BarPlusBazEqMatcher {
public:
using is_gtest_matcher = void;
//...
bool MatchAndExplain(const Foo& foo,
std::ostream* /* listener */) const {
return (foo.bar() + foo.baz()) == expected_sum_;
}
//...
private:
const int expected_sum_;
};
一般来说,当类具有公共接口(例如实现了特定功能)但不共享公共父类时,您无法使用该语言提供的虚函数系统,而必须推出自己的动态调度机制。请注意,您也无法使
ValuePolicy
模板类继承公共 ValuePolicyBase
,因为 ValuePolicy::Get()
函数不共享公共返回类型。