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(m_iterable.constPointer()); } + void *mutableIterable() { return m_iterable.mutablePointer(); } QConstIterator 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>; +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 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 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 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>())); - if (!f) + QSequentialIterable list; + if (!QMetaType::convert(fromType, from, QMetaType::fromType(), &list)) return false; - QSequentialIterable list; - (*f)(from, &list); QVariantList &l = *static_cast(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>())); - if (!f) + QAssociativeIterable map; + if (!QMetaType::convert(fromType, from, QMetaType::fromType(), &map)) return false; - QAssociativeIterable map; - (*f)(from, &map); QVariantMap &h = *static_cast(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>())); - if (!f) + QAssociativeIterable map; + if (!QMetaType::convert(fromType, from, QMetaType::fromType(), &map)) return false; - QAssociativeIterable map; - (*f)(from, &map); QVariantHash &h = *static_cast(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>()); + } +} + +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>()); + } +} + +static bool viewAsSequentialIterable(QMetaType fromType, void *from, void *to) +{ + using namespace QtMetaTypePrivate; + int fromTypeId = fromType.id(); + + QSequentialIterable &i = *static_cast(to); + if (fromTypeId == QMetaType::QVariantList) { + i = QSequentialIterable(reinterpret_cast(from)); + return true; + } + if (fromTypeId == QMetaType::QStringList) { + i = QSequentialIterable(reinterpret_cast(from)); + return true; + } + else if (fromTypeId == QMetaType::QByteArrayList) { + i = QSequentialIterable(reinterpret_cast(from)); + return true; + } + + QIterable j; + if (QMetaType::view( + fromType, from, QMetaType::fromType>(), &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>()); + } +} + +static bool canImplicitlyViewAsAssociativeIterable(QMetaType fromType) +{ + switch (fromType.id()) { + case QMetaType::QVariantMap: + case QMetaType::QVariantHash: + return true; + default: + return QMetaType::canView( + fromType, QMetaType::fromType>()); + } +} + +static bool viewAsAssociativeIterable(QMetaType fromType, void *from, void *to) +{ + using namespace QtMetaTypePrivate; + int fromTypeId = fromType.id(); + + QAssociativeIterable &i = *static_cast(to); + if (fromTypeId == QMetaType::QVariantMap) { + i = QAssociativeIterable(reinterpret_cast(from)); + return true; + } + if (fromTypeId == QMetaType::QVariantHash) { + i = QAssociativeIterable(reinterpret_cast(from)); + return true; + } + + QIterable j; + if (QMetaType::view( + fromType, from, QMetaType::fromType>(), &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(from); + // use dynamic metatype of from if possible + if (fromObject && fromObject->metaObject()->inherits(toType.metaObject())) { + *static_cast(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(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()) return convertToAssociativeIterable(fromType, from, to); - // handle QObject conversion - if ((fromType.flags() & QMetaType::PointerToQObject) && (toType.flags() & QMetaType::PointerToQObject)) { - QObject *fromObject = *static_cast(from); - // use dynamic metatype of from if possible - if (fromObject && fromObject->metaObject()->inherits(toType.metaObject())) { - *static_cast(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(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()) + return viewAsSequentialIterable(fromType, from, to); + + if (toTypeId == qMetaTypeId()) + 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()) + return canImplicitlyViewAsSequentialIterable(fromType); + + if (toTypeId == qMetaTypeId()) + 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>())) - return true; + if (toTypeId == qMetaTypeId()) + return canConvertToSequentialIterable(fromType); - if ((toTypeId == QVariantHash || toTypeId == QVariantMap) && hasRegisteredConverterFunction( - fromTypeId, qMetaTypeId>())) + if (toTypeId == qMetaTypeId()) + return canConvertToAssociativeIterable(fromType); + + if (toTypeId == QVariantList + && canConvert(fromType, QMetaType::fromType())) { return true; + } + + if ((toTypeId == QVariantHash || toTypeId == QVariantMap) + && canConvert(fromType, QMetaType::fromType())) { + return true; + } #endif if (toTypeId == QVariantPair && hasRegisteredConverterFunction( - fromTypeId, qMetaTypeId())) + fromType, QMetaType::fromType())) 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(fromTypeId, toTypeId)); + 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 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; + // type erased mutable view, primarily for containers + using MutableViewFunction = std::function; + // implicit conversion supported like double -> float template static bool registerConverter() @@ -479,6 +482,13 @@ public: static bool registerConverter(MemberFunctionOk function); template static bool registerConverter(UnaryFunction function); + + template + static bool registerMutableView(MemberFunction function); + template + static bool registerMutableView(MemberFunctionOk function); + template + static bool registerMutableView(UnaryFunction function); #else // member function as in "QString QFont::toString() const" template @@ -487,15 +497,33 @@ public: static_assert((!QMetaTypeId2::IsBuiltIn || !QMetaTypeId2::IsBuiltIn), "QMetaType::registerConverter: At least one of the types must be a custom type."); - const int fromTypeId = qMetaTypeId(); - const int toTypeId = qMetaTypeId(); + const QMetaType fromType = QMetaType::fromType(); + const QMetaType toType = QMetaType::fromType(); auto converter = [function](const void *from, void *to) -> bool { const From *f = static_cast(from); To *t = static_cast(to); *t = (f->*function)(); return true; }; - return registerConverterFunction(converter, fromTypeId, toTypeId); + return registerConverterFunction(converter, fromType, toType); + } + + // member function + template + static bool registerMutableView(To(From::*function)()) + { + static_assert((!QMetaTypeId2::IsBuiltIn || !QMetaTypeId2::IsBuiltIn), + "QMetaType::registerMutableView: At least one of the types must be a custom type."); + + const QMetaType fromType = QMetaType::fromType(); + const QMetaType toType = QMetaType::fromType(); + auto view = [function](void *from, void *to) -> bool { + From *f = static_cast(from); + To *t = static_cast(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::IsBuiltIn || !QMetaTypeId2::IsBuiltIn), "QMetaType::registerConverter: At least one of the types must be a custom type."); - const int fromTypeId = qMetaTypeId(); - const int toTypeId = qMetaTypeId(); + const QMetaType fromType = QMetaType::fromType(); + const QMetaType toType = QMetaType::fromType(); auto converter = [function](const void *from, void *to) -> bool { const From *f = static_cast(from); To *t = static_cast(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::IsBuiltIn || !QMetaTypeId2::IsBuiltIn), "QMetaType::registerConverter: At least one of the types must be a custom type."); - const int fromTypeId = qMetaTypeId(); - const int toTypeId = qMetaTypeId(); + const QMetaType fromType = QMetaType::fromType(); + const QMetaType toType = QMetaType::fromType(); auto converter = [function](const void *from, void *to) -> bool { const From *f = static_cast(from); To *t = static_cast(to); *t = function(*f); return true; }; - return registerConverterFunction(converter, fromTypeId, toTypeId); + return registerConverterFunction(converter, fromType, toType); + } + + // functor or function pointer + template + static bool registerMutableView(UnaryFunction function) + { + static_assert((!QMetaTypeId2::IsBuiltIn || !QMetaTypeId2::IsBuiltIn), + "QMetaType::registerMutableView: At least one of the types must be a custom type."); + + const QMetaType fromType = QMetaType::fromType(); + const QMetaType toType = QMetaType::fromType(); + auto view = [function](void *from, void *to) -> bool { + From *f = static_cast(from); + To *t = static_cast(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 static bool hasRegisteredConverterFunction() { - return hasRegisteredConverterFunction(qMetaTypeId(), qMetaTypeId()); + return hasRegisteredConverterFunction( + QMetaType::fromType(), QMetaType::fromType()); } - static bool hasRegisteredConverterFunction(int fromTypeId, int toTypeId); + static bool hasRegisteredConverterFunction(QMetaType fromType, QMetaType toType); + + template + static bool hasRegisteredMutableViewFunction() + { + return hasRegisteredMutableViewFunction( + QMetaType::fromType(), QMetaType::fromType()); + } + + static bool hasRegisteredMutableViewFunction(QMetaType fromType, QMetaType toType); #ifndef Q_CLANG_QDOC template friend struct QtPrivate::SequentialValueTypeIsMetaType; @@ -581,8 +640,11 @@ public: template friend struct QtPrivate::IsMetaTypePair; template 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::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::Defined> struct SequentialValueTypeIsMetaType { - static bool registerConverter(int) + static bool registerConverter() + { + return false; + } + + static bool registerMutableView() { return false; } }; template - struct SequentialContainerConverterHelper : SequentialValueTypeIsMetaType + struct SequentialContainerTransformationHelper : SequentialValueTypeIsMetaType { }; template::Value> - struct AssociativeContainerConverterHelper + struct AssociativeContainerTransformationHelper { - static bool registerConverter(int) + static bool registerConverter() { return false; } - }; - template::Defined> - struct AssociativeValueTypeIsMetaType - { - static bool registerConverter(int) + static bool registerMutableView() { return false; } }; template::Defined> - struct KeyAndValueTypeIsMetaType + struct AssociativeKeyTypeIsMetaType { - static bool registerConverter(int) + static bool registerConverter() + { + return false; + } + + static bool registerMutableView() + { + return false; + } + }; + + template::Defined> + struct AssociativeMappedTypeIsMetaType + { + static bool registerConverter() + { + return false; + } + + static bool registerMutableView() { return false; } }; template - struct KeyAndValueTypeIsMetaType : AssociativeValueTypeIsMetaType - { - }; - - template - struct AssociativeContainerConverterHelper : KeyAndValueTypeIsMetaType + struct AssociativeContainerTransformationHelper : AssociativeKeyTypeIsMetaType { }; @@ -896,7 +978,7 @@ namespace QtPrivate && QMetaTypeId2::Defined> struct IsMetaTypePair { - static bool registerConverter(int) + static bool registerConverter() { return false; } @@ -905,13 +987,13 @@ namespace QtPrivate template struct IsMetaTypePair { - inline static bool registerConverter(int id); + inline static bool registerConverter(); }; template struct IsPair { - static bool registerConverter(int) + static bool registerConverter() { return false; } @@ -925,7 +1007,7 @@ namespace QtPrivate template 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::registerConverter(id); - QtPrivate::AssociativeContainerConverterHelper::registerConverter(id); - QtPrivate::MetaTypePairHelper::registerConverter(id); - QtPrivate::MetaTypeSmartPointerHelper::registerConverter(id); + QtPrivate::SequentialContainerTransformationHelper::registerConverter(); + QtPrivate::SequentialContainerTransformationHelper::registerMutableView(); + QtPrivate::AssociativeContainerTransformationHelper::registerConverter(); + QtPrivate::AssociativeContainerTransformationHelper::registerMutableView(); + QtPrivate::MetaTypePairHelper::registerConverter(); + QtPrivate::MetaTypeSmartPointerHelper::registerConverter(); } return id; @@ -1338,10 +1422,10 @@ template \ struct MetaTypeSmartPointerHelper , \ typename std::enable_if::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>(), to)) { \ QtPrivate::QSmartPointerConvertFunctor > o; \ return QMetaType::registerConverter, QObject*>(o); \ } \ @@ -1421,10 +1505,10 @@ Q_DECLARE_METATYPE(QtMetaTypePrivate::QPairVariantInterfaceImpl) QT_BEGIN_NAMESPACE template -inline bool QtPrivate::IsMetaTypePair::registerConverter(int id) +inline bool QtPrivate::IsMetaTypePair::registerConverter() { - const int toId = qMetaTypeId(); - if (!QMetaType::hasRegisteredConverterFunction(id, toId)) { + const QMetaType to = QMetaType::fromType(); + if (!QMetaType::hasRegisteredConverterFunction(QMetaType::fromType(), to)) { QtMetaTypePrivate::QPairVariantInterfaceConvertFunctor o; return QMetaType::registerConverter(o); } @@ -1442,18 +1526,37 @@ struct QSequentialIterableConvertFunctor } }; +template +struct QSequentialIterableMutableViewFunctor +{ + QIterable operator()(From &f) const + { + return QIterable(QMetaSequence::fromContainer(), &f); + } +}; + template struct SequentialValueTypeIsMetaType { - static bool registerConverter(int id) + static bool registerConverter() { - const int toId = qMetaTypeId>(); - if (!QMetaType::hasRegisteredConverterFunction(id, toId)) { + const QMetaType to = QMetaType::fromType>(); + if (!QMetaType::hasRegisteredConverterFunction(QMetaType::fromType(), to)) { QSequentialIterableConvertFunctor o; return QMetaType::registerConverter>(o); } return true; - } + } + + static bool registerMutableView() + { + const QMetaType to = QMetaType::fromType>(); + if (!QMetaType::hasRegisteredMutableViewFunction(QMetaType::fromType(), to)) { + QSequentialIterableMutableViewFunctor o; + return QMetaType::registerMutableView>(o); + } + return true; + } }; template @@ -1465,24 +1568,41 @@ struct QAssociativeIterableConvertFunctor } }; -template -struct AssociativeValueTypeIsMetaType +template +struct QAssociativeIterableMutableViewFunctor { - static bool registerConverter(int id) + QIterable operator()(From &f) const { - const int toId = qMetaTypeId>(); - if (!QMetaType::hasRegisteredConverterFunction(id, toId)) { + return QIterable(QMetaAssociation::fromContainer(), &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 +struct AssociativeKeyTypeIsMetaType : AssociativeMappedTypeIsMetaType +{ + static bool registerConverter() + { + const QMetaType to = QMetaType::fromType>(); + if (!QMetaType::hasRegisteredConverterFunction(QMetaType::fromType(), to)) { QAssociativeIterableConvertFunctor o; return QMetaType::registerConverter>(o); } return true; } + + static bool registerMutableView() + { + const QMetaType to = QMetaType::fromType>(); + if (!QMetaType::hasRegisteredMutableViewFunction(QMetaType::fromType(), to)) { + QAssociativeIterableMutableViewFunctor o; + return QMetaType::registerMutableView>(o); + } + return true; + } }; -} - -namespace QtPrivate { - class QMetaTypeInterface { public: 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 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 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 @@ -363,6 +366,14 @@ class Q_CORE_EXPORT QVariant inline T value() const { return qvariant_cast(*this); } + template + inline T view() + { + T t{}; + QMetaType::view(metaType(), data(), QMetaType::fromType(), &t); + return t; + } + template static inline QVariant fromValue(const T &value) { @@ -383,6 +394,10 @@ class Q_CORE_EXPORT QVariant bool canConvert() const { return canConvert(QMetaType::fromType()); } + template + bool canView() const + { return canView(QMetaType::fromType()); } + 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 varList = listVariant.value(); QCOMPARE(varList.size(), (int)std::distance(sequence.begin(), sequence.end())); - QSequentialIterable listIter = listVariant.value(); + QSequentialIterable listIter = listVariant.view(); QCOMPARE(varList.size(), listIter.size()); typename Container::iterator containerIter = sequence.begin(); @@ -4758,7 +4759,7 @@ void tst_QVariant::sequentialIterableAppend() QList container { 1, 2 }; auto variant = QVariant::fromValue(container); QVERIFY(variant.canConvert>()); - QSequentialIterable asIterable = variant.value>(); + QSequentialIterable asIterable = variant.view>(); const int i = 3, j = 4; void *mutableIterable = asIterable.mutableIterable(); asIterable.metaContainer().addValueAtEnd(mutableIterable, &i); @@ -4778,7 +4779,7 @@ void tst_QVariant::sequentialIterableAppend() QSet container { QByteArray{"hello"}, QByteArray{"world"} }; auto variant = QVariant::fromValue(std::move(container)); QVERIFY(variant.canConvert>()); - QSequentialIterable asIterable = variant.value>(); + QSequentialIterable asIterable = variant.view>(); 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 &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 &) { + return MyTypeView { nullptr }; + }); + QVERIFY(!shouldFail); + + auto original = QVariant::fromValue(MyType {}); + + QVERIFY(original.canView()); + QVERIFY(!original.canConvert()); + + MyTypeView view = original.view(); + QVERIFY(calledView); + const char *txt = "lll"; + view.data->number = 113; + view.data->text = txt; + + MyType extracted = original.view(); + QCOMPARE(extracted.number, 0); + QCOMPARE(extracted.text, nullptr); +} + QTEST_MAIN(tst_QVariant) #include "tst_qvariant.moc"