summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--docs/gmock_cook_book.md4
-rw-r--r--googlemock/include/gmock/gmock-matchers.h30
-rw-r--r--googlemock/test/gmock-matchers-comparisons_test.cc60
-rw-r--r--googlemock/test/gmock-matchers-misc_test.cc19
4 files changed, 96 insertions, 17 deletions
diff --git a/docs/gmock_cook_book.md b/docs/gmock_cook_book.md
index 948abe9..633dbc3 100644
--- a/docs/gmock_cook_book.md
+++ b/docs/gmock_cook_book.md
@@ -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
other words, any value representable by `T` can also be represented by `U`);
and
-3. When `U` is a reference, `T` must also be a reference (as the underlying
- matcher may be interested in the address of the `U` value).
+3. When `U` is a non-const reference, `T` must also be a reference (as the
+ 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.
diff --git a/googlemock/include/gmock/gmock-matchers.h b/googlemock/include/gmock/gmock-matchers.h
index 8f82b27..18cbe16 100644
--- a/googlemock/include/gmock/gmock-matchers.h
+++ b/googlemock/include/gmock/gmock-matchers.h
@@ -408,13 +408,22 @@ class MatcherCastImpl<T, Matcher<U>> {
}
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:
explicit Impl(const Matcher<U>& source_matcher)
: source_matcher_(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<
typename std::remove_reference<T>::type>::type>::type;
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.
// Otherwise, let implicit conversions do the trick.
- using CastType =
- typename std::conditional<std::is_convertible<T&, const U&>::value,
- T&, U>::type;
+ using CastType = typename std::conditional<
+ std::is_convertible<ImplArgT&, const U&>::value, ImplArgT&, U>::type;
return source_matcher_.MatchAndExplain(static_cast<CastType>(x),
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
// 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>.
-// 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
-// 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>
inline Matcher<T> SafeMatcherCast(const Matcher<U>& matcher) {
// Enforce that T can be implicitly converted to U.
static_assert(std::is_convertible<const T&, const U&>::value,
- "T must be implicitly convertible to U");
- // Enforce that we are not converting a non-reference type T to a reference
- // type U.
- static_assert(std::is_reference<T>::value || !std::is_reference<U>::value,
- "cannot convert non reference arg to reference");
+ "T must be implicitly convertible to U (and T must be a "
+ "non-const reference if U is a non-const reference)");
// In case both T and U are arithmetic types, enforce that the
// conversion is not lossy.
typedef GTEST_REMOVE_REFERENCE_AND_CONST_(T) RawT;
diff --git a/googlemock/test/gmock-matchers-comparisons_test.cc b/googlemock/test/gmock-matchers-comparisons_test.cc
index a324c4c..a331aec 100644
--- a/googlemock/test/gmock-matchers-comparisons_test.cc
+++ b/googlemock/test/gmock-matchers-comparisons_test.cc
@@ -411,9 +411,27 @@ class IntValue {
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.
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
// can be statically converted to U.
TEST(MatcherCastTest, FromCompatibleType) {
@@ -429,14 +447,34 @@ TEST(MatcherCastTest, FromCompatibleType) {
// predicate.
EXPECT_TRUE(m4.Matches(1));
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&>.
TEST(MatcherCastTest, FromConstReferenceToNonReference) {
- Matcher<const int&> m1 = Eq(0);
+ int n = 0;
+ Matcher<const int&> m1 = Ref(n);
Matcher<int> m2 = MatcherCast<int>(m1);
- EXPECT_TRUE(m2.Matches(0));
- EXPECT_FALSE(m2.Matches(1));
+ int n1 = 0;
+ 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&>.
@@ -445,6 +483,12 @@ TEST(MatcherCastTest, FromReferenceToNonReference) {
Matcher<int> m2 = MatcherCast<int>(m1);
EXPECT_TRUE(m2.Matches(0));
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>.
@@ -649,6 +693,16 @@ TEST(SafeMatcherCastTest, FromBaseClass) {
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&>.
TEST(SafeMatcherCastTest, FromConstReferenceToReference) {
int n = 0;
diff --git a/googlemock/test/gmock-matchers-misc_test.cc b/googlemock/test/gmock-matchers-misc_test.cc
index ac976dd..de8b76c 100644
--- a/googlemock/test/gmock-matchers-misc_test.cc
+++ b/googlemock/test/gmock-matchers-misc_test.cc
@@ -32,6 +32,7 @@
// This file tests some commonly used argument matchers.
#include <array>
+#include <cstdint>
#include <memory>
#include <ostream>
#include <string>
@@ -747,6 +748,24 @@ TYPED_TEST(OptionalTest, DoesNotMatchNullopt) {
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>
class MoveOnlyOptionalTest : public testing::Test {};