diff --git a/samples/blackmiscquantities/samplesaviation.cpp b/samples/blackmiscquantities/samplesaviation.cpp index a6c6bcfa6..3094ec220 100644 --- a/samples/blackmiscquantities/samplesaviation.cpp +++ b/samples/blackmiscquantities/samplesaviation.cpp @@ -100,6 +100,17 @@ namespace BlackSample out << frankfurt << endl; out << "-----------------------------------------------" << endl; + CMetaMemberComparator cmp; + QList> list = cmp(station1, station3); + for (const auto &member : list) { out << member.first << (member.second ? " equal" : " NOT equal") << endl; } + out << endl; + list = cmp(station1, station3, { "controller" }); + for (const auto &member : list) { out << member.first << (member.second ? " equal" : " NOT equal") << endl; } + out << endl; + list = cmp(station1, station3, { "controller", "homebase" }); + for (const auto &member : list) { out << member.first << (member.second ? " equal" : " NOT equal") << endl; } + out << "-----------------------------------------------" << endl; + return 0; } } // namespace diff --git a/src/blackmisc/compare.h b/src/blackmisc/compare.h index 99cb88f49..0f0e44f80 100644 --- a/src/blackmisc/compare.h +++ b/src/blackmisc/compare.h @@ -15,6 +15,10 @@ #include "blackmisc/metaclass.h" #include "blackmisc/inheritancetraits.h" #include "blackmisc/typetraits.h" +#include "blackmisc/range.h" +#include +#include +#include namespace BlackMisc { @@ -180,6 +184,60 @@ namespace BlackMisc }; } // Mixin + + /*! + * Helps with debugging Mixin::EqualsByMetaClass. + */ + class CMetaMemberComparator + { + public: + //! Return list of pairs containing member name and bool indicating if member is equal. + template ::value, int> = 0> + QList> operator ()(const T &a, const T &b) const + { + auto meta = introspect().without(MetaFlags()); + auto result = baseEquals(static_cast *>(&a), static_cast *>(&b)); + meta.forEachMember([ & ](auto member) + { + constexpr auto flags = decltype(member.m_flags)(); + result.append(qMakePair(member.latin1Name(), membersEqual(member.in(a), member.in(b), flags & MetaFlags()))); + }); + return result; + } + + //! Recurse into one of the submembers of T, comparing members of that member. + template ::value, int> = 0> + QList> operator ()(const T &a, const T &b, QStringList memberNames) const + { + if (memberNames.isEmpty()) { return CMetaMemberComparator()(a, b); } + auto meta = introspect().without(MetaFlags()); + auto result = baseEquals(static_cast *>(&a), static_cast *>(&b), memberNames); + const auto memberName = memberNames.takeFirst(); + meta.forEachMember([ & ](auto member) + { + const auto p = [ & ](const auto &m) { return qMakePair(memberName + '.' + m.first, m.second); }; + if (member.latin1Name() == memberName) { result.append(makeRange(CMetaMemberComparator()(member.in(a), member.in(b), memberNames)).transform(p)); } + }); + return result; + } + + //! Return empty list if T doesn't have a metaclass. + template ::value, int> = 0> + QList> operator ()(const T &, const T &) const { return {}; } + + //! Return empty list if T doesn't have a metaclass. + template ::value, int> = 0> + QList> operator ()(const T &, const T &, QStringList) const { return {}; } + + private: + template static auto baseEquals(const T *a, const T *b) { return CMetaMemberComparator()(*a, *b); } + template static auto baseEquals(const T *a, const T *b, const QStringList &memberNames) { return CMetaMemberComparator()(*a, *b, memberNames); } + static auto baseEquals(const void *, const void *, const QStringList & = {}) { return QList>(); } + static auto baseEquals(const CEmpty *, const CEmpty *, const QStringList & = {}) { return QList>(); } + + 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; } + }; } // BlackMisc #endif diff --git a/src/blackmisc/metaclass.h b/src/blackmisc/metaclass.h index 49a239eea..11e10965d 100644 --- a/src/blackmisc/metaclass.h +++ b/src/blackmisc/metaclass.h @@ -263,6 +263,12 @@ namespace BlackMisc { return getIntrospector(Private::make_index_sequence()); } + + template + static std::true_type hasMetaClass(int, typename T::MetaClass * = nullptr) { return {}; } + + template + static std::false_type hasMetaClass(...) { return {}; } }; } @@ -277,6 +283,13 @@ namespace BlackMisc return Private::CMetaClassAccessor::getIntrospector(); } + /*! + * Trait that is true if T has a metaclass. + * \ingroup MetaClass + */ + template + struct THasMetaClass : public decltype(Private::CMetaClassAccessor::hasMetaClass(0)) {}; + } // namespace #endif