mirror of
https://git.mirrors.martin98.com/https://github.com/google/googletest.git
synced 2025-07-29 01:52:05 +08:00
Enable safe matcher casts from Matcher<const T&>
to Matcher<T>
.
PiperOrigin-RevId: 715826130 Change-Id: Id962fd456f6da21ea2a909f331f92d814f1dad46
This commit is contained in:
parent
504ea69cf7
commit
e4ece4881d
@ -936,8 +936,8 @@ casts a matcher `m` to type `Matcher<T>`. To ensure safety, gMock checks that
|
|||||||
floating-point numbers), the conversion from `T` to `U` is not lossy (in
|
floating-point numbers), the conversion from `T` to `U` is not lossy (in
|
||||||
other words, any value representable by `T` can also be represented by `U`);
|
other words, any value representable by `T` can also be represented by `U`);
|
||||||
and
|
and
|
||||||
3. When `U` is a reference, `T` must also be a reference (as the underlying
|
3. When `U` is a non-const reference, `T` must also be a reference (as the
|
||||||
matcher may be interested in the address of the `U` value).
|
underlying matcher may be interested in the address of the `U` value).
|
||||||
|
|
||||||
The code won't compile if any of these conditions isn't met.
|
The code won't compile if any of these conditions isn't met.
|
||||||
|
|
||||||
|
@ -408,13 +408,22 @@ class MatcherCastImpl<T, Matcher<U>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
class Impl : public MatcherInterface<T> {
|
// If it's possible to implicitly convert a `const T&` to U, then `Impl` can
|
||||||
|
// take that as input to avoid a copy. Otherwise, such as when `T` is a
|
||||||
|
// non-const reference type or a type explicitly constructible only from a
|
||||||
|
// non-const reference, then `Impl` must use `T` as-is (potentially copying).
|
||||||
|
using ImplArgT =
|
||||||
|
typename std::conditional<std::is_convertible<const T&, const U&>::value,
|
||||||
|
const T&, T>::type;
|
||||||
|
|
||||||
|
class Impl : public MatcherInterface<ImplArgT> {
|
||||||
public:
|
public:
|
||||||
explicit Impl(const Matcher<U>& source_matcher)
|
explicit Impl(const Matcher<U>& source_matcher)
|
||||||
: source_matcher_(source_matcher) {}
|
: source_matcher_(source_matcher) {}
|
||||||
|
|
||||||
// We delegate the matching logic to the source matcher.
|
// We delegate the matching logic to the source matcher.
|
||||||
bool MatchAndExplain(T x, MatchResultListener* listener) const override {
|
bool MatchAndExplain(ImplArgT x,
|
||||||
|
MatchResultListener* listener) const override {
|
||||||
using FromType = typename std::remove_cv<typename std::remove_pointer<
|
using FromType = typename std::remove_cv<typename std::remove_pointer<
|
||||||
typename std::remove_reference<T>::type>::type>::type;
|
typename std::remove_reference<T>::type>::type>::type;
|
||||||
using ToType = typename std::remove_cv<typename std::remove_pointer<
|
using ToType = typename std::remove_cv<typename std::remove_pointer<
|
||||||
@ -431,9 +440,8 @@ class MatcherCastImpl<T, Matcher<U>> {
|
|||||||
|
|
||||||
// Do the cast to `U` explicitly if necessary.
|
// Do the cast to `U` explicitly if necessary.
|
||||||
// Otherwise, let implicit conversions do the trick.
|
// Otherwise, let implicit conversions do the trick.
|
||||||
using CastType =
|
using CastType = typename std::conditional<
|
||||||
typename std::conditional<std::is_convertible<T&, const U&>::value,
|
std::is_convertible<ImplArgT&, const U&>::value, ImplArgT&, U>::type;
|
||||||
T&, U>::type;
|
|
||||||
|
|
||||||
return source_matcher_.MatchAndExplain(static_cast<CastType>(x),
|
return source_matcher_.MatchAndExplain(static_cast<CastType>(x),
|
||||||
listener);
|
listener);
|
||||||
@ -528,18 +536,16 @@ inline Matcher<T> SafeMatcherCast(const M& polymorphic_matcher_or_value) {
|
|||||||
// safely convert a Matcher<U> to a Matcher<T> (i.e. Matcher is
|
// safely convert a Matcher<U> to a Matcher<T> (i.e. Matcher is
|
||||||
// contravariant): just keep a copy of the original Matcher<U>, convert the
|
// contravariant): just keep a copy of the original Matcher<U>, convert the
|
||||||
// argument from type T to U, and then pass it to the underlying Matcher<U>.
|
// argument from type T to U, and then pass it to the underlying Matcher<U>.
|
||||||
// The only exception is when U is a reference and T is not, as the
|
// The only exception is when U is a non-const reference and T is not, as the
|
||||||
// underlying Matcher<U> may be interested in the argument's address, which
|
// underlying Matcher<U> may be interested in the argument's address, which
|
||||||
// is not preserved in the conversion from T to U.
|
// cannot be preserved in the conversion from T to U (since a copy of the input
|
||||||
|
// T argument would be required to provide a non-const reference U).
|
||||||
template <typename T, typename U>
|
template <typename T, typename U>
|
||||||
inline Matcher<T> SafeMatcherCast(const Matcher<U>& matcher) {
|
inline Matcher<T> SafeMatcherCast(const Matcher<U>& matcher) {
|
||||||
// Enforce that T can be implicitly converted to U.
|
// Enforce that T can be implicitly converted to U.
|
||||||
static_assert(std::is_convertible<const T&, const U&>::value,
|
static_assert(std::is_convertible<const T&, const U&>::value,
|
||||||
"T must be implicitly convertible to U");
|
"T must be implicitly convertible to U (and T must be a "
|
||||||
// Enforce that we are not converting a non-reference type T to a reference
|
"non-const reference if U is a non-const reference)");
|
||||||
// type U.
|
|
||||||
static_assert(std::is_reference<T>::value || !std::is_reference<U>::value,
|
|
||||||
"cannot convert non reference arg to reference");
|
|
||||||
// In case both T and U are arithmetic types, enforce that the
|
// In case both T and U are arithmetic types, enforce that the
|
||||||
// conversion is not lossy.
|
// conversion is not lossy.
|
||||||
typedef GTEST_REMOVE_REFERENCE_AND_CONST_(T) RawT;
|
typedef GTEST_REMOVE_REFERENCE_AND_CONST_(T) RawT;
|
||||||
|
@ -411,9 +411,27 @@ class IntValue {
|
|||||||
int value_;
|
int value_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// For testing casting matchers between compatible types. This is similar to
|
||||||
|
// IntValue, but takes a non-const reference to the value, showing MatcherCast
|
||||||
|
// works with such types (and doesn't, for example, use a const ref internally).
|
||||||
|
class MutableIntView {
|
||||||
|
public:
|
||||||
|
// An int& can be statically (although not implicitly) cast to a
|
||||||
|
// MutableIntView.
|
||||||
|
explicit MutableIntView(int& a_value) : value_(a_value) {}
|
||||||
|
|
||||||
|
int& value() const { return value_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
int& value_;
|
||||||
|
};
|
||||||
|
|
||||||
// For testing casting matchers between compatible types.
|
// For testing casting matchers between compatible types.
|
||||||
bool IsPositiveIntValue(const IntValue& foo) { return foo.value() > 0; }
|
bool IsPositiveIntValue(const IntValue& foo) { return foo.value() > 0; }
|
||||||
|
|
||||||
|
// For testing casting matchers between compatible types.
|
||||||
|
bool IsPositiveMutableIntView(MutableIntView foo) { return foo.value() > 0; }
|
||||||
|
|
||||||
// Tests that MatcherCast<T>(m) works when m is a Matcher<U> where T
|
// Tests that MatcherCast<T>(m) works when m is a Matcher<U> where T
|
||||||
// can be statically converted to U.
|
// can be statically converted to U.
|
||||||
TEST(MatcherCastTest, FromCompatibleType) {
|
TEST(MatcherCastTest, FromCompatibleType) {
|
||||||
@ -429,14 +447,34 @@ TEST(MatcherCastTest, FromCompatibleType) {
|
|||||||
// predicate.
|
// predicate.
|
||||||
EXPECT_TRUE(m4.Matches(1));
|
EXPECT_TRUE(m4.Matches(1));
|
||||||
EXPECT_FALSE(m4.Matches(0));
|
EXPECT_FALSE(m4.Matches(0));
|
||||||
|
|
||||||
|
Matcher<MutableIntView> m5 = Truly(IsPositiveMutableIntView);
|
||||||
|
Matcher<int> m6 = MatcherCast<int>(m5);
|
||||||
|
// In the following, the arguments 1 and 0 are statically converted to
|
||||||
|
// MutableIntView objects, and then tested by the IsPositiveMutableIntView()
|
||||||
|
// predicate.
|
||||||
|
EXPECT_TRUE(m6.Matches(1));
|
||||||
|
EXPECT_FALSE(m6.Matches(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tests that MatcherCast<T>(m) works when m is a Matcher<const T&>.
|
// Tests that MatcherCast<T>(m) works when m is a Matcher<const T&>.
|
||||||
TEST(MatcherCastTest, FromConstReferenceToNonReference) {
|
TEST(MatcherCastTest, FromConstReferenceToNonReference) {
|
||||||
Matcher<const int&> m1 = Eq(0);
|
int n = 0;
|
||||||
|
Matcher<const int&> m1 = Ref(n);
|
||||||
Matcher<int> m2 = MatcherCast<int>(m1);
|
Matcher<int> m2 = MatcherCast<int>(m1);
|
||||||
EXPECT_TRUE(m2.Matches(0));
|
int n1 = 0;
|
||||||
EXPECT_FALSE(m2.Matches(1));
|
EXPECT_TRUE(m2.Matches(n));
|
||||||
|
EXPECT_FALSE(m2.Matches(n1));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests that MatcherCast<T&>(m) works when m is a Matcher<const T&>.
|
||||||
|
TEST(MatcherCastTest, FromConstReferenceToReference) {
|
||||||
|
int n = 0;
|
||||||
|
Matcher<const int&> m1 = Ref(n);
|
||||||
|
Matcher<int&> m2 = MatcherCast<int&>(m1);
|
||||||
|
int n1 = 0;
|
||||||
|
EXPECT_TRUE(m2.Matches(n));
|
||||||
|
EXPECT_FALSE(m2.Matches(n1));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tests that MatcherCast<T>(m) works when m is a Matcher<T&>.
|
// Tests that MatcherCast<T>(m) works when m is a Matcher<T&>.
|
||||||
@ -445,6 +483,12 @@ TEST(MatcherCastTest, FromReferenceToNonReference) {
|
|||||||
Matcher<int> m2 = MatcherCast<int>(m1);
|
Matcher<int> m2 = MatcherCast<int>(m1);
|
||||||
EXPECT_TRUE(m2.Matches(0));
|
EXPECT_TRUE(m2.Matches(0));
|
||||||
EXPECT_FALSE(m2.Matches(1));
|
EXPECT_FALSE(m2.Matches(1));
|
||||||
|
|
||||||
|
// Of course, reference identity isn't preserved since a copy is required.
|
||||||
|
int n = 0;
|
||||||
|
Matcher<int&> m3 = Ref(n);
|
||||||
|
Matcher<int> m4 = MatcherCast<int>(m3);
|
||||||
|
EXPECT_FALSE(m4.Matches(n));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tests that MatcherCast<const T&>(m) works when m is a Matcher<T>.
|
// Tests that MatcherCast<const T&>(m) works when m is a Matcher<T>.
|
||||||
@ -649,6 +693,16 @@ TEST(SafeMatcherCastTest, FromBaseClass) {
|
|||||||
EXPECT_FALSE(m4.Matches(d2));
|
EXPECT_FALSE(m4.Matches(d2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tests that SafeMatcherCast<T>(m) works when m is a Matcher<const T&>.
|
||||||
|
TEST(SafeMatcherCastTest, FromConstReferenceToNonReference) {
|
||||||
|
int n = 0;
|
||||||
|
Matcher<const int&> m1 = Ref(n);
|
||||||
|
Matcher<int> m2 = SafeMatcherCast<int>(m1);
|
||||||
|
int n1 = 0;
|
||||||
|
EXPECT_TRUE(m2.Matches(n));
|
||||||
|
EXPECT_FALSE(m2.Matches(n1));
|
||||||
|
}
|
||||||
|
|
||||||
// Tests that SafeMatcherCast<T&>(m) works when m is a Matcher<const T&>.
|
// Tests that SafeMatcherCast<T&>(m) works when m is a Matcher<const T&>.
|
||||||
TEST(SafeMatcherCastTest, FromConstReferenceToReference) {
|
TEST(SafeMatcherCastTest, FromConstReferenceToReference) {
|
||||||
int n = 0;
|
int n = 0;
|
||||||
|
@ -32,6 +32,7 @@
|
|||||||
// This file tests some commonly used argument matchers.
|
// This file tests some commonly used argument matchers.
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include <cstdint>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
#include <string>
|
#include <string>
|
||||||
@ -747,6 +748,24 @@ TYPED_TEST(OptionalTest, DoesNotMatchNullopt) {
|
|||||||
EXPECT_FALSE(m.Matches(empty));
|
EXPECT_FALSE(m.Matches(empty));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TYPED_TEST(OptionalTest, ComposesWithMonomorphicMatchersTakingReferences) {
|
||||||
|
const Matcher<const int&> eq1 = Eq(1);
|
||||||
|
const Matcher<const int&> eq2 = Eq(2);
|
||||||
|
TypeParam opt(1);
|
||||||
|
EXPECT_THAT(opt, Optional(eq1));
|
||||||
|
EXPECT_THAT(opt, Optional(Not(eq2)));
|
||||||
|
EXPECT_THAT(opt, Optional(AllOf(eq1, Not(eq2))));
|
||||||
|
}
|
||||||
|
|
||||||
|
TYPED_TEST(OptionalTest, ComposesWithMonomorphicMatchersRequiringConversion) {
|
||||||
|
const Matcher<int64_t> eq1 = Eq(1);
|
||||||
|
const Matcher<int64_t> eq2 = Eq(2);
|
||||||
|
TypeParam opt(1);
|
||||||
|
EXPECT_THAT(opt, Optional(eq1));
|
||||||
|
EXPECT_THAT(opt, Optional(Not(eq2)));
|
||||||
|
EXPECT_THAT(opt, Optional(AllOf(eq1, Not(eq2))));
|
||||||
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class MoveOnlyOptionalTest : public testing::Test {};
|
class MoveOnlyOptionalTest : public testing::Test {};
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user