diff --git a/src/blackmisc/blackmisc.h b/src/blackmisc/blackmisc.h index 40c2a9e7d..14b77016c 100644 --- a/src/blackmisc/blackmisc.h +++ b/src/blackmisc/blackmisc.h @@ -68,4 +68,49 @@ * \brief Iterator classes for the containers. */ +/*! + * \namespace BlackMisc::Policy + * \brief Policy classes for CValueObjectStdTuple. + */ + +/*! + * \namespace BlackMisc::Policy::MetaType + * \copydoc BlackMisc::Policy + */ + +/*! + * \namespace BlackMisc::Policy::Equals + * \copydoc BlackMisc::Policy + */ + +/*! + * \namespace BlackMisc::Policy::LessThan + * \copydoc BlackMisc::Policy + */ + +/*! + * \namespace BlackMisc::Policy::Compare + * \copydoc BlackMisc::Policy + */ + +/*! + * \namespace BlackMisc::Policy::Hash + * \copydoc BlackMisc::Policy + */ + +/*! + * \namespace BlackMisc::Policy::DBus + * \copydoc BlackMisc::Policy + */ + +/*! + * \namespace BlackMisc::Policy::Json + * \copydoc BlackMisc::Policy + */ + +/*! + * \namespace BlackMisc::Policy::Private + * \private + */ + #endif diff --git a/src/blackmisc/tuple.h b/src/blackmisc/tuple.h index ee16e5a18..63cc4692d 100644 --- a/src/blackmisc/tuple.h +++ b/src/blackmisc/tuple.h @@ -69,7 +69,8 @@ namespace BlackMisc template <> class TupleConverter : TupleConverterBase \ { \ friend class T; \ - friend class BlackMisc::CValueObjectStdTuple; \ + template friend class BlackMisc::CValueObjectStdTuple; \ + friend class BlackMisc::Private::EncapsulationBreaker; \ static_assert(Private::HasEnabledTupleConversion::value, \ "Missing BLACK_ENABLE_TUPLE_CONVERSION macro in " #T); \ static auto toTuple(const T &o) -> decltype(BlackMisc::tie MEMBERS) \ @@ -120,7 +121,8 @@ namespace BlackMisc template class TupleConverter> : TupleConverterBase \ { \ friend class T; \ - friend class BlackMisc::CValueObjectStdTuple>; \ + template friend class BlackMisc::CValueObjectStdTuple; \ + friend class BlackMisc::Private::EncapsulationBreaker; \ static_assert(Private::HasEnabledTupleConversion>::value, \ "Missing BLACK_ENABLE_TUPLE_CONVERSION macro in " #T); \ static auto toTuple(const T &o) -> decltype(BlackMisc::tie MEMBERS) \ diff --git a/src/blackmisc/tuple_private.h b/src/blackmisc/tuple_private.h index b0937eccc..2d8b5b163 100644 --- a/src/blackmisc/tuple_private.h +++ b/src/blackmisc/tuple_private.h @@ -26,6 +26,7 @@ namespace BlackMisc { class CValueObject; + template class TupleConverter; namespace Private { @@ -33,6 +34,17 @@ namespace BlackMisc // Inhibit doxygen warnings about missing documentation //! \cond PRIVATE + // To allow CValueObjectStdTuple policy classes to use the tuple system + class EncapsulationBreaker + { + protected: + template + static auto toMetaTuple(T &o) -> decltype(TupleConverter::type>::toMetaTuple(o)) + { + return TupleConverter::type>::toMetaTuple(o); + } + }; + // Using SFINAE to help detect missing BLACK_ENABLE_TUPLE_CONVERSION macro in static_assert std::false_type hasEnabledTupleConversionHelper(...); template diff --git a/src/blackmisc/valueobject.h b/src/blackmisc/valueobject.h index b4f3467e7..0ad3e903f 100644 --- a/src/blackmisc/valueobject.h +++ b/src/blackmisc/valueobject.h @@ -17,6 +17,7 @@ #include "json.h" #include "variant.h" #include "blackmiscfreefunctions.h" +#include "valueobject_policy.h" #include #include #include @@ -271,29 +272,68 @@ namespace BlackMisc //! Parse from string, e.g. 100km/h virtual void parseFromString(const QString &) { qFatal("Not implemented"); } + }; + /*! + * Default policy classes for use by CValueObjectStdTuple. + * + * The default policies are inherited from the policies of the base class. There is a specialization + * for the terminating case in which the base class is CValueObject. + * + * Specialize this template to use non-default policies for a particular derived class. + * Due to the void default template parameter, specializations can inherit from CValueObjectStdTuplePolicy<> + * so that only the policies which differ from the default need be specified. + * Policy classes which can be used are defined in namespace BlackMisc::Policy. + */ + template struct CValueObjectStdTuplePolicy + { + using MetaType = Policy::MetaType::Inherit; //!< Metatype policy + using Equals = Policy::Equals::Inherit; //!< Equals policy + using LessThan = Policy::LessThan::Inherit; //!< LessThan policy + using Compare = Policy::Compare::Inherit; //!< Compare policy + using Hash = Policy::Hash::Inherit; //!< Hash policy + using DBus = Policy::DBus::Inherit; //!< DBus policy + using Json = Policy::Json::Inherit; //!< JSON policy + }; + + /*! + * Default policy classes for use by CValueObjectStdTuple. + * + * Specialization for the terminating case in which the base class is CValueObject. + */ + template <> struct CValueObjectStdTuplePolicy + { + using MetaType = Policy::MetaType::Default; //!< Metatype policy + using Equals = Policy::Equals::MetaTuple; //!< Equals policy + using LessThan = Policy::LessThan::MetaTuple; //!< Less than policy + using Compare = Policy::Compare::MetaTuple; //!< Compare policy + using Hash = Policy::Hash::MetaTuple; //!< Hash policy + using DBus = Policy::DBus::MetaTuple; //!< DBus policy + using Json = Policy::Json::MetaTuple; //!< JSon policy }; /*! * Standard implementation of CValueObject using meta tuple system. * + * This uses policy-based design. Specialize the class template CValueObjectStdTuplePolicy + * to specify different policy classes. + * * \tparam Derived The class which is inheriting from this one (CRTP). * \tparam Base The class which this one shall inherit from (default is CValueObject, * but this can be changed to create a deeper inheritance hierarchy). */ - template class CValueObjectStdTuple : public Base + template class CValueObjectStdTuple : + public Base, + private CValueObjectStdTuplePolicy::Equals::template Ops, + private CValueObjectStdTuplePolicy::LessThan::template Ops { static_assert(std::is_base_of::value, "Base must be derived from CValueObject"); - friend bool operator ==(const Derived &a, const Derived &b) - { - return equals(a, b); - } - - friend bool operator !=(const Derived &a, const Derived &b) - { - return !(a == b); - } + using MetaTypePolicy = typename CValueObjectStdTuplePolicy::MetaType; + using ComparePolicy = typename CValueObjectStdTuplePolicy::Compare; + using HashPolicy = typename CValueObjectStdTuplePolicy::Hash; + using DBusPolicy = typename CValueObjectStdTuplePolicy::DBus; + using JsonPolicy = typename CValueObjectStdTuplePolicy::Json; public: //! Base class @@ -302,13 +342,13 @@ namespace BlackMisc //! \copydoc CValueObject::getValueHash() virtual uint getValueHash() const override { - return qHash(TupleConverter::toMetaTuple(*derived())) ^ Base::getValueHash(); + return HashPolicy::hashImpl(*derived()) ^ Base::getValueHash(); } //! \copydoc CValueObject::toJson virtual QJsonObject toJson() const override { - QJsonObject json = BlackMisc::serializeJson(TupleConverter::toMetaTuple(*derived())); + QJsonObject json = JsonPolicy::serializeImpl(*derived()); return Json::appendJsonObject(json, Base::toJson()); } @@ -316,7 +356,7 @@ namespace BlackMisc virtual void convertFromJson(const QJsonObject &json) override { Base::convertFromJson(json); - BlackMisc::deserializeJson(json, TupleConverter::toMetaTuple(*derived())); + JsonPolicy::deserializeImpl(json, *derived()); } //! \copydoc CValueObject::toQVariant() @@ -334,8 +374,7 @@ namespace BlackMisc //! Register metadata static void registerMetadata() { - qRegisterMetaType(); - qDBusRegisterMetaType(); + MetaTypePolicy::template registerImpl(); } protected: @@ -371,7 +410,7 @@ namespace BlackMisc virtual int compareImpl(const CValueObject &other) const override { const auto &otherDerived = static_cast(other); - int result = compare(TupleConverter::toMetaTuple(*derived()), TupleConverter::toMetaTuple(otherDerived)); + int result = ComparePolicy::compareImpl(*derived(), otherDerived); if (result) return result; return Base::compareImpl(other); } @@ -380,28 +419,20 @@ namespace BlackMisc virtual void marshallToDbus(QDBusArgument &argument) const override { Base::marshallToDbus(argument); - argument << TupleConverter::toMetaTuple(*derived()); + DBusPolicy::marshallImpl(argument, *derived()); } //! \copydoc CValueObject::unmarshallFromDbus() virtual void unmarshallFromDbus(const QDBusArgument &argument) override { Base::unmarshallFromDbus(argument); - argument >> TupleConverter::toMetaTuple(*derived()); + DBusPolicy::unmarshallImpl(argument, *derived()); } private: const Derived *derived() const { return static_cast(this); } Derived *derived() { return static_cast(this); } - // Friend functions are implemented in terms of private static member functions, because - // friendship is not transitive: friends of CValueObjectStdTuple are not friends of TupleConverter. - static bool equals(const Derived &a, const Derived &b) - { - if (&a == &b) { return true; } - return TupleConverter::toMetaTuple(a) == TupleConverter::toMetaTuple(b); - } - // fallbacks in case Derived is not a registered meta type template using IsRegisteredQMetaType = std::integral_constant::Defined>; static int maybeGetMetaTypeId(std::true_type) { return qMetaTypeId(); } diff --git a/src/blackmisc/valueobject_policy.h b/src/blackmisc/valueobject_policy.h new file mode 100644 index 000000000..878295eda --- /dev/null +++ b/src/blackmisc/valueobject_policy.h @@ -0,0 +1,341 @@ +/* Copyright (C) 2014 + * swift project Community / Contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, + * including this file, may be copied, modified, propagated, or distributed except according to the terms + * contained in the LICENSE file. + */ + +//! \file + +#ifndef BLACKMISC_VALUEOBJECT_POLICY_H +#define BLACKMISC_VALUEOBJECT_POLICY_H + +#include "tuple.h" +#include +#include + +namespace BlackMisc +{ + template + struct CValueObjectStdTuplePolicy; + + namespace Policy + { + namespace Private + { + //! \private Alias for the policy of the base class of T + template + using Inherit = CValueObjectStdTuplePolicy; + + //! \private + using BlackMisc::Private::EncapsulationBreaker; + } + + namespace MetaType + { + //! CValueObjectStdTuple default registerMetadata policy + struct Default + { + //! Register with QMetaType + template + static void registerImpl() { qRegisterMetaType(); qDBusRegisterMetaType(); } + }; + + //! CValueObjectStdTuple registerMetadata policy which inherits the policy of the base class + struct Inherit + { + //! Register with QMetaType + template + static void registerImpl() { Private::Inherit::MetaType::template registerImpl(); } + }; + + //! CValueObjectStdTuple registerMetadata policy which also registers QList + struct DefaultAndQList + { + //! Register with QMetaType + template + static void registerImpl() { Default::registerImpl(); Default::registerImpl>(); } + }; + } + + namespace Equals + { + //! CValueObjectStdTuple policy for a class which should not have an equals operator + struct None + { + //! Inner class template which actually bestows the operators via the Barton-Nackman trick + template + struct Ops {}; + }; + + //! CValueObjectStdTuple equals operator policy which inherits the policy of the base class + struct Inherit + { + //! Inner class template which actually bestows the operators via the Barton-Nackman trick + template + struct Ops : public CValueObjectStdTuplePolicy::Equals::template Ops {}; + }; + + //! CValueObjectStdTuple policy for a class whose meta tuple members can be compared by the equals operator + struct MetaTuple + { + //! Inner class template which actually bestows the operators via the Barton-Nackman trick + template + struct Ops : private Private::EncapsulationBreaker + { + //! Equals operator + friend bool operator ==(const T &a, const T &b) { return Private::EncapsulationBreaker::toMetaTuple(a) == Private::EncapsulationBreaker::toMetaTuple(b); } + + //! Not equals operator + friend bool operator !=(const T &a, const T &b) { return !(a == b); } + }; + }; + + //! Some classes define their own custom equals operator; this policy provides a not equals operator which simply negates the result + struct OwnEquals + { + //! Inner class template which actually bestows the operators via the Barton-Nackman trick + template + struct Ops + { + //! Not equals operator; class T already has its own equals operator + friend bool operator !=(const T &a, const T &b) { return !(a == b); } + }; + }; + } + + namespace LessThan + { + //! CValueObjectStdTuple policy for a class which should not have a less than operator + struct None + { + //! Inner class template which actually bestows the operators via the Barton-Nackman trick + template + struct Ops {}; + }; + + //! CValueObjectStdTuple less than operator policy which inherits the policy of the base class + struct Inherit + { + //! Inner class template which actually bestows the operators via the Barton-Nackman trick + template + struct Ops : public CValueObjectStdTuplePolicy::LessThan::template Ops {}; + }; + + //! CValueObjectStdTuple policy for a class whose meta tuple members can be compared by the less than operator + struct MetaTuple + { + //! Inner class template which actually bestows the operators via the Barton-Nackman trick + template + struct Ops : private Private::EncapsulationBreaker + { + //! Less than operator + friend bool operator <(const T &a, const T &b) { return Private::EncapsulationBreaker::toMetaTuple(a) < Private::EncapsulationBreaker::toMetaTuple(b); } + + //! Greater than operator + friend bool operator >(const T &a, const T &b) { return b < a; } + + //! Greater or equal operator + friend bool operator >=(const T &a, const T &b) { return !(a < b); } + + //! Less or equal operator + friend bool operator <=(const T &a, const T &b) { return !(b < a); } + }; + }; + + //! Some classes define their own custom less than operator; this policy implements other comparison operators in terms of this one + struct OwnLessThan + { + //! Inner class template which actually bestows the operators via the Barton-Nackman trick + template + struct Ops + { + //! Greater than operator; class T already has its own less than operator + friend bool operator >(const T &a, const T &b) { return b < a; } + + //! Greater or equal operator; class T already has its own less than operator + friend bool operator >=(const T &a, const T &b) { return !(a < b); } + + //! Less or equal operator; class T already has its own less than operator + friend bool operator <=(const T &a, const T &b) { return !(b < a); } + }; + }; + } + + namespace Compare + { + //! CValueObjectStdTuple policy for a class without polymorphic comparison support + struct None + { + //! Policy implementation of CValueObject::compareImpl + template + static bool compareImpl(const T &, const T &) { qFatal("Not implemented"); return 0; } + }; + + //! CValueObjectStdTuple polymorphic comparison policy which inherits the policy of the base class + struct Inherit + { + //! Policy implementation of CValueObject::compareImpl + template + static bool compareImpl(const T &a, const T &b) { return Private::Inherit::Compare::template compareImpl(a, b); } + }; + + //! CValueObjectStdTuple policy for a class with default metatuple-based polymorphic comparison support + struct MetaTuple : private Private::EncapsulationBreaker + { + //! Policy implementation of CValueObject::compareImpl + template + static bool compareImpl(const T &a, const T &b) { return compare(Private::EncapsulationBreaker::toMetaTuple(a), Private::EncapsulationBreaker::toMetaTuple(b)); } + }; + + //! CValueObjectStdTuple policy for a class which implements its own custom poylymorphic comparison support + struct Own + { + //! Policy implementation of CValueObject::compareImpl + template + static bool compareImpl(const T &, const T &) { return 0; } + }; + } + + namespace Hash + { + //! CValueObjectStdTuple policy for a class without hashing support + struct None + { + //! \copydoc BlackMisc::CValueObject::getHashValue + template + static uint hashImpl(const T &) { qFatal("Not implemented"); return 0; } + }; + + //! CValueObjectStdTuple hashing policy which inherits the policy of the base class + struct Inherit + { + //! \copydoc BlackMisc::CValueObject::getHashValue + template + static uint hashImpl(const T &obj) { return Private::Inherit::Hash::template hashImpl(obj); } + }; + + //! CValueObjectStdTuple policy for a class with default metatuple-based hashing support + struct MetaTuple : private Private::EncapsulationBreaker + { + //! \copydoc BlackMisc::CValueObject::getHashValue + template + static uint hashImpl(const T &obj) { return qHash(Private::EncapsulationBreaker::toMetaTuple(obj)); } + }; + + //! CValueObjectStdTuple policy for a class which implements its own custom hashing support + struct Own + { + //! \copydoc BlackMisc::CValueObject::getHashValue + template + static uint hashImpl(const T &) { return 0; } + }; + } + + namespace DBus + { + //! CValueObjectStdTuple policy for a class without DBus marshalling support + struct None + { + //! \copydoc BlackMisc::CValueObject::marshallToDbus + template + static void marshallImpl(QDBusArgument &, const T &) { qFatal("Not implemented"); } + + //! \copydoc BlackMisc::CValueObject::unmarshallFromDbus + template + static void unmarshallImpl(const QDBusArgument &, T &) { qFatal("Not implemented"); } + }; + + //! CValueObjectStdTuple marshalling policy which inherits the policy of the base class + struct Inherit + { + //! \copydoc BlackMisc::CValueObject::marshallToDbus + template + static void marshallImpl(QDBusArgument &arg, const T &obj) { Private::Inherit::DBus::template marshallImpl(arg, obj); } + + //! \copydoc BlackMisc::CValueObject::unmarshallFromDbus + template + static void unmarshallImpl(const QDBusArgument &arg, T &obj) { return Private::Inherit::DBus::template unmarshallImpl(arg, obj); } + }; + + //! CValueObjectStdTuple policy for a class with default metatuple-based DBus marshalling support + struct MetaTuple : private Private::EncapsulationBreaker + { + //! \copydoc BlackMisc::CValueObject::marshallToDbus + template + static void marshallImpl(QDBusArgument &arg, const T &obj) { arg << Private::EncapsulationBreaker::toMetaTuple(obj); } + + //! \copydoc BlackMisc::CValueObject::unmarshallFromDbus + template + static void unmarshallImpl(const QDBusArgument &arg, T &obj) { arg >> Private::EncapsulationBreaker::toMetaTuple(obj); } + }; + + //! CValueObjectStdTuple policy for a class which implements its own custom DBus marshalling support + struct Own + { + //! \copydoc BlackMisc::CValueObject::marshallToDbus + template + static void marshallImpl(QDBusArgument &, const T &) {} + + //! \copydoc BlackMisc::CValueObject::unmarshallFromDbus + template + static void unmarshallImpl(const QDBusArgument &, T &) {} + }; + } + + namespace Json + { + //! CValueObjectStdTuple policy for a class without JSON support + struct None + { + //! \copydoc BlackMisc::serializeJson + template + static QJsonObject serializeImpl(const T &) { qFatal("Not implemented"); return {}; } + + //! \copydoc BlackMisc::deserializeJson + template + static void deserializeImpl(const QJsonObject &, T &) { qFatal("Not implemented"); } + }; + + //! CValueObjectStdTuple JSON policy which inherits the policy of the base class + struct Inherit + { + //! \copydoc BlackMisc::serializeJson + template + static QJsonObject serializeImpl(const T &obj) { return Private::Inherit::Json::template serializeImpl(obj); } + + //! \copydoc BlackMisc::deserializeJson + template + static void deserializeImpl(const QJsonObject &json, T &obj) { Private::Inherit::Json::template deserializeImpl(json, obj); } + }; + + //! CValueObjectStdTuple policy for a class with default metatuple-based JSON support + struct MetaTuple : private Private::EncapsulationBreaker + { + //! \copydoc BlackMisc::serializeJson + template + static QJsonObject serializeImpl(const T &obj) { return BlackMisc::serializeJson(Private::EncapsulationBreaker::toMetaTuple(obj)); } + + //! \copydoc BlackMisc::deserializeJson + template + static void deserializeImpl(const QJsonObject &json, T &obj) { BlackMisc::deserializeJson(json, Private::EncapsulationBreaker::toMetaTuple(obj)); } + }; + + //! CValueObjectStdTuple policy for a class which implements its own custom JSON support + struct Own + { + //! \copydoc BlackMisc::serializeJson + template + static QJsonObject serializeImpl(const T &) { return {}; } + + //! \copydoc BlackMisc::deserializeJson + template + static void deserializeImpl(const QJsonObject &, T &) {} + }; + } + } +} + +#endif