// SPDX-FileCopyrightText: Copyright (C) 2013 swift Project Community / Contributors // SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1 //! \file #ifndef BLACKMISC_VARIANT_H #define BLACKMISC_VARIANT_H #include "blackmisc/blackmiscexport.h" #include "blackmisc/mixin/mixincompare.h" #include "blackmisc/mixin/mixindbus.h" #include "blackmisc/mixin/mixindatastream.h" #include "blackmisc/inheritancetraits.h" #include "blackmisc/mixin/mixinjson.h" #include "blackmisc/range.h" #include "blackmisc/mixin/mixinstring.h" #include "blackmisc/variantprivate.h" #include "blackmisc/icons.h" #include #include #include #include #include #include #include #include #include #include namespace BlackMisc { class CIcon; class CPropertyIndex; /*! * 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. */ template void registerMetaValueType() { if (QMetaType::hasRegisteredConverterFunction()) { return; } bool ok = QMetaType::registerConverter(Private::CValueObjectMetaInfo::instance); 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. */ class BLACKMISC_EXPORT CVariant : public Mixin::EqualsByCompare, public Mixin::LessThanByCompare, public Mixin::DBusOperators, public Mixin::DataStreamOperators, public Mixin::JsonOperators, public Mixin::String { template struct tag {}; public: //! Default constructor. CVariant() {} //! Copy constructor. CVariant(const CVariant &) = default; //! Move constructor. CVariant(CVariant &&other) noexcept = default; //! Construct from a QVariant. CVariant(const QVariant &var) : m_v(var) {} //! Move-construct from a QVariant. CVariant(QVariant &&var) noexcept : m_v(std::move(var)) {} //! Construct a null variant of the given type. CVariant(QVariant::Type type) : m_v(type) {} //! Avoid unexpected implicit cast to QVariant::Type. (Use CVariant::from() instead.) CVariant(int) = delete; //! Implicit conversion from QString. CVariant(const QString &string) : m_v(string) {} //! Implicit conversion from C string. CVariant(const char *string) : m_v(string) {} //! Construct a variant from the given type and opaque pointer. CVariant(int typeId, const void *copy) : m_v(typeId, copy) {} //! \copydoc CValueObject::qHash friend uint qHash(const CVariant &var) { return var.getValueHash(); } //! Change the internal QVariant. void reset(const QVariant &var) { m_v = var; } //! Change the internal QVariant. void reset(QVariant &&var) { m_v = std::move(var); } //! Copy assignment operator. CVariant &operator=(const CVariant &other) = default; //! Move assignment operatior. CVariant &operator=(CVariant &&other) noexcept = default; //! Change the internal QVariant CVariant &operator=(const QVariant &var) { m_v = var; return *this; } //! Change the internal QVariant CVariant &operator=(QVariant &&var) noexcept { m_v = std::move(var); return *this; } //! Swap this variant with another. void swap(CVariant &other) noexcept { m_v.swap(other.m_v); } //! Swap the internal QVariant with another. void swap(QVariant &other) noexcept { m_v.swap(other); } //! Construct a variant from a value. template static CVariant fromValue(T &&value) { static_assert(!std::is_same_v>, "CVariant is an illegal type!"); return CVariant(QVariant::fromValue(std::forward(value))); } //! Synonym for fromValue(). template static CVariant from(T &&value) { static_assert(!std::is_same_v>, "CVariant is an illegal type!"); return CVariant(QVariant::fromValue(std::forward(value))); } //! Change the value. template void setValue(T &&value) { m_v.setValue(std::forward(value)); } //! Synonym for setValue(). template void set(T &&value) { m_v.setValue(std::forward(value)); } //! Return the value converted to the type T. template T value() const { return to(tag()); } //! Synonym for value(). template T to() const { return to(tag()); } //! Returns the value converted to the type T, or a default if it can not be converted. //! \details Parameter is passed by value to avoid odr-using the argument in case it is //! an inline-initialized static const integral data member without a definition (ยง9.4.2/3). template T valueOrDefault(T def) const { return canConvert() ? value() : def; } //! Return the internal QVariant. const QVariant &getQVariant() const { return m_v; } //! Return the internal QVariant. operator const QVariant &() const { return m_v; } //! Return the internal QVariant. operator QVariant() && { return std::move(m_v); } //! True if this variant can be converted to the type with the given metatype ID. bool canConvert(int typeId) const; //! True if this variant can be converted to the type T. template bool canConvert() const { return canConvert(qMetaTypeId()); } //! Convert this variant to the type with the given metatype ID and return true if successful. bool convert(int typeId); //! \copydoc BlackMisc::Mixin::String::toQString QString convertToQString(bool i18n = false) const; //! True if this variant's type is an integral type. bool isIntegral() const; //! True if this variant's type is an integral or floating-point type. bool isArithmetic() const; //! Convert this variant to a bool. bool toBool() const { return m_v.toBool(); } //! Convert this variant to an integer. int toInt(bool *ok = nullptr) const { return m_v.toInt(ok); } //! Convert this variant to a longlong integer. qlonglong toLongLong(bool *ok = nullptr) const { return m_v.toLongLong(ok); } //! Convert this variant to a unsigned longlong integer. qulonglong toULongLong(bool *ok = nullptr) const { return m_v.toULongLong(ok); } //! COnvert to qint64, which is used for all timestamps qint64 toQInt64(bool *ok = nullptr) const; //! Convert this variant to double. double toDouble(bool *ok = nullptr) const { return m_v.toDouble(ok); } //! Convert this variant to QDateTime. QDateTime toDateTime() const { return m_v.toDateTime(); } //! Convert this variant to QUrl. QUrl toUrl() const { return m_v.toUrl(); } //! Set the variant to null. void clear() { m_v.clear(); } //! True if this variant is null. bool isNull() const { return m_v.isNull(); } //! True if this variant is valid. bool isValid() const { return m_v.isValid(); } //! Return the metatype ID of the value in this variant, or QMetaType::User if it is a user type. QMetaType::Type type() const { return static_cast(m_v.type()); } //! Return the typename of the value in this variant. const char *typeName() const { return m_v.typeName(); } //! Return the metatype ID of the value in this variant. int userType() const { return m_v.userType(); } //! Return the QMetaType of the type in this variant. QMetaType metaType() const { return QMetaType(userType()); } //! \copydoc BlackMisc::Mixin::JsonByMetaClass::toJson QJsonObject toJson() const; //! Convenience function JSON as string QString toJsonString(QJsonDocument::JsonFormat format = QJsonDocument::Indented) const; //! \copydoc BlackMisc::Mixin::JsonByMetaClass::convertFromJson void convertFromJson(const QJsonObject &json); //! Call convertFromJson, catch any CJsonException that is thrown and return it as CStatusMessage. CStatusMessage convertFromJsonNoThrow(const QJsonObject &json, const CLogCategoryList &categories, const QString &prefix); //! To compact JSON format. QJsonObject toMemoizedJson() const; //! From compact JSON format. void convertFromMemoizedJson(const QJsonObject &json, bool allowFallbackToJson); //! Call convertFromMemoizedJson, catch any CJsonException that is thrown and return it as CStatusMessage. CStatusMessage convertFromMemoizedJsonNoThrow(const QJsonObject &json, const CLogCategoryList &categories, const QString &prefix); //! \copydoc BlackMisc::Mixin::DBusByMetaClass::marshallToDbus void marshallToDbus(QDBusArgument &argument) const; //! \copydoc BlackMisc::Mixin::DBusByMetaClass::unmarshallFromDbus void unmarshallFromDbus(const QDBusArgument &argument); //! \copydoc BlackMisc::Mixin::DataStreamByMetaClass::marshalToDataStream void marshalToDataStream(QDataStream &stream) const; //! \copydoc BlackMisc::Mixin::DataStreamByMetaClass::unmarshalFromDataStream void unmarshalFromDataStream(QDataStream &stream); //! \copydoc CValueObject::compare friend int compare(const CVariant &a, const CVariant &b) { return compareImpl(a, b); } //! \copydoc BlackMisc::Mixin::Index::setPropertyByIndex void setPropertyByIndex(CPropertyIndexRef index, const QVariant &variant); //! \copydoc BlackMisc::Mixin::Index::propertyByIndex QVariant propertyByIndex(BlackMisc::CPropertyIndexRef index) const; //! \copydoc CValueObject::equalsPropertyByIndex bool equalsPropertyByIndex(const CVariant &compareValue, CPropertyIndexRef index) const; //! \copydoc CIcon::toPixmap QPixmap toPixmap() const; //! \copydoc BlackMisc::Mixin::Icon::toIcon CIcons::IconIndex toIcon() const; //! \copydoc BlackMisc::Mixin::MetaType::registerMetadata static void registerMetadata(); //! \copydoc BlackMisc::Mixin::MetaType::getMetaTypeId int getMetaTypeId() const; //! \copydoc BlackMisc::Mixin::MetaType::getClassName QString getClassName() const; //! \copydoc BlackMisc::Mixin::MetaType::isA bool isA(int metaTypeId) const; //! If this is an event subscription, return true if it matches the given event. bool matches(const CVariant &event) const; private: QVariant m_v; Private::IValueObjectMetaInfo *getValueObjectMetaInfo() const { return Private::getValueObjectMetaInfo(m_v); } void *data() { return m_v.data(); } const void *data() const { return m_v.data(); } static int compareImpl(const CVariant &, const CVariant &); uint getValueHash() const; template T to(tag) const { auto copy = *this; copy.convert(qMetaTypeId()); return *static_cast(copy.data()); } template QList to(tag>) const { return toImpl>(); } template QVector to(tag>) const { return toImpl>(); } template CSequence to(tag>) const { return toImpl>(); } template T toImpl() const { using VT = typename T::value_type; T result; if (isVariantList()) { for (const auto &v : m_v.value>()) { result.push_back(v.value()); } } else { for (const auto &v : m_v.value()) { result.push_back(v.value()); } } return result; } bool isVariantList() const; }; } // namespace Q_DECLARE_METATYPE(BlackMisc::CVariant) namespace BlackMisc::Private { //! \cond PRIVATE template void MetaTypeHelper::maybeRegisterMetaList() { if constexpr (canConvertVariantList(0)) { if (QMetaType::hasRegisteredConverterFunction(qMetaTypeId(), qMetaTypeId>())) { return; } QMetaType::registerConverter>([](const T &list) -> QVector { return list.transform([](const typename T::value_type &v) { return CVariant::from(v); }); }); QMetaType::registerConverter, T>([](const QVector &list) -> T { return makeRange(list).transform([](const CVariant &v) { return v.to(); }); }); } } //! \endcond } // namespace #endif