diff --git a/src/blackgui/views/viewbase.h b/src/blackgui/views/viewbase.h index 114964a28..9ead6e484 100644 --- a/src/blackgui/views/viewbase.h +++ b/src/blackgui/views/viewbase.h @@ -16,6 +16,7 @@ #include "blackmisc/icons.h" #include "blackmisc/worker.h" #include "blackmisc/variant.h" +#include "blackmisc/propertyindex.h" #include #include #include diff --git a/src/blackmisc/audio/voiceroom.h b/src/blackmisc/audio/voiceroom.h index 08f02ae79..b41d7d1c1 100644 --- a/src/blackmisc/audio/voiceroom.h +++ b/src/blackmisc/audio/voiceroom.h @@ -10,6 +10,7 @@ //! \file #include "blackmisc/blackmiscexport.h" +#include "blackmisc/valueobject.h" #include "blackmisc/propertyindex.h" #include "blackmisc/blackmiscfreefunctions.h" #include diff --git a/src/blackmisc/aviation/aircrafticao.h b/src/blackmisc/aviation/aircrafticao.h index 0e2d9a674..a5e75d3eb 100644 --- a/src/blackmisc/aviation/aircrafticao.h +++ b/src/blackmisc/aviation/aircrafticao.h @@ -13,6 +13,7 @@ #define BLACKMISC_AVIATION_AIRCRAFTICAO_H #include "blackmisc/blackmiscexport.h" +#include "blackmisc/valueobject.h" #include "blackmisc/propertyindex.h" #include "blackmisc/blackmiscfreefunctions.h" diff --git a/src/blackmisc/aviation/aircraftparts.cpp b/src/blackmisc/aviation/aircraftparts.cpp index 13ba0f8bb..0d1ffe2e5 100644 --- a/src/blackmisc/aviation/aircraftparts.cpp +++ b/src/blackmisc/aviation/aircraftparts.cpp @@ -76,7 +76,7 @@ namespace BlackMisc switch (i) { case IndexEngines: - this->m_engines.setPropertyByIndex(variant, index.copyFrontRemoved()); + this->m_engines = variant.tom_engines)>(); break; case IndexFlapsPercentage: this->m_flapsPercentage = variant.toInt(); diff --git a/src/blackmisc/aviation/callsign.h b/src/blackmisc/aviation/callsign.h index b4c42019d..9173f772c 100644 --- a/src/blackmisc/aviation/callsign.h +++ b/src/blackmisc/aviation/callsign.h @@ -13,6 +13,7 @@ #define BLACKMISC_AVIATION_CALLSIGN_H #include "blackmisc/blackmiscexport.h" +#include "blackmisc/valueobject.h" #include "blackmisc/propertyindex.h" #include "blackmisc/icon.h" #include "blackmisc/blackmiscfreefunctions.h" diff --git a/src/blackmisc/blackmiscfreefunctions.h b/src/blackmisc/blackmiscfreefunctions.h index f65ef225a..d73d39724 100644 --- a/src/blackmisc/blackmiscfreefunctions.h +++ b/src/blackmisc/blackmiscfreefunctions.h @@ -13,6 +13,8 @@ #define BLACKMISC_FREEFUNCTIONS_H #include "blackmisc/blackmiscexport.h" +#include "blackmisc/tuple.h" +#include "blackmisc/inheritance_traits.h" #include // for Q_INIT_RESOURCE #include #include @@ -111,6 +113,103 @@ namespace BlackMisc //! Init resources BLACKMISC_EXPORT void initResources(); + namespace Mixin + { + + /*! + * 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 void *) { return 0; } + }; + + /*! + * CRTP class template from which a derived class can inherit string streaming operations. + */ + template + class String + { + public: + //! Stream << overload to be used in debugging messages + friend QDebug operator<<(QDebug debug, const Derived &obj) + { + debug << obj.stringForStreaming(); + return debug; + } + + //! Operator << when there is no debug stream + friend QNoDebug operator<<(QNoDebug nodebug, const Derived &obj) + { + Q_UNUSED(obj); + return nodebug; + } + + //! Operator << based on text stream + friend QTextStream &operator<<(QTextStream &stream, const Derived &obj) + { + stream << obj.stringForStreaming(); + return stream; + } + + //! Operator << for QDataStream + friend QDataStream &operator<<(QDataStream &stream, const Derived &obj) + { + stream << obj.stringForStreaming(); + return stream; + } + + //! Stream operator << for std::cout + friend std::ostream &operator<<(std::ostream &ostr, const Derived &obj) + { + ostr << obj.stringForStreaming().toStdString(); + return ostr; + } + + //! Cast as QString + QString toQString(bool i18n = false) const { return derived()->convertToQString(i18n); } + + //! Cast to pretty-printed QString + QString toFormattedQString(bool i18n = false) const { return derived()->toQString(i18n); } + + //! To std string + std::string toStdString(bool i18n = false) const { return derived()->convertToQString(i18n).toStdString(); } + + //! String for streaming operators + QString stringForStreaming() const { return derived()->convertToQString(); } + + private: + const Derived *derived() const { return static_cast(this); } + Derived *derived() { return static_cast(this); } + }; + + /*! + * When a derived class and a base class both inherit from Mixin::String, + * the derived class uses this macro to disambiguate the inherited members. + */ +# define BLACKMISC_DECLARE_USING_MIXIN_STRING(DERIVED) \ + using ::BlackMisc::Mixin::String::toQString; \ + using ::BlackMisc::Mixin::String::toFormattedQString; \ + using ::BlackMisc::Mixin::String::toStdString; \ + using ::BlackMisc::Mixin::String::stringForStreaming; + + } // Mixin + //! Checked version from QVariant template void setFromQVariant(T *value, const QVariant &variant) { diff --git a/src/blackmisc/collection.h b/src/blackmisc/collection.h index a06e1aa2d..067d42c28 100644 --- a/src/blackmisc/collection.h +++ b/src/blackmisc/collection.h @@ -9,8 +9,6 @@ //! \file -#include "valueobject.h" // outside include guard due to cyclic dependency hack (MS) - #ifndef BLACKMISC_COLLECTION_H #define BLACKMISC_COLLECTION_H diff --git a/src/blackmisc/compare.h b/src/blackmisc/compare.h new file mode 100644 index 000000000..90fe2221e --- /dev/null +++ b/src/blackmisc/compare.h @@ -0,0 +1,132 @@ +/* Copyright (C) 2015 + * 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_COMPARE_H +#define BLACKMISC_COMPARE_H + +#include "blackmisc/tuple.h" +#include "blackmisc/inheritance_traits.h" + +namespace BlackMisc +{ + namespace Mixin + { + + /*! + * CRTP class template from which a derived class can inherit operator== implemented using its compare function. + */ + template + class EqualsByCompare + { + public: + //! Equals + friend bool operator ==(const Derived &a, const Derived &b) { return compare(a, b) == 0; } + + //! Not equal + friend bool operator !=(const Derived &a, const Derived &b) { return compare(a, b) != 0; } + }; + + /*! + * 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) + { + 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 void *, const void *) { return true; } + }; + + /*! + * CRTP class template from which a derived class can inherit operator< implemented using its compare function. + */ + template + class LessThanByCompare + { + public: + //! Less than + friend bool operator <(const Derived &a, const Derived &b) { return compare(a, b) < 0; } + + //! Greater than + friend bool operator >(const Derived &a, const Derived &b) { return compare(a, b) > 0; } + + //! Less than or equal + friend bool operator <=(const Derived &a, const Derived &b) { return compare(a, b) <= 0; } + + //! Greater than or equal + friend bool operator >=(const Derived &a, const Derived &b) { return compare(a, b) >= 0; } + }; + + /*! + * 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) + { + 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 void *, const void *) { return false; } + }; + + /*! + * 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 void *, const void *) { return 0; } + }; + + } // Mixin +} // BlackMisc + +#endif diff --git a/src/blackmisc/containerbase.h b/src/blackmisc/containerbase.h index 2abb189cf..0b768a9b3 100644 --- a/src/blackmisc/containerbase.h +++ b/src/blackmisc/containerbase.h @@ -16,6 +16,9 @@ #include "blackmiscfreefunctions.h" #include "predicates.h" #include "json.h" +#include "variant.h" +#include "dbus.h" +#include "icon.h" #include namespace BlackMisc @@ -51,7 +54,6 @@ namespace BlackMisc public Mixin::DBusOperators>, public Mixin::JsonOperators>, public Mixin::String>, - public Mixin::Index>, public Mixin::Icon> { public: diff --git a/src/blackmisc/dbus.h b/src/blackmisc/dbus.h index a53386b9a..d0c733909 100644 --- a/src/blackmisc/dbus.h +++ b/src/blackmisc/dbus.h @@ -12,9 +12,86 @@ #ifndef BLACKMISC_DBUS_H #define BLACKMISC_DBUS_H +#include "blackmisc/tuple.h" +#include "blackmisc/inheritance_traits.h" #include #include +namespace BlackMisc +{ + namespace Mixin + { + + /*! + * CRTP class template which will generate marshalling operators for a derived class with its own marshalling implementation. + */ + template + class DBusOperators + { + public: + //! Unmarshalling operator >>, DBus to object + friend const QDBusArgument &operator>>(const QDBusArgument &arg, Derived &obj) + { + arg.beginStructure(); + obj.unmarshallFromDbus(arg); + arg.endStructure(); + return arg; + } + + //! Marshalling operator <<, object to DBus + friend QDBusArgument &operator<<(QDBusArgument &arg, const Derived &obj) + { + arg.beginStructure(); + obj.marshallToDbus(arg); + arg.endStructure(); + return arg; + } + }; + + /*! + * CRTP class template from which a derived class can inherit common methods dealing with marshalling instances by metatuple. + */ + template + class DBusByTuple : public DBusOperators, private Private::EncapsulationBreaker + { + public: + //! Marshall without begin/endStructure, for when composed within another object + void marshallToDbus(QDBusArgument &arg) const + { + baseMarshall(static_cast *>(derived()), arg); + using BlackMisc::operator<<; + arg << Private::EncapsulationBreaker::toMetaTuple(*derived()); + } + + //! Unmarshall without begin/endStructure, for when composed within another object + void unmarshallFromDbus(const QDBusArgument &arg) + { + baseUnmarshall(static_cast *>(derived()), arg); + using BlackMisc::operator>>; + arg >> Private::EncapsulationBreaker::toMetaTuple(*derived()); + } + + private: + const Derived *derived() const { return static_cast(this); } + Derived *derived() { return static_cast(this); } + + template static void baseMarshall(const T *base, QDBusArgument &arg) { base->marshallToDbus(arg); } + template static void baseUnmarshall(T *base, const QDBusArgument &arg) { base->unmarshallFromDbus(arg); } + static void baseMarshall(const void *, QDBusArgument &) {} + static void baseUnmarshall(void *, const QDBusArgument &) {} + }; + + /*! + * When a derived class and a base class both inherit from Mixin::DBusByTuple, + * the derived class uses this macro to disambiguate the inherited members. + */ +# define BLACKMISC_DECLARE_USING_MIXIN_DBUS(DERIVED) \ + using ::BlackMisc::Mixin::DBusByTuple::marshallToDbus; \ + using ::BlackMisc::Mixin::DBusByTuple::unmarshallFromDbus; + + } // Mixin +} // BlackMisc + /*! * Non-member non-friend operator for streaming enums to QDBusArgument. * @@ -31,4 +108,5 @@ operator>>(const QDBusArgument &argument, ENUM &enumType) enumType = static_cast(e); return argument; } + #endif // guard diff --git a/src/blackmisc/hardware/keyboardkey.h b/src/blackmisc/hardware/keyboardkey.h index d4d69ac4a..51fa8e527 100644 --- a/src/blackmisc/hardware/keyboardkey.h +++ b/src/blackmisc/hardware/keyboardkey.h @@ -14,6 +14,7 @@ #include "blackmisc/blackmiscexport.h" #include "blackmisc/propertyindex.h" +#include "blackmisc/valueobject.h" #include "blackmisc/blackmiscfreefunctions.h" #include #include diff --git a/src/blackmisc/hotkeyfunction.h b/src/blackmisc/hotkeyfunction.h index 71db77c9c..c8835a7ec 100644 --- a/src/blackmisc/hotkeyfunction.h +++ b/src/blackmisc/hotkeyfunction.h @@ -14,6 +14,7 @@ #include "blackmiscexport.h" #include "propertyindex.h" +#include "valueobject.h" #include "blackmiscfreefunctions.h" namespace BlackMisc diff --git a/src/blackmisc/icon.h b/src/blackmisc/icon.h index 2a85e2699..a92036c56 100644 --- a/src/blackmisc/icon.h +++ b/src/blackmisc/icon.h @@ -9,25 +9,66 @@ //! \file -#include "valueobject.h" // outside include guard due to cyclic dependency hack (MS) - #ifndef BLACKMISC_ICON_H #define BLACKMISC_ICON_H #include "blackmiscexport.h" #include "icons.h" +#include "variant.h" +#include "tuple.h" +#include "inheritance_traits.h" #include namespace BlackMisc { + class CIcon; + namespace PhysicalQuantities { class CAngle; } + namespace Mixin + { + /*! + * CRTP class template from which a derived class can inherit icon-related functions. + */ + template + class Icon + { + public: + //! As icon, not implemented by all classes + CIcon toIcon() const; + + //! As pixmap, required for most GUI views + QPixmap toPixmap() const; + + private: + const Derived *derived() const { return static_cast(this); } + Derived *derived() { return static_cast(this); } + }; + + /*! + * When a derived class and a base class both inherit from Mixin::Icon, + * the derived class uses this macro to disambiguate the inherited members. + */ +# define BLACKMISC_DECLARE_USING_MIXIN_ICON(DERIVED) \ + using ::BlackMisc::Mixin::Icon::toIcon; \ + using ::BlackMisc::Mixin::Icon::toPixmap; + } // Mixin + //! Value object for icons. An icon is stored in the global icon repository and //! identified by its index. It contains no(!) pyhsical data for the icon itself. - class BLACKMISC_EXPORT CIcon : public CValueObject + class BLACKMISC_EXPORT CIcon : + public Mixin::MetaType, + public Mixin::HashByTuple, + public Mixin::DBusByTuple, + public Mixin::JsonByTuple, + public Mixin::EqualsByTuple, + public Mixin::LessThanByTuple, + public Mixin::CompareByTuple, + public Mixin::String, + public Mixin::Icon { public: //! Default constructor. @@ -85,6 +126,20 @@ namespace BlackMisc //! \private Needed so we can copy forward-declared CIcon. inline void assign(CIcon &a, const CIcon &b) { a = b; } } + + namespace Mixin + { + template + CIcon Icon::toIcon() const + { + return CIcon::iconByIndex(IconIndex); + } + template + QPixmap Icon::toPixmap() const + { + return derived()->toIcon().toPixmap(); + } + } } // namespace BLACK_DECLARE_TUPLE_CONVERSION(BlackMisc::CIcon, (o.m_index, o.m_descriptiveText)) diff --git a/src/blackmisc/iconlist.h b/src/blackmisc/iconlist.h index ceee8c90c..3188f18bd 100644 --- a/src/blackmisc/iconlist.h +++ b/src/blackmisc/iconlist.h @@ -9,8 +9,6 @@ //! \file -#include "valueobject.h" // outside include guard due to cyclic dependency hack (MS) - #ifndef BLACKMISC_ICONLIST_H #define BLACKMISC_ICONLIST_H diff --git a/src/blackmisc/json.h b/src/blackmisc/json.h index a5cd2aff1..da361ab7c 100644 --- a/src/blackmisc/json.h +++ b/src/blackmisc/json.h @@ -14,7 +14,9 @@ #ifndef BLACKMISC_JSON_H #define BLACKMISC_JSON_H -#include "blackmiscexport.h" +#include "blackmisc/blackmiscexport.h" +#include "blackmisc/tuple.h" +#include "blackmisc/inheritance_traits.h" #include #include #include @@ -113,6 +115,7 @@ namespace BlackMisc { namespace Json { + //! \brief Append to first JSON object (concatenate) //! \ingroup JSON inline QJsonObject &appendJsonObject(QJsonObject &target, const QJsonObject &toBeAppended) @@ -125,7 +128,95 @@ namespace BlackMisc } return target; } - } // ns -} // ns + + } // Json + + namespace Mixin + { + + /*! + * CRTP class template which will generate marshalling operators for a derived class with its own marshalling implementation. + */ + template + class JsonOperators + { + public: + //! operator >> for JSON + friend const QJsonObject &operator>>(const QJsonObject &json, Derived &obj) + { + obj.convertFromJson(json); + return json; + } + + //! operator >> for JSON + friend const QJsonValue &operator>>(const QJsonValue &json, Derived &obj) + { + obj.convertFromJson(json.toObject()); + return json; + } + + //! operator >> for JSON + friend const QJsonValueRef &operator>>(const QJsonValueRef &json, Derived &obj) + { + obj.convertFromJson(json.toObject()); + return json; + } + + //! operator << for JSON + friend QJsonArray &operator<<(QJsonArray &json, const Derived &obj) + { + json.append(obj.toJson()); + return json; + } + + //! operator << for JSON + friend QJsonObject &operator<<(QJsonObject &json, const std::pair &value) + { + json.insert(value.first, QJsonValue(value.second.toJson())); + return json; + } + }; + + /*! + * CRTP class template from which a derived class can inherit common methods dealing with JSON by metatuple. + */ + template + class JsonByTuple : public JsonOperators, private Private::EncapsulationBreaker + { + public: + //! Cast to JSON object + QJsonObject toJson() const + { + QJsonObject json = BlackMisc::serializeJson(Private::EncapsulationBreaker::toMetaTuple(*derived())); + return Json::appendJsonObject(json, baseToJson(static_cast *>(derived()))); + } + + //! Assign from JSON object + void convertFromJson(const QJsonObject &json) + { + baseConvertFromJson(static_cast *>(derived()), json); + BlackMisc::deserializeJson(json, Private::EncapsulationBreaker::toMetaTuple(*derived())); + } + + private: + const Derived *derived() const { return static_cast(this); } + Derived *derived() { return static_cast(this); } + + template static QJsonObject baseToJson(const T *base) { return base->toJson(); } + template static void baseConvertFromJson(T *base, const QJsonObject &json) { base->convertFromJson(json); } + static QJsonObject baseToJson(const void *) { return {}; } + static void baseConvertFromJson(void *, const QJsonObject &) {} + }; + + /*! + * When a derived class and a base class both inherit from Mixin::JsonByTuple, + * the derived class uses this macro to disambiguate the inherited members. + */ +# define BLACKMISC_DECLARE_USING_MIXIN_JSON(DERIVED) \ + using ::BlackMisc::Mixin::JsonByTuple::toJson; \ + using ::BlackMisc::Mixin::JsonByTuple::convertFromJson; + + } // Mixin +} // BlackMisc #endif // guard diff --git a/src/blackmisc/logcategory.h b/src/blackmisc/logcategory.h index 8740b84d8..54b95b0ca 100644 --- a/src/blackmisc/logcategory.h +++ b/src/blackmisc/logcategory.h @@ -13,6 +13,7 @@ //! \file #include "blackmiscexport.h" +#include "valueobject.h" #include "sequence.h" namespace BlackMisc diff --git a/src/blackmisc/propertyindex.h b/src/blackmisc/propertyindex.h index a7966a86d..644ec3b32 100644 --- a/src/blackmisc/propertyindex.h +++ b/src/blackmisc/propertyindex.h @@ -9,13 +9,15 @@ //! \file -#include "valueobject.h" // outside include guard due to cyclic dependency hack (MS) - #ifndef BLACKMISC_PROPERTYINDEX_H #define BLACKMISC_PROPERTYINDEX_H #include "blackmiscexport.h" #include "blackmiscfreefunctions.h" +#include "variant.h" +#include "dbus.h" +#include "json.h" +#include "compare.h" #include namespace BlackMisc @@ -25,7 +27,15 @@ namespace BlackMisc * Property index. The index can be nested, that's why it is a sequence * (e.g. PropertyIndexPilot, PropertyIndexRealname). */ - class BLACKMISC_EXPORT CPropertyIndex : public CValueObject + class BLACKMISC_EXPORT CPropertyIndex : + public Mixin::MetaType, + public Mixin::HashByTuple, + public Mixin::DBusByTuple, + public Mixin::JsonByTuple, + public Mixin::EqualsByTuple, + public Mixin::LessThanByTuple, + public Mixin::CompareByTuple, + public Mixin::String { // In the first trial I have used CSequence as base class // This has created too much circular dependencies of the headers diff --git a/src/blackmisc/propertyindexlist.h b/src/blackmisc/propertyindexlist.h index b25a44b0b..8d6a1e7f6 100644 --- a/src/blackmisc/propertyindexlist.h +++ b/src/blackmisc/propertyindexlist.h @@ -9,8 +9,6 @@ //! \file -#include "valueobject.h" // outside include guard due to cyclic dependency hack (MS) - #ifndef BLACKMISC_PROPERTYINDEXLIST_H #define BLACKMISC_PROPERTYINDEXLIST_H diff --git a/src/blackmisc/propertyindexvariantmap.h b/src/blackmisc/propertyindexvariantmap.h index 6a2e35a27..7d5044d24 100644 --- a/src/blackmisc/propertyindexvariantmap.h +++ b/src/blackmisc/propertyindexvariantmap.h @@ -13,15 +13,57 @@ #define BLACKMISC_PROPERTYINDEXVARIANTMAP_H #include "variant.h" -#include "valueobject.h" #include "propertyindexlist.h" #include "blackmiscexport.h" +#include "tuple.h" +#include "inheritance_traits.h" #include #include namespace BlackMisc { + class CPropertyIndexVariantMap; + + namespace Mixin + { + /*! + * CRTP class template from which a derived class can inherit property indexing functions. + */ + template + class Index + { + public: + //! Base class enums + enum ColumnIndex + { + IndexPixmap = 10, // manually set to avoid circular dependencies + IndexIcon, + IndexString + }; + + //! Update by variant map + //! \return number of values changed, with skipEqualValues equal values will not be changed + CPropertyIndexList apply(const BlackMisc::CPropertyIndexVariantMap &indexMap, bool skipEqualValues = false); + + //! Set property by index + void setPropertyByIndex(const CVariant &variant, const CPropertyIndex &index); + + //! Property by index + CVariant propertyByIndex(const BlackMisc::CPropertyIndex &index) const; + + //! Property by index as String + QString propertyByIndexAsString(const CPropertyIndex &index, bool i18n = false) const; + + //! Is given variant equal to value of property index? + bool equalsPropertyByIndex(const CVariant &compareValue, const CPropertyIndex &index) const; + + private: + const Derived *derived() const { return static_cast(this); } + Derived *derived() { return static_cast(this); } + }; + } // Mixin + /*! * Specialized value object compliant map for variants, * based on indexes @@ -29,9 +71,7 @@ namespace BlackMisc class BLACKMISC_EXPORT CPropertyIndexVariantMap : public Mixin::MetaType, public Mixin::DBusOperators, - public Mixin::Index, - public Mixin::String, - public Mixin::Icon + public Mixin::String { public: /*! @@ -155,6 +195,81 @@ namespace BlackMisc //! \copydoc CValueObject::unmarshallFromDbus void unmarshallFromDbus(const QDBusArgument &argument); }; + + namespace Mixin + { + template + CPropertyIndexList Index::apply(const BlackMisc::CPropertyIndexVariantMap &indexMap, bool skipEqualValues) + { + if (indexMap.isEmpty()) return {}; + + CPropertyIndexList changed; + const auto &map = indexMap.map(); + for (auto it = map.begin(); it != map.end(); ++it) + { + const CVariant value = it.value().toCVariant(); + const CPropertyIndex index = it.key(); + if (skipEqualValues) + { + bool equal = derived()->equalsPropertyByIndex(value, index); + if (equal) { continue; } + } + derived()->setPropertyByIndex(value, index); + changed.push_back(index); + } + return changed; + } + template + void Index::setPropertyByIndex(const CVariant &variant, const CPropertyIndex &index) + { + if (index.isMyself()) + { + derived()->convertFromCVariant(variant); + return; + } + + // not all classes have implemented nesting + const QString m = QString("Property by index not found (setter), index: ").append(index.toQString()); + qFatal("%s", qPrintable(m)); + } + template + CVariant Index::propertyByIndex(const CPropertyIndex &index) const + { + if (index.isMyself()) + { + return derived()->toCVariant(); + } + auto i = index.frontCasted(); + switch (i) + { + case IndexIcon: + return CVariant::from(derived()->toIcon()); + case IndexPixmap: + return CVariant::from(derived()->toPixmap()); + case IndexString: + return CVariant(derived()->toQString()); + default: + break; + } + + // not all classes have implemented nesting + const QString m = QString("Property by index not found, index: ").append(index.toQString()); + qFatal("%s", qPrintable(m)); + return {}; + } + template + QString Index::propertyByIndexAsString(const CPropertyIndex &index, bool i18n) const + { + // default implementation, requires propertyByIndex + return derived()->propertyByIndex(index).toQString(i18n); + } + template + bool Index::equalsPropertyByIndex(const CVariant &compareValue, const CPropertyIndex &index) const + { + return derived()->propertyByIndex(index) == compareValue; + } + } // Mixin + } Q_DECLARE_METATYPE(BlackMisc::CPropertyIndexVariantMap) diff --git a/src/blackmisc/sequence.h b/src/blackmisc/sequence.h index 5c10d4c6b..14522cd64 100644 --- a/src/blackmisc/sequence.h +++ b/src/blackmisc/sequence.h @@ -9,8 +9,6 @@ //! \file -#include "valueobject.h" // outside include guard due to cyclic dependency hack (MS) - #ifndef BLACKMISC_SEQUENCE_H #define BLACKMISC_SEQUENCE_H diff --git a/src/blackmisc/valueobject.h b/src/blackmisc/valueobject.h index 4154685a4..d387a80fa 100644 --- a/src/blackmisc/valueobject.h +++ b/src/blackmisc/valueobject.h @@ -16,9 +16,11 @@ #include "dbus.h" #include "tuple.h" #include "json.h" -#include "icons.h" +#include "compare.h" +#include "variant.h" +#include "propertyindexvariantmap.h" +#include "iconlist.h" #include "blackmiscfreefunctions.h" -#include "valueobject_private.h" #include #include #include @@ -33,30 +35,6 @@ namespace BlackMisc { - class CPropertyIndex; - class CPropertyIndexList; - class CPropertyIndexVariantMap; - class CIcon; - class CVariant; - class CEmpty; - - /*! - * This registers the value type T with the BlackMisc meta type system, - * making it available for use with the extended feature set of BlackMisc::CVariant. - * - * The implementation (ab)uses the QMetaType converter function registration mechanism - * to store a type-erased representation of the set of operations supported by T. - * Unlike the singleton pattern, this approach means that CVariant can be used in plugins. - */ - template - void registerMetaValueType() - { - if (QMetaType::hasRegisteredConverterFunction()) { return; } - auto converter = [](const T &) { static Private::CValueObjectMetaInfo info; return &info; }; - bool ok = QMetaType::registerConverter(converter); - Q_ASSERT(ok); - Q_UNUSED(ok); - } /*! * Default base class for CValueObject. @@ -81,512 +59,6 @@ namespace BlackMisc ~CEmpty() = default; }; - namespace Mixin - { - - /*! - * CRTP class template from which a derived class can inherit common methods dealing with the metatype of the class. - */ - template - class MetaType - { - public: - //! Register metadata - static void registerMetadata() - { - Private::MetaTypeHelper::maybeRegisterMetaType(); - - [](...){}((qRegisterMetaType(), qDBusRegisterMetaType(), 0)...); - } - - //! Returns the Qt meta type ID of this object. - int getMetaTypeId() const - { - return Private::MetaTypeHelper::maybeGetMetaTypeId(); - } - - //! Returns true if this object is an instance of the class with the given meta type ID, or one of its subclasses. - bool isA(int metaTypeId) const - { - if (metaTypeId == QMetaType::UnknownType) { return false; } - if (metaTypeId == getMetaTypeId()) { return true; } - return baseIsA(static_cast *>(derived()), metaTypeId); - } - - //! Method to return CVariant - //! \deprecated Use CVariant::to() instead. - CVariant toCVariant() const; - - //! Set from CVariant - //! \deprecated Use CVariant::from() instead. - void convertFromCVariant(const CVariant &variant); - - //! Return QVariant, used with DBus QVariant lists - //! \deprecated Use QVariant::fromValue() instead. - QVariant toQVariant() const - { - return Private::MetaTypeHelper::maybeToQVariant(*derived()); - } - - //! Set from QVariant - //! \deprecated Use QVariant::value() instead. - void convertFromQVariant(const QVariant &variant) - { - return Private::MetaTypeHelper::maybeConvertFromQVariant(*derived(), variant); - } - - private: - const Derived *derived() const { return static_cast(this); } - Derived *derived() { return static_cast(this); } - - template static bool baseIsA(const Base2 *base, int metaTypeId) { return base->isA(metaTypeId); } - static bool baseIsA(const void *, int) { return false; } - }; - - /*! - * Variant of MetaType mixin which also registers QList with the type system. - */ - template - class MetaTypeAndQList : public MetaType> - {}; - - /*! - * When a derived class and a base class both inherit from Mixin::MetaType, - * the derived class uses this macro to disambiguate the inherited members. - */ -# define BLACKMISC_DECLARE_USING_MIXIN_METATYPE(DERIVED) \ - using ::BlackMisc::Mixin::MetaType::registerMetadata; \ - using ::BlackMisc::Mixin::MetaType::getMetaTypeId; \ - using ::BlackMisc::Mixin::MetaType::isA; \ - using ::BlackMisc::Mixin::MetaType::toCVariant; \ - using ::BlackMisc::Mixin::MetaType::toQVariant; \ - using ::BlackMisc::Mixin::MetaType::convertFromCVariant; \ - using ::BlackMisc::Mixin::MetaType::convertFromQVariant; - - /*! - * When a derived class and a base class both inherit from Mixin::MetaType, - * the derived class uses this macro to disambiguate the inherited members. - */ -# define BLACKMISC_DECLARE_USING_MIXIN_METATYPE_AND_QLIST(DERIVED) \ - using ::BlackMisc::Mixin::MetaTypeAndQList::registerMetadata; \ - using ::BlackMisc::Mixin::MetaTypeAndQList::getMetaTypeId; \ - using ::BlackMisc::Mixin::MetaTypeAndQList::isA; \ - using ::BlackMisc::Mixin::MetaTypeAndQList::toCVariant; \ - using ::BlackMisc::Mixin::MetaTypeAndQList::toQVariant; \ - using ::BlackMisc::Mixin::MetaTypeAndQList::convertFromCVariant; \ - using ::BlackMisc::Mixin::MetaTypeAndQList::convertFromQVariant; - - /*! - * 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 void *) { return 0; } - }; - - /*! - * CRTP class template which will generate marshalling operators for a derived class with its own marshalling implementation. - */ - template - class DBusOperators - { - public: - //! Unmarshalling operator >>, DBus to object - friend const QDBusArgument &operator>>(const QDBusArgument &arg, Derived &obj) - { - arg.beginStructure(); - obj.unmarshallFromDbus(arg); - arg.endStructure(); - return arg; - } - - //! Marshalling operator <<, object to DBus - friend QDBusArgument &operator<<(QDBusArgument &arg, const Derived &obj) - { - arg.beginStructure(); - obj.marshallToDbus(arg); - arg.endStructure(); - return arg; - } - }; - - /*! - * CRTP class template from which a derived class can inherit common methods dealing with marshalling instances by metatuple. - */ - template - class DBusByTuple : public DBusOperators, private Private::EncapsulationBreaker - { - public: - //! Marshall without begin/endStructure, for when composed within another object - void marshallToDbus(QDBusArgument &arg) const - { - baseMarshall(static_cast *>(derived()), arg); - using BlackMisc::operator<<; - arg << Private::EncapsulationBreaker::toMetaTuple(*derived()); - } - - //! Unmarshall without begin/endStructure, for when composed within another object - void unmarshallFromDbus(const QDBusArgument &arg) - { - baseUnmarshall(static_cast *>(derived()), arg); - using BlackMisc::operator>>; - arg >> Private::EncapsulationBreaker::toMetaTuple(*derived()); - } - - private: - const Derived *derived() const { return static_cast(this); } - Derived *derived() { return static_cast(this); } - - template static void baseMarshall(const T *base, QDBusArgument &arg) { base->marshallToDbus(arg); } - template static void baseUnmarshall(T *base, const QDBusArgument &arg) { base->unmarshallFromDbus(arg); } - static void baseMarshall(const void *, QDBusArgument &) {} - static void baseUnmarshall(void *, const QDBusArgument &) {} - }; - - /*! - * When a derived class and a base class both inherit from Mixin::DBusByTuple, - * the derived class uses this macro to disambiguate the inherited members. - */ -# define BLACKMISC_DECLARE_USING_MIXIN_DBUS(DERIVED) \ - using ::BlackMisc::Mixin::DBusByTuple::marshallToDbus; \ - using ::BlackMisc::Mixin::DBusByTuple::unmarshallFromDbus; - - /*! - * CRTP class template which will generate marshalling operators for a derived class with its own marshalling implementation. - */ - template - class JsonOperators - { - public: - //! operator >> for JSON - friend const QJsonObject &operator>>(const QJsonObject &json, Derived &obj) - { - obj.convertFromJson(json); - return json; - } - - //! operator >> for JSON - friend const QJsonValue &operator>>(const QJsonValue &json, Derived &obj) - { - obj.convertFromJson(json.toObject()); - return json; - } - - //! operator >> for JSON - friend const QJsonValueRef &operator>>(const QJsonValueRef &json, Derived &obj) - { - obj.convertFromJson(json.toObject()); - return json; - } - - //! operator << for JSON - friend QJsonArray &operator<<(QJsonArray &json, const Derived &obj) - { - json.append(obj.toJson()); - return json; - } - - //! operator << for JSON - friend QJsonObject &operator<<(QJsonObject &json, const std::pair &value) - { - json.insert(value.first, QJsonValue(value.second.toJson())); - return json; - } - }; - - /*! - * CRTP class template from which a derived class can inherit common methods dealing with JSON by metatuple. - */ - template - class JsonByTuple : public JsonOperators, private Private::EncapsulationBreaker - { - public: - //! Cast to JSON object - QJsonObject toJson() const - { - QJsonObject json = BlackMisc::serializeJson(Private::EncapsulationBreaker::toMetaTuple(*derived())); - return Json::appendJsonObject(json, baseToJson(static_cast *>(derived()))); - } - - //! Assign from JSON object - void convertFromJson(const QJsonObject &json) - { - baseConvertFromJson(static_cast *>(derived()), json); - BlackMisc::deserializeJson(json, Private::EncapsulationBreaker::toMetaTuple(*derived())); - } - - private: - const Derived *derived() const { return static_cast(this); } - Derived *derived() { return static_cast(this); } - - template static QJsonObject baseToJson(const T *base) { return base->toJson(); } - template static void baseConvertFromJson(T *base, const QJsonObject &json) { base->convertFromJson(json); } - static QJsonObject baseToJson(const void *) { return {}; } - static void baseConvertFromJson(void *, const QJsonObject &) {} - }; - - /*! - * When a derived class and a base class both inherit from Mixin::JsonByTuple, - * the derived class uses this macro to disambiguate the inherited members. - */ -# define BLACKMISC_DECLARE_USING_MIXIN_JSON(DERIVED) \ - using ::BlackMisc::Mixin::JsonByTuple::toJson; \ - using ::BlackMisc::Mixin::JsonByTuple::convertFromJson; - - /*! - * CRTP class template from which a derived class can inherit operator== implemented using its compare function. - */ - template - class EqualsByCompare - { - public: - //! Equals - friend bool operator ==(const Derived &a, const Derived &b) { return compare(a, b) == 0; } - - //! Not equal - friend bool operator !=(const Derived &a, const Derived &b) { return compare(a, b) != 0; } - }; - - /*! - * 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) - { - 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 void *, const void *) { return true; } - }; - - /*! - * CRTP class template from which a derived class can inherit operator< implemented using its compare function. - */ - template - class LessThanByCompare - { - public: - //! Less than - friend bool operator <(const Derived &a, const Derived &b) { return compare(a, b) < 0; } - - //! Greater than - friend bool operator >(const Derived &a, const Derived &b) { return compare(a, b) > 0; } - - //! Less than or equal - friend bool operator <=(const Derived &a, const Derived &b) { return compare(a, b) <= 0; } - - //! Greater than or equal - friend bool operator >=(const Derived &a, const Derived &b) { return compare(a, b) >= 0; } - }; - - /*! - * 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) - { - 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 void *, const void *) { return false; } - }; - - /*! - * 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 void *, const void *) { return 0; } - }; - - /*! - * CRTP class template from which a derived class can inherit string streaming operations. - */ - template - class String - { - public: - //! Stream << overload to be used in debugging messages - friend QDebug operator<<(QDebug debug, const Derived &obj) - { - debug << obj.stringForStreaming(); - return debug; - } - - //! Operator << when there is no debug stream - friend QNoDebug operator<<(QNoDebug nodebug, const Derived &obj) - { - Q_UNUSED(obj); - return nodebug; - } - - //! Operator << based on text stream - friend QTextStream &operator<<(QTextStream &stream, const Derived &obj) - { - stream << obj.stringForStreaming(); - return stream; - } - - //! Operator << for QDataStream - friend QDataStream &operator<<(QDataStream &stream, const Derived &obj) - { - stream << obj.stringForStreaming(); - return stream; - } - - //! Stream operator << for std::cout - friend std::ostream &operator<<(std::ostream &ostr, const Derived &obj) - { - ostr << obj.stringForStreaming().toStdString(); - return ostr; - } - - //! Cast as QString - QString toQString(bool i18n = false) const { return derived()->convertToQString(i18n); } - - //! Cast to pretty-printed QString - QString toFormattedQString(bool i18n = false) const { return derived()->toQString(i18n); } - - //! To std string - std::string toStdString(bool i18n = false) const { return derived()->convertToQString(i18n).toStdString(); } - - //! String for streaming operators - QString stringForStreaming() const { return derived()->convertToQString(); } - - private: - const Derived *derived() const { return static_cast(this); } - Derived *derived() { return static_cast(this); } - }; - - /*! - * When a derived class and a base class both inherit from Mixin::String, - * the derived class uses this macro to disambiguate the inherited members. - */ -# define BLACKMISC_DECLARE_USING_MIXIN_STRING(DERIVED) \ - using ::BlackMisc::Mixin::String::toQString; \ - using ::BlackMisc::Mixin::String::toFormattedQString; \ - using ::BlackMisc::Mixin::String::toStdString; \ - using ::BlackMisc::Mixin::String::stringForStreaming; - - /*! - * CRTP class template from which a derived class can inherit property indexing functions. - */ - template - class Index - { - public: - //! Base class enums - enum ColumnIndex - { - IndexPixmap = 10, // manually set to avoid circular dependencies - IndexIcon, - IndexString - }; - - //! Update by variant map - //! \return number of values changed, with skipEqualValues equal values will not be changed - CPropertyIndexList apply(const BlackMisc::CPropertyIndexVariantMap &indexMap, bool skipEqualValues = false); // implemented later due to cyclic include dependency - - //! Set property by index - void setPropertyByIndex(const CVariant &variant, const CPropertyIndex &index); // implemented later due to cyclic include dependency - - //! Property by index - CVariant propertyByIndex(const BlackMisc::CPropertyIndex &index) const; // implemented later due to cyclic include dependency - - //! Property by index as String - QString propertyByIndexAsString(const CPropertyIndex &index, bool i18n = false) const; // implemented later due to cyclic include dependency - - //! Is given variant equal to value of property index? - bool equalsPropertyByIndex(const CVariant &compareValue, const CPropertyIndex &index) const; // implemented later due to cyclic include dependency - - private: - const Derived *derived() const { return static_cast(this); } - Derived *derived() { return static_cast(this); } - }; - - /*! - * CRTP class template from which a derived class can inherit icon-related functions. - */ - template - class Icon - { - public: - //! As icon, not implemented by all classes - CIcon toIcon() const; // implemented later due to cyclic include dependency - - //! As pixmap, required for most GUI views - QPixmap toPixmap() const; // implemented later due to cyclic include dependency - - private: - const Derived *derived() const { return static_cast(this); } - Derived *derived() { return static_cast(this); } - }; - - /*! - * When a derived class and a base class both inherit from Mixin::Icon, - * the derived class uses this macro to disambiguate the inherited members. - */ -# define BLACKMISC_DECLARE_USING_MIXIN_ICON(DERIVED) \ - using ::BlackMisc::Mixin::Icon::toIcon; \ - using ::BlackMisc::Mixin::Icon::toPixmap; - - } - /*! * Standard implementation of CValueObject using meta tuple system. * @@ -702,108 +174,4 @@ namespace BlackMisc } // namespace -// TODO Includes due to cyclic dependencies can be removed when CValueObject is split into parts along policy boundaries. -#include "variant.h" -#include "propertyindex.h" -#include "propertyindexlist.h" -#include "iconlist.h" - -// TODO Implementations of templates that must appear after those includes, should be moved at the same time that policies are refactored. -namespace BlackMisc -{ - namespace Mixin - { - template - CVariant MetaType::toCVariant() const - { - return CVariant(derived()->toQVariant()); - } - template - void MetaType::convertFromCVariant(const CVariant &variant) - { - derived()->convertFromQVariant(variant.getQVariant()); - } - template - CPropertyIndexList Index::apply(const BlackMisc::CPropertyIndexVariantMap &indexMap, bool skipEqualValues) - { - if (indexMap.isEmpty()) return {}; - - CPropertyIndexList changed; - const auto &map = indexMap.map(); - for (auto it = map.begin(); it != map.end(); ++it) - { - const CVariant value = it.value().toCVariant(); - const CPropertyIndex index = it.key(); - if (skipEqualValues) - { - bool equal = derived()->equalsPropertyByIndex(value, index); - if (equal) { continue; } - } - derived()->setPropertyByIndex(value, index); - changed.push_back(index); - } - return changed; - } - template - void Index::setPropertyByIndex(const CVariant &variant, const CPropertyIndex &index) - { - if (index.isMyself()) - { - derived()->convertFromCVariant(variant); - return; - } - - // not all classes have implemented nesting - const QString m = QString("Property by index not found (setter), index: ").append(index.toQString()); - qFatal("%s", qPrintable(m)); - } - template - CVariant Index::propertyByIndex(const CPropertyIndex &index) const - { - if (index.isMyself()) - { - return derived()->toCVariant(); - } - auto i = index.frontCasted(); - switch (i) - { - case IndexIcon: - return CVariant::from(derived()->toIcon()); - case IndexPixmap: - return CVariant::from(derived()->toPixmap()); - case IndexString: - return CVariant(derived()->toQString()); - default: - break; - } - - // not all classes have implemented nesting - const QString m = QString("Property by index not found, index: ").append(index.toQString()); - qFatal("%s", qPrintable(m)); - return {}; - } - template - QString Index::propertyByIndexAsString(const CPropertyIndex &index, bool i18n) const - { - // default implementation, requires propertyByIndex - return derived()->propertyByIndex(index).toQString(i18n); - } - template - bool Index::equalsPropertyByIndex(const CVariant &compareValue, const CPropertyIndex &index) const - { - return derived()->propertyByIndex(index) == compareValue; - } - template - CIcon Icon::toIcon() const - { - return CIconList::iconByIndex(IconIndex); - } - template - QPixmap Icon::toPixmap() const - { - return derived()->toIcon().toPixmap(); - } - } -} - #endif // guard diff --git a/src/blackmisc/variant.h b/src/blackmisc/variant.h index 3e4d106f3..5befc4d80 100644 --- a/src/blackmisc/variant.h +++ b/src/blackmisc/variant.h @@ -9,12 +9,16 @@ //! \file -#include "valueobject.h" // outside include guard due to cyclic dependency hack (MS) - #ifndef BLACKMISC_VARIANT_H #define BLACKMISC_VARIANT_H +#include "variant_private.h" #include "blackmiscexport.h" +#include "blackmiscfreefunctions.h" +#include "tuple.h" +#include "compare.h" +#include "dbus.h" +#include "json.h" #include #include #include @@ -22,12 +26,126 @@ #include #include -class QDBusArgument; - namespace BlackMisc { + class CPropertyIndex; class CIcon; + namespace Mixin + { + + /*! + * CRTP class template from which a derived class can inherit common methods dealing with the metatype of the class. + */ + template + class MetaType + { + public: + //! Register metadata + static void registerMetadata() + { + Private::MetaTypeHelper::maybeRegisterMetaType(); + + [](...){}((qRegisterMetaType(), qDBusRegisterMetaType(), 0)...); + } + + //! Returns the Qt meta type ID of this object. + int getMetaTypeId() const + { + return Private::MetaTypeHelper::maybeGetMetaTypeId(); + } + + //! Returns true if this object is an instance of the class with the given meta type ID, or one of its subclasses. + bool isA(int metaTypeId) const + { + if (metaTypeId == QMetaType::UnknownType) { return false; } + if (metaTypeId == getMetaTypeId()) { return true; } + return baseIsA(static_cast *>(derived()), metaTypeId); + } + + //! Method to return CVariant + //! \deprecated Use CVariant::to() instead. + CVariant toCVariant() const; + + //! Set from CVariant + //! \deprecated Use CVariant::from() instead. + void convertFromCVariant(const CVariant &variant); + + //! Return QVariant, used with DBus QVariant lists + //! \deprecated Use QVariant::fromValue() instead. + QVariant toQVariant() const + { + return Private::MetaTypeHelper::maybeToQVariant(*derived()); + } + + //! Set from QVariant + //! \deprecated Use QVariant::value() instead. + void convertFromQVariant(const QVariant &variant) + { + return Private::MetaTypeHelper::maybeConvertFromQVariant(*derived(), variant); + } + + private: + const Derived *derived() const { return static_cast(this); } + Derived *derived() { return static_cast(this); } + + template static bool baseIsA(const Base2 *base, int metaTypeId) { return base->isA(metaTypeId); } + static bool baseIsA(const void *, int) { return false; } + }; + + /*! + * Variant of MetaType mixin which also registers QList with the type system. + */ + template + class MetaTypeAndQList : public MetaType> + {}; + + /*! + * When a derived class and a base class both inherit from Mixin::MetaType, + * the derived class uses this macro to disambiguate the inherited members. + */ +# define BLACKMISC_DECLARE_USING_MIXIN_METATYPE(DERIVED) \ + using ::BlackMisc::Mixin::MetaType::registerMetadata; \ + using ::BlackMisc::Mixin::MetaType::getMetaTypeId; \ + using ::BlackMisc::Mixin::MetaType::isA; \ + using ::BlackMisc::Mixin::MetaType::toCVariant; \ + using ::BlackMisc::Mixin::MetaType::toQVariant; \ + using ::BlackMisc::Mixin::MetaType::convertFromCVariant; \ + using ::BlackMisc::Mixin::MetaType::convertFromQVariant; + + /*! + * When a derived class and a base class both inherit from Mixin::MetaType, + * the derived class uses this macro to disambiguate the inherited members. + */ +# define BLACKMISC_DECLARE_USING_MIXIN_METATYPE_AND_QLIST(DERIVED) \ + using ::BlackMisc::Mixin::MetaTypeAndQList::registerMetadata; \ + using ::BlackMisc::Mixin::MetaTypeAndQList::getMetaTypeId; \ + using ::BlackMisc::Mixin::MetaTypeAndQList::isA; \ + using ::BlackMisc::Mixin::MetaTypeAndQList::toCVariant; \ + using ::BlackMisc::Mixin::MetaTypeAndQList::toQVariant; \ + using ::BlackMisc::Mixin::MetaTypeAndQList::convertFromCVariant; \ + using ::BlackMisc::Mixin::MetaTypeAndQList::convertFromQVariant; + + } // Mixin + + /*! + * This registers the value type T with the BlackMisc meta type system, + * making it available for use with the extended feature set of BlackMisc::CVariant. + * + * The implementation (ab)uses the QMetaType converter function registration mechanism + * to store a type-erased representation of the set of operations supported by T. + * Unlike the singleton pattern, this approach means that CVariant can be used in plugins. + */ + template + void registerMetaValueType() + { + if (QMetaType::hasRegisteredConverterFunction()) { return; } + auto converter = [](const T &) { static Private::CValueObjectMetaInfo info; return &info; }; + bool ok = QMetaType::registerConverter(converter); + Q_ASSERT(ok); + Q_UNUSED(ok); + } + /*! * Wrapper around QVariant which provides transparent access to CValueObject methods * of the contained object if it is registered with BlackMisc::registerMetaValueType. @@ -38,9 +156,7 @@ namespace BlackMisc public Mixin::LessThanByCompare, public Mixin::DBusOperators, public Mixin::JsonOperators, - public Mixin::Index, - public Mixin::String, - public Mixin::Icon + public Mixin::String { public: //! Default constructor. @@ -252,6 +368,21 @@ namespace BlackMisc //! \private Needed so we can copy forward-declared CVariant. inline void assign(CVariant &a, const CVariant &b) { a = b; } } + + namespace Mixin + { + template + CVariant MetaType::toCVariant() const + { + return CVariant(derived()->toQVariant()); + } + template + void MetaType::convertFromCVariant(const CVariant &variant) + { + derived()->convertFromQVariant(variant.getQVariant()); + } + } + } // namespace Q_DECLARE_METATYPE(BlackMisc::CVariant) diff --git a/src/blackmisc/valueobject_private.h b/src/blackmisc/variant_private.h similarity index 99% rename from src/blackmisc/valueobject_private.h rename to src/blackmisc/variant_private.h index e149c820d..0b5db67c6 100644 --- a/src/blackmisc/valueobject_private.h +++ b/src/blackmisc/variant_private.h @@ -9,10 +9,11 @@ //! \file -#ifndef BLACKMISC_VALUEOBJECT_PRIVATE_H -#define BLACKMISC_VALUEOBJECT_PRIVATE_H +#ifndef BLACKMISC_VARIANT_PRIVATE_H +#define BLACKMISC_VARIANT_PRIVATE_H #include "blackmisc/blackmiscexport.h" +#include "blackmisc/blackmiscfreefunctions.h" #include "blackmisc/inheritance_traits.h" #include #include