mirror of
https://github.com/google/googletest.git
synced 2024-12-27 01:55:04 +08:00
Googletest export
Change Matcher<T> to allow binding an implementation by value directly: - Drop the requirement of MatcherInterface. Doing manual type erasure avoid extra layers in many cases. - Avoid the adaptor for `MatcherInterface<T>` and `MatcherInterface<const T&>` mismatch. - Use a small object optimization when possible. This makes things like `_` and `Eq(1)` really cheap and do not require memory allocations. - Migrate some matchers to the new model to speed them up and to test the new framework. More matchers to come in future changes. PiperOrigin-RevId: 350580998
This commit is contained in:
parent
489283524e
commit
c13c27a513
@ -1315,32 +1315,30 @@ how you can define a matcher to do it:
|
||||
|
||||
```cpp
|
||||
using ::testing::Matcher;
|
||||
using ::testing::MatcherInterface;
|
||||
using ::testing::MatchResultListener;
|
||||
|
||||
class BarPlusBazEqMatcher : public MatcherInterface<const Foo&> {
|
||||
class BarPlusBazEqMatcher {
|
||||
public:
|
||||
explicit BarPlusBazEqMatcher(int expected_sum)
|
||||
: expected_sum_(expected_sum) {}
|
||||
|
||||
bool MatchAndExplain(const Foo& foo,
|
||||
MatchResultListener* /* listener */) const override {
|
||||
std::ostream* /* listener */) const {
|
||||
return (foo.bar() + foo.baz()) == expected_sum_;
|
||||
}
|
||||
|
||||
void DescribeTo(std::ostream* os) const override {
|
||||
*os << "bar() + baz() equals " << expected_sum_;
|
||||
void DescribeTo(std::ostream& os) const {
|
||||
os << "bar() + baz() equals " << expected_sum_;
|
||||
}
|
||||
|
||||
void DescribeNegationTo(std::ostream* os) const override {
|
||||
*os << "bar() + baz() does not equal " << expected_sum_;
|
||||
void DescribeNegationTo(std::ostream& os) const {
|
||||
os << "bar() + baz() does not equal " << expected_sum_;
|
||||
}
|
||||
private:
|
||||
const int expected_sum_;
|
||||
};
|
||||
|
||||
Matcher<const Foo&> BarPlusBazEq(int expected_sum) {
|
||||
return MakeMatcher(new BarPlusBazEqMatcher(expected_sum));
|
||||
return BarPlusBazEqMatcher(expected_sum);
|
||||
}
|
||||
|
||||
...
|
||||
@ -3535,21 +3533,147 @@ MATCHER_P2(Blah, a, b, description_string_2) { ... }
|
||||
```
|
||||
|
||||
While it's tempting to always use the `MATCHER*` macros when defining a new
|
||||
matcher, you should also consider implementing `MatcherInterface` or using
|
||||
`MakePolymorphicMatcher()` instead (see the recipes that follow), especially if
|
||||
you need to use the matcher a lot. While these approaches require more work,
|
||||
they give you more control on the types of the value being matched and the
|
||||
matcher parameters, which in general leads to better compiler error messages
|
||||
that pay off in the long run. They also allow overloading matchers based on
|
||||
parameter types (as opposed to just based on the number of parameters).
|
||||
matcher, you should also consider implementing the matcher interface directly
|
||||
instead (see the recipes that follow), especially if you need to use the matcher
|
||||
a lot. While these approaches require more work, they give you more control on
|
||||
the types of the value being matched and the matcher parameters, which in
|
||||
general leads to better compiler error messages that pay off in the long run.
|
||||
They also allow overloading matchers based on parameter types (as opposed to
|
||||
just based on the number of parameters).
|
||||
|
||||
### Writing New Monomorphic Matchers
|
||||
|
||||
A matcher of argument type `T` implements `::testing::MatcherInterface<T>` and
|
||||
does two things: it tests whether a value of type `T` matches the matcher, and
|
||||
can describe what kind of values it matches. The latter ability is used for
|
||||
A matcher of argument type `T` implements the matcher interface for `T` and does
|
||||
two things: it tests whether a value of type `T` matches the matcher, and can
|
||||
describe what kind of values it matches. The latter ability is used for
|
||||
generating readable error messages when expectations are violated.
|
||||
|
||||
A matcher of `T` must declare a typedef like:
|
||||
|
||||
```cpp
|
||||
using is_gtest_matcher = void;
|
||||
```
|
||||
|
||||
and supports the following operations:
|
||||
|
||||
```cpp
|
||||
// Match a value and optionally explain into an ostream.
|
||||
bool matched = matcher.MatchAndExplain(value, maybe_os);
|
||||
// where `value` is of type `T` and
|
||||
// `maybe_os` is of type `std::ostream*`, where it can be null if the caller
|
||||
// is not interested in there textual explanation.
|
||||
|
||||
matcher.DescribeTo(os);
|
||||
matcher.DescribeNegationTo(os);
|
||||
// where `os` is of type `std::ostream*`.
|
||||
```
|
||||
|
||||
If you need a custom matcher but `Truly()` is not a good option (for example,
|
||||
you may not be happy with the way `Truly(predicate)` describes itself, or you
|
||||
may want your matcher to be polymorphic as `Eq(value)` is), you can define a
|
||||
matcher to do whatever you want in two steps: first implement the matcher
|
||||
interface, and then define a factory function to create a matcher instance. The
|
||||
second step is not strictly needed but it makes the syntax of using the matcher
|
||||
nicer.
|
||||
|
||||
For example, you can define a matcher to test whether an `int` is divisible by 7
|
||||
and then use it like this:
|
||||
|
||||
```cpp
|
||||
using ::testing::Matcher;
|
||||
|
||||
class DivisibleBy7Matcher {
|
||||
public:
|
||||
bool MatchAndExplain(int n, std::ostream*) const {
|
||||
return (n % 7) == 0;
|
||||
}
|
||||
|
||||
void DescribeTo(std::ostream* os) const {
|
||||
*os << "is divisible by 7";
|
||||
}
|
||||
|
||||
void DescribeNegationTo(std::ostream* os) const {
|
||||
*os << "is not divisible by 7";
|
||||
}
|
||||
};
|
||||
|
||||
Matcher<int> DivisibleBy7() {
|
||||
return DivisibleBy7Matcher();
|
||||
}
|
||||
|
||||
...
|
||||
EXPECT_CALL(foo, Bar(DivisibleBy7()));
|
||||
```
|
||||
|
||||
You may improve the matcher message by streaming additional information to the
|
||||
`os` argument in `MatchAndExplain()`:
|
||||
|
||||
```cpp
|
||||
class DivisibleBy7Matcher {
|
||||
public:
|
||||
bool MatchAndExplain(int n, std::ostream* os) const {
|
||||
const int remainder = n % 7;
|
||||
if (remainder != 0 && os != nullptr) {
|
||||
*os << "the remainder is " << remainder;
|
||||
}
|
||||
return remainder == 0;
|
||||
}
|
||||
...
|
||||
};
|
||||
```
|
||||
|
||||
Then, `EXPECT_THAT(x, DivisibleBy7());` may generate a message like this:
|
||||
|
||||
```shell
|
||||
Value of: x
|
||||
Expected: is divisible by 7
|
||||
Actual: 23 (the remainder is 2)
|
||||
```
|
||||
|
||||
### Writing New Polymorphic Matchers
|
||||
|
||||
Expanding what we learned above to *polymorphic* matchers is now just as simple
|
||||
as adding templates in the right place.
|
||||
|
||||
```cpp
|
||||
|
||||
class NotNullMatcher {
|
||||
public:
|
||||
// To implement a polymorphic matcher, we just need to make MatchAndExplain a
|
||||
// template on its first argument.
|
||||
|
||||
// In this example, we want to use NotNull() with any pointer, so
|
||||
// MatchAndExplain() accepts a pointer of any type as its first argument.
|
||||
// In general, you can define MatchAndExplain() as an ordinary method or
|
||||
// a method template, or even overload it.
|
||||
template <typename T>
|
||||
bool MatchAndExplain(T* p, std::ostream*) const {
|
||||
return p != nullptr;
|
||||
}
|
||||
|
||||
// Describes the property of a value matching this matcher.
|
||||
void DescribeTo(std::ostream& os) const { *os << "is not NULL"; }
|
||||
|
||||
// Describes the property of a value NOT matching this matcher.
|
||||
void DescribeNegationTo(std::ostream* os) const { *os << "is NULL"; }
|
||||
};
|
||||
|
||||
NotNullMatcher NotNull() {
|
||||
return NotNullMatcher();
|
||||
}
|
||||
|
||||
...
|
||||
|
||||
EXPECT_CALL(foo, Bar(NotNull())); // The argument must be a non-NULL pointer.
|
||||
```
|
||||
|
||||
### Legacy Matcher Implementation
|
||||
|
||||
Defining matchers used to be somewhat more complicated, in which it required
|
||||
several supporting classes and virtual functions. To implement a matcher for
|
||||
type `T` using the legacy API you have to derive from `MatcherInterface<T>` and
|
||||
call `MakeMatcher` to construct the object.
|
||||
|
||||
The interface looks like this:
|
||||
|
||||
```cpp
|
||||
@ -3582,83 +3706,6 @@ class MatcherInterface {
|
||||
};
|
||||
```
|
||||
|
||||
If you need a custom matcher but `Truly()` is not a good option (for example,
|
||||
you may not be happy with the way `Truly(predicate)` describes itself, or you
|
||||
may want your matcher to be polymorphic as `Eq(value)` is), you can define a
|
||||
matcher to do whatever you want in two steps: first implement the matcher
|
||||
interface, and then define a factory function to create a matcher instance. The
|
||||
second step is not strictly needed but it makes the syntax of using the matcher
|
||||
nicer.
|
||||
|
||||
For example, you can define a matcher to test whether an `int` is divisible by 7
|
||||
and then use it like this:
|
||||
|
||||
```cpp
|
||||
using ::testing::MakeMatcher;
|
||||
using ::testing::Matcher;
|
||||
using ::testing::MatcherInterface;
|
||||
using ::testing::MatchResultListener;
|
||||
|
||||
class DivisibleBy7Matcher : public MatcherInterface<int> {
|
||||
public:
|
||||
bool MatchAndExplain(int n,
|
||||
MatchResultListener* /* listener */) const override {
|
||||
return (n % 7) == 0;
|
||||
}
|
||||
|
||||
void DescribeTo(std::ostream* os) const override {
|
||||
*os << "is divisible by 7";
|
||||
}
|
||||
|
||||
void DescribeNegationTo(std::ostream* os) const override {
|
||||
*os << "is not divisible by 7";
|
||||
}
|
||||
};
|
||||
|
||||
Matcher<int> DivisibleBy7() {
|
||||
return MakeMatcher(new DivisibleBy7Matcher);
|
||||
}
|
||||
|
||||
...
|
||||
EXPECT_CALL(foo, Bar(DivisibleBy7()));
|
||||
```
|
||||
|
||||
You may improve the matcher message by streaming additional information to the
|
||||
`listener` argument in `MatchAndExplain()`:
|
||||
|
||||
```cpp
|
||||
class DivisibleBy7Matcher : public MatcherInterface<int> {
|
||||
public:
|
||||
bool MatchAndExplain(int n,
|
||||
MatchResultListener* listener) const override {
|
||||
const int remainder = n % 7;
|
||||
if (remainder != 0) {
|
||||
*listener << "the remainder is " << remainder;
|
||||
}
|
||||
return remainder == 0;
|
||||
}
|
||||
...
|
||||
};
|
||||
```
|
||||
|
||||
Then, `EXPECT_THAT(x, DivisibleBy7());` may generate a message like this:
|
||||
|
||||
```shell
|
||||
Value of: x
|
||||
Expected: is divisible by 7
|
||||
Actual: 23 (the remainder is 2)
|
||||
```
|
||||
|
||||
### Writing New Polymorphic Matchers
|
||||
|
||||
You've learned how to write your own matchers in the previous recipe. Just one
|
||||
problem: a matcher created using `MakeMatcher()` only works for one particular
|
||||
type of arguments. If you want a *polymorphic* matcher that works with arguments
|
||||
of several types (for instance, `Eq(x)` can be used to match a *`value`* as long
|
||||
as `value == x` compiles -- *`value`* and `x` don't have to share the same
|
||||
type), you can learn the trick from `testing/base/public/gmock-matchers.h` but
|
||||
it's a bit involved.
|
||||
|
||||
Fortunately, most of the time you can define a polymorphic matcher easily with
|
||||
the help of `MakePolymorphicMatcher()`. Here's how you can define `NotNull()` as
|
||||
an example:
|
||||
|
@ -735,31 +735,25 @@ OutIter TransformTupleValues(Func f, const Tuple& t, OutIter out) {
|
||||
return TransformTupleValuesHelper<Tuple, Func, OutIter>::Run(f, t, out);
|
||||
}
|
||||
|
||||
// Implements A<T>().
|
||||
template <typename T>
|
||||
class AnyMatcherImpl : public MatcherInterface<const T&> {
|
||||
public:
|
||||
bool MatchAndExplain(const T& /* x */,
|
||||
MatchResultListener* /* listener */) const override {
|
||||
return true;
|
||||
}
|
||||
void DescribeTo(::std::ostream* os) const override { *os << "is anything"; }
|
||||
void DescribeNegationTo(::std::ostream* os) const override {
|
||||
// This is mostly for completeness' safe, as it's not very useful
|
||||
// to write Not(A<bool>()). However we cannot completely rule out
|
||||
// such a possibility, and it doesn't hurt to be prepared.
|
||||
*os << "never matches";
|
||||
}
|
||||
};
|
||||
|
||||
// Implements _, a matcher that matches any value of any
|
||||
// type. This is a polymorphic matcher, so we need a template type
|
||||
// conversion operator to make it appearing as a Matcher<T> for any
|
||||
// type T.
|
||||
class AnythingMatcher {
|
||||
public:
|
||||
using is_gtest_matcher = void;
|
||||
|
||||
template <typename T>
|
||||
operator Matcher<T>() const { return A<T>(); }
|
||||
bool MatchAndExplain(const T& /* x */, std::ostream* /* listener */) const {
|
||||
return true;
|
||||
}
|
||||
void DescribeTo(std::ostream* os) const { *os << "is anything"; }
|
||||
void DescribeNegationTo(::std::ostream* os) const {
|
||||
// This is mostly for completeness' sake, as it's not very useful
|
||||
// to write Not(A<bool>()). However we cannot completely rule out
|
||||
// such a possibility, and it doesn't hurt to be prepared.
|
||||
*os << "never matches";
|
||||
}
|
||||
};
|
||||
|
||||
// Implements the polymorphic IsNull() matcher, which matches any raw or smart
|
||||
@ -3443,7 +3437,9 @@ class UnorderedElementsAreMatcherImpl
|
||||
: UnorderedElementsAreMatcherImplBase(matcher_flags) {
|
||||
for (; first != last; ++first) {
|
||||
matchers_.push_back(MatcherCast<const Element&>(*first));
|
||||
matcher_describers().push_back(matchers_.back().GetDescriber());
|
||||
}
|
||||
for (const auto& m : matchers_) {
|
||||
matcher_describers().push_back(m.GetDescriber());
|
||||
}
|
||||
}
|
||||
|
||||
@ -4068,12 +4064,14 @@ const internal::AnythingMatcher _ = {};
|
||||
// Creates a matcher that matches any value of the given type T.
|
||||
template <typename T>
|
||||
inline Matcher<T> A() {
|
||||
return Matcher<T>(new internal::AnyMatcherImpl<T>());
|
||||
return _;
|
||||
}
|
||||
|
||||
// Creates a matcher that matches any value of the given type T.
|
||||
template <typename T>
|
||||
inline Matcher<T> An() { return A<T>(); }
|
||||
inline Matcher<T> An() {
|
||||
return _;
|
||||
}
|
||||
|
||||
template <typename T, typename M>
|
||||
Matcher<T> internal::MatcherCastImpl<T, M>::CastImpl(
|
||||
|
@ -410,7 +410,7 @@ TEST(StringMatcherTest,
|
||||
// MatcherInterface* without requiring the user to explicitly
|
||||
// write the type.
|
||||
TEST(MakeMatcherTest, ConstructsMatcherFromMatcherInterface) {
|
||||
const MatcherInterface<int>* dummy_impl = nullptr;
|
||||
const MatcherInterface<int>* dummy_impl = new EvenMatcherImpl;
|
||||
Matcher<int> m = MakeMatcher(dummy_impl);
|
||||
}
|
||||
|
||||
|
@ -39,6 +39,7 @@
|
||||
#ifndef GTEST_INCLUDE_GTEST_GTEST_MATCHERS_H_
|
||||
#define GTEST_INCLUDE_GTEST_GTEST_MATCHERS_H_
|
||||
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
@ -63,20 +64,15 @@ GTEST_DISABLE_MSC_WARNINGS_PUSH_(
|
||||
namespace testing {
|
||||
|
||||
// To implement a matcher Foo for type T, define:
|
||||
// 1. a class FooMatcherImpl that implements the
|
||||
// MatcherInterface<T> interface, and
|
||||
// 1. a class FooMatcherMatcher that implements the matcher interface:
|
||||
// using is_gtest_matcher = void;
|
||||
// bool MatchAndExplain(const T&, std::ostream*);
|
||||
// void DescribeTo(std::ostream*);
|
||||
// void DescribeNegationTo(std::ostream*);
|
||||
//
|
||||
// 2. a factory function that creates a Matcher<T> object from a
|
||||
// FooMatcherImpl*.
|
||||
//
|
||||
// The two-level delegation design makes it possible to allow a user
|
||||
// to write "v" instead of "Eq(v)" where a Matcher is expected, which
|
||||
// is impossible if we pass matchers by pointers. It also eases
|
||||
// ownership management as Matcher objects can now be copied like
|
||||
// plain values.
|
||||
// FooMatcherMatcher.
|
||||
|
||||
// MatchResultListener is an abstract class. Its << operator can be
|
||||
// used by a matcher to explain why a value matches or doesn't match.
|
||||
//
|
||||
class MatchResultListener {
|
||||
public:
|
||||
// Creates a listener object with the given underlying ostream. The
|
||||
@ -181,31 +177,6 @@ class MatcherInterface : public MatcherDescriberInterface {
|
||||
|
||||
namespace internal {
|
||||
|
||||
// Converts a MatcherInterface<T> to a MatcherInterface<const T&>.
|
||||
template <typename T>
|
||||
class MatcherInterfaceAdapter : public MatcherInterface<const T&> {
|
||||
public:
|
||||
explicit MatcherInterfaceAdapter(const MatcherInterface<T>* impl)
|
||||
: impl_(impl) {}
|
||||
~MatcherInterfaceAdapter() override { delete impl_; }
|
||||
|
||||
void DescribeTo(::std::ostream* os) const override { impl_->DescribeTo(os); }
|
||||
|
||||
void DescribeNegationTo(::std::ostream* os) const override {
|
||||
impl_->DescribeNegationTo(os);
|
||||
}
|
||||
|
||||
bool MatchAndExplain(const T& x,
|
||||
MatchResultListener* listener) const override {
|
||||
return impl_->MatchAndExplain(x, listener);
|
||||
}
|
||||
|
||||
private:
|
||||
const MatcherInterface<T>* const impl_;
|
||||
|
||||
GTEST_DISALLOW_COPY_AND_ASSIGN_(MatcherInterfaceAdapter);
|
||||
};
|
||||
|
||||
struct AnyEq {
|
||||
template <typename A, typename B>
|
||||
bool operator()(const A& a, const B& b) const { return a == b; }
|
||||
@ -252,16 +223,35 @@ class StreamMatchResultListener : public MatchResultListener {
|
||||
GTEST_DISALLOW_COPY_AND_ASSIGN_(StreamMatchResultListener);
|
||||
};
|
||||
|
||||
struct SharedPayloadBase {
|
||||
std::atomic<int> ref{1};
|
||||
void Ref() { ref.fetch_add(1, std::memory_order_relaxed); }
|
||||
bool Unref() { return ref.fetch_sub(1, std::memory_order_acq_rel) == 1; }
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct SharedPayload : SharedPayloadBase {
|
||||
explicit SharedPayload(const T& v) : value(v) {}
|
||||
explicit SharedPayload(T&& v) : value(std::move(v)) {}
|
||||
|
||||
static void Destroy(SharedPayloadBase* shared) {
|
||||
delete static_cast<SharedPayload*>(shared);
|
||||
}
|
||||
|
||||
T value;
|
||||
};
|
||||
|
||||
// An internal class for implementing Matcher<T>, which will derive
|
||||
// from it. We put functionalities common to all Matcher<T>
|
||||
// specializations here to avoid code duplication.
|
||||
template <typename T>
|
||||
class MatcherBase {
|
||||
class MatcherBase : private MatcherDescriberInterface {
|
||||
public:
|
||||
// Returns true if and only if the matcher matches x; also explains the
|
||||
// match result to 'listener'.
|
||||
bool MatchAndExplain(const T& x, MatchResultListener* listener) const {
|
||||
return impl_->MatchAndExplain(x, listener);
|
||||
GTEST_CHECK_(vtable_ != nullptr);
|
||||
return vtable_->match_and_explain(*this, x, listener);
|
||||
}
|
||||
|
||||
// Returns true if and only if this matcher matches x.
|
||||
@ -271,11 +261,15 @@ class MatcherBase {
|
||||
}
|
||||
|
||||
// Describes this matcher to an ostream.
|
||||
void DescribeTo(::std::ostream* os) const { impl_->DescribeTo(os); }
|
||||
void DescribeTo(::std::ostream* os) const final {
|
||||
GTEST_CHECK_(vtable_ != nullptr);
|
||||
vtable_->describe(*this, os, false);
|
||||
}
|
||||
|
||||
// Describes the negation of this matcher to an ostream.
|
||||
void DescribeNegationTo(::std::ostream* os) const {
|
||||
impl_->DescribeNegationTo(os);
|
||||
void DescribeNegationTo(::std::ostream* os) const final {
|
||||
GTEST_CHECK_(vtable_ != nullptr);
|
||||
vtable_->describe(*this, os, true);
|
||||
}
|
||||
|
||||
// Explains why x matches, or doesn't match, the matcher.
|
||||
@ -288,31 +282,190 @@ class MatcherBase {
|
||||
// of the describer, which is only guaranteed to be alive when
|
||||
// this matcher object is alive.
|
||||
const MatcherDescriberInterface* GetDescriber() const {
|
||||
return impl_.get();
|
||||
if (vtable_ == nullptr) return nullptr;
|
||||
return vtable_->get_describer(*this);
|
||||
}
|
||||
|
||||
protected:
|
||||
MatcherBase() {}
|
||||
MatcherBase() : vtable_(nullptr) {}
|
||||
|
||||
// Constructs a matcher from its implementation.
|
||||
explicit MatcherBase(const MatcherInterface<const T&>* impl) : impl_(impl) {}
|
||||
|
||||
template <typename U>
|
||||
explicit MatcherBase(
|
||||
const MatcherInterface<U>* impl,
|
||||
typename std::enable_if<!std::is_same<U, const U&>::value>::type* =
|
||||
nullptr)
|
||||
: impl_(new internal::MatcherInterfaceAdapter<U>(impl)) {}
|
||||
explicit MatcherBase(const MatcherInterface<U>* impl) {
|
||||
Init(impl);
|
||||
}
|
||||
|
||||
MatcherBase(const MatcherBase&) = default;
|
||||
MatcherBase& operator=(const MatcherBase&) = default;
|
||||
MatcherBase(MatcherBase&&) = default;
|
||||
MatcherBase& operator=(MatcherBase&&) = default;
|
||||
template <typename M, typename = typename std::remove_reference<
|
||||
M>::type::is_gtest_matcher>
|
||||
MatcherBase(M&& m) { // NOLINT
|
||||
Init(std::forward<M>(m));
|
||||
}
|
||||
|
||||
virtual ~MatcherBase() {}
|
||||
MatcherBase(const MatcherBase& other)
|
||||
: vtable_(other.vtable_), buffer_(other.buffer_) {
|
||||
if (IsShared()) buffer_.shared->Ref();
|
||||
}
|
||||
|
||||
MatcherBase& operator=(const MatcherBase& other) {
|
||||
if (this == &other) return *this;
|
||||
Destroy();
|
||||
vtable_ = other.vtable_;
|
||||
buffer_ = other.buffer_;
|
||||
if (IsShared()) buffer_.shared->Ref();
|
||||
return *this;
|
||||
}
|
||||
|
||||
MatcherBase(MatcherBase&& other)
|
||||
: vtable_(other.vtable_), buffer_(other.buffer_) {
|
||||
other.vtable_ = nullptr;
|
||||
}
|
||||
|
||||
MatcherBase& operator=(MatcherBase&& other) {
|
||||
if (this == &other) return *this;
|
||||
Destroy();
|
||||
vtable_ = other.vtable_;
|
||||
buffer_ = other.buffer_;
|
||||
other.vtable_ = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
~MatcherBase() override { Destroy(); }
|
||||
|
||||
private:
|
||||
std::shared_ptr<const MatcherInterface<const T&>> impl_;
|
||||
struct VTable {
|
||||
bool (*match_and_explain)(const MatcherBase&, const T&,
|
||||
MatchResultListener*);
|
||||
void (*describe)(const MatcherBase&, std::ostream*, bool negation);
|
||||
// Returns the captured object if it implements the interface, otherwise
|
||||
// returns the MatcherBase itself.
|
||||
const MatcherDescriberInterface* (*get_describer)(const MatcherBase&);
|
||||
// Called on shared instances when the reference count reaches 0.
|
||||
void (*shared_destroy)(SharedPayloadBase*);
|
||||
};
|
||||
|
||||
bool IsShared() const {
|
||||
return vtable_ != nullptr && vtable_->shared_destroy != nullptr;
|
||||
}
|
||||
|
||||
// If the implementation uses a listener, call that.
|
||||
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());
|
||||
}
|
||||
|
||||
template <typename P>
|
||||
static auto MatchAndExplainImpl(const MatcherBase& m, const T& value,
|
||||
MatchResultListener* listener)
|
||||
-> decltype(P::Get(m).MatchAndExplain(value, listener)) {
|
||||
return P::Get(m).MatchAndExplain(value, listener);
|
||||
}
|
||||
|
||||
template <typename P>
|
||||
static void DescribeImpl(const MatcherBase& m, std::ostream* os,
|
||||
bool negation) {
|
||||
if (negation) {
|
||||
P::Get(m).DescribeNegationTo(os);
|
||||
} else {
|
||||
P::Get(m).DescribeTo(os);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename P>
|
||||
static const MatcherDescriberInterface* GetDescriberImpl(
|
||||
const MatcherBase& m) {
|
||||
// If the impl is a MatcherDescriberInterface, then return it.
|
||||
// Otherwise use MatcherBase itself.
|
||||
// This allows us to implement the GetDescriber() function without support
|
||||
// from the impl, but some users really want to get their impl back when
|
||||
// they call GetDescriber().
|
||||
// We use std::get on a tuple as a workaround of not having `if constexpr`.
|
||||
return std::get<(
|
||||
std::is_convertible<decltype(&P::Get(m)),
|
||||
const MatcherDescriberInterface*>::value
|
||||
? 1
|
||||
: 0)>(std::make_tuple(&m, &P::Get(m)));
|
||||
}
|
||||
|
||||
template <typename P>
|
||||
const VTable* GetVTable() {
|
||||
static constexpr VTable kVTable = {&MatchAndExplainImpl<P>,
|
||||
&DescribeImpl<P>, &GetDescriberImpl<P>,
|
||||
P::shared_destroy};
|
||||
return &kVTable;
|
||||
}
|
||||
|
||||
union Buffer {
|
||||
// Add some types to give Buffer some common alignment/size use cases.
|
||||
void* ptr;
|
||||
double d;
|
||||
int64_t i;
|
||||
// And add one for the out-of-line cases.
|
||||
SharedPayloadBase* shared;
|
||||
};
|
||||
|
||||
void Destroy() {
|
||||
if (IsShared() && buffer_.shared->Unref()) {
|
||||
vtable_->shared_destroy(buffer_.shared);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename M>
|
||||
static constexpr bool IsInlined() {
|
||||
return sizeof(M) <= sizeof(Buffer) && alignof(M) <= alignof(Buffer) &&
|
||||
std::is_trivially_copy_constructible<M>::value &&
|
||||
std::is_trivially_destructible<M>::value;
|
||||
}
|
||||
|
||||
template <typename M, bool = IsInlined<M>()>
|
||||
struct ValuePolicy {
|
||||
static const M& Get(const MatcherBase& m) {
|
||||
return reinterpret_cast<const M&>(m.buffer_);
|
||||
}
|
||||
static void Init(MatcherBase& m, M impl) {
|
||||
::new (static_cast<void*>(&m.buffer_)) M(impl);
|
||||
}
|
||||
static constexpr auto shared_destroy = nullptr;
|
||||
};
|
||||
|
||||
template <typename M>
|
||||
struct ValuePolicy<M, false> {
|
||||
using Shared = SharedPayload<M>;
|
||||
static const M& Get(const MatcherBase& m) {
|
||||
return static_cast<Shared*>(m.buffer_.shared)->value;
|
||||
}
|
||||
template <typename Arg>
|
||||
static void Init(MatcherBase& m, Arg&& arg) {
|
||||
m.buffer_.shared = new Shared(std::forward<Arg>(arg));
|
||||
}
|
||||
static constexpr auto shared_destroy = &Shared::Destroy;
|
||||
};
|
||||
|
||||
template <typename U, bool B>
|
||||
struct ValuePolicy<const MatcherInterface<U>*, B> {
|
||||
using M = const MatcherInterface<U>;
|
||||
using Shared = SharedPayload<std::unique_ptr<M>>;
|
||||
static const M& Get(const MatcherBase& m) {
|
||||
return *static_cast<Shared*>(m.buffer_.shared)->value;
|
||||
}
|
||||
static void Init(MatcherBase& m, M* impl) {
|
||||
m.buffer_.shared = new Shared(std::unique_ptr<M>(impl));
|
||||
}
|
||||
|
||||
static constexpr auto shared_destroy = &Shared::Destroy;
|
||||
};
|
||||
|
||||
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_;
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
@ -340,6 +493,10 @@ class Matcher : public internal::MatcherBase<T> {
|
||||
nullptr)
|
||||
: internal::MatcherBase<T>(impl) {}
|
||||
|
||||
template <typename M, typename = typename std::remove_reference<
|
||||
M>::type::is_gtest_matcher>
|
||||
Matcher(M&& m) : internal::MatcherBase<T>(std::forward<M>(m)) {} // NOLINT
|
||||
|
||||
// Implicit constructor here allows people to write
|
||||
// EXPECT_CALL(foo, Bar(5)) instead of EXPECT_CALL(foo, Bar(Eq(5))) sometimes
|
||||
Matcher(T value); // NOLINT
|
||||
@ -357,6 +514,11 @@ class GTEST_API_ Matcher<const std::string&>
|
||||
explicit Matcher(const MatcherInterface<const std::string&>* impl)
|
||||
: internal::MatcherBase<const std::string&>(impl) {}
|
||||
|
||||
template <typename M, typename = typename std::remove_reference<
|
||||
M>::type::is_gtest_matcher>
|
||||
Matcher(M&& m) // NOLINT
|
||||
: internal::MatcherBase<const std::string&>(std::forward<M>(m)) {}
|
||||
|
||||
// Allows the user to write str instead of Eq(str) sometimes, where
|
||||
// str is a std::string object.
|
||||
Matcher(const std::string& s); // NOLINT
|
||||
@ -376,6 +538,11 @@ class GTEST_API_ Matcher<std::string>
|
||||
explicit Matcher(const MatcherInterface<std::string>* impl)
|
||||
: internal::MatcherBase<std::string>(impl) {}
|
||||
|
||||
template <typename M, typename = typename std::remove_reference<
|
||||
M>::type::is_gtest_matcher>
|
||||
Matcher(M&& m) // NOLINT
|
||||
: internal::MatcherBase<std::string>(std::forward<M>(m)) {}
|
||||
|
||||
// Allows the user to write str instead of Eq(str) sometimes, where
|
||||
// str is a string object.
|
||||
Matcher(const std::string& s); // NOLINT
|
||||
@ -397,6 +564,12 @@ class GTEST_API_ Matcher<const internal::StringView&>
|
||||
explicit Matcher(const MatcherInterface<const internal::StringView&>* impl)
|
||||
: internal::MatcherBase<const internal::StringView&>(impl) {}
|
||||
|
||||
template <typename M, typename = typename std::remove_reference<
|
||||
M>::type::is_gtest_matcher>
|
||||
Matcher(M&& m) // NOLINT
|
||||
: internal::MatcherBase<const internal::StringView&>(std::forward<M>(m)) {
|
||||
}
|
||||
|
||||
// Allows the user to write str instead of Eq(str) sometimes, where
|
||||
// str is a std::string object.
|
||||
Matcher(const std::string& s); // NOLINT
|
||||
@ -419,6 +592,11 @@ class GTEST_API_ Matcher<internal::StringView>
|
||||
explicit Matcher(const MatcherInterface<internal::StringView>* impl)
|
||||
: internal::MatcherBase<internal::StringView>(impl) {}
|
||||
|
||||
template <typename M, typename = typename std::remove_reference<
|
||||
M>::type::is_gtest_matcher>
|
||||
Matcher(M&& m) // NOLINT
|
||||
: internal::MatcherBase<internal::StringView>(std::forward<M>(m)) {}
|
||||
|
||||
// Allows the user to write str instead of Eq(str) sometimes, where
|
||||
// str is a std::string object.
|
||||
Matcher(const std::string& s); // NOLINT
|
||||
@ -529,37 +707,32 @@ template <typename D, typename Rhs, typename Op>
|
||||
class ComparisonBase {
|
||||
public:
|
||||
explicit ComparisonBase(const Rhs& rhs) : rhs_(rhs) {}
|
||||
|
||||
using is_gtest_matcher = void;
|
||||
|
||||
template <typename Lhs>
|
||||
operator Matcher<Lhs>() const {
|
||||
return Matcher<Lhs>(new Impl<const Lhs&>(rhs_));
|
||||
bool MatchAndExplain(const Lhs& lhs, std::ostream*) const {
|
||||
return Op()(lhs, Unwrap(rhs_));
|
||||
}
|
||||
void DescribeTo(std::ostream* os) const {
|
||||
*os << D::Desc() << " ";
|
||||
UniversalPrint(Unwrap(rhs_), os);
|
||||
}
|
||||
void DescribeNegationTo(std::ostream* os) const {
|
||||
*os << D::NegatedDesc() << " ";
|
||||
UniversalPrint(Unwrap(rhs_), os);
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename T>
|
||||
static const T& Unwrap(const T& v) { return v; }
|
||||
static const T& Unwrap(const T& v) {
|
||||
return v;
|
||||
}
|
||||
template <typename T>
|
||||
static const T& Unwrap(std::reference_wrapper<T> v) { return v; }
|
||||
static const T& Unwrap(std::reference_wrapper<T> v) {
|
||||
return v;
|
||||
}
|
||||
|
||||
template <typename Lhs, typename = Rhs>
|
||||
class Impl : public MatcherInterface<Lhs> {
|
||||
public:
|
||||
explicit Impl(const Rhs& rhs) : rhs_(rhs) {}
|
||||
bool MatchAndExplain(Lhs lhs,
|
||||
MatchResultListener* /* listener */) const override {
|
||||
return Op()(lhs, Unwrap(rhs_));
|
||||
}
|
||||
void DescribeTo(::std::ostream* os) const override {
|
||||
*os << D::Desc() << " ";
|
||||
UniversalPrint(Unwrap(rhs_), os);
|
||||
}
|
||||
void DescribeNegationTo(::std::ostream* os) const override {
|
||||
*os << D::NegatedDesc() << " ";
|
||||
UniversalPrint(Unwrap(rhs_), os);
|
||||
}
|
||||
|
||||
private:
|
||||
Rhs rhs_;
|
||||
};
|
||||
Rhs rhs_;
|
||||
};
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user