Skip to content

Commit e4ece48

Browse files
Abseil Teamcopybara-github
Abseil Team
authored andcommitted
Enable safe matcher casts from Matcher<const T&> to Matcher<T>.
PiperOrigin-RevId: 715826130 Change-Id: Id962fd456f6da21ea2a909f331f92d814f1dad46
1 parent 504ea69 commit e4ece48

File tree

4 files changed

+96
-17
lines changed

4 files changed

+96
-17
lines changed

docs/gmock_cook_book.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -936,8 +936,8 @@ casts a matcher `m` to type `Matcher<T>`. To ensure safety, gMock checks that
936936
floating-point numbers), the conversion from `T` to `U` is not lossy (in
937937
other words, any value representable by `T` can also be represented by `U`);
938938
and
939-
3. When `U` is a reference, `T` must also be a reference (as the underlying
940-
matcher may be interested in the address of the `U` value).
939+
3. When `U` is a non-const reference, `T` must also be a reference (as the
940+
underlying matcher may be interested in the address of the `U` value).
941941

942942
The code won't compile if any of these conditions isn't met.
943943

googlemock/include/gmock/gmock-matchers.h

+18-12
Original file line numberDiff line numberDiff line change
@@ -408,13 +408,22 @@ class MatcherCastImpl<T, Matcher<U>> {
408408
}
409409

