/* Copyright (C) 2014 * swift project Community / Contributors * * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, * including this file, may be copied, modified, propagated, or distributed except according to the terms * contained in the LICENSE file. */ //! \file #ifndef BLACKMISC_VARIANT_PRIVATE_H #define BLACKMISC_VARIANT_PRIVATE_H #include "blackmisc/blackmiscexport.h" #include "blackmisc/blackmiscfreefunctions.h" #include "blackmisc/inheritancetraits.h" #include #include #include #include #include #include namespace BlackMisc { class CEmpty; class CVariant; class CPropertyIndex; class CIcon; template void registerMetaValueType(); //! Checked version from QVariant template void setFromQVariant(T *value, const QVariant &variant) { if (variant.canConvert()) { (*value) = variant.value(); } else { const QString m = QString("Target type: %1 Variant type: %2 %3 %4"). arg(qMetaTypeId()). arg(static_cast(variant.type())).arg(variant.userType()).arg(variant.typeName()); Q_ASSERT_X(false, "setFromQVariant", m.toLocal8Bit().constData()); Q_UNUSED(m); } } namespace Private { //! \private Needed so we can copy forward-declared CVariant. void assign(CVariant &, const CVariant &); //! \private Needed so we can copy forward-declared CIcon. void assign(CIcon &, const CIcon &); //! \private Abstract base class representing the set of operations supported by a particular value type. struct BLACKMISC_EXPORT IValueObjectMetaInfo { virtual ~IValueObjectMetaInfo() = default; virtual QString toQString(const void *object, bool i18n) const = 0; virtual QJsonObject toJson(const void *object) const = 0; virtual void convertFromJson(const QJsonObject &json, void *object) const = 0; virtual void unmarshall(const QDBusArgument &arg, void *object) const = 0; virtual uint getValueHash(const void *object) const = 0; virtual int getMetaTypeId() const = 0; virtual const void *upCastTo(const void *object, int metaTypeId) const = 0; virtual int compareImpl(const void *lhs, const void *rhs) const = 0; virtual void setPropertyByIndex(void *object, const CVariant &variant, const CPropertyIndex &index) const = 0; virtual void propertyByIndex(const void *object, CVariant &o_variant, const BlackMisc::CPropertyIndex &index) const = 0; virtual QString propertyByIndexAsString(const void *object, const CPropertyIndex &index, bool i18n) const = 0; virtual bool equalsPropertyByIndex(const void *object, const CVariant &compareValue, const CPropertyIndex &index) const = 0; virtual void toIcon(const void *object, CIcon &o_icon) const = 0; }; //! \private Exception to signal that an unsupported operation was requested. class CVariantException : public std::invalid_argument { public: template CVariantException(const T &, const QString &opName) : CVariantException(qMetaTypeId(), opName) {} CVariantException(int typeId, const QString &opName) : std::invalid_argument((blurb(typeId, opName)).toStdString()), m_operationName(opName) {} const QString &operationName() const { return m_operationName; } ~CVariantException() Q_DECL_NOEXCEPT {} private: QString m_operationName; static QString blurb(int typeId, const QString &operationName) { return QString("CVariant requested unsupported operation of contained ") + QMetaType::typeName(typeId) + " object: " + operationName; } }; //! \private namespace Fallback { //! \private Class with a conversion constructor template from any type. struct FromAny { template FromAny(const T &) : m_type(qMetaTypeId()) {} int m_type; }; //! \private Fallback in case qHash is not defined for T. inline uint qHash(const FromAny &object) { throw CVariantException(object.m_type, "qHash"); } //! \private Fallback in case compare is not defined for T. inline int compare(const FromAny &a, const FromAny &) { throw CVariantException(a.m_type, "compare"); } } //! \private Implementation of IValueObjectMetaInfo representing the set of operations supported by T. template struct CValueObjectMetaInfo : public IValueObjectMetaInfo { //! \cond PRIVATE // http://en.wikibooks.org/wiki/More_C++_Idioms/Member_Detector struct Fallback { int toJson, convertFromJson, setPropertyByIndex, propertyByIndex, propertyByIndexAsString, equalsPropertyByIndex, toIcon; }; template struct int_t { typedef int type; }; template struct Derived : public U, public Fallback {}; # define DISABLE_IF_HAS(MEMBER) typename int_t<&Derived::MEMBER>::type //! \endcond template static QJsonObject toJsonHelper(const U &object, DISABLE_IF_HAS(toJson)) { throw CVariantException(object, "toJson"); } template static QJsonObject toJsonHelper(const U &object, ...) { return object.toJson(); } template static void convertFromJsonHelper(const QJsonObject &, U &object, DISABLE_IF_HAS(convertFromJson)) { throw CVariantException(object, "convertFromJson"); } template static void convertFromJsonHelper(const QJsonObject &json, U &object, ...) { object.convertFromJson(json); } template static void setPropertyByIndexHelper(U &object, const CVariant &, const CPropertyIndex &, DISABLE_IF_HAS(setPropertyByIndex)) { throw CVariantException(object, "setPropertyByIndex"); } template static void setPropertyByIndexHelper(U &object, const CVariant &variant, const CPropertyIndex &index, ...) { object.setPropertyByIndex(variant, index); } template static void propertyByIndexHelper(CVariant &, const U &object, const CPropertyIndex &, DISABLE_IF_HAS(propertyByIndex)) { throw CVariantException(object, "propertyByIndex"); } template static void propertyByIndexHelper(CVariant &o_variant, const U &object, const CPropertyIndex &index, ...) { assign(o_variant, object.propertyByIndex(index)); } template static QString propertyByIndexAsStringHelper(const U &object, const CPropertyIndex &, bool, DISABLE_IF_HAS(propertyByIndexAsString)) { throw CVariantException(object, "propertyByIndexAsString"); } template static QString propertyByIndexAsStringHelper(const U &object, const CPropertyIndex &index, bool i18n, ...) { return object.propertyByIndexAsString(index, i18n); } template static bool equalsPropertyByIndexHelper(const U &object, const CVariant &, const CPropertyIndex &, DISABLE_IF_HAS(equalsPropertyByIndex)) { throw CVariantException(object, "equalsPropertyByIndex"); } template static bool equalsPropertyByIndexHelper(const U &object, const CVariant &variant, const CPropertyIndex &index, ...) { return object.equalsPropertyByIndex(variant, index); } template static void toIconHelper(const U &object, CIcon &, DISABLE_IF_HAS(toIcon)) { throw CVariantException(object, "toIcon"); } template static void toIconHelper(const U &object, CIcon &, typename std::enable_if::value, int>::type) { throw CVariantException(object, "toIcon"); } // CIcon is incomplete when CValueObjectMetaInfo is instantiated template static void toIconHelper(const U &object, CIcon &o_icon, ...) { assign(o_icon, object.toIcon()); } # undef DISABLE_IF_HAS virtual QString toQString(const void *object, bool i18n) const override { return cast(object).toQString(i18n); } virtual QJsonObject toJson(const void *object) const override { return toJsonHelper(cast(object), 0); } virtual void convertFromJson(const QJsonObject &json, void *object) const override { convertFromJsonHelper(json, cast(object), 0); } virtual void unmarshall(const QDBusArgument &arg, void *object) const override { arg >> cast(object); } virtual uint getValueHash(const void *object) const override { using Private::Fallback::qHash; return qHash(cast(object)); } virtual int getMetaTypeId() const override { return maybeGetMetaTypeId(std::integral_constant::Defined> {}); } virtual const void *upCastTo(const void *object, int metaTypeId) const override { const auto base = static_cast(static_cast *>(&cast(object))); return metaTypeId == getMetaTypeId() ? object : CValueObjectMetaInfo> {} .upCastTo(base, metaTypeId); } virtual int compareImpl(const void *lhs, const void *rhs) const override { using Private::Fallback::compare; return compare(cast(lhs), cast(rhs)); } virtual void setPropertyByIndex(void *object, const CVariant &variant, const CPropertyIndex &index) const override { setPropertyByIndexHelper(cast(object), variant, index, 0); } virtual void propertyByIndex(const void *object, CVariant &o_variant, const BlackMisc::CPropertyIndex &index) const override { propertyByIndexHelper(o_variant, cast(object), index, 0); } virtual QString propertyByIndexAsString(const void *object, const CPropertyIndex &index, bool i18n) const override { return propertyByIndexAsStringHelper(cast(object), index, i18n, 0); } virtual bool equalsPropertyByIndex(const void *object, const CVariant &compareValue, const CPropertyIndex &index) const override { return equalsPropertyByIndexHelper(cast(object), compareValue, index, 0); } virtual void toIcon(const void *object, CIcon &o_icon) const override { toIconHelper(cast(object), o_icon, 0); } static const T &cast(const void *object) { return *static_cast(object); } static T &cast(void *object) { return *static_cast(object); } static int maybeGetMetaTypeId(std::true_type) { return qMetaTypeId(); } static int maybeGetMetaTypeId(std::false_type) { return QMetaType::UnknownType; } }; //! \private Explicit specialization for the terminating case of the recursive CValueObjectMetaInfo::upCastTo. template <> struct CValueObjectMetaInfo { const void *upCastTo(const void *, int) const { return nullptr; } }; //! \private Getter to obtain the IValueObjectMetaInfo which was stored by BlackMisc::registerMetaValueType. IValueObjectMetaInfo *getValueObjectMetaInfo(int typeId); //! \private Getter to obtain the IValueObjectMetaInfo which was stored by BlackMisc::registerMetaValueType. IValueObjectMetaInfo *getValueObjectMetaInfo(const QVariant &); //! \private Getter to obtain the IValueObjectMetaInfo which was stored by BlackMisc::registerMetaValueType. template IValueObjectMetaInfo *getValueObjectMetaInfo() { return getValueObjectMetaInfo(qMetaTypeId()); } //! \cond PRIVATE template struct MetaTypeHelperImpl { static Q_DECL_CONSTEXPR int maybeGetMetaTypeId() { return qMetaTypeId(); } static void maybeRegisterMetaType() { qRegisterMetaType(); qDBusRegisterMetaType(); registerMetaValueType(); } static QVariant maybeToQVariant(const T &obj) { return QVariant::fromValue(obj); } static void maybeConvertFromQVariant(T &obj, const QVariant &var) { BlackMisc::setFromQVariant(&obj, var); } }; template struct MetaTypeHelperImpl { static Q_DECL_CONSTEXPR int maybeGetMetaTypeId() { return QMetaType::UnknownType; } static void maybeRegisterMetaType() {} static QVariant maybeToQVariant(const T &) { return {}; } static void maybeConvertFromQVariant(T &, const QVariant &) {} }; template using MetaTypeHelper = MetaTypeHelperImpl::Defined>; //! \endcond } } Q_DECLARE_METATYPE(BlackMisc::Private::IValueObjectMetaInfo *) #endif