diff options
-rw-r--r-- | src/corelib/kernel/qiterable.h | 4 | ||||
-rw-r--r-- | src/corelib/kernel/qmetatype.cpp | 342 | ||||
-rw-r--r-- | src/corelib/kernel/qmetatype.h | 230 | ||||
-rw-r--r-- | src/corelib/kernel/qvariant.cpp | 27 | ||||
-rw-r--r-- | src/corelib/kernel/qvariant.h | 18 | ||||
-rw-r--r-- | tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp | 45 |
6 files changed, 559 insertions, 107 deletions
diff --git a/src/corelib/kernel/qiterable.h b/src/corelib/kernel/qiterable.h index 8a230d5f0e..20d937591a 100644 --- a/src/corelib/kernel/qiterable.h +++ b/src/corelib/kernel/qiterable.h @@ -501,9 +501,7 @@ public: } const void *constIterable() const { return m_iterable.constPointer(); } - - // TODO: fix this when introducing mutable iterables - void *mutableIterable() { return const_cast<void *>(m_iterable.constPointer()); } + void *mutableIterable() { return m_iterable.mutablePointer(); } QConstIterator<Container> constBegin() const { 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())); } /*! diff --git a/src/corelib/kernel/qmetatype.h b/src/corelib/kernel/qmetatype.h index f28bfead5c..971388fabd 100644 --- a/src/corelib/kernel/qmetatype.h +++ b/src/corelib/kernel/qmetatype.h @@ -465,6 +465,9 @@ public: // type erased converter function using ConverterFunction = std::function<bool(const void *src, void *target)>; + // type erased mutable view, primarily for containers + using MutableViewFunction = std::function<bool(void *src, void *target)>; + // implicit conversion supported like double -> float template<typename From, typename To> static bool registerConverter() @@ -479,6 +482,13 @@ public: static bool registerConverter(MemberFunctionOk function); template<typename UnaryFunction> static bool registerConverter(UnaryFunction function); + + template<typename MemberFunction, int> + static bool registerMutableView(MemberFunction function); + template<typename MemberFunctionOk, char> + static bool registerMutableView(MemberFunctionOk function); + template<typename UnaryFunction> + static bool registerMutableView(UnaryFunction function); #else // member function as in "QString QFont::toString() const" template<typename From, typename To> @@ -487,15 +497,33 @@ public: static_assert((!QMetaTypeId2<To>::IsBuiltIn || !QMetaTypeId2<From>::IsBuiltIn), "QMetaType::registerConverter: At least one of the types must be a custom type."); - const int fromTypeId = qMetaTypeId<From>(); - const int toTypeId = qMetaTypeId<To>(); + const QMetaType fromType = QMetaType::fromType<From>(); + const QMetaType toType = QMetaType::fromType<To>(); auto converter = [function](const void *from, void *to) -> bool { const From *f = static_cast<const From *>(from); To *t = static_cast<To *>(to); *t = (f->*function)(); return true; }; - return registerConverterFunction(converter, fromTypeId, toTypeId); + return registerConverterFunction(converter, fromType, toType); + } + + // member function + template<typename From, typename To> + static bool registerMutableView(To(From::*function)()) + { + static_assert((!QMetaTypeId2<To>::IsBuiltIn || !QMetaTypeId2<From>::IsBuiltIn), + "QMetaType::registerMutableView: At least one of the types must be a custom type."); + + const QMetaType fromType = QMetaType::fromType<From>(); + const QMetaType toType = QMetaType::fromType<To>(); + auto view = [function](void *from, void *to) -> bool { + From *f = static_cast<From *>(from); + To *t = static_cast<To *>(to); + *t = (f->*function)(); + return true; + }; + return registerMutableViewFunction(view, fromType, toType); } // member function as in "double QString::toDouble(bool *ok = nullptr) const" @@ -505,8 +533,8 @@ public: static_assert((!QMetaTypeId2<To>::IsBuiltIn || !QMetaTypeId2<From>::IsBuiltIn), "QMetaType::registerConverter: At least one of the types must be a custom type."); - const int fromTypeId = qMetaTypeId<From>(); - const int toTypeId = qMetaTypeId<To>(); + const QMetaType fromType = QMetaType::fromType<From>(); + const QMetaType toType = QMetaType::fromType<To>(); auto converter = [function](const void *from, void *to) -> bool { const From *f = static_cast<const From *>(from); To *t = static_cast<To *>(to); @@ -516,7 +544,7 @@ public: *t = To(); return result; }; - return registerConverterFunction(converter, fromTypeId, toTypeId); + return registerConverterFunction(converter, fromType, toType); } // functor or function pointer @@ -526,20 +554,41 @@ public: static_assert((!QMetaTypeId2<To>::IsBuiltIn || !QMetaTypeId2<From>::IsBuiltIn), "QMetaType::registerConverter: At least one of the types must be a custom type."); - const int fromTypeId = qMetaTypeId<From>(); - const int toTypeId = qMetaTypeId<To>(); + const QMetaType fromType = QMetaType::fromType<From>(); + const QMetaType toType = QMetaType::fromType<To>(); auto converter = [function](const void *from, void *to) -> bool { const From *f = static_cast<const From *>(from); To *t = static_cast<To *>(to); *t = function(*f); return true; }; - return registerConverterFunction(converter, fromTypeId, toTypeId); + return registerConverterFunction(converter, fromType, toType); + } + + // functor or function pointer + template<typename From, typename To, typename UnaryFunction> + static bool registerMutableView(UnaryFunction function) + { + static_assert((!QMetaTypeId2<To>::IsBuiltIn || !QMetaTypeId2<From>::IsBuiltIn), + "QMetaType::registerMutableView: At least one of the types must be a custom type."); + + const QMetaType fromType = QMetaType::fromType<From>(); + const QMetaType toType = QMetaType::fromType<To>(); + auto view = [function](void *from, void *to) -> bool { + From *f = static_cast<From *>(from); + To *t = static_cast<To *>(to); + *t = function(*f); + return true; + }; + return registerMutableViewFunction(view, fromType, toType); } #endif static bool convert(QMetaType fromType, const void *from, QMetaType toType, void *to); static bool canConvert(QMetaType fromType, QMetaType toType); + + static bool view(QMetaType fromType, void *from, QMetaType toType, void *to); + static bool canView(QMetaType fromType, QMetaType toType); #if QT_DEPRECATED_SINCE(6, 0) QT_DEPRECATED_VERSION_6_0 static bool convert(const void *from, int fromTypeId, void *to, int toTypeId) @@ -570,10 +619,20 @@ public: template<typename From, typename To> static bool hasRegisteredConverterFunction() { - return hasRegisteredConverterFunction(qMetaTypeId<From>(), qMetaTypeId<To>()); + return hasRegisteredConverterFunction( + QMetaType::fromType<From>(), QMetaType::fromType<To>()); } - static bool hasRegisteredConverterFunction(int fromTypeId, int toTypeId); + static bool hasRegisteredConverterFunction(QMetaType fromType, QMetaType toType); + + template<typename From, typename To> + static bool hasRegisteredMutableViewFunction() + { + return hasRegisteredMutableViewFunction( + QMetaType::fromType<From>(), QMetaType::fromType<To>()); + } + + static bool hasRegisteredMutableViewFunction(QMetaType fromType, QMetaType toType); #ifndef Q_CLANG_QDOC template<typename, bool> friend struct QtPrivate::SequentialValueTypeIsMetaType; @@ -581,8 +640,11 @@ public: template<typename, bool> friend struct QtPrivate::IsMetaTypePair; template<typename, typename> friend struct QtPrivate::MetaTypeSmartPointerHelper; #endif - static bool registerConverterFunction(const ConverterFunction &f, int from, int to); - static void unregisterConverterFunction(int from, int to); + static bool registerConverterFunction(const ConverterFunction &f, QMetaType from, QMetaType to); + static void unregisterConverterFunction(QMetaType from, QMetaType to); + + static bool registerMutableViewFunction(const MutableViewFunction &f, QMetaType from, QMetaType to); + static void unregisterMutableViewFunction(QMetaType from, QMetaType to); static void unregisterMetaType(QMetaType type); QtPrivate::QMetaTypeInterface *iface() { return d_ptr; } @@ -833,9 +895,14 @@ namespace QtPrivate }; template<typename T, bool = QtPrivate::IsSequentialContainer<T>::Value> - struct SequentialContainerConverterHelper + struct SequentialContainerTransformationHelper { - static bool registerConverter(int) + static bool registerConverter() + { + return false; + } + + static bool registerMutableView() { return false; } @@ -844,51 +911,66 @@ namespace QtPrivate template<typename T, bool = QMetaTypeId2<typename T::value_type>::Defined> struct SequentialValueTypeIsMetaType { - static bool registerConverter(int) + static bool registerConverter() + { + return false; + } + + static bool registerMutableView() { return false; } }; template<typename T> - struct SequentialContainerConverterHelper<T, true> : SequentialValueTypeIsMetaType<T> + struct SequentialContainerTransformationHelper<T, true> : SequentialValueTypeIsMetaType<T> { }; template<typename T, bool = QtPrivate::IsAssociativeContainer<T>::Value> - struct AssociativeContainerConverterHelper + struct AssociativeContainerTransformationHelper { - static bool registerConverter(int) + static bool registerConverter() { return false; } - }; - template<typename T, bool = QMetaTypeId2<typename T::mapped_type>::Defined> - struct AssociativeValueTypeIsMetaType - { - static bool registerConverter(int) + static bool registerMutableView() { return false; } }; template<typename T, bool = QMetaTypeId2<typename T::key_type>::Defined> - struct KeyAndValueTypeIsMetaType + struct AssociativeKeyTypeIsMetaType { - static bool registerConverter(int) + static bool registerConverter() + { + return false; + } + + static bool registerMutableView() { return false; } }; - template<typename T> - struct KeyAndValueTypeIsMetaType<T, true> : AssociativeValueTypeIsMetaType<T> + template<typename T, bool = QMetaTypeId2<typename T::mapped_type>::Defined> + struct AssociativeMappedTypeIsMetaType { + static bool registerConverter() + { + return false; + } + + static bool registerMutableView() + { + return false; + } }; template<typename T> - struct AssociativeContainerConverterHelper<T, true> : KeyAndValueTypeIsMetaType<T> + struct AssociativeContainerTransformationHelper<T, true> : AssociativeKeyTypeIsMetaType<T> { }; @@ -896,7 +978,7 @@ namespace QtPrivate && QMetaTypeId2<typename T::second_type>::Defined> struct IsMetaTypePair { - static bool registerConverter(int) + static bool registerConverter() { return false; } @@ -905,13 +987,13 @@ namespace QtPrivate template<typename T> struct IsMetaTypePair<T, true> { - inline static bool registerConverter(int id); + inline static bool registerConverter(); }; template<typename T> struct IsPair { - static bool registerConverter(int) + static bool registerConverter() { return false; } @@ -925,7 +1007,7 @@ namespace QtPrivate template<typename T, typename = void> struct MetaTypeSmartPointerHelper { - static bool registerConverter(int) { return false; } + static bool registerConverter() { return false; } }; Q_CORE_EXPORT bool isBuiltinType(const QByteArray &type); @@ -1042,10 +1124,12 @@ int qRegisterNormalizedMetaType(const QT_PREPEND_NAMESPACE(QByteArray) &normaliz if (id > 0) { QMetaType::registerNormalizedTypedef(normalizedTypeName, metaType); - QtPrivate::SequentialContainerConverterHelper<T>::registerConverter(id); - QtPrivate::AssociativeContainerConverterHelper<T>::registerConverter(id); - QtPrivate::MetaTypePairHelper<T>::registerConverter(id); - QtPrivate::MetaTypeSmartPointerHelper<T>::registerConverter(id); + QtPrivate::SequentialContainerTransformationHelper<T>::registerConverter(); + QtPrivate::SequentialContainerTransformationHelper<T>::registerMutableView(); + QtPrivate::AssociativeContainerTransformationHelper<T>::registerConverter(); + QtPrivate::AssociativeContainerTransformationHelper<T>::registerMutableView(); + QtPrivate::MetaTypePairHelper<T>::registerConverter(); + QtPrivate::MetaTypeSmartPointerHelper<T>::registerConverter(); } return id; @@ -1338,10 +1422,10 @@ template<typename T> \ struct MetaTypeSmartPointerHelper<SMART_POINTER<T> , \ typename std::enable_if<IsPointerToTypeDerivedFromQObject<T*>::Value>::type> \ { \ - static bool registerConverter(int id) \ + static bool registerConverter() \ { \ - const int toId = QMetaType::QObjectStar; \ - if (!QMetaType::hasRegisteredConverterFunction(id, toId)) { \ + const QMetaType to = QMetaType(QMetaType::QObjectStar); \ + if (!QMetaType::hasRegisteredConverterFunction(QMetaType::fromType<SMART_POINTER<T>>(), to)) { \ QtPrivate::QSmartPointerConvertFunctor<SMART_POINTER<T> > o; \ return QMetaType::registerConverter<SMART_POINTER<T>, QObject*>(o); \ } \ @@ -1421,10 +1505,10 @@ Q_DECLARE_METATYPE(QtMetaTypePrivate::QPairVariantInterfaceImpl) QT_BEGIN_NAMESPACE template <typename T> -inline bool QtPrivate::IsMetaTypePair<T, true>::registerConverter(int id) +inline bool QtPrivate::IsMetaTypePair<T, true>::registerConverter() { - const int toId = qMetaTypeId<QtMetaTypePrivate::QPairVariantInterfaceImpl>(); - if (!QMetaType::hasRegisteredConverterFunction(id, toId)) { + const QMetaType to = QMetaType::fromType<QtMetaTypePrivate::QPairVariantInterfaceImpl>(); + if (!QMetaType::hasRegisteredConverterFunction(QMetaType::fromType<T>(), to)) { QtMetaTypePrivate::QPairVariantInterfaceConvertFunctor<T> o; return QMetaType::registerConverter<T, QtMetaTypePrivate::QPairVariantInterfaceImpl>(o); } @@ -1442,18 +1526,37 @@ struct QSequentialIterableConvertFunctor } }; +template<typename From> +struct QSequentialIterableMutableViewFunctor +{ + QIterable<QMetaSequence> operator()(From &f) const + { + return QIterable<QMetaSequence>(QMetaSequence::fromContainer<From>(), &f); + } +}; + template<typename T> struct SequentialValueTypeIsMetaType<T, true> { - static bool registerConverter(int id) + static bool registerConverter() { - const int toId = qMetaTypeId<QIterable<QMetaSequence>>(); - if (!QMetaType::hasRegisteredConverterFunction(id, toId)) { + const QMetaType to = QMetaType::fromType<QIterable<QMetaSequence>>(); + if (!QMetaType::hasRegisteredConverterFunction(QMetaType::fromType<T>(), to)) { QSequentialIterableConvertFunctor<T> o; return QMetaType::registerConverter<T, QIterable<QMetaSequence>>(o); } return true; - } + } + + static bool registerMutableView() + { + const QMetaType to = QMetaType::fromType<QIterable<QMetaSequence>>(); + if (!QMetaType::hasRegisteredMutableViewFunction(QMetaType::fromType<T>(), to)) { + QSequentialIterableMutableViewFunctor<T> o; + return QMetaType::registerMutableView<T, QIterable<QMetaSequence>>(o); + } + return true; + } }; template<typename From> @@ -1465,23 +1568,40 @@ struct QAssociativeIterableConvertFunctor } }; +template<typename From> +struct QAssociativeIterableMutableViewFunctor +{ + QIterable<QMetaAssociation> operator()(From &f) const + { + return QIterable<QMetaAssociation>(QMetaAssociation::fromContainer<From>(), &f); + } +}; + +// Mapped type can be omitted, for example in case of a set. +// However, if it is available, we want to instantiate the metatype here. template<typename T> -struct AssociativeValueTypeIsMetaType<T, true> +struct AssociativeKeyTypeIsMetaType<T, true> : AssociativeMappedTypeIsMetaType<T> { - static bool registerConverter(int id) + static bool registerConverter() { - const int toId = qMetaTypeId<QIterable<QMetaAssociation>>(); - if (!QMetaType::hasRegisteredConverterFunction(id, toId)) { + const QMetaType to = QMetaType::fromType<QIterable<QMetaAssociation>>(); + if (!QMetaType::hasRegisteredConverterFunction(QMetaType::fromType<T>(), to)) { QAssociativeIterableConvertFunctor<T> o; return QMetaType::registerConverter<T, QIterable<QMetaAssociation>>(o); } return true; } -}; -} - -namespace QtPrivate { + static bool registerMutableView() + { + const QMetaType to = QMetaType::fromType<QIterable<QMetaAssociation>>(); + if (!QMetaType::hasRegisteredMutableViewFunction(QMetaType::fromType<T>(), to)) { + QAssociativeIterableMutableViewFunctor<T> o; + return QMetaType::registerMutableView<T, QIterable<QMetaAssociation>>(o); + } + return true; + } +}; class QMetaTypeInterface { diff --git a/src/corelib/kernel/qvariant.cpp b/src/corelib/kernel/qvariant.cpp index 603c49ebfe..65cdfc8580 100644 --- a/src/corelib/kernel/qvariant.cpp +++ b/src/corelib/kernel/qvariant.cpp @@ -2068,6 +2068,13 @@ bool QVariant::convert(int type, void *ptr) const return QMetaType::convert(d.type(), constData(), QMetaType(type), ptr); } +/*! + \internal +*/ +bool QVariant::view(int type, void *ptr) +{ + return QMetaType::view(d.type(), data(), QMetaType(type), ptr); +} /*! \fn bool operator==(const QVariant &v1, const QVariant &v2) @@ -2457,6 +2464,18 @@ QDebug operator<<(QDebug dbg, const QVariant::Type p) \sa setValue(), fromValue(), canConvert(), Q_DECLARE_SEQUENTIAL_CONTAINER_METATYPE() */ +/*! \fn template<typename T> T QVariant::view() + + Returns a mutable view of template type \c{T} on the stored value. + Call canView() to find out whether such a view is supported. + If no such view can be created, returns the stored value converted to the + template type \c{T}. Call canConvert() to find out whether a type can be + converted. If the value can neither be viewed nor converted, a + \l{default-constructed value} will be returned. + + \sa canView(), Q_DECLARE_SEQUENTIAL_CONTAINER_METATYPE() +*/ + /*! \fn bool QVariant::canConvert() const Returns \c true if the variant can be converted to the template type \c{T}, @@ -2473,6 +2492,14 @@ QDebug operator<<(QDebug dbg, const QVariant::Type p) \sa convert() */ +/*! \fn bool QVariant::canView() const + + Returns \c true if a mutable view of the template type \c{T} can be created on this variant, + otherwise \c false. + + \sa value() +*/ + /*! \fn template<typename T> static QVariant QVariant::fromValue(const T &value) Returns a QVariant containing a copy of \a value. Behaves diff --git a/src/corelib/kernel/qvariant.h b/src/corelib/kernel/qvariant.h index 1825d4be88..83cb40a7c7 100644 --- a/src/corelib/kernel/qvariant.h +++ b/src/corelib/kernel/qvariant.h @@ -248,6 +248,9 @@ class Q_CORE_EXPORT QVariant { return QMetaType::canConvert(d.type(), targetType); } bool convert(QMetaType type); + bool canView(QMetaType targetType) const + { return QMetaType::canView(d.type(), targetType); } + #if QT_DEPRECATED_SINCE(6, 0) QT_DEPRECATED_VERSION_6_0 bool canConvert(int targetTypeId) const @@ -364,6 +367,14 @@ class Q_CORE_EXPORT QVariant { return qvariant_cast<T>(*this); } template<typename T> + inline T view() + { + T t{}; + QMetaType::view(metaType(), data(), QMetaType::fromType<T>(), &t); + return t; + } + + template<typename T> static inline QVariant fromValue(const T &value) { return QVariant(QMetaType::fromType<T>(), std::addressof(value)); @@ -383,6 +394,10 @@ class Q_CORE_EXPORT QVariant bool canConvert() const { return canConvert(QMetaType::fromType<T>()); } + template<typename T> + bool canView() const + { return canView(QMetaType::fromType<T>()); } + public: struct PrivateShared { @@ -488,7 +503,8 @@ protected: Private d; void create(int type, const void *copy); bool equals(const QVariant &other) const; - bool convert(int t, void *ptr) const; + bool convert(int type, void *ptr) const; + bool view(int type, void *ptr); private: // force compile error, prevent QVariant(bool) to be called diff --git a/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp b/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp index 4190a0cb9f..8122220b88 100644 --- a/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp +++ b/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp @@ -278,6 +278,7 @@ private slots: void sequentialIterableAppend(); void preferDirectConversionOverInterfaces(); + void mutableView(); private: void dataStream_data(QDataStream::Version version); @@ -4173,7 +4174,7 @@ void testSequentialIteration() QVERIFY(listVariant.canConvert<QVariantList>()); QVariantList varList = listVariant.value<QVariantList>(); QCOMPARE(varList.size(), (int)std::distance(sequence.begin(), sequence.end())); - QSequentialIterable listIter = listVariant.value<QSequentialIterable>(); + QSequentialIterable listIter = listVariant.view<QSequentialIterable>(); QCOMPARE(varList.size(), listIter.size()); typename Container::iterator containerIter = sequence.begin(); @@ -4758,7 +4759,7 @@ void tst_QVariant::sequentialIterableAppend() QList<int> container { 1, 2 }; auto variant = QVariant::fromValue(container); QVERIFY(variant.canConvert<QIterable<QMetaSequence>>()); - QSequentialIterable asIterable = variant.value<QIterable<QMetaSequence>>(); + QSequentialIterable asIterable = variant.view<QIterable<QMetaSequence>>(); const int i = 3, j = 4; void *mutableIterable = asIterable.mutableIterable(); asIterable.metaContainer().addValueAtEnd(mutableIterable, &i); @@ -4778,7 +4779,7 @@ void tst_QVariant::sequentialIterableAppend() QSet<QByteArray> container { QByteArray{"hello"}, QByteArray{"world"} }; auto variant = QVariant::fromValue(std::move(container)); QVERIFY(variant.canConvert<QIterable<QMetaSequence>>()); - QSequentialIterable asIterable = variant.value<QIterable<QMetaSequence>>(); + QSequentialIterable asIterable = variant.view<QIterable<QMetaSequence>>(); QByteArray qba1 {"goodbye"}; QByteArray qba2 { "moon" }; void *mutableIterable = asIterable.mutableIterable(); @@ -4834,5 +4835,43 @@ void tst_QVariant::preferDirectConversionOverInterfaces() QVERIFY(calledCorrectConverter); } +struct MyTypeView +{ + MyType *data; +}; + +void tst_QVariant::mutableView() +{ + bool calledView = false; + const bool success = QMetaType::registerMutableView<MyType, MyTypeView>([&](MyType &data) { + calledView = true; + return MyTypeView { &data }; + }); + QVERIFY(success); + + QTest::ignoreMessage( + QtWarningMsg, + "Mutable view on type already registered from type MyType to type MyTypeView"); + const bool shouldFail = QMetaType::registerMutableView<MyType, MyTypeView>([&](MyType &) { + return MyTypeView { nullptr }; + }); + QVERIFY(!shouldFail); + + auto original = QVariant::fromValue(MyType {}); + + QVERIFY(original.canView<MyTypeView>()); + QVERIFY(!original.canConvert<MyTypeView>()); + + MyTypeView view = original.view<MyTypeView>(); + QVERIFY(calledView); + const char *txt = "lll"; + view.data->number = 113; + view.data->text = txt; + + MyType extracted = original.view<MyType>(); + QCOMPARE(extracted.number, 0); + QCOMPARE(extracted.text, nullptr); +} + QTEST_MAIN(tst_QVariant) #include "tst_qvariant.moc" |