Generate relational matchers (Eq,Lt, etc) with CRTP instead of macro.

This commit is contained in:
kosak 2014-11-17 01:47:54 +00:00
parent d370f85b02
commit 506340a66b
2 changed files with 161 additions and 88 deletions

View File

@ -199,6 +199,31 @@ class StringMatchResultListener : public MatchResultListener {
namespace internal { namespace internal {
struct AnyEq {
template <typename A, typename B>
bool operator()(const A& a, const B& b) const { return a == b; }
};
struct AnyNe {
template <typename A, typename B>
bool operator()(const A& a, const B& b) const { return a != b; }
};
struct AnyLt {
template <typename A, typename B>
bool operator()(const A& a, const B& b) const { return a < b; }
};
struct AnyGt {
template <typename A, typename B>
bool operator()(const A& a, const B& b) const { return a > b; }
};
struct AnyLe {
template <typename A, typename B>
bool operator()(const A& a, const B& b) const { return a <= b; }
};
struct AnyGe {
template <typename A, typename B>
bool operator()(const A& a, const B& b) const { return a >= b; }
};
// A match result listener that ignores the explanation. // A match result listener that ignores the explanation.
class DummyMatchResultListener : public MatchResultListener { class DummyMatchResultListener : public MatchResultListener {
public: public:
@ -862,55 +887,90 @@ class AnythingMatcher {
// used to match an int, a short, a double, etc). Therefore we use // used to match an int, a short, a double, etc). Therefore we use
// a template type conversion operator in the implementation. // a template type conversion operator in the implementation.
// //
// We define this as a macro in order to eliminate duplicated source
// code.
//
// The following template definition assumes that the Rhs parameter is // The following template definition assumes that the Rhs parameter is
// a "bare" type (i.e. neither 'const T' nor 'T&'). // a "bare" type (i.e. neither 'const T' nor 'T&').
#define GMOCK_IMPLEMENT_COMPARISON_MATCHER_( \ template <typename D, typename Rhs, typename Op>
name, op, relation, negated_relation) \ class ComparisonBase {
template <typename Rhs> class name##Matcher { \ public:
public: \ explicit ComparisonBase(const Rhs& rhs) : rhs_(rhs) {}
explicit name##Matcher(const Rhs& rhs) : rhs_(rhs) {} \ template <typename Lhs>
template <typename Lhs> \ operator Matcher<Lhs>() const {
operator Matcher<Lhs>() const { \ return MakeMatcher(new Impl<Lhs>(rhs_));
return MakeMatcher(new Impl<Lhs>(rhs_)); \
} \
private: \
template <typename Lhs> \
class Impl : public MatcherInterface<Lhs> { \
public: \
explicit Impl(const Rhs& rhs) : rhs_(rhs) {} \
virtual bool MatchAndExplain(\
Lhs lhs, MatchResultListener* /* listener */) const { \
return lhs op rhs_; \
} \
virtual void DescribeTo(::std::ostream* os) const { \
*os << relation " "; \
UniversalPrint(rhs_, os); \
} \
virtual void DescribeNegationTo(::std::ostream* os) const { \
*os << negated_relation " "; \
UniversalPrint(rhs_, os); \
} \
private: \
Rhs rhs_; \
GTEST_DISALLOW_ASSIGN_(Impl); \
}; \
Rhs rhs_; \
GTEST_DISALLOW_ASSIGN_(name##Matcher); \
} }
// Implements Eq(v), Ge(v), Gt(v), Le(v), Lt(v), and Ne(v) private:
// respectively. template <typename Lhs>
GMOCK_IMPLEMENT_COMPARISON_MATCHER_(Eq, ==, "is equal to", "isn't equal to"); class Impl : public MatcherInterface<Lhs> {
GMOCK_IMPLEMENT_COMPARISON_MATCHER_(Ge, >=, "is >=", "isn't >="); public:
GMOCK_IMPLEMENT_COMPARISON_MATCHER_(Gt, >, "is >", "isn't >"); explicit Impl(const Rhs& rhs) : rhs_(rhs) {}
GMOCK_IMPLEMENT_COMPARISON_MATCHER_(Le, <=, "is <=", "isn't <="); virtual bool MatchAndExplain(
GMOCK_IMPLEMENT_COMPARISON_MATCHER_(Lt, <, "is <", "isn't <"); Lhs lhs, MatchResultListener* /* listener */) const {
GMOCK_IMPLEMENT_COMPARISON_MATCHER_(Ne, !=, "isn't equal to", "is equal to"); return Op()(lhs, rhs_);
}
virtual void DescribeTo(::std::ostream* os) const {
*os << D::Desc() << " ";
UniversalPrint(rhs_, os);
}
virtual void DescribeNegationTo(::std::ostream* os) const {
*os << D::NegatedDesc() << " ";
UniversalPrint(rhs_, os);
}
private:
Rhs rhs_;
GTEST_DISALLOW_ASSIGN_(Impl);
};
Rhs rhs_;
GTEST_DISALLOW_ASSIGN_(ComparisonBase);
};
#undef GMOCK_IMPLEMENT_COMPARISON_MATCHER_ template <typename Rhs>
class EqMatcher : public ComparisonBase<EqMatcher<Rhs>, Rhs, AnyEq> {
public:
explicit EqMatcher(const Rhs& rhs)
: ComparisonBase<EqMatcher<Rhs>, Rhs, AnyEq>(rhs) { }
static const char* Desc() { return "is equal to"; }
static const char* NegatedDesc() { return "isn't equal to"; }
};
template <typename Rhs>
class NeMatcher : public ComparisonBase<NeMatcher<Rhs>, Rhs, AnyNe> {
public:
explicit NeMatcher(const Rhs& rhs)
: ComparisonBase<NeMatcher<Rhs>, Rhs, AnyNe>(rhs) { }
static const char* Desc() { return "isn't equal to"; }
static const char* NegatedDesc() { return "is equal to"; }
};
template <typename Rhs>
class LtMatcher : public ComparisonBase<LtMatcher<Rhs>, Rhs, AnyLt> {
public:
explicit LtMatcher(const Rhs& rhs)
: ComparisonBase<LtMatcher<Rhs>, Rhs, AnyLt>(rhs) { }
static const char* Desc() { return "is <"; }
static const char* NegatedDesc() { return "isn't <"; }
};
template <typename Rhs>
class GtMatcher : public ComparisonBase<GtMatcher<Rhs>, Rhs, AnyGt> {
public:
explicit GtMatcher(const Rhs& rhs)
: ComparisonBase<GtMatcher<Rhs>, Rhs, AnyGt>(rhs) { }
static const char* Desc() { return "is >"; }
static const char* NegatedDesc() { return "isn't >"; }
};
template <typename Rhs>
class LeMatcher : public ComparisonBase<LeMatcher<Rhs>, Rhs, AnyLe> {
public:
explicit LeMatcher(const Rhs& rhs)
: ComparisonBase<LeMatcher<Rhs>, Rhs, AnyLe>(rhs) { }
static const char* Desc() { return "is <="; }
static const char* NegatedDesc() { return "isn't <="; }
};
template <typename Rhs>
class GeMatcher : public ComparisonBase<GeMatcher<Rhs>, Rhs, AnyGe> {
public:
explicit GeMatcher(const Rhs& rhs)
: ComparisonBase<GeMatcher<Rhs>, Rhs, AnyGe>(rhs) { }
static const char* Desc() { return "is >="; }
static const char* NegatedDesc() { return "isn't >="; }
};
// Implements the polymorphic IsNull() matcher, which matches any raw or smart // Implements the polymorphic IsNull() matcher, which matches any raw or smart
// pointer that is NULL. // pointer that is NULL.
@ -1309,51 +1369,64 @@ class MatchesRegexMatcher {
// used to match a tuple<int, short>, a tuple<const long&, double>, // used to match a tuple<int, short>, a tuple<const long&, double>,
// etc). Therefore we use a template type conversion operator in the // etc). Therefore we use a template type conversion operator in the
// implementation. // implementation.
// template <typename D, typename Op>
// We define this as a macro in order to eliminate duplicated source class PairMatchBase {
// code. public:
#define GMOCK_IMPLEMENT_COMPARISON2_MATCHER_(name, op, relation) \ template <typename T1, typename T2>
class name##2Matcher { \ operator Matcher< ::testing::tuple<T1, T2> >() const {
public: \ return MakeMatcher(new Impl< ::testing::tuple<T1, T2> >);
template <typename T1, typename T2> \ }
operator Matcher< ::testing::tuple<T1, T2> >() const { \ template <typename T1, typename T2>
return MakeMatcher(new Impl< ::testing::tuple<T1, T2> >); \ operator Matcher<const ::testing::tuple<T1, T2>&>() const {
} \ return MakeMatcher(new Impl<const ::testing::tuple<T1, T2>&>);
template <typename T1, typename T2> \
operator Matcher<const ::testing::tuple<T1, T2>&>() const { \
return MakeMatcher(new Impl<const ::testing::tuple<T1, T2>&>); \
} \
private: \
template <typename Tuple> \
class Impl : public MatcherInterface<Tuple> { \
public: \
virtual bool MatchAndExplain( \
Tuple args, \
MatchResultListener* /* listener */) const { \
return ::testing::get<0>(args) op ::testing::get<1>(args); \
} \
virtual void DescribeTo(::std::ostream* os) const { \
*os << "are " relation; \
} \
virtual void DescribeNegationTo(::std::ostream* os) const { \
*os << "aren't " relation; \
} \
}; \
} }
// Implements Eq(), Ge(), Gt(), Le(), Lt(), and Ne() respectively. private:
GMOCK_IMPLEMENT_COMPARISON2_MATCHER_(Eq, ==, "an equal pair"); static ::std::ostream& GetDesc(::std::ostream& os) { // NOLINT
GMOCK_IMPLEMENT_COMPARISON2_MATCHER_( return os << D::Desc();
Ge, >=, "a pair where the first >= the second"); }
GMOCK_IMPLEMENT_COMPARISON2_MATCHER_(
Gt, >, "a pair where the first > the second");
GMOCK_IMPLEMENT_COMPARISON2_MATCHER_(
Le, <=, "a pair where the first <= the second");
GMOCK_IMPLEMENT_COMPARISON2_MATCHER_(
Lt, <, "a pair where the first < the second");
GMOCK_IMPLEMENT_COMPARISON2_MATCHER_(Ne, !=, "an unequal pair");
#undef GMOCK_IMPLEMENT_COMPARISON2_MATCHER_ template <typename Tuple>
class Impl : public MatcherInterface<Tuple> {
public:
virtual bool MatchAndExplain(
Tuple args,
MatchResultListener* /* listener */) const {
return Op()(::testing::get<0>(args), ::testing::get<1>(args));
}
virtual void DescribeTo(::std::ostream* os) const {
*os << "are " << GetDesc;
}
virtual void DescribeNegationTo(::std::ostream* os) const {
*os << "aren't " << GetDesc;
}
};
};
class Eq2Matcher : public PairMatchBase<Eq2Matcher, AnyEq> {
public:
static const char* Desc() { return "an equal pair"; }
};
class Ne2Matcher : public PairMatchBase<Ne2Matcher, AnyNe> {
public:
static const char* Desc() { return "an unequal pair"; }
};
class Lt2Matcher : public PairMatchBase<Lt2Matcher, AnyLt> {
public:
static const char* Desc() { return "a pair where the first < the second"; }
};
class Gt2Matcher : public PairMatchBase<Gt2Matcher, AnyGt> {
public:
static const char* Desc() { return "a pair where the first > the second"; }
};
class Le2Matcher : public PairMatchBase<Le2Matcher, AnyLe> {
public:
static const char* Desc() { return "a pair where the first <= the second"; }
};
class Ge2Matcher : public PairMatchBase<Ge2Matcher, AnyGe> {
public:
static const char* Desc() { return "a pair where the first >= the second"; }
};
// Implements the Not(...) matcher for a particular argument type T. // Implements the Not(...) matcher for a particular argument type T.
// We do not nest it inside the NotMatcher class template, as that // We do not nest it inside the NotMatcher class template, as that

View File

@ -607,11 +607,11 @@ TEST(MatcherCastTest, FromSameType) {
EXPECT_FALSE(m2.Matches(1)); EXPECT_FALSE(m2.Matches(1));
} }
// Implicitly convertible form any type. // Implicitly convertible from any type.
struct ConvertibleFromAny { struct ConvertibleFromAny {
ConvertibleFromAny(int a_value) : value(a_value) {} ConvertibleFromAny(int a_value) : value(a_value) {}
template <typename T> template <typename T>
ConvertibleFromAny(const T& a_value) : value(-1) { ConvertibleFromAny(const T& /*a_value*/) : value(-1) {
ADD_FAILURE() << "Conversion constructor called"; ADD_FAILURE() << "Conversion constructor called";
} }
int value; int value;