From 97d83e5e418d97e6207214662558e5308926265b Mon Sep 17 00:00:00 2001 From: Mathew Sutcliffe Date: Thu, 30 Apr 2015 20:28:40 +0100 Subject: [PATCH] refs #413 Decomposed comparison operations of CValueObject into mixins. --- src/blackmisc/valueobject.h | 104 ++++++++++++++++++++++++++-- src/blackmisc/valueobject_policy.h | 15 ++++ src/blackmisc/valueobject_private.h | 4 ++ src/blackmisc/variant.h | 16 +++++ 4 files changed, 133 insertions(+), 6 deletions(-) diff --git a/src/blackmisc/valueobject.h b/src/blackmisc/valueobject.h index 87fd760ef..f7058d5c6 100644 --- a/src/blackmisc/valueobject.h +++ b/src/blackmisc/valueobject.h @@ -102,9 +102,6 @@ namespace BlackMisc ~CEmpty() = default; }; - //! Dummy comparison. - inline int compare(const CEmpty &, const CEmpty &) { return 0; } - /*! * Default policy classes for use by CValueObject. * @@ -465,6 +462,101 @@ namespace BlackMisc static void baseConvertFromJson(CEmpty &, const QJsonObject &) {} }; + /*! + * CRTP class template from which a derived class can inherit operator== implemented by metatuple. + */ + template + class EqualsByTuple : private Private::EncapsulationBreaker + { + public: + //! Equals + friend bool operator ==(const Derived &a, const Derived &b) { return equals(a, b); } + + //! Not equal + friend bool operator !=(const Derived &a, const Derived &b) { return ! equals(a, b); } + + private: + static bool equals(const Derived &a, const Derived &b) + { + using Base = typename Derived::base_type; + return toMetaTuple(a) == toMetaTuple(b) && baseEquals(static_cast(a), static_cast(b)); + } + template static bool baseEquals(const T &a, const T &b) { return a == b; } + static bool baseEquals(const CEmpty &, const CEmpty &) { return true; } + }; + + /*! + * Specialization of EqualsByTuple for classes not registered with the tuple system. + */ + template + class EqualsByTuple + {}; + + /*! + * CRTP class template from which a derived class can inherit operator< implemented by metatuple. + */ + template + class LessThanByTuple : private Private::EncapsulationBreaker + { + public: + //! Less than + friend bool operator <(const Derived &a, const Derived &b) { return less(a, b); } + + //! Greater than + friend bool operator >(const Derived &a, const Derived &b) { return less(b, a); } + + //! Less than or equal + friend bool operator <=(const Derived &a, const Derived &b) { return ! less(b, a); } + + //! Greater than or equal + friend bool operator >=(const Derived &a, const Derived &b) { return ! less(a, b); } + + private: + static bool less(const Derived &a, const Derived &b) + { + using Base = typename Derived::base_type; + if (baseLess(static_cast(a), static_cast(b))) { return true; } + return toMetaTuple(a) < toMetaTuple(b); + } + template static bool baseLess(const T &a, const T &b) { return a < b; } + static bool baseLess(const CEmpty &, const CEmpty &) { return false; } + }; + + /*! + * Specialization of LessThanByTuple for classes not registered with the tuple system. + */ + template + class LessThanByTuple + {}; + + /*! + * CRTP class template from which a derived class can inherit non-member compare() implemented by metatuple. + */ + template + class CompareByTuple : private Private::EncapsulationBreaker + { + public: + //! Return negative, zero, or positive if a is less than, equal to, or greater than b. + friend int compare(const Derived &a, const Derived &b) { return compareImpl(a, b); } + + private: + static int compareImpl(const Derived &a, const Derived &b) + { + int baseCmp = baseCompare(static_cast(a), static_cast(b)); + if (baseCmp) { return baseCmp; } + return BlackMisc::compare(toMetaTuple(a), toMetaTuple(b)); + } + template static int baseCompare(const T &a, const T &b) { return compare(a, b); } + static int baseCompare(const CEmpty &, const CEmpty &) { return 0; } + }; + + /*! + * Specialization of CompareByTuple for classes not registered with the tuple system. + */ + template + class CompareByTuple + {}; + } /*! @@ -483,9 +575,9 @@ namespace BlackMisc public Mixin::HashByTuple::value>, public Mixin::DBusByTuple::value>, public Mixin::JsonByTuple::value>, - private CValueObjectPolicy::Equals::template Ops, - private CValueObjectPolicy::LessThan::template Ops, - private CValueObjectPolicy::Compare::template Ops + public Mixin::EqualsByTuple::value>, + public Mixin::LessThanByTuple::value>, + public Mixin::CompareByTuple::value> { static_assert(std::is_same::value || IsValueObject::value, "Base must be either CEmpty or derived from CValueObject"); diff --git a/src/blackmisc/valueobject_policy.h b/src/blackmisc/valueobject_policy.h index 7c2c4813c..050a2ed7f 100644 --- a/src/blackmisc/valueobject_policy.h +++ b/src/blackmisc/valueobject_policy.h @@ -138,6 +138,11 @@ namespace BlackMisc friend bool operator !=(const T &a, const T &b) { return !(a == b); } }; }; + + //! \private Detect the policy of T, following inheritance. + template ::Equals> struct IsMetaTuple : public std::false_type {}; + template struct IsMetaTuple : public std::true_type {}; + template struct IsMetaTuple : public IsMetaTuple {}; } namespace LessThan @@ -196,6 +201,11 @@ namespace BlackMisc friend bool operator <=(const T &a, const T &b) { return !(b < a); } }; }; + + //! \private Detect the policy of T, following inheritance. + template ::LessThan> struct IsMetaTuple : public std::false_type {}; + template struct IsMetaTuple : public std::true_type {}; + template struct IsMetaTuple : public IsMetaTuple {}; } namespace Compare @@ -248,6 +258,11 @@ namespace BlackMisc } }; }; + + //! \private Detect the policy of T, following inheritance. + template ::Compare> struct IsMetaTuple : public std::false_type {}; + template struct IsMetaTuple : public std::true_type {}; + template struct IsMetaTuple : public IsMetaTuple {}; } namespace Hash diff --git a/src/blackmisc/valueobject_private.h b/src/blackmisc/valueobject_private.h index 766e1c68f..653073568 100644 --- a/src/blackmisc/valueobject_private.h +++ b/src/blackmisc/valueobject_private.h @@ -55,6 +55,10 @@ namespace BlackMisc template uint qHash(const T &) { return 0; } + //! \private Fallback in case compare is not defined for T. + template + int compare(const T &, const T &) { return 0; } + //! \private Implementation of IValueObjectMetaInfo representing the set of operations supported by T. template struct CValueObjectMetaInfo : public IValueObjectMetaInfo diff --git a/src/blackmisc/variant.h b/src/blackmisc/variant.h index beb7aff80..4ea826a78 100644 --- a/src/blackmisc/variant.h +++ b/src/blackmisc/variant.h @@ -193,9 +193,25 @@ namespace BlackMisc //! Equal operator. friend bool operator ==(const CVariant &a, const CVariant &b) { return compare(a, b) == 0; } + //! Not equal operator. + //! \todo temporary, remove after refactoring + friend bool operator !=(const CVariant &a, const CVariant &b) { return compare(a, b) != 0; } + //! Less than operator. friend bool operator <(const CVariant &a, const CVariant &b) { return compare(a, b) < 0; } + //! Greater than operator. + //! \todo temporary, remove after refactoring + friend bool operator >(const CVariant &a, const CVariant &b) { return compare(a, b) > 0; } + + //! Less than or equal operator. + //! \todo temporary, remove after refactoring + friend bool operator <=(const CVariant &a, const CVariant &b) { return compare(a, b) <= 0; } + + //! Greater than or equal operator. + //! \todo temporary, remove after refactoring + friend bool operator >=(const CVariant &a, const CVariant &b) { return compare(a, b) >= 0; } + //! \copydoc CValueObject::compare friend int compare(const CVariant &a, const CVariant &b) { return compareImpl(a, b); }