410410
private:
411-
class Impl : public MatcherInterface<T> {
411+
// If it's possible to implicitly convert a `const T&` to U, then `Impl` can
412+
// take that as input to avoid a copy. Otherwise, such as when `T` is a
413+
// non-const reference type or a type explicitly constructible only from a
414+
// non-const reference, then `Impl` must use `T` as-is (potentially copying).
415+
using ImplArgT =
416+
typename std::conditional<std::is_convertible<const T&, const U&>::value,
417+
const T&, T>::type;
418+
419+
class Impl : public MatcherInterface<ImplArgT> {
412420
public:
413421
explicit Impl(const Matcher<U>& source_matcher)
414422
: source_matcher_(source_matcher) {}
415423

416424
// We delegate the matching logic to the source matcher.
417-
bool MatchAndExplain(T x, MatchResultListener* listener) const override {
425+
bool MatchAndExplain(ImplArgT x,
426+
MatchResultListener* listener) const override {
418427
using FromType = typename std::remove_cv<typename std::remove_pointer<
419428
typename std::remove_reference<T>::type>::type>::type;
420429
using ToType = typename std::remove_cv<typename std::remove_pointer<
@@ -431,9 +440,8 @@ class MatcherCastImpl<T, Matcher<U>> {
431440

432441
// Do the cast to `U` explicitly if necessary.
433442
// Otherwise, let implicit conversions do the trick.
434-
using CastType =
435-
typename std::conditional<std::is_convertible<T&, const U&>::value,
436-
T&, U>::type;
443+
using CastType = typename std::conditional<
444+
std::is_convertible<ImplArgT&, const U&>::value, ImplArgT&, U>::type;
437445

438446
return source_matcher_.MatchAndExplain(static_cast<CastType>(x),
439447
listener);
@@ -528,18 +536,16 @@ inline Matcher<T> SafeMatcherCast(const M& polymorphic_matcher_or_value) {
528536
// safely convert a Matcher<U> to a Matcher<T> (i.e. Matcher is
529537
// contravariant): just keep a copy of the original Matcher<U>, convert the
530538
// argument from type T to U, and then pass it to the underlying Matcher<U>.
531-
// The only exception is when U is a reference and T is not, as the
539+
// The only exception is when U is a non-const reference and T is not, as the
532540
// underlying Matcher<U> may be interested in the argument's address, which
533-
// is not preserved in the conversion from T to U.
541+
// cannot be preserved in the conversion from T to U (since a copy of the input
542+
// T argument would be required to provide a non-const reference U).
534543
template <typename T, typename U>
535544
inline Matcher<T> SafeMatcherCast(const Matcher<U>& matcher) {
536545
// Enforce that T can be implicitly converted to U.
537546
static_assert(std::is_convertible<const T&, const U&>::value,
538-
"T must be implicitly convertible to U");
539-
// Enforce that we are not converting a non-reference type T to a reference
540-
// type U.
541-
static_assert(std::is_reference<T>::value || !std::is_reference<U>::value,
542-
"cannot convert non reference arg to reference");
547+
"T must be implicitly convertible to U (and T must be a "
548+
"non-const reference if U is a non-const reference)");
543549
// In case both T and U are arithmetic types, enforce that the
544550
// conversion is not lossy.
545551
typedef GTEST_REMOVE_REFERENCE_AND_CONST_(T) RawT;

googlemock/test/gmock-matchers-comparisons_test.cc

+57-3
Original file line numberDiff line numberDiff line change
@@ -411,9 +411,27 @@ class IntValue {
411411
int value_;
412412
};
413413

414+
// For testing casting matchers between compatible types. This is similar to
415+
// IntValue, but takes a non-const reference to the value, showing MatcherCast
416+
// works with such types (and doesn't, for example, use a const ref internally).
417+
class MutableIntView {
418+
public:
419+
// An int& can be statically (although not implicitly) cast to a
420+
// MutableIntView.
421+
explicit MutableIntView(int& a_value) : value_(a_value) {}
422+
423+
int& value() const { return value_; }
424+
425+
private:
426+
int& value_;
427+
};
428+
414429
// For testing casting matchers between compatible types.
415430
bool IsPositiveIntValue(const IntValue& foo) { return foo.value() > 0; }
416431

432+
// For testing casting matchers between compatible types.
433+
bool IsPositiveMutableIntView(MutableIntView foo) { return foo.value() > 0; }
434+
417435
// Tests that MatcherCast<T>(m) works when m is a Matcher<U> where T
418436
// can be statically converted to U.
419437
TEST(MatcherCastTest, FromCompatibleType) {
@@ -429,14 +447,34 @@ TEST(MatcherCastTest, FromCompatibleType) {
429447
// predicate.
430448
EXPECT_TRUE(m4.Matches(1));
431449
EXPECT_FALSE(m4.Matches(0));
450+
451+
Matcher<MutableIntView> m5 = Truly(IsPositiveMutableIntView);
452+
Matcher<int> m6 = MatcherCast<int>(m5);
453+
// In the following, the arguments 1 and 0 are statically converted to
454+
// MutableIntView objects, and then tested by the IsPositiveMutableIntView()
455+
// predicate.
456+
EXPECT_TRUE(m6.Matches(1));
457+
EXPECT_FALSE(m6.Matches(0));
432458
}
433459

434460
// Tests that MatcherCast<T>(m) works when m is a Matcher<const T&>.
435461
TEST(MatcherCastTest, FromConstReferenceToNonReference) {
436-
Matcher<const int&> m1 = Eq(0);
462+
int n = 0;
463+
Matcher<const int&> m1 = Ref(n);
437464
Matcher<int> m2 = MatcherCast<int>(m1);
438-
EXPECT_TRUE(m2.Matches(0));
439-
EXPECT_FALSE(m2.Matches(1));
465+
int n1 = 0;
466+
EXPECT_TRUE(m2.Matches(n));
467+
EXPECT_FALSE(m2.Matches(n1));
468+
}
469+
470+
// Tests that MatcherCast<T&>(m) works when m is a Matcher<const T&>.
471+
TEST(MatcherCastTest, FromConstReferenceToReference) {
472+
int n = 0;
473+
Matcher<const int&> m1 = Ref(n);
474+
Matcher<int&> m2 = MatcherCast<int&>(m1);
475+
int n1 = 0;
476+
EXPECT_TRUE(m2.Matches(n));
477+
EXPECT_FALSE(m2.Matches(n1));
440478
}
441479

442480
// Tests that MatcherCast<T>(m) works when m is a Matcher<T&>.
@@ -445,6 +483,12 @@ TEST(MatcherCastTest, FromReferenceToNonReference) {
445483
Matcher<int> m2 = MatcherCast<int>(m1);
446484
EXPECT_TRUE(m2.Matches(0));
447485
EXPECT_FALSE(m2.Matches(1));
486+
487+
// Of course, reference identity isn't preserved since a copy is required.
488+
int n = 0;
489+
Matcher<int&> m3 = Ref(n);
490+
Matcher<int> m4 = MatcherCast<int>(m3);
491+
EXPECT_FALSE(m4.Matches(n));
448492
}
449493

450494
// Tests that MatcherCast<const T&>(m) works when m is a Matcher<T>.
@@ -649,6 +693,16 @@ TEST(SafeMatcherCastTest, FromBaseClass) {
649693
EXPECT_FALSE(m4.Matches(d2));
650694
}
651695

696+
// Tests that SafeMatcherCast<T>(m) works when m is a Matcher<const T&>.
697+
TEST(SafeMatcherCastTest, FromConstReferenceToNonReference) {
698+
int n = 0;
699+
Matcher<const int&> m1 = Ref(n);
700+
Matcher<int> m2 = SafeMatcherCast<int>(m1);
701+
int n1 = 0;
702+
EXPECT_TRUE(m2.Matches(n));
703+
EXPECT_FALSE(m2.Matches(n1));
704+
}
705+
652706
// Tests that SafeMatcherCast<T&>(m) works when m is a Matcher<const T&>.
653707
TEST(SafeMatcherCastTest, FromConstReferenceToReference) {
654708
int n = 0;

googlemock/test/gmock-matchers-misc_test.cc

+19
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
// This file tests some commonly used argument matchers.
3333

3434
#include <array>
35+
#include <cstdint>
3536
#include <memory>
3637
#include <ostream>
3738
#include <string>
@@ -747,6 +748,24 @@ TYPED_TEST(OptionalTest, DoesNotMatchNullopt) {
747748
EXPECT_FALSE(m.Matches(empty));
748749
}
749750

751+
TYPED_TEST(OptionalTest, ComposesWithMonomorphicMatchersTakingReferences) {
752+
const Matcher<const int&> eq1 = Eq(1);
753+
const Matcher<const int&> eq2 = Eq(2);
754+
TypeParam opt(1);
755+
EXPECT_THAT(opt, Optional(eq1));
756+
EXPECT_THAT(opt, Optional(Not(eq2)));
757+
EXPECT_THAT(opt, Optional(AllOf(eq1, Not(eq2))));
758+
}
759+
760+
TYPED_TEST(OptionalTest, ComposesWithMonomorphicMatchersRequiringConversion) {
761+
const Matcher<int64_t> eq1 = Eq(1);
762+
const Matcher<int64_t> eq2 = Eq(2);
763+
TypeParam opt(1);
764+
EXPECT_THAT(opt, Optional(eq1));
765+
EXPECT_THAT(opt, Optional(Not(eq2)));
766+
EXPECT_THAT(opt, Optional(AllOf(eq1, Not(eq2))));
767+
}
768+
750769
template <typename T>
751770
class MoveOnlyOptionalTest : public testing::Test {};
752771

0 commit comments

Comments
 (0)