From 2f55caf7c62cfaeb90f8219b119efce9d7cf9668 Mon Sep 17 00:00:00 2001 From: Mathew Sutcliffe Date: Wed, 29 Apr 2015 01:31:19 +0100 Subject: [PATCH] refs #413 Decomposed hash-related functions of CValueObject into Mixin::HashByTuple. --- src/blackmisc/containerbase.h | 4 +-- src/blackmisc/dictionary.h | 3 -- src/blackmisc/logpattern.h | 6 +++- src/blackmisc/pq/measurementunit.h | 6 ++-- src/blackmisc/pq/physicalquantity.h | 7 ++-- src/blackmisc/propertyindexvariantmap.h | 7 ++-- src/blackmisc/valueobject.h | 47 ++++++++++++++++--------- src/blackmisc/valueobject_policy.h | 5 +++ src/blackmisc/valueobject_private.h | 6 +++- src/blackmisc/variant.h | 5 +-- tests/blackmisc/testvariantandmap.cpp | 6 ++-- 11 files changed, 67 insertions(+), 35 deletions(-) diff --git a/src/blackmisc/containerbase.h b/src/blackmisc/containerbase.h index a939c3c49..85bf96268 100644 --- a/src/blackmisc/containerbase.h +++ b/src/blackmisc/containerbase.h @@ -118,8 +118,8 @@ namespace BlackMisc //! \copydoc CValueObject::convertFromQVariant virtual void convertFromQVariant(const QVariant &variant) override { BlackMisc::setFromQVariant< C >(&derived(), variant); } - //! \copydoc BlackMisc::CValueObject::getValueHash - virtual uint getValueHash() const override { return qHash(&derived()); } + //! Simplifies composition, returns 0 for performance + friend uint qHash(const C &) { return 0; } //! \copydoc CValueObject::toJson virtual QJsonObject toJson() const override diff --git a/src/blackmisc/dictionary.h b/src/blackmisc/dictionary.h index 21c7a3cba..0aaf77e89 100644 --- a/src/blackmisc/dictionary.h +++ b/src/blackmisc/dictionary.h @@ -232,9 +232,6 @@ namespace BlackMisc //! \copydoc CValueObject::convertFromQVariant virtual void convertFromQVariant(const QVariant &variant) override { BlackMisc::setFromQVariant(this, variant); } - //! \copydoc BlackMisc::CValueObject::getValueHash - virtual uint getValueHash() const override { return qHash(this); } - //! \copydoc CValueObject::toJson virtual QJsonObject toJson() const override { diff --git a/src/blackmisc/logpattern.h b/src/blackmisc/logpattern.h index 45ac5960d..63f344ec1 100644 --- a/src/blackmisc/logpattern.h +++ b/src/blackmisc/logpattern.h @@ -122,6 +122,10 @@ namespace BlackMisc } Q_DECLARE_METATYPE(BlackMisc::CLogPattern) -BLACK_DECLARE_TUPLE_CONVERSION(BlackMisc::CLogPattern, (o.m_severities, o.m_strategy, o.m_strings)) +BLACK_DECLARE_TUPLE_CONVERSION(BlackMisc::CLogPattern, ( + attr(o.m_severities, flags()), + attr(o.m_strategy), + attr(o.m_strings, flags()) +)) #endif diff --git a/src/blackmisc/pq/measurementunit.h b/src/blackmisc/pq/measurementunit.h index b05322e27..f55de0d0a 100644 --- a/src/blackmisc/pq/measurementunit.h +++ b/src/blackmisc/pq/measurementunit.h @@ -279,10 +279,10 @@ namespace BlackMisc //! Unequal operator != bool operator != (const CMeasurementUnit &other) const; - //! \copydoc CValueObject::getValueHash - virtual uint getValueHash() const override + //! \copydoc CValueObject::qHash + friend uint qHash(const CMeasurementUnit &unit) { - return qHash(this->getName()); + return ::qHash(unit.getName()); } //! Name such as "meter" diff --git a/src/blackmisc/pq/physicalquantity.h b/src/blackmisc/pq/physicalquantity.h index 2a8afc8d1..31711465f 100644 --- a/src/blackmisc/pq/physicalquantity.h +++ b/src/blackmisc/pq/physicalquantity.h @@ -198,8 +198,11 @@ namespace BlackMisc //! \copydoc CValueObject::unmarshallFromDbus virtual void unmarshallFromDbus(const QDBusArgument &argument) override; - //! \copydoc CValueObject::getValueHash - virtual uint getValueHash() const override; + //! \copydoc CValueObject::qHash + uint getValueHash() const; + + //! \copydoc CValueObject::qHash + friend uint qHash(const PQ &pq) { return pq.getValueHash(); }; //! \copydoc CValueObject::toJson virtual QJsonObject toJson() const override; diff --git a/src/blackmisc/propertyindexvariantmap.h b/src/blackmisc/propertyindexvariantmap.h index 74970a850..5b46fd6b2 100644 --- a/src/blackmisc/propertyindexvariantmap.h +++ b/src/blackmisc/propertyindexvariantmap.h @@ -137,8 +137,11 @@ namespace BlackMisc //! Map const QMap &map() const { return this->m_values; } - //! \copydoc CValueObject::getValueHash - virtual uint getValueHash() const override; + //! Hash value + uint getValueHash() const; + + //! \copydoc CValueObject::qHash + friend uint qHash(const CPropertyIndexVariantMap &vm) { return vm.getValueHash(); } //! \copydoc CValueObject::toQVariant virtual QVariant toQVariant() const override { return QVariant::fromValue(*this); } diff --git a/src/blackmisc/valueobject.h b/src/blackmisc/valueobject.h index 84a502d56..faea69d15 100644 --- a/src/blackmisc/valueobject.h +++ b/src/blackmisc/valueobject.h @@ -110,9 +110,6 @@ namespace BlackMisc */ struct CValueObjectDummyBase { - //! Value hash - static uint getValueHash() { return 0; } - //! To JSON static QJsonObject toJson() { return {}; } @@ -240,6 +237,36 @@ namespace BlackMisc static bool baseIsA(const CEmpty *, int) { return false; } }; + /*! + * CRTP class template from which a derived class can inherit common methods dealing with hashing instances by metatuple. + */ + template + class HashByTuple : private Private::EncapsulationBreaker + { + public: + //! qHash overload, needed for storing value in a QSet. + friend uint qHash(const Derived &value, uint seed = 0) + { + return ::qHash(hashImpl(value), seed); + } + + private: + static uint hashImpl(const Derived &value) + { + return BlackMisc::qHash(toMetaTuple(value)) ^ baseHash(static_cast(value)); + } + + template static uint baseHash(const T &base) { return qHash(base); } + static uint baseHash(const CEmpty &) { return 0; } + }; + + /*! + * Specialization of HashByTuple for classes not registered with the tuple system. + */ + template + class HashByTuple + {}; + } /*! @@ -255,13 +282,13 @@ namespace BlackMisc template class CValueObject : public Base, public Mixin::MetaType, + public Mixin::HashByTuple::value>, private CValueObjectPolicy::Equals::template Ops, private CValueObjectPolicy::LessThan::template Ops, private CValueObjectPolicy::Compare::template Ops { static_assert(std::is_same::value || IsValueObject::value, "Base must be either CEmpty or derived from CValueObject"); - using HashPolicy = typename CValueObjectPolicy::Hash; using DBusPolicy = typename CValueObjectPolicy::DBus; using JsonPolicy = typename CValueObjectPolicy::Json; using PropertyIndexPolicy = typename CValueObjectPolicy::PropertyIndex; @@ -356,12 +383,6 @@ namespace BlackMisc return json; } - //! qHash overload, needed for storing value in a QSet. - friend uint qHash(const Derived &value, uint seed = 0) - { - return qHash(value.getValueHash(), seed); - } - public: //! Base class using base_type = Base; @@ -396,12 +417,6 @@ namespace BlackMisc //! \copydoc BlackMisc::Mixin::MetaType::convertFromCVariant using Mixin::MetaType::convertFromCVariant; - //! Value hash, allows comparisons between QVariants - virtual uint getValueHash() const - { - return HashPolicy::hashImpl(*derived()) ^ BaseOrDummy::getValueHash(); - } - //! Cast to JSON object virtual QJsonObject toJson() const { diff --git a/src/blackmisc/valueobject_policy.h b/src/blackmisc/valueobject_policy.h index f8fe8936b..65346bfe1 100644 --- a/src/blackmisc/valueobject_policy.h +++ b/src/blackmisc/valueobject_policy.h @@ -283,6 +283,11 @@ namespace BlackMisc template static uint hashImpl(const T &) { return 0; } }; + + //! \private Detect the policy of T, following inheritance. + template ::Hash> struct IsMetaTuple : public std::false_type {}; + template struct IsMetaTuple : public std::true_type {}; + template struct IsMetaTuple : public IsMetaTuple {}; } namespace DBus diff --git a/src/blackmisc/valueobject_private.h b/src/blackmisc/valueobject_private.h index b896a8a07..766e1c68f 100644 --- a/src/blackmisc/valueobject_private.h +++ b/src/blackmisc/valueobject_private.h @@ -51,6 +51,10 @@ namespace BlackMisc virtual bool equalsPropertyByIndex(const void *object, const CVariant &compareValue, const CPropertyIndex &index) const = 0; }; + //! \private Fallback in case qHash is not defined for T. + template + uint qHash(const T &) { return 0; } + //! \private Implementation of IValueObjectMetaInfo representing the set of operations supported by T. template struct CValueObjectMetaInfo : public IValueObjectMetaInfo @@ -73,7 +77,7 @@ namespace BlackMisc } virtual uint getValueHash(const void *object) const override { - return cast(object).getValueHash(); + return qHash(cast(object)); } virtual int getMetaTypeId() const override { diff --git a/src/blackmisc/variant.h b/src/blackmisc/variant.h index 3192729c9..6d4595c0a 100644 --- a/src/blackmisc/variant.h +++ b/src/blackmisc/variant.h @@ -77,8 +77,8 @@ namespace BlackMisc //! Construct a variant from the given type and opaque pointer. CVariant(int typeId, const void *copy) : m_v(typeId, copy) {} - //! \copydoc CValueObject::getValueHash - virtual uint getValueHash() const override; + //! \copydoc CValueObject::qHash + friend uint qHash(const CVariant &var) { return var.getValueHash(); } //! Change the internal QVariant. void reset(const QVariant &var) { m_v = var; } @@ -219,6 +219,7 @@ namespace BlackMisc const void *data() const { return m_v.data(); } static int compareImpl(const CVariant &, const CVariant &); + uint getValueHash() const; }; //! Compare stored value of CVariant with any CValueObject derived class. diff --git a/tests/blackmisc/testvariantandmap.cpp b/tests/blackmisc/testvariantandmap.cpp index befffbeac..d5ad3e236 100644 --- a/tests/blackmisc/testvariantandmap.cpp +++ b/tests/blackmisc/testvariantandmap.cpp @@ -100,14 +100,14 @@ namespace BlackMiscTest QVERIFY2(station1 != vmNoWildcard, "Station should not be equal to empty list"); QVERIFY2(station1 == vm, "Controller should match"); QVERIFY2(vmWildcard == vmCopy, "Maps should be equal"); - QVERIFY2(vmWildcard.getValueHash() == vmCopy.getValueHash(), "Hashs should be equal (simple)"); + QVERIFY2(qHash(vmWildcard) == qHash(vmCopy), "Hashs should be equal (simple)"); vm.addValue(CAtcStation::IndexFrequency, CFrequency(118.7, CFrequencyUnit::MHz())); vm.addValue(CAtcStation::IndexPosition, geoPos); vmCopy = vm; - QVERIFY2(vm.getValueHash() == vmCopy.getValueHash(), "Hashs should be equal (detailed)"); + QVERIFY2(qHash(vm) == qHash(vmCopy), "Hashs should be equal (detailed)"); vmCopy.setWildcard(!vm.isWildcard()); - QVERIFY2(vm.getValueHash() != vmCopy.getValueHash(), "Hashs should not be equal (detailed)"); + QVERIFY2(qHash(vm) != qHash(vmCopy), "Hashs should not be equal (detailed)"); } } // namespace