From 4ee4b17bf5ae1bf6cdb95693c174b8830898c00b Mon Sep 17 00:00:00 2001 From: Zhanyong Wan Date: Tue, 11 Mar 2025 06:58:07 -0700 Subject: [PATCH] Allow `DistanceFrom()` to use user-defined `abs()` by default. `std::abs()` doesn't work on custom types. While one can use the 3-argument version of `DistanceFrom()` to specify how to compute the distance, it's not as convenient as defining `abs()` for the custom type once in the type's namespace and then use the 2-argument version. PiperOrigin-RevId: 735741409 Change-Id: If8fb668455eb963a2ccf089f7467c64965a2e7a6 --- docs/reference/matchers.md | 2 +- googlemock/include/gmock/gmock-matchers.h | 6 ++- .../test/gmock-matchers-arithmetic_test.cc | 37 +++++++++++++++++++ 3 files changed, 42 insertions(+), 3 deletions(-) diff --git a/docs/reference/matchers.md b/docs/reference/matchers.md index 1fc45f2c..8ff9e0bc 100644 --- a/docs/reference/matchers.md +++ b/docs/reference/matchers.md @@ -42,7 +42,7 @@ Matcher | Description | `Lt(value)` | `argument < value` | | `Ne(value)` | `argument != value` | | `IsFalse()` | `argument` evaluates to `false` in a Boolean context. | -| `DistanceFrom(target, m)` | The distance between `argument` and `target` (computed by `std::abs(argument - target)`) matches `m`. | +| `DistanceFrom(target, m)` | The distance between `argument` and `target` (computed by `abs(argument - target)`) matches `m`. | | `DistanceFrom(target, get_distance, m)` | The distance between `argument` and `target` (computed by `get_distance(argument, target)`) matches `m`. | | `IsTrue()` | `argument` evaluates to `true` in a Boolean context. | | `IsNull()` | `argument` is a `NULL` pointer (raw or smart). | diff --git a/googlemock/include/gmock/gmock-matchers.h b/googlemock/include/gmock/gmock-matchers.h index 49a6ff2e..fbc072b7 100644 --- a/googlemock/include/gmock/gmock-matchers.h +++ b/googlemock/include/gmock/gmock-matchers.h @@ -3042,7 +3042,9 @@ auto Second(T& x, Rank1) -> decltype((x.second)) { // NOLINT struct DefaultGetDistance { template auto operator()(const T& lhs, const U& rhs) const { - return std::abs(lhs - rhs); + using std::abs; + // Allow finding abs() in the type's namespace via ADL. + return abs(lhs - rhs); } }; @@ -4470,7 +4472,7 @@ inline internal::FloatingEqMatcher DoubleNear(double rhs, // // 1. compute the distance between the value and the target using // get_distance(value, target) if get_distance is provided; otherwise compute -// the distance as std::abs(value - target). +// the distance as abs(value - target). // 2. match the distance against the user-provided matcher m; if the match // succeeds, the DistanceFrom() match succeeds. // diff --git a/googlemock/test/gmock-matchers-arithmetic_test.cc b/googlemock/test/gmock-matchers-arithmetic_test.cc index 6a3fc89c..b6c35119 100644 --- a/googlemock/test/gmock-matchers-arithmetic_test.cc +++ b/googlemock/test/gmock-matchers-arithmetic_test.cc @@ -544,6 +544,43 @@ TEST(DistanceFrom, CanCustomizeDistanceAndComparator) { EXPECT_FALSE(m.Matches(Double(0.61))); } +// For testing using DistanceFrom() with a type that supports both - and abs. +class Float { + public: + explicit Float(float value) : value_(value) {} + Float(const Float& other) = default; + float value() const { return value_; } + + private: + float value_ = 0.0f; +}; + +// Returns the difference between two Float values. This must be defined in the +// same namespace as Float. +Float operator-(const Float& lhs, const Float& rhs) { + return Float(lhs.value() - rhs.value()); +} + +// Returns the absolute value of a Float value. This must be defined in the +// same namespace as Float. +Float abs(Float value) { return Float(std::abs(value.value())); } + +// Returns true if and only if the first Float value is less than the second +// Float value. This must be defined in the same namespace as Float. +bool operator<(const Float& lhs, const Float& rhs) { + return lhs.value() < rhs.value(); +} + +// Tests that DistanceFrom() can be used with a type that supports both - and +// abs. +TEST(DistanceFrom, CanBeUsedWithTypeThatSupportsBothMinusAndAbs) { + const Matcher m = DistanceFrom(Float(0.5f), Lt(Float(0.1f))); + EXPECT_TRUE(m.Matches(Float(0.45f))); + EXPECT_TRUE(m.Matches(Float(0.55f))); + EXPECT_FALSE(m.Matches(Float(0.39f))); + EXPECT_FALSE(m.Matches(Float(0.61f))); +} + // Tests that Not(m) matches any value that doesn't match m. TEST(NotTest, NegatesMatcher) { Matcher m;