diff options
author | Ulf Hermann <ulf.hermann@qt.io> | 2020-09-10 18:30:22 +0200 |
---|---|---|
committer | Ulf Hermann <ulf.hermann@qt.io> | 2020-09-19 11:14:36 +0200 |
commit | 217a25a6bf59bb9ce0845267f46244fedd8bcaaa (patch) | |
tree | ae54c75f951553001472d5d8bb0f54e5a4f9056c /src/corelib/kernel/qmetatype.cpp | |
parent | 5c808073af5b8f1290602fcccf60666c9a3682f8 (diff) |
QMetaType: Allow registration of mutable views and register iterables
In order to modify a container through an iterable, we need the original
container to be mutable. The iterable, then, is not a conversion of the
container, but rather a view on the container. The concept may be
extended to other types.
In order to facilitate this, provide a set of methods in QMetaType and
QVariant similar to the convert family. The new methods are non-const
and expect the original value to stay available during the life time of
the view.
Change-Id: I363621033f7fc600edcea2acb786820ccba49c86
Reviewed-by: Lars Knoll <lars.knoll@qt.io>
Diffstat (limited to 'src/corelib/kernel/qmetatype.cpp')
-rw-r--r-- | src/corelib/kernel/qmetatype.cpp | 342 |
1 files changed, 297 insertions, 45 deletions
diff --git a/src/corelib/kernel/qmetatype.cpp b/src/corelib/kernel/qmetatype.cpp index 7977335c98..87835be25c 100644 --- a/src/corelib/kernel/qmetatype.cpp +++ b/src/corelib/kernel/qmetatype.cpp @@ -1580,6 +1580,10 @@ QMetaTypeConverterRegistry; Q_GLOBAL_STATIC(QMetaTypeConverterRegistry, customTypesConversionRegistry) +using QMetaTypeMutableViewRegistry + = QMetaTypeFunctionRegistry<QMetaType::MutableViewFunction, QPair<int,int>>; +Q_GLOBAL_STATIC(QMetaTypeMutableViewRegistry, customTypesMutableViewRegistry) + /*! \fn bool QMetaType::registerConverter() \since 5.2 @@ -1618,26 +1622,78 @@ Q_GLOBAL_STATIC(QMetaTypeConverterRegistry, customTypesConversionRegistry) \since 5.2 \internal */ -bool QMetaType::registerConverterFunction(const ConverterFunction &f, int from, int to) +bool QMetaType::registerConverterFunction(const ConverterFunction &f, QMetaType from, QMetaType to) { - if (!customTypesConversionRegistry()->insertIfNotContains(qMakePair(from, to), f)) { + if (!customTypesConversionRegistry()->insertIfNotContains(qMakePair(from.id(), to.id()), f)) { qWarning("Type conversion already registered from type %s to type %s", - QMetaType(from).name(), QMetaType(to).name()); + from.name(), to.name()); return false; } return true; } /*! + \fn template<typename MemberFunction, int> bool QMetaType::registerMutableView(MemberFunction function) + \since 6.0 + \overload + Registers a method \a function like \c {To From::function()} as mutable view of type \c {To} on + type \c {From} in the meta type system. Returns \c true if the registration succeeded, otherwise + \c false. +*/ + +/*! + \fn template<typename MemberFunctionOk, char> bool QMetaType::registerMutableView(MemberFunctionOk function) + \since 6.0 + \overload + Registers a method \a function like To From::function(bool *ok) as mutable view of type To on + type From in the meta type system. Returns \c true if the registration succeeded, otherwise + \c false. +*/ + +/*! + \fn template<typename UnaryFunction> bool QMetaType::registerMutableView(UnaryFunction function) + \since 6.0 + \overload + Registers a unary function object \a function as mutable view of type To on type From + in the meta type system. Returns \c true if the registration succeeded, otherwise \c false. +*/ + +/*! + Registers function \a f as mutable view of type id \a to on type id \a from. + Returns \c true if the registration succeeded, otherwise \c false. + \since 6.0 + \internal +*/ +bool QMetaType::registerMutableViewFunction(const MutableViewFunction &f, QMetaType from, QMetaType to) +{ + if (!customTypesMutableViewRegistry()->insertIfNotContains(qMakePair(from.id(), to.id()), f)) { + qWarning("Mutable view on type already registered from type %s to type %s", + from.name(), to.name()); + return false; + } + return true; +} + +/*! + \internal + */ +void QMetaType::unregisterMutableViewFunction(QMetaType from, QMetaType to) +{ + if (customTypesMutableViewRegistry.isDestroyed()) + return; + customTypesMutableViewRegistry()->remove(from.id(), to.id()); +} + +/*! \internal Invoked automatically when a converter function object is destroyed. */ -void QMetaType::unregisterConverterFunction(int from, int to) +void QMetaType::unregisterConverterFunction(QMetaType from, QMetaType to) { if (customTypesConversionRegistry.isDestroyed()) return; - customTypesConversionRegistry()->remove(from, to); + customTypesConversionRegistry()->remove(from.id(), to.id()); } #ifndef QT_NO_DEBUG_STREAM @@ -1833,14 +1889,10 @@ static bool convertToEnum(QMetaType fromType, const void *from, QMetaType toType #ifndef QT_BOOTSTRAPPED static bool convertIterableToVariantList(QMetaType fromType, const void *from, void *to) { - const QMetaType::ConverterFunction * const f = - customTypesConversionRegistry()->function( - qMakePair(fromType.id(), qMetaTypeId<QIterable<QMetaSequence>>())); - if (!f) + QSequentialIterable list; + if (!QMetaType::convert(fromType, from, QMetaType::fromType<QSequentialIterable>(), &list)) return false; - QSequentialIterable list; - (*f)(from, &list); QVariantList &l = *static_cast<QVariantList *>(to); l.clear(); l.reserve(list.size()); @@ -1852,14 +1904,10 @@ static bool convertIterableToVariantList(QMetaType fromType, const void *from, v static bool convertIterableToVariantMap(QMetaType fromType, const void *from, void *to) { - const QMetaType::ConverterFunction * const f = - customTypesConversionRegistry()->function( - qMakePair(fromType.id(), qMetaTypeId<QIterable<QMetaAssociation>>())); - if (!f) + QAssociativeIterable map; + if (!QMetaType::convert(fromType, from, QMetaType::fromType<QAssociativeIterable>(), &map)) return false; - QAssociativeIterable map; - (*f)(from, &map); QVariantMap &h = *static_cast<QVariantMap *>(to); h.clear(); auto end = map.end(); @@ -1870,14 +1918,10 @@ static bool convertIterableToVariantMap(QMetaType fromType, const void *from, vo static bool convertIterableToVariantHash(QMetaType fromType, const void *from, void *to) { - const QMetaType::ConverterFunction * const f = - customTypesConversionRegistry()->function( - qMakePair(fromType.id(), qMetaTypeId<QIterable<QMetaAssociation>>())); - if (!f) + QAssociativeIterable map; + if (!QMetaType::convert(fromType, from, QMetaType::fromType<QAssociativeIterable>(), &map)) return false; - QAssociativeIterable map; - (*f)(from, &map); QVariantHash &h = *static_cast<QVariantHash *>(to); h.clear(); h.reserve(map.size()); @@ -1948,6 +1992,60 @@ static bool convertToSequentialIterable(QMetaType fromType, const void *from, vo return false; } +static bool canConvertToSequentialIterable(QMetaType fromType) +{ + switch (fromType.id()) { + case QMetaType::QVariantList: + case QMetaType::QStringList: + case QMetaType::QByteArrayList: + return true; + default: + return QMetaType::canConvert(fromType, QMetaType::fromType<QIterable<QMetaSequence>>()); + } +} + +static bool canImplicitlyViewAsSequentialIterable(QMetaType fromType) +{ + switch (fromType.id()) { + case QMetaType::QVariantList: + case QMetaType::QStringList: + case QMetaType::QByteArrayList: + return true; + default: + return QMetaType::canView( + fromType, QMetaType::fromType<QIterable<QMetaSequence>>()); + } +} + +static bool viewAsSequentialIterable(QMetaType fromType, void *from, void *to) +{ + using namespace QtMetaTypePrivate; + int fromTypeId = fromType.id(); + + QSequentialIterable &i = *static_cast<QSequentialIterable *>(to); + if (fromTypeId == QMetaType::QVariantList) { + i = QSequentialIterable(reinterpret_cast<QVariantList *>(from)); + return true; + } + if (fromTypeId == QMetaType::QStringList) { + i = QSequentialIterable(reinterpret_cast<QStringList *>(from)); + return true; + } + else if (fromTypeId == QMetaType::QByteArrayList) { + i = QSequentialIterable(reinterpret_cast<QByteArrayList *>(from)); + return true; + } + + QIterable<QMetaSequence> j; + if (QMetaType::view( + fromType, from, QMetaType::fromType<QIterable<QMetaSequence>>(), &j)) { + i = std::move(j); + return true; + } + + return false; +} + static bool convertToAssociativeIterable(QMetaType fromType, const void *from, void *to) { using namespace QtMetaTypePrivate; @@ -1981,6 +2079,74 @@ static bool canConvertMetaObject(QMetaType fromType, QMetaType toType) } return false; } + +static bool canConvertToAssociativeIterable(QMetaType fromType) +{ + switch (fromType.id()) { + case QMetaType::QVariantMap: + case QMetaType::QVariantHash: + return true; + default: + return QMetaType::canConvert(fromType, QMetaType::fromType<QIterable<QMetaAssociation>>()); + } +} + +static bool canImplicitlyViewAsAssociativeIterable(QMetaType fromType) +{ + switch (fromType.id()) { + case QMetaType::QVariantMap: + case QMetaType::QVariantHash: + return true; + default: + return QMetaType::canView( + fromType, QMetaType::fromType<QIterable<QMetaAssociation>>()); + } +} + +static bool viewAsAssociativeIterable(QMetaType fromType, void *from, void *to) +{ + using namespace QtMetaTypePrivate; + int fromTypeId = fromType.id(); + + QAssociativeIterable &i = *static_cast<QAssociativeIterable *>(to); + if (fromTypeId == QMetaType::QVariantMap) { + i = QAssociativeIterable(reinterpret_cast<QVariantMap *>(from)); + return true; + } + if (fromTypeId == QMetaType::QVariantHash) { + i = QAssociativeIterable(reinterpret_cast<QVariantHash *>(from)); + return true; + } + + QIterable<QMetaAssociation> j; + if (QMetaType::view( + fromType, from, QMetaType::fromType<QIterable<QMetaAssociation>>(), &j)) { + i = std::move(j); + return true; + } + + return false; +} + +static bool convertQObject(QMetaType fromType, const void *from, QMetaType toType, void *to) +{ + // handle QObject conversion + if ((fromType.flags() & QMetaType::PointerToQObject) && (toType.flags() & QMetaType::PointerToQObject)) { + QObject *fromObject = *static_cast<QObject * const *>(from); + // use dynamic metatype of from if possible + if (fromObject && fromObject->metaObject()->inherits(toType.metaObject())) { + *static_cast<QObject **>(to) = toType.metaObject()->cast(fromObject); + return true; + } else if (!fromObject && fromType.metaObject()) { + // if fromObject is null, use static fromType to check if conversion works + *static_cast<void **>(to) = nullptr; + return fromType.metaObject()->inherits(toType.metaObject()); + } else { + return false; + } + } + return false; +} #endif /*! @@ -2053,21 +2219,82 @@ bool QMetaType::convert(QMetaType fromType, const void *from, QMetaType toType, if (toTypeId == qMetaTypeId<QAssociativeIterable>()) return convertToAssociativeIterable(fromType, from, to); - // handle QObject conversion - if ((fromType.flags() & QMetaType::PointerToQObject) && (toType.flags() & QMetaType::PointerToQObject)) { - QObject *fromObject = *static_cast<QObject * const *>(from); - // use dynamic metatype of from if possible - if (fromObject && fromObject->metaObject()->inherits(toType.metaObject())) { - *static_cast<QObject **>(to) = toType.metaObject()->cast(fromObject); - return true; - } else if (!fromObject && fromType.metaObject()) { - // if fromObject is null, use static fromType to check if conversion works - *static_cast<void **>(to) = nullptr; - return fromType.metaObject()->inherits(toType.metaObject()); - } else { - return false; - } - } + return convertQObject(fromType, from, toType, to); +#else + return false; +#endif +} + +/*! + Creates a mutable view on the object at \a from of \a fromType in the preallocated space at + \a to typed \a toType. Returns \c true if the conversion succeeded, otherwise false. + \since 6.0 +*/ +bool QMetaType::view(QMetaType fromType, void *from, QMetaType toType, void *to) +{ + if (!fromType.isValid() || !toType.isValid()) + return false; + + int fromTypeId = fromType.id(); + int toTypeId = toType.id(); + + const QMetaType::MutableViewFunction * const f = + customTypesMutableViewRegistry()->function(qMakePair(fromTypeId, toTypeId)); + if (f) + return (*f)(from, to); + +#ifndef QT_BOOTSTRAPPED + if (toTypeId == qMetaTypeId<QSequentialIterable>()) + return viewAsSequentialIterable(fromType, from, to); + + if (toTypeId == qMetaTypeId<QAssociativeIterable>()) + return viewAsAssociativeIterable(fromType, from, to); + + return convertQObject(fromType, from, toType, to); +#else + return false; +#endif +} + +/*! + Returns \c true if QMetaType::view can create a mutable view of type \a toType + on type \a fromType. + + Converting between pointers of types derived from QObject will return true for this + function if a qobject_cast from the type described by \a fromType to the type described + by \a toType would succeed. + + You can create a mutable view of type QSequentialIterable on any container registered with + Q_DECLARE_SEQUENTIAL_CONTAINER_METATYPE(). + + Similarly you can create a mutable view of type QAssociativeIterable on any container + registered with Q_DECLARE_ASSOCIATIVE_CONTAINER_METATYPE(). + + \sa convert(), QSequentialIterable, Q_DECLARE_SEQUENTIAL_CONTAINER_METATYPE(), + QAssociativeIterable, Q_DECLARE_ASSOCIATIVE_CONTAINER_METATYPE() +*/ +bool QMetaType::canView(QMetaType fromType, QMetaType toType) +{ + int fromTypeId = fromType.id(); + int toTypeId = toType.id(); + + if (fromTypeId == UnknownType || toTypeId == UnknownType) + return false; + + const MutableViewFunction * const f = + customTypesMutableViewRegistry()->function(qMakePair(fromTypeId, toTypeId)); + if (f) + return true; + +#ifndef QT_BOOTSTRAPPED + if (toTypeId == qMetaTypeId<QSequentialIterable>()) + return canImplicitlyViewAsSequentialIterable(fromType); + + if (toTypeId == qMetaTypeId<QAssociativeIterable>()) + return canImplicitlyViewAsAssociativeIterable(fromType); + + if (canConvertMetaObject(fromType, toType)) + return true; #endif return false; @@ -2165,17 +2392,25 @@ bool QMetaType::canConvert(QMetaType fromType, QMetaType toType) return true; #ifndef QT_BOOTSTRAPPED - if (toTypeId == QVariantList && hasRegisteredConverterFunction( - fromTypeId, qMetaTypeId<QIterable<QMetaSequence>>())) + if (toTypeId == qMetaTypeId<QSequentialIterable>()) + return canConvertToSequentialIterable(fromType); + + if (toTypeId == qMetaTypeId<QAssociativeIterable>()) + return canConvertToAssociativeIterable(fromType); + + if (toTypeId == QVariantList + && canConvert(fromType, QMetaType::fromType<QSequentialIterable>())) { return true; + } - if ((toTypeId == QVariantHash || toTypeId == QVariantMap) && hasRegisteredConverterFunction( - fromTypeId, qMetaTypeId<QIterable<QMetaAssociation>>())) + if ((toTypeId == QVariantHash || toTypeId == QVariantMap) + && canConvert(fromType, QMetaType::fromType<QAssociativeIterable>())) { return true; + } #endif if (toTypeId == QVariantPair && hasRegisteredConverterFunction( - fromTypeId, qMetaTypeId<QtMetaTypePrivate::QPairVariantInterfaceImpl>())) + fromType, QMetaType::fromType<QtMetaTypePrivate::QPairVariantInterfaceImpl>())) return true; if (fromType.flags() & IsEnumeration) { @@ -2219,9 +2454,26 @@ bool QMetaType::canConvert(QMetaType fromType, QMetaType toType) to \a toTypeId \since 5.2 */ -bool QMetaType::hasRegisteredConverterFunction(int fromTypeId, int toTypeId) +bool QMetaType::hasRegisteredConverterFunction(QMetaType fromType, QMetaType toType) +{ + return customTypesConversionRegistry()->contains(qMakePair(fromType.id(), toType.id())); +} + +/*! + \fn bool QMetaType::hasRegisteredMutableViewFunction() + Returns \c true, if the meta type system has a registered mutable view on type From of type To. + \since 6.0 + \overload +*/ + +/*! + Returns \c true, if the meta type system has a registered mutable view on meta type id + \a fromTypeId of meta type id \a toTypeId. + \since 5.2 +*/ +bool QMetaType::hasRegisteredMutableViewFunction(QMetaType fromType, QMetaType toType) { - return customTypesConversionRegistry()->contains(qMakePair(fromTypeId, toTypeId)); + return customTypesMutableViewRegistry()->contains(qMakePair(fromType.id(), toType.id())); } /*! |