From db22c227826b82e1ad05d6c47facfef73c99e057 Mon Sep 17 00:00:00 2001 From: "zhanyong.wan" Date: Thu, 28 Jan 2010 21:52:29 +0000 Subject: [PATCH] BREAKING CHANGE: drops the old matcher API. See http://code.google.com/p/googlemock/wiki/FrequentlyAskedQuestions for details. --- include/gmock/gmock-matchers.h | 280 +++++++------------------- test/gmock-generated-matchers_test.cc | 14 +- test/gmock-matchers_test.cc | 61 +++--- 3 files changed, 114 insertions(+), 241 deletions(-) diff --git a/include/gmock/gmock-matchers.h b/include/gmock/gmock-matchers.h index 9c457730..ae7e131d 100644 --- a/include/gmock/gmock-matchers.h +++ b/include/gmock/gmock-matchers.h @@ -108,10 +108,7 @@ class MatcherInterface { // Returns true iff the matcher matches x; also explains the match // result to 'listener'. // - // You should override this method when defining a new matcher. For - // backward compatibility, we provide a default implementation that - // just forwards to the old, deprecated matcher API (Matches() and - // ExplainMatchResultTo()). + // You should override this method when defining a new matcher. // // It's the responsibility of the caller (Google Mock) to guarantee // that 'listener' is not NULL. This helps to simplify a matcher's @@ -119,19 +116,7 @@ class MatcherInterface { // can talk to 'listener' without checking its validity first. // However, in order to implement dummy listeners efficiently, // listener->stream() may be NULL. - virtual bool MatchAndExplain(T x, MatchResultListener* listener) const { - const bool match = Matches(x); - if (listener->stream() != NULL) { - ExplainMatchResultTo(x, listener->stream()); - } - return match; - } - - // DEPRECATED. This method will be removed. Override - // MatchAndExplain() instead. - // - // Returns true iff the matcher matches x. - virtual bool Matches(T /* x */) const { return false; } + virtual bool MatchAndExplain(T x, MatchResultListener* listener) const = 0; // Describes this matcher to an ostream. virtual void DescribeTo(::std::ostream* os) const = 0; @@ -147,18 +132,6 @@ class MatcherInterface { DescribeTo(os); *os << ")"; } - - // DEPRECATED. This method will be removed. Override - // MatchAndExplain() instead. - // - // Explains why x matches, or doesn't match, the matcher. Override - // this to provide any additional information that helps a user - // understand the match result. - virtual void ExplainMatchResultTo(T /* x */, ::std::ostream* /* os */) const { - // By default, nothing more needs to be explained, as Google Mock - // has already printed the value of x when this function is - // called. - } }; namespace internal { @@ -254,38 +227,6 @@ class MatcherBase { ::testing::internal::linked_ptr > impl_; }; -// The default implementation of ExplainMatchResultTo() for -// polymorphic matchers. -template -inline void ExplainMatchResultTo(const PolymorphicMatcherImpl& /* impl */, - const T& /* x */, - ::std::ostream* /* os */) { - // By default, nothing more needs to be said, as Google Mock already - // prints the value of x elsewhere. -} - -// The default implementation of MatchAndExplain() for polymorphic -// matchers. The type of argument x cannot be const T&, in case -// impl.Matches() takes a non-const reference. -template -inline bool MatchAndExplain(const PolymorphicMatcherImpl& impl, - T& x, - MatchResultListener* listener) { - const bool match = impl.Matches(x); - - ::std::ostream* const os = listener->stream(); - if (os != NULL) { - using ::testing::internal::ExplainMatchResultTo; - // When resolving the following call, both - // ::testing::internal::ExplainMatchResultTo() and - // foo::ExplainMatchResultTo() are considered, where foo is the - // namespace where class PolymorphicMatcherImpl is defined. - ExplainMatchResultTo(impl, x, os); - } - - return match; -} - } // namespace internal // A Matcher is a copyable and IMMUTABLE (except by assignment) @@ -350,29 +291,12 @@ class Matcher // polymorphic matcher (i.e. a matcher that can match values of more // than one type, e.g. Eq(n) and NotNull()). // -// To define a polymorphic matcher in the old, deprecated way, a user -// first provides an Impl class that has a Matches() method, a -// DescribeTo() method, and a DescribeNegationTo() method. The -// Matches() method is usually a method template (such that it works -// with multiple types). Then the user creates the polymorphic -// matcher using MakePolymorphicMatcher(). To provide additional -// explanation to the match result, define a FREE function (or -// function template) +// To define a polymorphic matcher, a user should provide an Impl +// class that has a DescribeTo() method and a DescribeNegationTo() +// method, and define a member function (or member function template) // -// void ExplainMatchResultTo(const Impl& matcher, const Value& value, -// ::std::ostream* os); -// -// in the SAME NAME SPACE where Impl is defined. -// -// The new, recommended way to define a polymorphic matcher is to -// provide an Impl class that has a DescribeTo() method and a -// DescribeNegationTo() method, and define a FREE function (or -// function template) -// -// bool MatchAndExplain(const Impl& matcher, const Value& value, -// MatchResultListener* listener); -// -// in the SAME NAME SPACE where Impl is defined. +// bool MatchAndExplain(const Value& value, +// MatchResultListener* listener) const; // // See the definition of NotNull() for a complete example. template @@ -408,14 +332,7 @@ class PolymorphicMatcher { } virtual bool MatchAndExplain(T x, MatchResultListener* listener) const { - // C++ uses Argument-Dependent Look-up (aka Koenig Look-up) to - // resolve the call to MatchAndExplain() here. This means that - // if there's a MatchAndExplain() function defined in the name - // space where class Impl is defined, it will be picked by the - // compiler as the better match. Otherwise the default - // implementation of it in ::testing::internal will be picked. - using ::testing::internal::MatchAndExplain; - return MatchAndExplain(impl_, x, listener); + return impl_.MatchAndExplain(x, listener); } private: @@ -578,7 +495,7 @@ class TuplePrefix { // We remove the reference in type Value to prevent the // universal printer from printing the address of value, which // isn't interesting to the user most of the time. The - // matcher's ExplainMatchResultTo() method handles the case when + // matcher's MatchAndExplain() method handles the case when // the address is interesting. internal::UniversalPrinter:: Print(value, os); @@ -782,7 +699,10 @@ GMOCK_IMPLEMENT_COMPARISON_MATCHER_(Ne, !=, "not equal to"); class IsNullMatcher { public: template - bool Matches(const Pointer& p) const { return GetRawPointer(p) == NULL; } + bool MatchAndExplain(const Pointer& p, + MatchResultListener* /* listener */) const { + return GetRawPointer(p) == NULL; + } void DescribeTo(::std::ostream* os) const { *os << "is NULL"; } void DescribeNegationTo(::std::ostream* os) const { @@ -790,18 +710,15 @@ class IsNullMatcher { } }; -template -bool MatchAndExplain(const IsNullMatcher& impl, Pointer& p, - MatchResultListener* /* listener */) { - return impl.Matches(p); -} - // Implements the polymorphic NotNull() matcher, which matches any raw or smart // pointer that is not NULL. class NotNullMatcher { public: template - bool Matches(const Pointer& p) const { return GetRawPointer(p) != NULL; } + bool MatchAndExplain(const Pointer& p, + MatchResultListener* /* listener */) const { + return GetRawPointer(p) != NULL; + } void DescribeTo(::std::ostream* os) const { *os << "is not NULL"; } void DescribeNegationTo(::std::ostream* os) const { @@ -809,12 +726,6 @@ class NotNullMatcher { } }; -template -bool MatchAndExplain(const NotNullMatcher& impl, Pointer& p, - MatchResultListener* /* listener */) { - return impl.Matches(p); -} - // Ref(variable) matches any argument that is a reference to // 'variable'. This matcher is polymorphic as it can match any // super type of the type of 'variable'. @@ -860,8 +771,8 @@ class RefMatcher { public: explicit Impl(Super& x) : object_(x) {} // NOLINT - // Matches() takes a Super& (as opposed to const Super&) in - // order to match the interface MatcherInterface. + // MatchAndExplain() takes a Super& (as opposed to const Super&) + // in order to match the interface MatcherInterface. virtual bool MatchAndExplain( Super& x, MatchResultListener* listener) const { *listener << "is located @" << static_cast(&x); @@ -936,14 +847,16 @@ class StrEqualityMatcher { // When expect_eq_ is true, returns true iff s is equal to string_; // otherwise returns true iff s is not equal to string_. - bool Matches(ConstCharPointer s) const { + bool MatchAndExplain(ConstCharPointer s, + MatchResultListener* listener) const { if (s == NULL) { return !expect_eq_; } - return Matches(StringType(s)); + return MatchAndExplain(StringType(s), listener); } - bool Matches(const StringType& s) const { + bool MatchAndExplain(const StringType& s, + MatchResultListener* /* listener */) const { const bool eq = case_sensitive_ ? s == string_ : CaseInsensitiveStringEquals(s, string_); return expect_eq_ == eq; @@ -977,12 +890,6 @@ class StrEqualityMatcher { GTEST_DISALLOW_ASSIGN_(StrEqualityMatcher); }; -template -bool MatchAndExplain(const StrEqualityMatcher& impl, T& s, - MatchResultListener* /* listener */) { - return impl.Matches(s); -} - // Implements the polymorphic HasSubstr(substring) matcher, which // can be used as a Matcher as long as T can be converted to a // string. @@ -997,11 +904,13 @@ class HasSubstrMatcher { // These overloaded methods allow HasSubstr(substring) to be used as a // Matcher as long as T can be converted to string. Returns true // iff s contains substring_ as a substring. - bool Matches(ConstCharPointer s) const { - return s != NULL && Matches(StringType(s)); + bool MatchAndExplain(ConstCharPointer s, + MatchResultListener* listener) const { + return s != NULL && MatchAndExplain(StringType(s), listener); } - bool Matches(const StringType& s) const { + bool MatchAndExplain(const StringType& s, + MatchResultListener* /* listener */) const { return s.find(substring_) != StringType::npos; } @@ -1022,12 +931,6 @@ class HasSubstrMatcher { GTEST_DISALLOW_ASSIGN_(HasSubstrMatcher); }; -template -bool MatchAndExplain(const HasSubstrMatcher& impl, T& s, - MatchResultListener* /* listener */) { - return impl.Matches(s); -} - // Implements the polymorphic StartsWith(substring) matcher, which // can be used as a Matcher as long as T can be converted to a // string. @@ -1042,11 +945,13 @@ class StartsWithMatcher { // These overloaded methods allow StartsWith(prefix) to be used as a // Matcher as long as T can be converted to string. Returns true // iff s starts with prefix_. - bool Matches(ConstCharPointer s) const { - return s != NULL && Matches(StringType(s)); + bool MatchAndExplain(ConstCharPointer s, + MatchResultListener* listener) const { + return s != NULL && MatchAndExplain(StringType(s), listener); } - bool Matches(const StringType& s) const { + bool MatchAndExplain(const StringType& s, + MatchResultListener* /* listener */) const { return s.length() >= prefix_.length() && s.substr(0, prefix_.length()) == prefix_; } @@ -1067,12 +972,6 @@ class StartsWithMatcher { GTEST_DISALLOW_ASSIGN_(StartsWithMatcher); }; -template -bool MatchAndExplain(const StartsWithMatcher& impl, T& s, - MatchResultListener* /* listener */) { - return impl.Matches(s); -} - // Implements the polymorphic EndsWith(substring) matcher, which // can be used as a Matcher as long as T can be converted to a // string. @@ -1086,11 +985,13 @@ class EndsWithMatcher { // These overloaded methods allow EndsWith(suffix) to be used as a // Matcher as long as T can be converted to string. Returns true // iff s ends with suffix_. - bool Matches(ConstCharPointer s) const { - return s != NULL && Matches(StringType(s)); + bool MatchAndExplain(ConstCharPointer s, + MatchResultListener* listener) const { + return s != NULL && MatchAndExplain(StringType(s), listener); } - bool Matches(const StringType& s) const { + bool MatchAndExplain(const StringType& s, + MatchResultListener* /* listener */) const { return s.length() >= suffix_.length() && s.substr(s.length() - suffix_.length()) == suffix_; } @@ -1111,12 +1012,6 @@ class EndsWithMatcher { GTEST_DISALLOW_ASSIGN_(EndsWithMatcher); }; -template -bool MatchAndExplain(const EndsWithMatcher& impl, T& s, - MatchResultListener* /* listener */) { - return impl.Matches(s); -} - // Implements polymorphic matchers MatchesRegex(regex) and // ContainsRegex(regex), which can be used as a Matcher as long as // T can be converted to a string. @@ -1129,11 +1024,13 @@ class MatchesRegexMatcher { // a Matcher as long as T can be converted to string. Returns // true iff s matches regular expression regex. When full_match_ is // true, a full match is done; otherwise a partial match is done. - bool Matches(const char* s) const { - return s != NULL && Matches(internal::string(s)); + bool MatchAndExplain(const char* s, + MatchResultListener* listener) const { + return s != NULL && MatchAndExplain(internal::string(s), listener); } - bool Matches(const internal::string& s) const { + bool MatchAndExplain(const internal::string& s, + MatchResultListener* /* listener */) const { return full_match_ ? RE::FullMatch(s, *regex_) : RE::PartialMatch(s, *regex_); } @@ -1157,12 +1054,6 @@ class MatchesRegexMatcher { GTEST_DISALLOW_ASSIGN_(MatchesRegexMatcher); }; -template -bool MatchAndExplain(const MatchesRegexMatcher& impl, T& s, - MatchResultListener* /* listener */) { - return impl.Matches(s); -} - // Implements a matcher that compares the two fields of a 2-tuple // using one of the ==, <=, <, etc, operators. The two fields being // compared don't have to have the same type. @@ -1438,7 +1329,8 @@ class TrulyMatcher { // argument is passed by reference as the predicate may be // interested in the address of the argument. template - bool Matches(T& x) const { // NOLINT + bool MatchAndExplain(T& x, // NOLINT + MatchResultListener* /* listener */) const { #if GTEST_OS_WINDOWS // MSVC warns about converting a value into bool (warning 4800). #pragma warning(push) // Saves the current warning state. @@ -1464,12 +1356,6 @@ class TrulyMatcher { GTEST_DISALLOW_ASSIGN_(TrulyMatcher); }; -template -bool MatchAndExplain(const TrulyMatcher& impl, T& x, - MatchResultListener* /* listener */) { - return impl.Matches(x); -} - // Used for implementing Matches(matcher), which turns a matcher into // a predicate. template @@ -1744,11 +1630,20 @@ class FieldMatcher { matcher_.DescribeNegationTo(os); } - // The first argument of MatchAndExplain() is needed to help + template + bool MatchAndExplain(const T& value, MatchResultListener* listener) const { + return MatchAndExplainImpl( + typename ::testing::internal:: + is_pointer::type(), + value, listener); + } + + private: + // The first argument of MatchAndExplainImpl() is needed to help // Symbian's C++ compiler choose which overload to use. Its type is // true_type iff the Field() matcher is used to match a pointer. - bool MatchAndExplain(false_type /* is_not_pointer */, const Class& obj, - MatchResultListener* listener) const { + bool MatchAndExplainImpl(false_type /* is_not_pointer */, const Class& obj, + MatchResultListener* listener) const { StringMatchResultListener inner_listener; const bool match = matcher_.MatchAndExplain(obj.*field_, &inner_listener); const internal::string s = inner_listener.str(); @@ -1758,32 +1653,23 @@ class FieldMatcher { return match; } - bool MatchAndExplain(true_type /* is_pointer */, const Class* p, - MatchResultListener* listener) const { + bool MatchAndExplainImpl(true_type /* is_pointer */, const Class* p, + MatchResultListener* listener) const { if (p == NULL) return false; // Since *p has a field, it must be a class/struct/union type and // thus cannot be a pointer. Therefore we pass false_type() as // the first argument. - return MatchAndExplain(false_type(), *p, listener); + return MatchAndExplainImpl(false_type(), *p, listener); } - private: const FieldType Class::*field_; const Matcher matcher_; GTEST_DISALLOW_ASSIGN_(FieldMatcher); }; -template -bool MatchAndExplain(const FieldMatcher& matcher, - T& value, MatchResultListener* listener) { - return matcher.MatchAndExplain( - typename ::testing::internal::is_pointer::type(), - value, listener); -} - // Implements the Property() matcher for matching a property // (i.e. return value of a getter method) of an object. template @@ -1809,11 +1695,20 @@ class PropertyMatcher { matcher_.DescribeNegationTo(os); } - // The first argument of MatchAndExplain() is needed to help + template + bool MatchAndExplain(const T&value, MatchResultListener* listener) const { + return MatchAndExplainImpl( + typename ::testing::internal:: + is_pointer::type(), + value, listener); + } + + private: + // The first argument of MatchAndExplainImpl() is needed to help // Symbian's C++ compiler choose which overload to use. Its type is // true_type iff the Property() matcher is used to match a pointer. - bool MatchAndExplain(false_type /* is_not_pointer */, const Class& obj, - MatchResultListener* listener) const { + bool MatchAndExplainImpl(false_type /* is_not_pointer */, const Class& obj, + MatchResultListener* listener) const { StringMatchResultListener inner_listener; const bool match = matcher_.MatchAndExplain((obj.*property_)(), &inner_listener); @@ -1824,32 +1719,23 @@ class PropertyMatcher { return match; } - bool MatchAndExplain(true_type /* is_pointer */, const Class* p, - MatchResultListener* listener) const { + bool MatchAndExplainImpl(true_type /* is_pointer */, const Class* p, + MatchResultListener* listener) const { if (p == NULL) return false; // Since *p has a property method, it must be a class/struct/union // type and thus cannot be a pointer. Therefore we pass // false_type() as the first argument. - return MatchAndExplain(false_type(), *p, listener); + return MatchAndExplainImpl(false_type(), *p, listener); } - private: PropertyType (Class::*property_)() const; const Matcher matcher_; GTEST_DISALLOW_ASSIGN_(PropertyMatcher); }; -template -bool MatchAndExplain(const PropertyMatcher& matcher, - T& value, MatchResultListener* listener) { - return matcher.MatchAndExplain( - typename ::testing::internal::is_pointer::type(), - value, listener); -} - // Type traits specifying various features of different functors for ResultOf. // The default template specifies features for functor objects. // Functor classes have to typedef argument_type and result_type @@ -1947,13 +1833,6 @@ class ResultOfMatcher { GTEST_DISALLOW_ASSIGN_(ResultOfMatcher); }; -// Explains the result of matching a value against a functor matcher. -template -void ExplainMatchResultTo(const ResultOfMatcher& matcher, - T obj, ::std::ostream* os) { - matcher.ExplainMatchResultTo(obj, os); -} - // Implements an equality matcher for any STL-style container whose elements // support ==. This matcher is like Eq(), but its failure explanations provide // more detailed information that is useful when the container is used as a set. @@ -2048,13 +1927,6 @@ class ContainerEqMatcher { GTEST_DISALLOW_ASSIGN_(ContainerEqMatcher); }; -template -bool MatchAndExplain(const ContainerEqMatcher& matcher, - LhsContainer& lhs, - MatchResultListener* listener) { - return matcher.MatchAndExplain(lhs, listener); -} - // Implements Contains(element_matcher) for the given argument type Container. template class ContainsMatcherImpl : public MatcherInterface { diff --git a/test/gmock-generated-matchers_test.cc b/test/gmock-generated-matchers_test.cc index 40c2367c..5e14c42a 100644 --- a/test/gmock-generated-matchers_test.cc +++ b/test/gmock-generated-matchers_test.cc @@ -68,6 +68,7 @@ using testing::Lt; using testing::MakeMatcher; using testing::Matcher; using testing::MatcherInterface; +using testing::MatchResultListener; using testing::Ne; using testing::Not; using testing::Pointee; @@ -217,21 +218,22 @@ class GreaterThanMatcher : public MatcherInterface { public: explicit GreaterThanMatcher(int rhs) : rhs_(rhs) {} - virtual bool Matches(int lhs) const { return lhs > rhs_; } - virtual void DescribeTo(::std::ostream* os) const { *os << "is greater than " << rhs_; } - virtual void ExplainMatchResultTo(int lhs, ::std::ostream* os) const { + virtual bool MatchAndExplain(int lhs, + MatchResultListener* listener) const { const int diff = lhs - rhs_; if (diff > 0) { - *os << "is " << diff << " more than " << rhs_; + *listener << "is " << diff << " more than " << rhs_; } else if (diff == 0) { - *os << "is the same as " << rhs_; + *listener << "is the same as " << rhs_; } else { - *os << "is " << -diff << " less than " << rhs_; + *listener << "is " << -diff << " less than " << rhs_; } + + return lhs > rhs_; } private: diff --git a/test/gmock-matchers_test.cc b/test/gmock-matchers_test.cc index 555cc228..b674cd8a 100644 --- a/test/gmock-matchers_test.cc +++ b/test/gmock-matchers_test.cc @@ -135,21 +135,22 @@ class GreaterThanMatcher : public MatcherInterface { public: explicit GreaterThanMatcher(int rhs) : rhs_(rhs) {} - virtual bool Matches(int lhs) const { return lhs > rhs_; } - virtual void DescribeTo(::std::ostream* os) const { *os << "is greater than " << rhs_; } - virtual void ExplainMatchResultTo(int lhs, ::std::ostream* os) const { + virtual bool MatchAndExplain(int lhs, + MatchResultListener* listener) const { const int diff = lhs - rhs_; if (diff > 0) { - *os << "is " << diff << " more than " << rhs_; + *listener << "is " << diff << " more than " << rhs_; } else if (diff == 0) { - *os << "is the same as " << rhs_; + *listener << "is the same as " << rhs_; } else { - *os << "is " << -diff << " less than " << rhs_; + *listener << "is " << -diff << " less than " << rhs_; } + + return lhs > rhs_; } private: @@ -188,7 +189,10 @@ string Explain(const MatcherType& m, const Value& x) { // change. class EvenMatcherImpl : public MatcherInterface { public: - virtual bool Matches(int x) const { return x % 2 == 0; } + virtual bool MatchAndExplain(int x, + MatchResultListener* /* listener */) const { + return x % 2 == 0; + } virtual void DescribeTo(::std::ostream* os) const { *os << "is an even number"; @@ -330,7 +334,8 @@ const int bar = 1; class ReferencesBarOrIsZeroImpl { public: template - bool Matches(const T& x) const { + bool MatchAndExplain(const T& x, + MatchResultListener* /* listener */) const { const void* p = &x; return p == &bar || x == 0; } @@ -373,20 +378,19 @@ class PolymorphicIsEvenImpl { void DescribeNegationTo(::std::ostream* os) const { *os << "is odd"; } -}; -template -bool MatchAndExplain(const PolymorphicIsEvenImpl& /* impl */, - T x, MatchResultListener* listener) { - // Verifies that we can stream to the listener directly. - *listener << "% " << 2; - if (listener->stream() != NULL) { - // Verifies that we can stream to the listener's underlying stream - // too. - *listener->stream() << " == " << (x % 2); + template + bool MatchAndExplain(const T& x, MatchResultListener* listener) const { + // Verifies that we can stream to the listener directly. + *listener << "% " << 2; + if (listener->stream() != NULL) { + // Verifies that we can stream to the listener's underlying stream + // too. + *listener->stream() << " == " << (x % 2); + } + return (x % 2) == 0; } - return (x % 2) == 0; -} +}; PolymorphicMatcher PolymorphicIsEven() { return MakePolymorphicMatcher(PolymorphicIsEvenImpl()); @@ -2135,8 +2139,8 @@ TEST(MatcherAssertionTest, WorksForByRefArguments) { // ASSERT_THAT("hello", starts_with_he) fails to compile with Nokia's // Symbian compiler: it tries to compile // template class MatcherCastImpl { ... -// virtual bool Matches(T x) const { -// return source_matcher_.Matches(static_cast(x)); +// virtual bool MatchAndExplain(T x, ...) const { +// return source_matcher_.MatchAndExplain(static_cast(x), ...); // with U == string and T == const char* // With ASSERT_THAT("hello"...) changed to ASSERT_THAT(string("hello") ... ) // the compiler silently crashes with no output. @@ -3075,8 +3079,11 @@ class DivisibleByImpl { public: explicit DivisibleByImpl(int a_divider) : divider_(a_divider) {} + // For testing using ExplainMatchResultTo() with polymorphic matchers. template - bool Matches(const T& n) const { + bool MatchAndExplain(const T& n, MatchResultListener* listener) const { + *listener << "is " << (n % divider_) << " modulo " + << divider_; return (n % divider_) == 0; } @@ -3095,14 +3102,6 @@ class DivisibleByImpl { int divider_; }; -// For testing using ExplainMatchResultTo() with polymorphic matchers. -template -void ExplainMatchResultTo(const DivisibleByImpl& impl, const T& n, - ::std::ostream* os) { - *os << "is " << (n % impl.divider()) << " modulo " - << impl.divider(); -} - PolymorphicMatcher DivisibleBy(int n) { return MakePolymorphicMatcher(DivisibleByImpl(n)); }