diff options
Diffstat (limited to 'googletest')
-rw-r--r-- | googletest/include/gtest/gtest-matchers.h | 337 |
1 files changed, 255 insertions, 82 deletions
diff --git a/googletest/include/gtest/gtest-matchers.h b/googletest/include/gtest/gtest-matchers.h index 04cc63d..6b1bb6a 100644 --- a/googletest/include/gtest/gtest-matchers.h +++ b/googletest/include/gtest/gtest-matchers.h @@ -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 -// 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. - -// MatchResultListener is an abstract class. Its << operator can be -// used by a matcher to explain why a value matches or doesn't match. +// 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 +// FooMatcherMatcher. + 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; } - - 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); - } + static const T& Unwrap(std::reference_wrapper<T> v) { + return v; + } - private: - Rhs rhs_; - }; Rhs rhs_; }; |