diff --git a/src/blackmisc/valueobject_private.h b/src/blackmisc/valueobject_private.h index 90a442560..e149c820d 100644 --- a/src/blackmisc/valueobject_private.h +++ b/src/blackmisc/valueobject_private.h @@ -19,6 +19,7 @@ #include #include #include +#include namespace BlackMisc { @@ -57,17 +58,37 @@ namespace BlackMisc 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 &operationName) : std::invalid_argument((blurb(operationName)).toStdString()), m_operationName(operationName) {} + + const QString &operationName() const { return m_operationName; } + + ~CVariantException() Q_DECL_NOEXCEPT {} + + private: + QString m_operationName; + + template + static QString blurb(const QString &operationName) + { + return QString("CVariant requested unsupported operation of contained ") + QMetaType::typeName(qMetaTypeId()) + " object: " + operationName; + } + }; //! \private namespace Fallback { //! \private Fallback in case qHash is not defined for T. template - uint qHash(const T &object) { return 0; } + uint qHash(const T &object) { throw CVariantException(object, "qHash"); } //! \private Fallback in case compare is not defined for T. template - int compare(const T &a, const T &) { return 0; } + int compare(const T &a, const T &) { throw CVariantException(a, "compare"); } } //! \private Implementation of IValueObjectMetaInfo representing the set of operations supported by T. @@ -80,20 +101,20 @@ namespace BlackMisc template struct Derived : public U, public Fallback {}; # define DISABLE_IF_HAS(MEMBER) typename int_t<&Derived::MEMBER>::type - template static QJsonObject toJsonHelper(const U &, DISABLE_IF_HAS(toJson)) { return {}; } + 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 &, DISABLE_IF_HAS(convertFromJson)) {} + 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 &, const CVariant &, const CPropertyIndex &, DISABLE_IF_HAS(setPropertyByIndex)) {} + 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 &, const CPropertyIndex &, DISABLE_IF_HAS(propertyByIndex)) {} + 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 &, const CPropertyIndex &, bool, DISABLE_IF_HAS(propertyByIndexAsString)) { return {}; } + 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 &, const CVariant &, const CPropertyIndex &, DISABLE_IF_HAS(equalsPropertyByIndex)) { return false; } + 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 &, CIcon &, DISABLE_IF_HAS(toIcon)) {} - template static void toIconHelper(const U &, CIcon &, typename std::enable_if::value, int>::type) {} // CIcon is incomplete when CValueObjectMetaInfo is instantiated + 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 diff --git a/src/blackmisc/variant.cpp b/src/blackmisc/variant.cpp index ce76aa88d..e07702e50 100644 --- a/src/blackmisc/variant.cpp +++ b/src/blackmisc/variant.cpp @@ -10,6 +10,7 @@ #include "variant.h" #include "blackmiscfreefunctions.h" #include "icon.h" +#include "logmessage.h" #include #include #include @@ -44,18 +45,26 @@ namespace BlackMisc auto *bMeta = b.getValueObjectMetaInfo(); if (aMeta && bMeta) { - const void *casted = nullptr; - if ((casted = aMeta->upCastTo(a.data(), bMeta->getMetaTypeId()))) + try { - return bMeta->compareImpl(casted, b.data()); + const void *casted = nullptr; + if ((casted = aMeta->upCastTo(a.data(), bMeta->getMetaTypeId()))) + { + return bMeta->compareImpl(casted, b.data()); + } + else if ((casted = bMeta->upCastTo(b.data(), aMeta->getMetaTypeId()))) + { + return aMeta->compareImpl(a.data(), casted); + } + else + { + qWarning() << "Comparing two CVariants containing unrelated value objects"; + return 0; + } } - else if ((casted = bMeta->upCastTo(b.data(), aMeta->getMetaTypeId()))) + catch (const Private::CVariantException &ex) { - return aMeta->compareImpl(a.data(), casted); - } - else - { - qWarning() << "Comparing two CVariants containing unrelated value objects"; + CLogMessage().debug() << ex.what(); return 0; } } @@ -81,6 +90,7 @@ namespace BlackMisc case QVariant::Char: json.insert("value", m_v.toString()); case QVariant::ByteArray: json.insert("value", m_v.toString()); default: + try { auto *meta = getValueObjectMetaInfo(); if (meta) @@ -96,6 +106,10 @@ namespace BlackMisc qWarning() << "Unsupported CVariant type for toJson"; } } + catch (const Private::CVariantException &ex) + { + CLogMessage().debug() << ex.what(); + } } return json; } @@ -117,6 +131,7 @@ namespace BlackMisc case QVariant::Char: m_v.setValue(json.value("value").toString().size() > 0 ? json.value("value").toString().at(0) : '\0'); case QVariant::ByteArray: m_v.setValue(json.value("value").toString().toLatin1()); default: + try { auto *meta = Private::getValueObjectMetaInfo(typeId); if (meta) @@ -137,6 +152,10 @@ namespace BlackMisc qWarning() << "Unsupported CVariant type for fromJson"; } } + catch (const Private::CVariantException &ex) + { + CLogMessage().debug() << ex.what(); + } } } @@ -154,6 +173,7 @@ namespace BlackMisc case QVariant::Char: return qHash(m_v.toChar()); case QVariant::ByteArray: return qHash(m_v.toByteArray()); default: + try { auto *meta = getValueObjectMetaInfo(); if (meta) @@ -170,6 +190,11 @@ namespace BlackMisc return 0; } } + catch (const Private::CVariantException &ex) + { + CLogMessage().debug() << ex.what(); + return 0; + } } } @@ -191,39 +216,78 @@ namespace BlackMisc { auto *meta = getValueObjectMetaInfo(); Q_ASSERT(meta); - meta->setPropertyByIndex(data(), variant, index); + try + { + meta->setPropertyByIndex(data(), variant, index); + } + catch (const Private::CVariantException &ex) + { + CLogMessage().debug() << ex.what(); + } } CVariant CVariant::propertyByIndex(const BlackMisc::CPropertyIndex &index) const { auto *meta = getValueObjectMetaInfo(); Q_ASSERT(meta); - CVariant result; - meta->propertyByIndex(data(), result, index); - return result; + try + { + CVariant result; + meta->propertyByIndex(data(), result, index); + return result; + } + catch (const Private::CVariantException &ex) + { + CLogMessage().debug() << ex.what(); + return {}; + } } QString CVariant::propertyByIndexAsString(const CPropertyIndex &index, bool i18n) const { auto *meta = getValueObjectMetaInfo(); Q_ASSERT(meta); - return meta->propertyByIndexAsString(data(), index, i18n); + try + { + return meta->propertyByIndexAsString(data(), index, i18n); + } + catch (const Private::CVariantException &ex) + { + CLogMessage().debug() << ex.what(); + return {}; + } } bool CVariant::equalsPropertyByIndex(const CVariant &compareValue, const CPropertyIndex &index) const { auto *meta = getValueObjectMetaInfo(); Q_ASSERT(meta); - return meta->equalsPropertyByIndex(data(), compareValue, index); + try + { + return meta->equalsPropertyByIndex(data(), compareValue, index); + } + catch (const Private::CVariantException &ex) + { + CLogMessage().debug() << ex.what(); + return false; + } } CIcon CVariant::toIcon() const { auto *meta = getValueObjectMetaInfo(); if (! meta) { return {}; } - CIcon result; - meta->toIcon(data(), result); - return result; + try + { + CIcon result; + meta->toIcon(data(), result); + return result; + } + catch (const Private::CVariantException &ex) + { + CLogMessage().debug() << ex.what(); + return {}; + } } QPixmap CVariant::toPixmap() const