refs #413 To signal when contained object doesn't support an operation, throw exception in CValueObjectMetaInfo and catch in CVariant.

This commit is contained in:
Mathew Sutcliffe
2015-05-05 00:10:48 +01:00
parent 38cfebc55c
commit 9cdacf8d86
2 changed files with 113 additions and 28 deletions

View File

@@ -19,6 +19,7 @@
#include <QDBusMetaType>
#include <QDBusArgument>
#include <QJsonObject>
#include <stdexcept>
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 <class T>
CVariantException(const T &, const QString &operationName) : std::invalid_argument((blurb<T>(operationName)).toStdString()), m_operationName(operationName) {}
const QString &operationName() const { return m_operationName; }
~CVariantException() Q_DECL_NOEXCEPT {}
private:
QString m_operationName;
template <class T>
static QString blurb(const QString &operationName)
{
return QString("CVariant requested unsupported operation of contained ") + QMetaType::typeName(qMetaTypeId<T>()) + " object: " + operationName;
}
};
//! \private
namespace Fallback
{
//! \private Fallback in case qHash is not defined for T.
template <typename T>
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 <typename T>
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 <typename U> struct Derived : public U, public Fallback {};
# define DISABLE_IF_HAS(MEMBER) typename int_t<&Derived<U>::MEMBER>::type
template <typename U> static QJsonObject toJsonHelper(const U &, DISABLE_IF_HAS(toJson)) { return {}; }
template <typename U> static QJsonObject toJsonHelper(const U &object, DISABLE_IF_HAS(toJson)) { throw CVariantException(object, "toJson"); }
template <typename U> static QJsonObject toJsonHelper(const U &object, ...) { return object.toJson(); }
template <typename U> static void convertFromJsonHelper(const QJsonObject &, U &, DISABLE_IF_HAS(convertFromJson)) {}
template <typename U> static void convertFromJsonHelper(const QJsonObject &, U &object, DISABLE_IF_HAS(convertFromJson)) { throw CVariantException(object, "convertFromJson"); }
template <typename U> static void convertFromJsonHelper(const QJsonObject &json, U &object, ...) { object.convertFromJson(json); }
template <typename U> static void setPropertyByIndexHelper(U &, const CVariant &, const CPropertyIndex &, DISABLE_IF_HAS(setPropertyByIndex)) {}
template <typename U> static void setPropertyByIndexHelper(U &object, const CVariant &, const CPropertyIndex &, DISABLE_IF_HAS(setPropertyByIndex)) { throw CVariantException(object, "setPropertyByIndex"); }
template <typename U> static void setPropertyByIndexHelper(U &object, const CVariant &variant, const CPropertyIndex &index, ...) { object.setPropertyByIndex(variant, index); }
template <typename U> static void propertyByIndexHelper(CVariant &, const U &, const CPropertyIndex &, DISABLE_IF_HAS(propertyByIndex)) {}
template <typename U> static void propertyByIndexHelper(CVariant &, const U &object, const CPropertyIndex &, DISABLE_IF_HAS(propertyByIndex)) { throw CVariantException(object, "propertyByIndex"); }
template <typename U> static void propertyByIndexHelper(CVariant &o_variant, const U &object, const CPropertyIndex &index, ...) { assign(o_variant, object.propertyByIndex(index)); }
template <typename U> static QString propertyByIndexAsStringHelper(const U &, const CPropertyIndex &, bool, DISABLE_IF_HAS(propertyByIndexAsString)) { return {}; }
template <typename U> static QString propertyByIndexAsStringHelper(const U &object, const CPropertyIndex &, bool, DISABLE_IF_HAS(propertyByIndexAsString)) { throw CVariantException(object, "propertyByIndexAsString"); }
template <typename U> static QString propertyByIndexAsStringHelper(const U &object, const CPropertyIndex &index, bool i18n, ...) { return object.propertyByIndexAsString(index, i18n); }
template <typename U> static bool equalsPropertyByIndexHelper(const U &, const CVariant &, const CPropertyIndex &, DISABLE_IF_HAS(equalsPropertyByIndex)) { return false; }
template <typename U> static bool equalsPropertyByIndexHelper(const U &object, const CVariant &, const CPropertyIndex &, DISABLE_IF_HAS(equalsPropertyByIndex)) { throw CVariantException(object, "equalsPropertyByIndex"); }
template <typename U> static bool equalsPropertyByIndexHelper(const U &object, const CVariant &variant, const CPropertyIndex &index, ...) { return object.equalsPropertyByIndex(variant, index); }
template <typename U> static void toIconHelper(const U &, CIcon &, DISABLE_IF_HAS(toIcon)) {}
template <typename U> static void toIconHelper(const U &, CIcon &, typename std::enable_if<std::is_same<U, CVariant>::value, int>::type) {} // CIcon is incomplete when CValueObjectMetaInfo<CVariant> is instantiated
template <typename U> static void toIconHelper(const U &object, CIcon &, DISABLE_IF_HAS(toIcon)) { throw CVariantException(object, "toIcon"); }
template <typename U> static void toIconHelper(const U &object, CIcon &, typename std::enable_if<std::is_same<U, CVariant>::value, int>::type) { throw CVariantException(object, "toIcon"); } // CIcon is incomplete when CValueObjectMetaInfo<CVariant> is instantiated
template <typename U> static void toIconHelper(const U &object, CIcon &o_icon, ...) { assign(o_icon, object.toIcon()); }
# undef DISABLE_IF_HAS

View File

@@ -10,6 +10,7 @@
#include "variant.h"
#include "blackmiscfreefunctions.h"
#include "icon.h"
#include "logmessage.h"
#include <QDBusArgument>
#include <QDBusMetaType>
#include <QDBusVariant>
@@ -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