diff --git a/src/blackmisc/inheritancetraits.h b/src/blackmisc/inheritancetraits.h index d1a6d812a..c45bd5db1 100644 --- a/src/blackmisc/inheritancetraits.h +++ b/src/blackmisc/inheritancetraits.h @@ -36,6 +36,12 @@ namespace BlackMisc }; //! \endcond + /*! + * True if T has a member typedef base_type which is a registered metatype. + */ + template + inline constexpr bool THasMetaBaseV = QMetaTypeId::type>::Defined; + /*! * It T has a member typedef base_type which is a registered metatype, this trait will obtain it, otherwise void. */ @@ -43,7 +49,7 @@ namespace BlackMisc struct TMetaBaseOf { //! Type of T::base_type, or void if not declared. - using type = std::conditional_t::type>::Defined, typename TBaseOf::type, void>; + using type = std::conditional_t, typename TBaseOf::type, void>; }; /*! diff --git a/src/blackmisc/mixin/mixincompare.h b/src/blackmisc/mixin/mixincompare.h index 8699b74b7..b3fc32cfb 100644 --- a/src/blackmisc/mixin/mixincompare.h +++ b/src/blackmisc/mixin/mixincompare.h @@ -56,30 +56,46 @@ namespace BlackMisc friend bool operator !=(const Derived &a, const Derived &b) { return ! equals(a, b); } private: - static bool equals(const Derived &a, const Derived &b) - { - bool result = baseEquals(static_cast *>(&a), static_cast *>(&b)); - introspect().forEachMember([ & ](auto member) - { - if constexpr (!decltype(member)::has(MetaFlags())) - { - result = result && EqualsByMetaClass::membersEqual(member.in(a), member.in(b), member.m_flags); - } - }); - 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; } - + static bool equals(const Derived &a, const Derived &b); + template static bool baseEquals(const T *a, const T *b); + static bool baseEquals(const void *, const void *); + static bool baseEquals(const CEmpty *, const CEmpty *); template - static bool membersEqual(const T &a, const T &b, Flags) - { - if constexpr (static_cast(Flags::value & CaseInsensitiveComparison)) { return a.compare(b, Qt::CaseInsensitive) == 0; } - else { return a == b; } - } + static bool membersEqual(const T &a, const T &b, Flags); }; + template + bool EqualsByMetaClass::equals(const Derived &a, const Derived &b) + { + bool result = baseEquals(static_cast *>(&a), static_cast *>(&b)); + introspect().forEachMember([ & ](auto member) + { + if constexpr (!decltype(member)::has(MetaFlags())) + { + result = result && EqualsByMetaClass::membersEqual(member.in(a), member.in(b), member.m_flags); + } + }); + return result; + } + + template + template + bool EqualsByMetaClass::baseEquals(const T *a, const T *b) { return *a == *b; } + + template + bool EqualsByMetaClass::baseEquals(const void *, const void *) { return true; } + + template + bool EqualsByMetaClass::baseEquals(const CEmpty *, const CEmpty *) { return true; } + + template + template + bool EqualsByMetaClass::membersEqual(const T &a, const T &b, Flags) + { + if constexpr (static_cast(Flags::value & CaseInsensitiveComparison)) { return a.compare(b, Qt::CaseInsensitive) == 0; } + else { return a == b; } + } + /*! * CRTP class template from which a derived class can inherit operator< implemented using its compare function. * @@ -122,36 +138,52 @@ namespace BlackMisc friend bool operator >=(const Derived &a, const Derived &b) { return ! less(a, b); } private: - static bool less(const Derived &a, const Derived &b) - { - bool result = baseLess(static_cast *>(&a), static_cast *>(&b)); - bool gt = baseLess(static_cast *>(&b), static_cast *>(&a)); - introspect().forEachMember([ & ](auto member) - { - if constexpr (!decltype(member)::has(MetaFlags())) - { - result = result || LessThanByMetaClass::membersLess(gt, member.in(a), member.in(b), member.m_flags); - } - }); - 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; } - + static bool less(const Derived &a, const Derived &b); + template static bool baseLess(const T *a, const T *b); + static bool baseLess(const void *, const void *); + static bool baseLess(const CEmpty *, const CEmpty *); template - static bool membersLess(bool &io_greaterThan, const T &a, const T &b, Flags) - { - if (io_greaterThan) { return false; } - if constexpr (static_cast(Flags::value & CaseInsensitiveComparison)) - { - io_greaterThan = b.compare(a, Qt::CaseInsensitive) < 0; - return a.compare(b, Qt::CaseInsensitive) < 0; - } - else { io_greaterThan = b < a; return a < b; } - } + static bool membersLess(bool &io_greaterThan, const T &a, const T &b, Flags); }; + template + bool LessThanByMetaClass::less(const Derived &a, const Derived &b) + { + bool result = baseLess(static_cast *>(&a), static_cast *>(&b)); + bool gt = baseLess(static_cast *>(&b), static_cast *>(&a)); + introspect().forEachMember([ & ](auto member) + { + if constexpr (!decltype(member)::has(MetaFlags())) + { + result = result || LessThanByMetaClass::membersLess(gt, member.in(a), member.in(b), member.m_flags); + } + }); + return result; + } + + template + template + bool LessThanByMetaClass::baseLess(const T *a, const T *b) { return *a < *b; } + + template + bool LessThanByMetaClass::baseLess(const void *, const void *) { return false; } + + template + bool LessThanByMetaClass::baseLess(const CEmpty *, const CEmpty *) { return false; } + + template + template + bool LessThanByMetaClass::membersLess(bool &io_greaterThan, const T &a, const T &b, Flags) + { + if (io_greaterThan) { return false; } + if constexpr (static_cast(Flags::value & CaseInsensitiveComparison)) + { + io_greaterThan = b.compare(a, Qt::CaseInsensitive) < 0; + return a.compare(b, Qt::CaseInsensitive) < 0; + } + else { io_greaterThan = b < a; return a < b; } + } + /*! * CRTP class template from which a derived class can inherit non-member compare() implemented by metaclass. */ @@ -163,31 +195,47 @@ namespace BlackMisc friend int compare(const Derived &a, const Derived &b) { return compareImpl(a, b); } private: - static int compareImpl(const Derived &a, const Derived &b) - { - int result = baseCompare(static_cast *>(&a), static_cast *>(&b)); - introspect().forEachMember([ & ](auto member) - { - if constexpr (!decltype(member)::has(MetaFlags())) - { - result = result ? result : CompareByMetaClass::membersCompare(member.in(a), member.in(b), member.m_flags); - } - }); - 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; } - + static int compareImpl(const Derived &a, const Derived &b); + template static int baseCompare(const T *a, const T *b); + static int baseCompare(const void *, const void *); + static int baseCompare(const CEmpty *, const CEmpty *); template - static int membersCompare(const T &a, const T &b, Flags) - { - if constexpr (static_cast(Flags::value & CaseInsensitiveComparison)) { return a.compare(b, Qt::CaseInsensitive); } - else if constexpr (THasCompare::value) { return compare(a, b); } - else { return a < b ? -1 : b < a ? 1 : 0; } - } + static int membersCompare(const T &a, const T &b, Flags); }; + template + int CompareByMetaClass::compareImpl(const Derived &a, const Derived &b) + { + int result = baseCompare(static_cast *>(&a), static_cast *>(&b)); + introspect().forEachMember([ & ](auto member) + { + if constexpr (!decltype(member)::has(MetaFlags())) + { + result = result ? result : CompareByMetaClass::membersCompare(member.in(a), member.in(b), member.m_flags); + } + }); + return result; + } + + template + template + int CompareByMetaClass::baseCompare(const T *a, const T *b) { return compare(*a, *b); } + + template + int CompareByMetaClass::baseCompare(const void *, const void *) { return 0; } + + template + int CompareByMetaClass::baseCompare(const CEmpty *, const CEmpty *) { return 0; } + + template + template + int CompareByMetaClass::membersCompare(const T &a, const T &b, Flags) + { + if constexpr (static_cast(Flags::value & CaseInsensitiveComparison)) { return a.compare(b, Qt::CaseInsensitive); } + else if constexpr (THasCompare::value) { return compare(a, b); } + else { return a < b ? -1 : b < a ? 1 : 0; } + } + } // Mixin } // BlackMisc diff --git a/src/blackmisc/mixin/mixindatastream.h b/src/blackmisc/mixin/mixindatastream.h index 3f2a871ea..defaf538c 100644 --- a/src/blackmisc/mixin/mixindatastream.h +++ b/src/blackmisc/mixin/mixindatastream.h @@ -55,43 +55,75 @@ namespace BlackMisc { public: //! Marshal a value to a QDataStream. - void marshalToDataStream(QDataStream &stream) const - { - baseMarshal(static_cast *>(derived()), stream); - introspect().forEachMember([ &, this ](auto member) - { - if constexpr (!decltype(member)::has(MetaFlags())) - { - stream << member.in(*this->derived()); - } - }); - } + void marshalToDataStream(QDataStream &stream) const; //! Unmarshal a value from a QDataStream. - void unmarshalFromDataStream(QDataStream &stream) - { - baseUnmarshal(static_cast *>(derived()), stream); - introspect().forEachMember([ &, this ](auto member) - { - if constexpr (!decltype(member)::has(MetaFlags())) - { - stream >> member.in(*this->derived()); - } - }); - } + void unmarshalFromDataStream(QDataStream &stream); private: - const Derived *derived() const { return static_cast(this); } - Derived *derived() { return static_cast(this); } + const Derived *derived() const; + Derived *derived(); - template static void baseMarshal(const T *base, QDataStream &stream) { base->marshalToDataStream(stream); } - template static void baseUnmarshal(T *base, QDataStream &stream) { base->unmarshalFromDataStream(stream); } - static void baseMarshal(const void *, QDataStream &) {} - static void baseUnmarshal(void *, QDataStream &) {} - static void baseMarshal(const CEmpty *, QDataStream &) {} - static void baseUnmarshal(CEmpty *, QDataStream &) {} + template static void baseMarshal(const T *base, QDataStream &stream); + template static void baseUnmarshal(T *base, QDataStream &stream); + static void baseMarshal(const void *, QDataStream &); + static void baseUnmarshal(void *, QDataStream &); + static void baseMarshal(const CEmpty *, QDataStream &); + static void baseUnmarshal(CEmpty *, QDataStream &); }; + template + void DataStreamByMetaClass::marshalToDataStream(QDataStream &stream) const + { + baseMarshal(static_cast *>(derived()), stream); + introspect().forEachMember([ &, this ](auto member) + { + if constexpr (!decltype(member)::has(MetaFlags())) + { + stream << member.in(*this->derived()); + } + }); + } + + template + void DataStreamByMetaClass::unmarshalFromDataStream(QDataStream &stream) + { + baseUnmarshal(static_cast *>(derived()), stream); + introspect().forEachMember([ &, this ](auto member) + { + if constexpr (!decltype(member)::has(MetaFlags())) + { + stream >> member.in(*this->derived()); + } + }); + } + + template + const Derived *DataStreamByMetaClass::derived() const { return static_cast(this); } + + template + Derived *DataStreamByMetaClass::derived() { return static_cast(this); } + + template + template + void DataStreamByMetaClass::baseMarshal(const T *base, QDataStream &stream) { base->marshalToDataStream(stream); } + + template + template + void DataStreamByMetaClass::baseUnmarshal(T *base, QDataStream &stream) { base->unmarshalFromDataStream(stream); } + + template + void DataStreamByMetaClass::baseMarshal(const void *, QDataStream &) {} + + template + void DataStreamByMetaClass::baseUnmarshal(void *, QDataStream &) {} + + template + void DataStreamByMetaClass::baseMarshal(const CEmpty *, QDataStream &) {} + + template + void DataStreamByMetaClass::baseUnmarshal(CEmpty *, QDataStream &) {} + /*! * When a derived class and a base class both inherit from Mixin::DataStreamByMetaClass, * the derived class uses this macro to disambiguate the inherited members. diff --git a/src/blackmisc/mixin/mixindbus.h b/src/blackmisc/mixin/mixindbus.h index 98a128899..21eecfb82 100644 --- a/src/blackmisc/mixin/mixindbus.h +++ b/src/blackmisc/mixin/mixindbus.h @@ -68,61 +68,93 @@ namespace BlackMisc { public: //! Marshall without begin/endStructure, for when composed within another object - void marshallToDbus(QDBusArgument &arg, Tags...) const - { - baseMarshall(static_cast *>(derived()), arg); - introspect().forEachMember([ &, this ](auto member) - { - if constexpr (!decltype(member)::has(MetaFlags())) - { - const auto &value = member.in(*this->derived()); - if constexpr (THasMarshallMethods>::value) - { - if constexpr (member.has(MetaFlags())) - { - value.marshallToDbus(arg, LosslessTag()); - } - else { value.marshallToDbus(arg); } - } - else { arg << value; } - } - }); - } + void marshallToDbus(QDBusArgument &arg, Tags...) const; //! Unmarshall without begin/endStructure, for when composed within another object - void unmarshallFromDbus(const QDBusArgument &arg, Tags...) - { - baseUnmarshall(static_cast *>(derived()), arg); - introspect().forEachMember([ &, this ](auto member) - { - if constexpr (!decltype(member)::has(MetaFlags())) - { - auto &value = member.in(*this->derived()); - if constexpr (THasMarshallMethods>::value) - { - if constexpr (member.has(MetaFlags())) - { - value.unmarshallFromDbus(arg, LosslessTag()); - } - else { value.unmarshallFromDbus(arg); } - } - else { arg >> value; } - } - }); - } + void unmarshallFromDbus(const QDBusArgument &arg, Tags...); private: - const Derived *derived() const { return static_cast(this); } - Derived *derived() { return static_cast(this); } + const Derived *derived() const; + Derived *derived(); - template static void baseMarshall(const T *base, QDBusArgument &arg) { base->marshallToDbus(arg, Tags()...); } - template static void baseUnmarshall(T *base, const QDBusArgument &arg) { base->unmarshallFromDbus(arg, Tags()...); } - static void baseMarshall(const void *, QDBusArgument &) {} - static void baseUnmarshall(void *, const QDBusArgument &) {} - static void baseMarshall(const CEmpty *, QDBusArgument &) {} - static void baseUnmarshall(CEmpty *, const QDBusArgument &) {} + template static void baseMarshall(const T *base, QDBusArgument &arg); + template static void baseUnmarshall(T *base, const QDBusArgument &arg); + static void baseMarshall(const void *, QDBusArgument &); + static void baseUnmarshall(void *, const QDBusArgument &); + static void baseMarshall(const CEmpty *, QDBusArgument &); + static void baseUnmarshall(CEmpty *, const QDBusArgument &); }; + template + void DBusByMetaClass::marshallToDbus(QDBusArgument &arg, Tags...) const + { + baseMarshall(static_cast *>(derived()), arg); + introspect().forEachMember([ &, this ](auto member) + { + if constexpr (!decltype(member)::has(MetaFlags())) + { + const auto &value = member.in(*this->derived()); + if constexpr (THasMarshallMethods>::value) + { + if constexpr (member.has(MetaFlags())) + { + value.marshallToDbus(arg, LosslessTag()); + } + else { value.marshallToDbus(arg); } + } + else { arg << value; } + } + }); + } + + template + void DBusByMetaClass::unmarshallFromDbus(const QDBusArgument &arg, Tags...) + { + baseUnmarshall(static_cast *>(derived()), arg); + introspect().forEachMember([ &, this ](auto member) + { + if constexpr (!decltype(member)::has(MetaFlags())) + { + auto &value = member.in(*this->derived()); + if constexpr (THasMarshallMethods>::value) + { + if constexpr (member.has(MetaFlags())) + { + value.unmarshallFromDbus(arg, LosslessTag()); + } + else { value.unmarshallFromDbus(arg); } + } + else { arg >> value; } + } + }); + } + + template + const Derived *DBusByMetaClass::derived() const { return static_cast(this); } + + template + Derived *DBusByMetaClass::derived() { return static_cast(this); } + + template + template + void DBusByMetaClass::baseMarshall(const T *base, QDBusArgument &arg) { base->marshallToDbus(arg, Tags()...); } + + template + template + void DBusByMetaClass::baseUnmarshall(T *base, const QDBusArgument &arg) { base->unmarshallFromDbus(arg, Tags()...); } + + template + void DBusByMetaClass::baseMarshall(const void *, QDBusArgument &) {} + + template + void DBusByMetaClass::baseUnmarshall(void *, const QDBusArgument &) {} + + template + void DBusByMetaClass::baseMarshall(const CEmpty *, QDBusArgument &) {} + + template + void DBusByMetaClass::baseUnmarshall(CEmpty *, const QDBusArgument &) {} + // *INDENT-OFF* /*! * When a derived class and a base class both inherit from Mixin::DBusByTuple, diff --git a/src/blackmisc/mixin/mixinhash.h b/src/blackmisc/mixin/mixinhash.h index 4f165f67b..e61273c3c 100644 --- a/src/blackmisc/mixin/mixinhash.h +++ b/src/blackmisc/mixin/mixinhash.h @@ -57,23 +57,32 @@ namespace BlackMisc } private: - static uint hashImpl(const Derived &value) - { - uint hash = baseHash(static_cast *>(&value)); - introspect().forEachMember([ & ](auto member) - { - if constexpr (!decltype(member)::has(MetaFlags())) - { - hash ^= qHash(member.in(value)); - } - }); - return hash; - } + static uint hashImpl(const Derived &value); template static uint baseHash(const T *base) { return qHash(*base); } - static uint baseHash(const void *) { return 0; } - static uint baseHash(const CEmpty *) { return 0; } + static uint baseHash(const void *); + static uint baseHash(const CEmpty *); }; + + template + uint HashByMetaClass::hashImpl(const Derived &value) + { + uint hash = baseHash(static_cast *>(&value)); + introspect().forEachMember([ & ](auto member) + { + if constexpr (!decltype(member)::has(MetaFlags())) + { + hash ^= qHash(member.in(value)); + } + }); + return hash; + } + + template + uint HashByMetaClass::baseHash(const void *) { return 0; } + + template + uint HashByMetaClass::baseHash(const CEmpty *) { return 0; } } } // namespace BlackMisc diff --git a/src/blackmisc/mixin/mixinicon.h b/src/blackmisc/mixin/mixinicon.h index 4cd090ab9..e14e9dd9b 100644 --- a/src/blackmisc/mixin/mixinicon.h +++ b/src/blackmisc/mixin/mixinicon.h @@ -31,13 +31,22 @@ namespace BlackMisc { public: //! As icon, not implemented by all classes - CIcons::IconIndex toIcon() const { return IconIndex; } + CIcons::IconIndex toIcon() const; private: - const Derived *derived() const { return static_cast(this); } - Derived *derived() { return static_cast(this); } + const Derived *derived() const; + Derived *derived(); }; + template + CIcons::IconIndex Icon::toIcon() const { return IconIndex; } + + template + const Derived *Icon::derived() const { return static_cast(this); } + + template + Derived *Icon::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. diff --git a/src/blackmisc/mixin/mixinindex.h b/src/blackmisc/mixin/mixinindex.h index 74aa5c3e8..a5712f512 100644 --- a/src/blackmisc/mixin/mixinindex.h +++ b/src/blackmisc/mixin/mixinindex.h @@ -67,40 +67,67 @@ namespace BlackMisc bool equalsPropertyByIndex(const QVariant &compareValue, CPropertyIndexRef index) const; private: - const Derived *derived() const { return static_cast(this); } - Derived *derived() { return static_cast(this); } + const Derived *derived() const; + Derived *derived(); template - QVariant myself() const - { - if constexpr (std::is_default_constructible_v) { return QVariant::fromValue(*derived()); } - else { qFatal("isMyself should have been handled before reaching here"); return {}; } - } + QVariant myself() const; template - void myself(const QVariant &variant) - { - if constexpr (std::is_default_constructible_v) { *derived() = variant.value(); } - else { qFatal("isMyself should have been handled before reaching here"); } - } + void myself(const QVariant &variant); template - QVariant basePropertyByIndex(const T *base, CPropertyIndexRef index) const { return base->propertyByIndex(index); } + QVariant basePropertyByIndex(const T *base, CPropertyIndexRef index) const; template - void baseSetPropertyByIndex(T *base, const QVariant &var, CPropertyIndexRef index) { base->setPropertyByIndex(index, var); } + void baseSetPropertyByIndex(T *base, const QVariant &var, CPropertyIndexRef index); - QVariant basePropertyByIndex(const void *, CPropertyIndexRef) const - { - //qFatal("%s", qPrintable("Property by index not found, index: " + index.toQString())); return {}; - qFatal("Property by index not found"); return {}; - } - - void baseSetPropertyByIndex(void *, const QVariant &, CPropertyIndexRef) - { - //qFatal("%s", qPrintable("Property by index not found (setter), index: " + index.toQString())); - qFatal("Property by index not found"); - } + QVariant basePropertyByIndex(const void *, CPropertyIndexRef) const; + void baseSetPropertyByIndex(void *, const QVariant &, CPropertyIndexRef); }; + template + const Derived *Index::derived() const { return static_cast(this); } + + template + Derived *Index::derived() { return static_cast(this); } + + template + template + QVariant Index::myself() const + { + if constexpr (std::is_default_constructible_v) { return QVariant::fromValue(*derived()); } + else { qFatal("isMyself should have been handled before reaching here"); return {}; } + } + + template + template + void Index::myself(const QVariant &variant) + { + if constexpr (std::is_default_constructible_v) { *derived() = variant.value(); } + else { qFatal("isMyself should have been handled before reaching here"); } + } + + template + template + QVariant Index::basePropertyByIndex(const T *base, CPropertyIndexRef index) const { return base->propertyByIndex(index); } + + template + template + void Index::baseSetPropertyByIndex(T *base, const QVariant &var, CPropertyIndexRef index) { base->setPropertyByIndex(index, var); } + + template + QVariant Index::basePropertyByIndex(const void *, CPropertyIndexRef) const + { + //qFatal("%s", qPrintable("Property by index not found, index: " + index.toQString())); return {}; + qFatal("Property by index not found"); return {}; + } + + template + void Index::baseSetPropertyByIndex(void *, const QVariant &, CPropertyIndexRef) + { + //qFatal("%s", qPrintable("Property by index not found (setter), index: " + index.toQString())); + qFatal("Property by index not found"); + } + /*! * When a derived class and a base class both inherit from Mixin::Index, * the derived class uses this macro to disambiguate the inherited members. diff --git a/src/blackmisc/mixin/mixinjson.h b/src/blackmisc/mixin/mixinjson.h index 47bceee83..3cf5a51d0 100644 --- a/src/blackmisc/mixin/mixinjson.h +++ b/src/blackmisc/mixin/mixinjson.h @@ -93,111 +93,161 @@ namespace BlackMisc { public: //! Cast to JSON object - QJsonObject toJson() const - { - QJsonObject json; - introspect().forEachMember([ &, this ](auto member) - { - if constexpr (!decltype(member)::has(MetaFlags())) - { - json << std::make_pair(CExplicitLatin1String(member.latin1Name()), std::cref(member.in(*this->derived()))); - } - }); - return Json::appendJsonObject(json, baseToJson(static_cast *>(derived()))); - } + QJsonObject toJson() const; //! Convenience function JSON as string - QString toJsonString(QJsonDocument::JsonFormat format = QJsonDocument::Indented) const - { - QJsonDocument jsonDoc(toJson()); - return jsonDoc.toJson(format); - } + QString toJsonString(QJsonDocument::JsonFormat format = QJsonDocument::Indented) const; //! Assign from JSON object - void convertFromJson(const QJsonObject &json) - { - baseConvertFromJson(static_cast *>(derived()), json); - introspect().forEachMember([ &, this ](auto member) - { - if constexpr (!decltype(member)::has(MetaFlags())) - { - const auto value = json.value(CExplicitLatin1String(member.latin1Name())); - if (value.isUndefined()) - { - constexpr bool required = false; //! \fixme add RequiredForJson flag in metaclass system - // cppcheck-suppress knownConditionTrueFalse - // QLatin1String used instead of QStringLiteral below since the latter causes an internal compiler bug - // in GCC 8 and higher - if (required) { throw CJsonException(QLatin1String("Missing required member '%1'").arg(member.latin1Name())); } - } - else - { - CJsonScope scope(member.latin1Name()); - Q_UNUSED(scope); - value >> member.in(*this->derived()); - } - } - }); - } + void convertFromJson(const QJsonObject &json); //! Assign from JSON object string - void convertFromJson(const QString &jsonString, bool acceptCacheFormat = false) - { - const QJsonObject jsonObject = BlackMisc::Json::jsonObjectFromString(jsonString, acceptCacheFormat); - convertFromJson(jsonObject); - } + void convertFromJson(const QString &jsonString, bool acceptCacheFormat = false); //! Get object from QJsonObject template - static DerivedObj fromJson(const QJsonObject &json) - { - DerivedObj obj; - obj.convertFromJson(json); - return obj; - } + static DerivedObj fromJson(const QJsonObject &json); //! Get object from JSON string template - static DerivedObj fromJson(const QString &jsonString, bool acceptCacheJson = false) + static DerivedObj fromJson(const QString &jsonString, bool acceptCacheJson = false); + + //! Get object from JSON string + template + static Derived fromJsonNoThrow(const QString &jsonString, bool acceptCacheJson, bool &success, QString &errMsg); + + private: + const Derived *derived() const; + Derived *derived(); + + template static QJsonObject baseToJson(const T *base); + template static void baseConvertFromJson(T *base, const QJsonObject &json); + static QJsonObject baseToJson(const void *); + static void baseConvertFromJson(void *, const QJsonObject &); + static QJsonObject baseToJson(const CEmpty *); + static void baseConvertFromJson(CEmpty *, const QJsonObject &); + }; + + template + QJsonObject JsonByMetaClass::toJson() const + { + QJsonObject json; + introspect().forEachMember([ &, this ](auto member) + { + if constexpr (!decltype(member)::has(MetaFlags())) + { + json << std::make_pair(CExplicitLatin1String(member.latin1Name()), std::cref(member.in(*this->derived()))); + } + }); + return Json::appendJsonObject(json, baseToJson(static_cast *>(derived()))); + } + + template + QString JsonByMetaClass::toJsonString(QJsonDocument::JsonFormat format) const + { + QJsonDocument jsonDoc(toJson()); + return jsonDoc.toJson(format); + } + + template + void JsonByMetaClass::convertFromJson(const QJsonObject &json) + { + baseConvertFromJson(static_cast *>(derived()), json); + introspect().forEachMember([ &, this ](auto member) + { + if constexpr (!decltype(member)::has(MetaFlags())) + { + const auto value = json.value(CExplicitLatin1String(member.latin1Name())); + if (value.isUndefined()) + { + constexpr bool required = false; //! \fixme add RequiredForJson flag in metaclass system + // cppcheck-suppress knownConditionTrueFalse + // QLatin1String used instead of QStringLiteral below since the latter causes an internal compiler bug + // in GCC 8 and higher + if (required) { throw CJsonException(QLatin1String("Missing required member '%1'").arg(member.latin1Name())); } + } + else + { + CJsonScope scope(member.latin1Name()); + Q_UNUSED(scope); + value >> member.in(*this->derived()); + } + } + }); + } + + template + void JsonByMetaClass::convertFromJson(const QString &jsonString, bool acceptCacheFormat) + { + const QJsonObject jsonObject = BlackMisc::Json::jsonObjectFromString(jsonString, acceptCacheFormat); + convertFromJson(jsonObject); + } + + template + template + DerivedObj JsonByMetaClass::fromJson(const QJsonObject &json) + { + DerivedObj obj; + obj.convertFromJson(json); + return obj; + } + + template + template + DerivedObj JsonByMetaClass::fromJson(const QString &jsonString, bool acceptCacheJson) + { + DerivedObj obj; + if (jsonString.isEmpty()) { return obj; } + const QJsonObject jsonObj = acceptCacheJson ? Json::swiftDataObjectValue(jsonString) : Json::jsonObjectFromString(jsonString); + obj.convertFromJson(jsonObj); + return obj; + } + + template + template + Derived JsonByMetaClass::fromJsonNoThrow(const QString &jsonString, bool acceptCacheJson, bool &success, QString &errMsg) + { + success = false; + Derived obj; + try { - DerivedObj obj; if (jsonString.isEmpty()) { return obj; } const QJsonObject jsonObj = acceptCacheJson ? Json::swiftDataObjectValue(jsonString) : Json::jsonObjectFromString(jsonString); obj.convertFromJson(jsonObj); - return obj; + success = true; } - - //! Get object from JSON string - template - static Derived fromJsonNoThrow(const QString &jsonString, bool acceptCacheJson, bool &success, QString &errMsg) + catch (const CJsonException &ex) { - success = false; - Derived obj; - try - { - if (jsonString.isEmpty()) { return obj; } - const QJsonObject jsonObj = acceptCacheJson ? Json::swiftDataObjectValue(jsonString) : Json::jsonObjectFromString(jsonString); - obj.convertFromJson(jsonObj); - success = true; - } - catch (const CJsonException &ex) - { - errMsg = ex.toString("JSON conversion"); - } - return obj; + errMsg = ex.toString("JSON conversion"); } + return obj; + } - private: - const Derived *derived() const { return static_cast(this); } - Derived *derived() { return static_cast(this); } + template + const Derived *JsonByMetaClass::derived() const { 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 &) {} - static QJsonObject baseToJson(const CEmpty *) { return {}; } - static void baseConvertFromJson(CEmpty *, const QJsonObject &) {} - }; + template + Derived *JsonByMetaClass::derived() { return static_cast(this); } + + template + template + QJsonObject JsonByMetaClass::baseToJson(const T *base) { return base->toJson(); } + + template + template + void JsonByMetaClass::baseConvertFromJson(T *base, const QJsonObject &json) { base->convertFromJson(json); } + + template + QJsonObject JsonByMetaClass::baseToJson(const void *) { return {}; } + + template + void JsonByMetaClass::baseConvertFromJson(void *, const QJsonObject &) {} + + template + QJsonObject JsonByMetaClass::baseToJson(const CEmpty *) { return {}; } + + template + void JsonByMetaClass::baseConvertFromJson(CEmpty *, const QJsonObject &) {} /*! * When a derived class and a base class both inherit from Mixin::JsonByTuple, diff --git a/src/blackmisc/mixin/mixinmetatype.h b/src/blackmisc/mixin/mixinmetatype.h index 6f9c80fec..149426ccc 100644 --- a/src/blackmisc/mixin/mixinmetatype.h +++ b/src/blackmisc/mixin/mixinmetatype.h @@ -32,40 +32,65 @@ namespace BlackMisc { public: //! Register metadata - static void registerMetadata() - { - Private::MetaTypeHelper::maybeRegisterMetaType(); - } + static void registerMetadata(); //! Returns the Qt meta type ID of this object //! \remark for CVariant this returns the id of CVariant, not of the encapsulated object. valueVariant.userType()` returns metatype of the contained object - int getMetaTypeId() const - { - return Private::MetaTypeHelper::maybeGetMetaTypeId(); - } + int getMetaTypeId() const; //! Class name - QString getClassName() const - { - return QMetaType::typeName(getMetaTypeId()); - } + QString getClassName() const; //! 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); - } + bool isA(int metaTypeId) const; private: - const Derived *derived() const { return static_cast(this); } - Derived *derived() { return static_cast(this); } + const Derived *derived() const; + Derived *derived(); - template static bool baseIsA(const Base2 *base, int metaTypeId) { return base->isA(metaTypeId); } - static bool baseIsA(const void *, int) { return false; } + template static bool baseIsA(const Base2 *base, int metaTypeId); + static bool baseIsA(const void *, int); }; + template + void MetaType::registerMetadata() + { + Private::MetaTypeHelper::maybeRegisterMetaType(); + } + + template + int MetaType::getMetaTypeId() const + { + return Private::MetaTypeHelper::maybeGetMetaTypeId(); + } + + template + QString MetaType::getClassName() const + { + return QMetaType::typeName(getMetaTypeId()); + } + + template + bool MetaType::isA(int metaTypeId) const + { + if (metaTypeId == QMetaType::UnknownType) { return false; } + if (metaTypeId == getMetaTypeId()) { return true; } + return baseIsA(static_cast *>(derived()), metaTypeId); + } + + template + const Derived *MetaType::derived() const { return static_cast(this); } + + template + Derived *MetaType::derived() { return static_cast(this); } + + template + template + bool MetaType::baseIsA(const Base2 *base, int metaTypeId) { return base->isA(metaTypeId); } + + template + bool MetaType::baseIsA(const void *, int) { return false; } + // *INDENT-OFF* /*! * When a derived class and a base class both inherit from Mixin::MetaType, diff --git a/src/blackmisc/mixin/mixinstring.h b/src/blackmisc/mixin/mixinstring.h index 6eff9ec6f..5a83a73ba 100644 --- a/src/blackmisc/mixin/mixinstring.h +++ b/src/blackmisc/mixin/mixinstring.h @@ -62,23 +62,41 @@ namespace BlackMisc::Mixin } //! Cast as QString - QString toQString(bool i18n = false) const { return derived()->convertToQString(i18n); } + QString toQString(bool i18n = false) const; //! Cast to pretty-printed QString //! \deprecated not really used and just using toQString - QString toFormattedQString(bool i18n = false) const { return derived()->toQString(i18n); } + QString toFormattedQString(bool i18n = false) const; //! To std string - std::string toStdString(bool i18n = false) const { return derived()->convertToQString(i18n).toStdString(); } + std::string toStdString(bool i18n = false) const; //! String for streaming operators - QString stringForStreaming() const { return derived()->convertToQString(); } + QString stringForStreaming() const; private: - const Derived *derived() const { return static_cast(this); } - Derived *derived() { return static_cast(this); } + const Derived *derived() const; + Derived *derived(); }; + template + QString String::toQString(bool i18n) const { return derived()->convertToQString(i18n); } + + template + QString String::toFormattedQString(bool i18n) const { return derived()->toQString(i18n); } + + template + std::string String::toStdString(bool i18n) const { return derived()->convertToQString(i18n).toStdString(); } + + template + QString String::stringForStreaming() const { return derived()->convertToQString(); } + + template + const Derived *String::derived() const { return static_cast(this); } + + template + Derived *String::derived() { return static_cast(this); } + // *INDENT-OFF* /*! * When a derived class and a base class both inherit from Mixin::String, diff --git a/src/blackmisc/variant.h b/src/blackmisc/variant.h index 43e789ed2..f23e88f88 100644 --- a/src/blackmisc/variant.h +++ b/src/blackmisc/variant.h @@ -50,8 +50,7 @@ namespace BlackMisc void registerMetaValueType() { if (QMetaType::hasRegisteredConverterFunction()) { return; } - auto converter = [](const T &) { static Private::CValueObjectMetaInfo info; return &info; }; - bool ok = QMetaType::registerConverter(converter); + bool ok = QMetaType::registerConverter(Private::CValueObjectMetaInfo::instance); Q_ASSERT(ok); Q_UNUSED(ok); } diff --git a/src/blackmisc/variantprivate.h b/src/blackmisc/variantprivate.h index 9d52fb80c..0a666182c 100644 --- a/src/blackmisc/variantprivate.h +++ b/src/blackmisc/variantprivate.h @@ -142,81 +142,115 @@ namespace BlackMisc template struct CValueObjectMetaInfo : public IValueObjectMetaInfo { - virtual QString toQString(const void *object, bool i18n) const override - { - return cast(object).toQString(i18n); - } - virtual QJsonObject toJson(const void *object) const override - { - return CValueObjectMetaInfoHelper::toJson(cast(object), 0); - } - virtual void convertFromJson(const QJsonObject &json, void *object) const override - { - CValueObjectMetaInfoHelper::convertFromJson(json, cast(object), 0); - } - virtual QJsonObject toMemoizedJson(const void *object) const override - { - return CValueObjectMetaInfoHelper::toMemoizedJson(cast(object), 0); - } - virtual void convertFromMemoizedJson(const QJsonObject &json, void *object, bool allowFallbackToJson) const override - { - CValueObjectMetaInfoHelper::convertFromMemoizedJson(json, cast(object), allowFallbackToJson, 0); - } - virtual void unmarshall(const QDBusArgument &arg, void *object) const override - { - arg >> cast(object); - } - virtual uint getValueHash(const void *object) const override - { - return CValueObjectMetaInfoHelper::getValueHash(cast(object), 0); - } - virtual int getMetaTypeId() const override - { - if constexpr (QMetaTypeId::Defined) { return qMetaTypeId(); } else { return QMetaType::UnknownType; } - } - virtual const void *upCastTo(const void *object, int metaTypeId) const override + CValueObjectMetaInfo(); + virtual ~CValueObjectMetaInfo() override; + static CValueObjectMetaInfo *instance(const T &); + + CValueObjectMetaInfo(const CValueObjectMetaInfo &) = delete; + CValueObjectMetaInfo &operator =(const CValueObjectMetaInfo &) = delete; + + virtual QString toQString(const void *object, bool i18n) const override; + virtual QJsonObject toJson(const void *object) const override; + virtual void convertFromJson(const QJsonObject &json, void *object) const override; + virtual QJsonObject toMemoizedJson(const void *object) const override; + virtual void convertFromMemoizedJson(const QJsonObject &json, void *object, bool allowFallbackToJson) const override; + virtual void unmarshall(const QDBusArgument &arg, void *object) const override; + virtual uint getValueHash(const void *object) const override; + virtual int getMetaTypeId() const override; + virtual const void *upCastTo(const void *object, int metaTypeId) const override; + virtual int compareImpl(const void *lhs, const void *rhs) const override; + virtual void setPropertyByIndex(void *object, const QVariant &variant, CPropertyIndexRef index) const override; + virtual void propertyByIndex(const void *object, QVariant &o_variant, BlackMisc::CPropertyIndexRef index) const override; + virtual bool equalsPropertyByIndex(const void *object, const QVariant &compareValue, CPropertyIndexRef index) const override; + virtual int toIcon(const void *object) const override; + virtual bool matches(const void *object, const CVariant &value) const override; + + static const T &cast(const void *object); + static T &cast(void *object); + }; + + template CValueObjectMetaInfo::CValueObjectMetaInfo() + {} + template CValueObjectMetaInfo::~CValueObjectMetaInfo() + {} + template CValueObjectMetaInfo *CValueObjectMetaInfo::instance(const T &) + { + static CValueObjectMetaInfo mi; + return &mi; + } + template QString CValueObjectMetaInfo::toQString(const void *object, bool i18n) const + { + return cast(object).toQString(i18n); + } + template QJsonObject CValueObjectMetaInfo::toJson(const void *object) const + { + return CValueObjectMetaInfoHelper::toJson(cast(object), 0); + } + template void CValueObjectMetaInfo::convertFromJson(const QJsonObject &json, void *object) const + { + CValueObjectMetaInfoHelper::convertFromJson(json, cast(object), 0); + } + template QJsonObject CValueObjectMetaInfo::toMemoizedJson(const void *object) const + { + return CValueObjectMetaInfoHelper::toMemoizedJson(cast(object), 0); + } + template void CValueObjectMetaInfo::convertFromMemoizedJson(const QJsonObject &json, void *object, bool allowFallbackToJson) const + { + CValueObjectMetaInfoHelper::convertFromMemoizedJson(json, cast(object), allowFallbackToJson, 0); + } + template void CValueObjectMetaInfo::unmarshall(const QDBusArgument &arg, void *object) const + { + arg >> cast(object); + } + template uint CValueObjectMetaInfo::getValueHash(const void *object) const + { + return CValueObjectMetaInfoHelper::getValueHash(cast(object), 0); + } + template int CValueObjectMetaInfo::getMetaTypeId() const + { + if constexpr (QMetaTypeId::Defined) { return qMetaTypeId(); } else { return QMetaType::UnknownType; } + } + template const void *CValueObjectMetaInfo::upCastTo(const void *object, int metaTypeId) const + { + if constexpr (THasMetaBaseV) { const auto base = static_cast(static_cast *>(&cast(object))); - return metaTypeId == getMetaTypeId() ? object : CValueObjectMetaInfo> {} .upCastTo(base, metaTypeId); + return metaTypeId == getMetaTypeId() ? object : CValueObjectMetaInfo>::instance(cast(object))->upCastTo(base, metaTypeId); } - virtual int compareImpl(const void *lhs, const void *rhs) const override - { - return CValueObjectMetaInfoHelper::compareImpl(cast(lhs), cast(rhs), 0); - } - virtual void setPropertyByIndex(void *object, const QVariant &variant, CPropertyIndexRef index) const override - { - CValueObjectMetaInfoHelper::setPropertyByIndex(cast(object), variant, index, 0); - } - virtual void propertyByIndex(const void *object, QVariant &o_variant, BlackMisc::CPropertyIndexRef index) const override - { - CValueObjectMetaInfoHelper::propertyByIndex(o_variant, cast(object), index, 0); - } - virtual bool equalsPropertyByIndex(const void *object, const QVariant &compareValue, CPropertyIndexRef index) const override - { - return CValueObjectMetaInfoHelper::equalsPropertyByIndex(cast(object), compareValue, index, 0); - } - virtual int toIcon(const void *object) const override - { - return CValueObjectMetaInfoHelper::toIcon(cast(object), 0); - } - virtual bool matches(const void *object, const CVariant &value) const override - { - return CValueObjectMetaInfoHelper::matches(cast(object), value, 0); - } - - static const T &cast(const void *object) { return *static_cast(object); } - static T &cast(void *object) { return *static_cast(object); } - }; - - //! \private Explicit specialization for the terminating case of the recursive CValueObjectMetaInfo::upCastTo. - template <> - struct CValueObjectMetaInfo + else { Q_UNUSED(metaTypeId); return object; } + } + template int CValueObjectMetaInfo::compareImpl(const void *lhs, const void *rhs) const { - const void *upCastTo(const void *, int) const - { - return nullptr; - } - }; + return CValueObjectMetaInfoHelper::compareImpl(cast(lhs), cast(rhs), 0); + } + template void CValueObjectMetaInfo::setPropertyByIndex(void *object, const QVariant &variant, CPropertyIndexRef index) const + { + CValueObjectMetaInfoHelper::setPropertyByIndex(cast(object), variant, index, 0); + } + template void CValueObjectMetaInfo::propertyByIndex(const void *object, QVariant &o_variant, BlackMisc::CPropertyIndexRef index) const + { + CValueObjectMetaInfoHelper::propertyByIndex(o_variant, cast(object), index, 0); + } + template bool CValueObjectMetaInfo::equalsPropertyByIndex(const void *object, const QVariant &compareValue, CPropertyIndexRef index) const + { + return CValueObjectMetaInfoHelper::equalsPropertyByIndex(cast(object), compareValue, index, 0); + } + template int CValueObjectMetaInfo::toIcon(const void *object) const + { + return CValueObjectMetaInfoHelper::toIcon(cast(object), 0); + } + template bool CValueObjectMetaInfo::matches(const void *object, const CVariant &value) const + { + return CValueObjectMetaInfoHelper::matches(cast(object), value, 0); + } + template const T &CValueObjectMetaInfo::cast(const void *object) + { + return *static_cast(object); + } + template T &CValueObjectMetaInfo::cast(void *object) + { + return *static_cast(object); + } //! \private Getter to obtain the IValueObjectMetaInfo which was stored by BlackMisc::registerMetaValueType. IValueObjectMetaInfo *getValueObjectMetaInfo(int typeId); @@ -242,19 +276,22 @@ namespace BlackMisc if constexpr (QMetaTypeId::Defined) { return qMetaTypeId(); } else { return QMetaType::UnknownType; } } - static void maybeRegisterMetaType() - { - if constexpr (QMetaTypeId::Defined) - { - qRegisterMetaType(); - qDBusRegisterMetaType(); - qRegisterMetaTypeStreamOperators(); - registerMetaValueType(); - maybeRegisterMetaList(); - } - } + static void maybeRegisterMetaType(); static void maybeRegisterMetaList(); }; + + template + void MetaTypeHelper::maybeRegisterMetaType() + { + if constexpr (QMetaTypeId::Defined) + { + qRegisterMetaType(); + qDBusRegisterMetaType(); + qRegisterMetaTypeStreamOperators(); + registerMetaValueType(); + maybeRegisterMetaList(); + } + }; //! \endcond } }