From fe5e122f12495b650e33184d39197c34cf3c1896 Mon Sep 17 00:00:00 2001 From: Mathew Sutcliffe Date: Sat, 21 May 2016 16:23:55 +0100 Subject: [PATCH] Comparison mixins implemented with forEachMemberPair instead of toCaseAwareTuple. --- src/blackmisc/compare.h | 102 ++++++++++++++----------------- src/blackmisc/metaclass.h | 31 ++++------ src/blackmisc/metaclassprivate.h | 32 ---------- 3 files changed, 58 insertions(+), 107 deletions(-) diff --git a/src/blackmisc/compare.h b/src/blackmisc/compare.h index 5bcc7a65a..1e1fee68e 100644 --- a/src/blackmisc/compare.h +++ b/src/blackmisc/compare.h @@ -20,56 +20,6 @@ namespace BlackMisc { class CEmpty; - //! \cond PRIVATE - namespace Private - { - template - int compareImpl(const T &a, const U &b, std::true_type) - { - return compare(a, b); - } - - template - int compareImpl(const T &a, const U &b, std::false_type) - { - return a < b ? -1 : b < a ? 1 : 0; - } - - template - struct CompareHelper - { - template - static int compareTuples(const T &a, const U &b) - { - int cmp = compareImpl(std::get(a), std::get(b), HasCompare, std::tuple_element_t>()); - return cmp ? cmp : CompareHelper::compareTuples(a, b); - } - }; - - template - struct CompareHelper - { - template - static int compareTuples(const T &, const U &) - { - return 0; - } - }; - } - //! \endcond - - /*! - * Lexicographically compare two tuples and return negative, positive, or zero, - * if a is less than, greater than, or equal to b. - * Each element is compared with compare(a', b') if supported by the element type, operator less-than otherwise. - */ - template - int compare(const std::tuple &a, const std::tuple &b) - { - static_assert(sizeof...(Ts) == sizeof...(Us), "tuples must be same size"); - return Private::CompareHelper::compareTuples(a, b); - } - namespace Mixin { @@ -106,11 +56,23 @@ namespace BlackMisc static bool equals(const Derived &a, const Derived &b) { auto meta = introspect().without(MetaFlags()); - return meta.toCaseAwareTuple(a) == meta.toCaseAwareTuple(b) && baseEquals(static_cast *>(&a), static_cast *>(&b)); + bool result = baseEquals(static_cast *>(&a), static_cast *>(&b)); + meta.forEachMemberPair(a, b, [ & ](auto &&... args) { result = result && EqualsByMetaClass::membersEqual(std::forward(args)...); }); + return result; } template static bool baseEquals(const T *a, const T *b) { return *a == *b; } static bool baseEquals(const void *, const void *) { return true; } static bool baseEquals(const CEmpty *, const CEmpty *) { return true; } + + template + static bool membersEqual(const T &a, const T &b, Flags) + { + return membersEqual(a, b, std::integral_constant(Flags::value & CaseInsensitiveComparison)>()); + } + template + static bool membersEqual(const T &a, const T &b, std::true_type) { return a.compare(b, Qt::CaseInsensitive) == 0; } + template + static bool membersEqual(const T &a, const T &b, std::false_type) { return a == b; } }; /*! @@ -157,13 +119,28 @@ namespace BlackMisc private: static bool less(const Derived &a, const Derived &b) { - if (baseLess(static_cast *>(&a), static_cast *>(&b))) { return true; } auto meta = introspect().without(MetaFlags()); - return meta.toCaseAwareTuple(a) < meta.toCaseAwareTuple(b); + bool result = baseLess(static_cast *>(&a), static_cast *>(&b)); + bool gt = baseLess(static_cast *>(&b), static_cast *>(&a)); + meta.forEachMemberPair(a, b, [ & ](auto &&... args) { result = result || LessThanByMetaClass::membersLess(gt, std::forward(args)...); }); + return result; } template static bool baseLess(const T *a, const T *b) { return *a < *b; } static bool baseLess(const void *, const void *) { return false; } static bool baseLess(const CEmpty *, const CEmpty *) { return false; } + + template + static bool membersLess(bool &io_greaterThan, const T &a, const T &b, Flags) + { + using CaseInsensitive = std::integral_constant(Flags::value & CaseInsensitiveComparison)>; + if (io_greaterThan) { return false; } + io_greaterThan = membersLess(b, a, CaseInsensitive()); + return membersLess(a, b, CaseInsensitive()); + } + template + static bool membersLess(const T &a, const T &b, std::true_type) { return a.compare(b, Qt::CaseInsensitive) < 0; } + template + static bool membersLess(const T &a, const T &b, std::false_type) { return a < b; } }; /*! @@ -179,14 +156,27 @@ namespace BlackMisc private: static int compareImpl(const Derived &a, const Derived &b) { - int baseCmp = baseCompare(static_cast *>(&a), static_cast *>(&b)); - if (baseCmp) { return baseCmp; } auto meta = introspect().without(MetaFlags()); - return BlackMisc::compare(meta.toCaseAwareTuple(a), meta.toCaseAwareTuple(b)); + int result = baseCompare(static_cast *>(&a), static_cast *>(&b)); + meta.forEachMemberPair(a, b, [ & ](auto &&... args) { result = result ? result : CompareByMetaClass::membersCompare(std::forward(args)...); }); + return result; } template static int baseCompare(const T *a, const T *b) { return compare(*a, *b); } static int baseCompare(const void *, const void *) { return 0; } static int baseCompare(const CEmpty *, const CEmpty *) { return 0; } + + template + static int membersCompare(const T &a, const T &b, Flags) + { + using CaseInsensitive = std::integral_constant(Flags::value & CaseInsensitiveComparison)>; + return membersCompare(a, b, CaseInsensitive(), HasCompare()); + } + template + static int membersCompare(const T &a, const T &b, std::true_type, U) { return a.compare(b, Qt::CaseInsensitive); } + template + static int membersCompare(const T &a, const T &b, std::false_type, std::true_type) { return compare(a, b); } + template + static int membersCompare(const T &a, const T &b, std::false_type, std::false_type) { return a < b ? -1 : b < a ? 1 : 0; } }; } // Mixin diff --git a/src/blackmisc/metaclass.h b/src/blackmisc/metaclass.h index 4c7058dd3..fa5230846 100644 --- a/src/blackmisc/metaclass.h +++ b/src/blackmisc/metaclass.h @@ -199,20 +199,17 @@ namespace BlackMisc static auto toTuple(const T &object) { return std::tie((members().at(index()).in(object))...); } //! @} - //! Like toTuple, but members with the CaseInsensitiveComparison flag will be wrapped so that their comparisons are case insensitive. - static auto toCaseAwareTuple(const T &object) { return std::make_tuple(caseAwareWrap(members().at(index()).in(object))...); } - //! For each member in object, pass member as argument to visitor function. //! @{ template static void forEachMember(T &object, F &&visitor) { - forEachImpl([ & ](auto &&member) { std::forward(visitor)(member.in(object)); }); + forEachImpl([ & ](auto &&member, auto) { std::forward(visitor)(member.in(object)); }); } template static void forEachMember(const T &object, F &&visitor) { - forEachImpl([ & ](auto &&member) { std::forward(visitor)(member.in(object)); }); + forEachImpl([ & ](auto &&member, auto) { std::forward(visitor)(member.in(object)); }); } //! @} @@ -221,22 +218,22 @@ namespace BlackMisc template static void forEachMemberPair(T &left, T &right, F &&visitor) { - forEachImpl([ & ](auto &&member) { std::forward(visitor)(member.in(left), member.in(right)); }); + forEachImpl([ & ](auto &&member, auto flags) { std::forward(visitor)(member.in(left), member.in(right), flags); }); } template static void forEachMemberPair(const T &left, T &right, F &&visitor) { - forEachImpl([ & ](auto &&member) { std::forward(visitor)(member.in(left), member.in(right)); }); + forEachImpl([ & ](auto &&member, auto flags) { std::forward(visitor)(member.in(left), member.in(right), flags); }); } template static void forEachMemberPair(T &left, const T &right, F &&visitor) { - forEachImpl([ & ](auto &&member) { std::forward(visitor)(member.in(left), member.in(right)); }); + forEachImpl([ & ](auto &&member, auto flags) { std::forward(visitor)(member.in(left), member.in(right), flags); }); } template static void forEachMemberPair(const T &left, const T &right, F &&visitor) { - forEachImpl([ & ](auto &&member) { std::forward(visitor)(member.in(left), member.in(right)); }); + forEachImpl([ & ](auto &&member, auto flags) { std::forward(visitor)(member.in(left), member.in(right), flags); }); } //! @} @@ -245,12 +242,12 @@ namespace BlackMisc template static void forEachMemberName(T &object, F &&visitor) { - forEachImpl([ & ](auto &&member) { std::forward(visitor)(member.in(object), QString(member.m_name)); }); + forEachImpl([ & ](auto &&member, auto) { std::forward(visitor)(member.in(object), QString(member.m_name)); }); } template static void forEachMemberName(const T &object, F &&visitor) { - forEachImpl([ & ](auto &&member) { std::forward(visitor)(member.in(object), QString(member.m_name)); }); + forEachImpl([ & ](auto &&member, auto) { std::forward(visitor)(member.in(object), QString(member.m_name)); }); } //! @} @@ -270,14 +267,10 @@ namespace BlackMisc static void forEachImpl(F &&visitor) { // parameter pack swallow idiom - static_cast(std::initializer_list { (static_cast(std::forward(visitor)(members().at(index()))), 0)... }); - } - - template - static auto caseAwareWrap(const U &value) - { - using IsCaseInsensitive = std::integral_constant()).has(MetaFlags())>; - return Private::caseAwareWrap(IsCaseInsensitive(), value); + static_cast(std::initializer_list + { + (static_cast(std::forward(visitor)(members().at(index()), MetaFlags()).m_flags>())), 0)... + }); } }; diff --git a/src/blackmisc/metaclassprivate.h b/src/blackmisc/metaclassprivate.h index 4edb581e4..b5a4cc373 100644 --- a/src/blackmisc/metaclassprivate.h +++ b/src/blackmisc/metaclassprivate.h @@ -99,38 +99,6 @@ namespace BlackMisc template constexpr auto make_tuple(Ts &&... vs) { return tuple...>(std::forward(vs)...); } #endif // ! BLACK_HAS_CONSTEXPR_STDLIB - - // Helper for case insensitive comparisons. - template - struct CaseInsensitiveWrapper - { - const T &m_ref; - - explicit CaseInsensitiveWrapper(const T &ref) : m_ref(ref) {} - - friend int compare(CaseInsensitiveWrapper a, CaseInsensitiveWrapper b) - { - return a.m_ref.compare(b.m_ref, Qt::CaseInsensitive); - } - friend bool operator ==(CaseInsensitiveWrapper a, CaseInsensitiveWrapper b) - { - return compare(a, b) == 0; - } - friend bool operator <(CaseInsensitiveWrapper a, CaseInsensitiveWrapper b) - { - return compare(a, b) < 0; - } - }; - template - auto caseAwareWrap(std::false_type, const T &value) - { - return std::cref(value); - } - template - auto caseAwareWrap(std::true_type, const T &value) - { - return CaseInsensitiveWrapper(value); - } } }