From c5b463fafb0aa6c60a179a28c57e85bde96776b0 Mon Sep 17 00:00:00 2001 From: Mathew Sutcliffe Date: Wed, 12 Nov 2014 19:03:47 +0000 Subject: [PATCH] refs #247 Created a facility to register our own custom metatype metadata. --- src/blackmisc/valueobject.h | 31 ++++++++ src/blackmisc/valueobject_private.h | 106 ++++++++++++++++++++++++++++ src/blackmisc/variant.cpp | 9 +++ src/blackmisc/variant.h | 1 - 4 files changed, 146 insertions(+), 1 deletion(-) create mode 100644 src/blackmisc/valueobject_private.h diff --git a/src/blackmisc/valueobject.h b/src/blackmisc/valueobject.h index 2cdc4e29e..20cbcb96c 100644 --- a/src/blackmisc/valueobject.h +++ b/src/blackmisc/valueobject.h @@ -17,6 +17,7 @@ #include "json.h" #include "variant.h" #include "blackmiscfreefunctions.h" +#include "valueobject_private.h" #include "valueobject_policy.h" #include #include @@ -56,6 +57,24 @@ namespace BlackMisc }; } + /*! + * 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. + * Unlike the singleton pattern, this approach means that CVariant can be used in plugins. + */ + template + void registerMetaValueType() + { + if (QMetaType::hasRegisteredConverterFunction()) { return; } + auto converter = [](const T &) { static Private::CValueObjectMetaInfo info; return &info; }; + bool ok = QMetaType::registerConverter(converter); + Q_ASSERT(ok); + Q_UNUSED(ok); + } + /*! * Base class for value types. */ @@ -194,6 +213,9 @@ namespace BlackMisc static const CValueObject *fromQVariant(const QVariant &variant); protected: + template + friend struct Private::CValueObjectMetaInfo; + //! Default constructor. CValueObject() = default; @@ -238,6 +260,15 @@ namespace BlackMisc virtual void parseFromString(const QString &) { qFatal("Not implemented"); } }; + //! \private FIXME defined out-of-line because it depends on CValueObject + template + int Private::CValueObjectMetaInfo::compare(const void *lhs, const void *rhs) const + { + // Casting to CValueObject here only because compareImpl is protected and CValueObjectMetaInfo is a friend of CValueObject. + // TODO: make compareImpl public (and typesafe), then this doesn't need to be defined out-of-line. + return static_cast(lhs)->compareImpl(*static_cast(rhs)); + } + /*! * Default policy classes for use by CValueObjectStdTuple. * diff --git a/src/blackmisc/valueobject_private.h b/src/blackmisc/valueobject_private.h new file mode 100644 index 000000000..5853189ef --- /dev/null +++ b/src/blackmisc/valueobject_private.h @@ -0,0 +1,106 @@ +/* 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_VALUEOBJECT_PRIVATE_H +#define BLACKMISC_VALUEOBJECT_PRIVATE_H + +#include +#include +#include + +namespace BlackMisc +{ + class CValueObject; + + template typename std::enable_if::value, QDBusArgument>::type const & + operator>>(const QDBusArgument &argument, T &valueObject); + + namespace Private + { + //! \private Abstract base class representing the set of operations supported by a particular value type. + struct 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 compare(const void *lhs, const void *rhs) const = 0; + }; + + //! \private Implementation of IValueObjectMetaInfo representing the set of operations supported by T. + 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 cast(object).toJson(); + } + virtual void convertFromJson(const QJsonObject &json, void *object) const override + { + cast(object).convertFromJson(json); + } + virtual void unmarshall(const QDBusArgument &arg, void *object) const override + { + // FIXME Using the usual >> operator syntax here attempts to call the Container overload in QtDBus/qdbusargument.h + BlackMisc::operator >>(arg, cast(object)); + } + virtual uint getValueHash(const void *object) const override + { + return cast(object).getValueHash(); + } + 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 compare(const void *lhs, const void *rhs) const override; // FIXME defined out-of-line in valueobject.h because it uses CValueObject + + 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 <> + inline const void *CValueObjectMetaInfo::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()); } + } +} + +Q_DECLARE_METATYPE(BlackMisc::Private::IValueObjectMetaInfo *) + +#endif diff --git a/src/blackmisc/variant.cpp b/src/blackmisc/variant.cpp index c9348ed1b..b92b74cc4 100644 --- a/src/blackmisc/variant.cpp +++ b/src/blackmisc/variant.cpp @@ -16,6 +16,15 @@ namespace BlackMisc { + Private::IValueObjectMetaInfo *Private::getValueObjectMetaInfo(int typeId) + { + return getValueObjectMetaInfo(QVariant(typeId, nullptr)); + } + + Private::IValueObjectMetaInfo *Private::getValueObjectMetaInfo(const QVariant &v) + { + return v.value(); + } void CVariant::registerMetadata() { diff --git a/src/blackmisc/variant.h b/src/blackmisc/variant.h index aeb70bcd2..4f452d514 100644 --- a/src/blackmisc/variant.h +++ b/src/blackmisc/variant.h @@ -23,7 +23,6 @@ class QDBusArgument; namespace BlackMisc { - // KB: Would it make sense to turn CVariant into an CValueObject? #307 /*! * Wrapper class for QVariant, for more natural and transparent DBus integration.