diff options
Diffstat (limited to 'src/corelib/kernel/qmetatype.cpp')
-rw-r--r-- | src/corelib/kernel/qmetatype.cpp | 3835 |
1 files changed, 2286 insertions, 1549 deletions
diff --git a/src/corelib/kernel/qmetatype.cpp b/src/corelib/kernel/qmetatype.cpp index 89765fb11a..387c0f49ab 100644 --- a/src/corelib/kernel/qmetatype.cpp +++ b/src/corelib/kernel/qmetatype.cpp @@ -1,43 +1,7 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtCore module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include <bitset> +// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com +// Copyright (C) 2021 Intel Corporation. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qmetatype.h" #include "qmetatype_p.h" @@ -45,24 +9,25 @@ #include "qdatetime.h" #include "qbytearray.h" #include "qreadwritelock.h" +#include "qhash.h" +#include "qmap.h" #include "qstring.h" #include "qstringlist.h" -#include "qvector.h" +#include "qlist.h" #include "qlocale.h" +#include "qdebug.h" #if QT_CONFIG(easingcurve) #include "qeasingcurve.h" #endif #include "quuid.h" -#include "qvariant.h" -#include "qdatastream.h" -#include "qregexp.h" -#include "qmetatypeswitcher_p.h" #if QT_CONFIG(regularexpression) # include "qregularexpression.h" #endif #ifndef QT_BOOTSTRAPPED +# include "qdatastream.h" + # include "qbitarray.h" # include "qurl.h" # include "qvariant.h" @@ -74,6 +39,10 @@ # include "qcborarray.h" # include "qcbormap.h" # include "qbytearraylist.h" +# include "qmetaobject.h" +# include "qsequentialiterable.h" +# include "qassociativeiterable.h" +# include "qobject.h" #endif #if QT_CONFIG(itemmodel) @@ -87,20 +56,156 @@ # include "qline.h" #endif +#include <new> +#include <cstring> + QT_BEGIN_NAMESPACE #define NS(x) QT_PREPEND_NAMESPACE(x) +QT_IMPL_METATYPE_EXTERN_TAGGED(QtMetaTypePrivate::QPairVariantInterfaceImpl, QPairVariantInterfaceImpl) + +using QtMetaTypePrivate::isInterfaceFor; namespace { -struct DefinedTypesFilter { - template<typename T> - struct Acceptor { - static const bool IsAccepted = QtMetaTypePrivate::TypeDefinition<T>::IsAvailable && QModulesPrivate::QTypeModuleInfo<T>::IsCore; +struct QMetaTypeDeleter +{ + const QtPrivate::QMetaTypeInterface *iface; + void operator()(void *data) + { + if (iface->alignment > __STDCPP_DEFAULT_NEW_ALIGNMENT__) { + operator delete(data, std::align_val_t(iface->alignment)); + } else { + operator delete(data); + } + } +}; + +struct QMetaTypeCustomRegistry +{ + +#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0) && !defined(QT_BOOTSTRAPPED) + QMetaTypeCustomRegistry() + { + /* qfloat16 was neither a builtin, nor unconditionally registered + in QtCore in Qt <= 6.2. + Inserting it as an alias ensures that a QMetaType::id call + will get the correct built-in type-id (the interface pointers + might still not match, but we already deal with that case. + */ + aliases.insert("qfloat16", QtPrivate::qMetaTypeInterfaceForType<qfloat16>()); + } +#endif + + QReadWriteLock lock; + QList<const QtPrivate::QMetaTypeInterface *> registry; + QHash<QByteArray, const QtPrivate::QMetaTypeInterface *> aliases; + // index of first empty (unregistered) type in registry, if any. + int firstEmpty = 0; + + int registerCustomType(const QtPrivate::QMetaTypeInterface *cti) + { + // we got here because cti->typeId is 0, so this is a custom meta type + // (not read-only) + auto ti = const_cast<QtPrivate::QMetaTypeInterface *>(cti); + { + QWriteLocker l(&lock); + if (int id = ti->typeId.loadRelaxed()) + return id; + QByteArray name = +#ifndef QT_NO_QOBJECT + QMetaObject::normalizedType +#endif + (ti->name); + if (auto ti2 = aliases.value(name)) { + const auto id = ti2->typeId.loadRelaxed(); + ti->typeId.storeRelaxed(id); + return id; + } + aliases[name] = ti; + int size = registry.size(); + while (firstEmpty < size && registry[firstEmpty]) + ++firstEmpty; + if (firstEmpty < size) { + registry[firstEmpty] = ti; + ++firstEmpty; + } else { + registry.append(ti); + firstEmpty = registry.size(); + } + ti->typeId.storeRelaxed(firstEmpty + QMetaType::User); + } + if (ti->legacyRegisterOp) + ti->legacyRegisterOp(); + return ti->typeId.loadRelaxed(); }; + + void unregisterDynamicType(int id) + { + if (!id) + return; + Q_ASSERT(id > QMetaType::User); + QWriteLocker l(&lock); + int idx = id - QMetaType::User - 1; + auto &ti = registry[idx]; + + // We must unregister all names. + aliases.removeIf([ti] (const auto &kv) { return kv.value() == ti; }); + + ti = nullptr; + + firstEmpty = std::min(firstEmpty, idx); + } + + const QtPrivate::QMetaTypeInterface *getCustomType(int id) + { + QReadLocker l(&lock); + return registry.value(id - QMetaType::User - 1); + } }; + +Q_GLOBAL_STATIC(QMetaTypeCustomRegistry, customTypeRegistry) + } // namespace +// used by QVariant::save(): returns the name used in the Q_DECLARE_METATYPE +// macro (one of them, indetermine which one) +const char *QtMetaTypePrivate::typedefNameForType(const QtPrivate::QMetaTypeInterface *type_d) +{ + const char *name = nullptr; + if (!customTypeRegistry.exists()) + return name; + QMetaTypeCustomRegistry *r = &*customTypeRegistry; + + QByteArrayView officialName(type_d->name); + QReadLocker l(&r->lock); + auto it = r->aliases.constBegin(); + auto end = r->aliases.constEnd(); + for ( ; it != end; ++it) { + if (it.value() != type_d) + continue; + if (it.key() == officialName) + continue; // skip the official name + name = it.key().constData(); + ++it; + break; + } + +#ifndef QT_NO_DEBUG + QByteArrayList otherNames; + for ( ; it != end; ++it) { + if (it.value() == type_d && it.key() != officialName) + otherNames << it.key(); + } + l.unlock(); + if (!otherNames.isEmpty()) + qWarning("QMetaType: type %s has more than one typedef alias: %s, %s", + type_d->name, name, otherNames.join(", ").constData()); +#endif + + return name; +} + /*! \macro Q_DECLARE_OPAQUE_POINTER(PointerType) \relates QMetaType @@ -155,15 +260,25 @@ struct DefinedTypesFilter { \list \li Pointers to classes derived from QObject - \li QList<T>, QVector<T>, QQueue<T>, QStack<T>, QSet<T> or QLinkedList<T> + \li QList<T>, QQueue<T>, QStack<T> or QSet<T> where T is a registered meta type - \li QHash<T1, T2>, QMap<T1, T2> or QPair<T1, T2> where T1 and T2 are + \li QHash<T1, T2>, QMap<T1, T2> or std::pair<T1, T2> where T1 and T2 are registered meta types \li QPointer<T>, QSharedPointer<T>, QWeakPointer<T>, where T is a class that derives from QObject \li Enumerations registered with Q_ENUM or Q_FLAG \li Classes that have a Q_GADGET macro \endlist + \note This method also registers the stream and debug operators for the type if they + are visible at registration time. As this is done automatically in some places, + it is strongly recommended to declare the stream operators for a type directly + after the type itself. Because of the argument dependent lookup rules of C++, it is + also strongly recommended to declare the operators in the same namespace as the type itself. + + The stream operators should have the following signatures: + + \snippet code/src_corelib_kernel_qmetatype.cpp 6 + \sa qRegisterMetaType() */ @@ -237,14 +352,20 @@ struct DefinedTypesFilter { \value LongLong LongLong \value Short \c{short} \value Char \c{char} + \value Char16 \c{char16_t} + \value Char32 \c{char32_t} \value ULong \c{unsigned long} \value ULongLong ULongLong \value UShort \c{unsigned short} \value SChar \c{signed char} \value UChar \c{unsigned char} \value Float \c float + \value Float16 qfloat16 + \omitvalue Float128 + \omitvalue BFloat16 + \omitvalue Int128 + \omitvalue UInt128 \value QObjectStar QObject * - \value QVariant QVariant \value QCursor QCursor \value QDate QDate @@ -254,6 +375,7 @@ struct DefinedTypesFilter { \value QPolygon QPolygon \value QPolygonF QPolygonF \value QColor QColor + \value QColorSpace QColorSpace (introduced in Qt 5.15) \value QSizeF QSizeF \value QRectF QRectF \value QLine QLine @@ -261,6 +383,7 @@ struct DefinedTypesFilter { \value QStringList QStringList \value QVariantMap QVariantMap \value QVariantHash QVariantHash + \value QVariantPair QVariantPair \value QIcon QIcon \value QPen QPen \value QLineF QLineF @@ -268,7 +391,6 @@ struct DefinedTypesFilter { \value QRect QRect \value QPoint QPoint \value QUrl QUrl - \value QRegExp QRegExp \value QRegularExpression QRegularExpression \value QDateTime QDateTime \value QPointF QPointF @@ -283,7 +405,6 @@ struct DefinedTypesFilter { \value QPixmap QPixmap \value QLocale QLocale \value QBitmap QBitmap - \value QMatrix QMatrix \value QTransform QTransform \value QMatrix4x4 QMatrix4x4 \value QVector2D QVector2D @@ -300,16 +421,16 @@ struct DefinedTypesFilter { \value QCborMap QCborMap \value QCborSimpleType QCborSimpleType \value QModelIndex QModelIndex - \value QPersistentModelIndex QPersistentModelIndex (since 5.5) + \value QPersistentModelIndex QPersistentModelIndex (introduced in Qt 5.5) \value QUuid QUuid \value QByteArrayList QByteArrayList + \value QVariant QVariant \value User Base value for user types \value UnknownType This is an invalid type id. It is returned from QMetaType for types that are not registered - \omitvalue LastCoreType - \omitvalue LastGuiType - Additional types can be registered using Q_DECLARE_METATYPE(). + Additional types can be registered using qRegisterMetaType() or by calling + registerType(). \sa type(), typeName() */ @@ -319,17 +440,31 @@ struct DefinedTypesFilter { The enum describes attributes of a type supported by QMetaType. - \value NeedsConstruction This type has non-trivial constructors. If the flag is not set instances can be safely initialized with memset to 0. - \value NeedsDestruction This type has a non-trivial destructor. If the flag is not set calls to the destructor are not necessary before discarding objects. - \value MovableType An instance of a type having this attribute can be safely moved by memcpy. + \value NeedsConstruction This type has a default constructor. If the flag is not set, instances can be safely initialized with memset to 0. + \value NeedsCopyConstruction (since 6.5) This type has a non-trivial copy constructor. If the flag is not set, instances can be copied with memcpy. + \value NeedsMoveConstruction (since 6.5) This type has a non-trivial move constructor. If the flag is not set, instances can be moved with memcpy. + \value NeedsDestruction This type has a non-trivial destructor. If the flag is not set, calls to the destructor are not necessary before discarding objects. + \value RelocatableType An instance of a type having this attribute can be safely moved to a different memory location using memcpy. + \omitvalue MovableType \omitvalue SharedPointerToQObject - \value IsEnumeration This type is an enumeration - \value PointerToQObject This type is a pointer to a derived of QObject + \value IsEnumeration This type is an enumeration. + \value IsUnsignedEnumeration If the type is an Enumeration, its underlying type is unsigned. + \value PointerToQObject This type is a pointer to a class derived from QObject. + \value IsPointer This type is a pointer to another type. \omitvalue WeakPointerToQObject \omitvalue TrackingPointerToQObject - \omitvalue WasDeclaredAsMetaType - \omitvalue IsGadget \omit This type is a Q_GADGET and it's corresponding QMetaObject can be accessed with QMetaType::metaObject Since 5.5. \endomit + \omitvalue IsGadget \omit (since Qt 5.5) This type is a Q_GADGET and its corresponding QMetaObject can be accessed with QMetaType::metaObject. \endomit \omitvalue PointerToGadget + \omitvalue IsQmlList + \value IsConst Indicates that values of this type are immutable; for instance, because they are pointers to const objects. + + \note Before Qt 6.5, both the NeedsConstruction and NeedsDestruction flags + were incorrectly set if the either copy construtor or destructor were + non-trivial (that is, if the type was not trivial). + + Note that the Needs flags may be set but the meta type may not have a + publicly-accessible constructor of the relevant type or a + publicly-accessible destructor. */ /*! @@ -343,17 +478,19 @@ struct DefinedTypesFilter { The class is used as a helper to marshall types in QVariant and in queued signals and slots connections. It associates a type name to a type so that it can be created and destructed - dynamically at run-time. Declare new types with Q_DECLARE_METATYPE() - to make them available to QVariant and other template-based functions. - Call qRegisterMetaType() to make types available to non-template based - functions, such as the queued signal and slot connections. + dynamically at run-time. - Any class or struct that has a public default - constructor, a public copy constructor, and a public destructor - can be registered. + Type names can be registered with QMetaType by using either + qRegisterMetaType() or registerType(). Registration is not required for + most operations; it's only required for operations that attempt to resolve + a type name in string form back to a QMetaType object or the type's ID. + Those include some old-style signal-slot connections using + QObject::connect(), reading user-types from \l QDataStream to \l QVariant, + or binding to other languages and IPC mechanisms, like QML, D-Bus, + JavaScript, etc. - The following code allocates and destructs an instance of - \c{MyClass}: + The following code allocates and destructs an instance of \c{MyClass} by + its name, which requires that \c{MyClass} have been previously registered: \snippet code/src_corelib_kernel_qmetatype.cpp 3 @@ -371,25 +508,61 @@ struct DefinedTypesFilter { Returns \c true if this QMetaType object contains valid information about a type, false otherwise. + + \sa isRegistered() */ +bool QMetaType::isValid() const +{ + return d_ptr; +} /*! \fn bool QMetaType::isRegistered() const \since 5.0 - Returns \c true if this QMetaType object contains valid - information about a type, false otherwise. + Returns \c true if this QMetaType object has been registered with the Qt + global metatype registry. Registration allows the type to be found by its + name (using QMetaType::fromName()) or by its ID (using the constructor). + + \sa qRegisterMetaType(), isValid() */ +bool QMetaType::isRegistered() const +{ + return d_ptr && d_ptr->typeId.loadRelaxed(); +} /*! \fn int QMetaType::id() const \since 5.13 - Returns id type hold by this QMetatype instance. + Returns id type held by this QMetatype instance. */ /*! - \fn bool QMetaType::sizeOf() const + \fn void QMetaType::registerType() const + \since 6.5 + + Registers this QMetaType with the type registry so it can be found by name, + using QMetaType::fromName(). + + \sa qRegisterMetaType() + */ +/*! + \internal + Out-of-line path for registerType() and slow path id(). + */ +int QMetaType::registerHelper(const QtPrivate::QMetaTypeInterface *iface) +{ + Q_ASSERT(iface); + auto reg = customTypeRegistry(); + if (reg) { + return reg->registerCustomType(iface); + } + return 0; +} + +/*! + \fn constexpr qsizetype QMetaType::sizeOf() const \since 5.0 Returns the size of the type in bytes (i.e. sizeof(T), @@ -399,41 +572,62 @@ struct DefinedTypesFilter { This function is typically used together with construct() to perform low-level management of the memory used by a type. - \sa QMetaType::construct(), QMetaType::sizeOf() + \sa QMetaType::construct(), QMetaType::sizeOf(), QMetaType::alignOf() */ /*! - \fn TypeFlags QMetaType::flags() const + \fn constexpr int QMetaType::alignOf() const + \since 6.0 + + Returns the alignment of the type in bytes (i.e. alignof(T), + where T is the actual type for which this QMetaType instance + was constructed for). + + This function is typically used together with construct() + to perform low-level management of the memory used by a type. + + \sa QMetaType::construct(), QMetaType::sizeOf() + + */ + +/*! + \fn constexpr TypeFlags QMetaType::flags() const \since 5.0 - Returns flags of the type for which this QMetaType instance was constructed. + Returns flags of the type for which this QMetaType instance was + constructed. To inspect specific type traits, prefer using one of the "is-" + functions rather than the flags directly. - \sa QMetaType::TypeFlags, QMetaType::typeFlags() + \sa QMetaType::TypeFlags, QMetaType::flags(), isDefaultConstructible(), + isCopyConstructible(), isMoveConstructible(), isDestructible(), + isEqualityComparable(), isOrdered() */ /*! - \fn const QMetaObject *QMetaType::metaObject() const + \fn constexpr const QMetaObject *QMetaType::metaObject() const \since 5.5 - return a QMetaObject relative to this type. + Returns a QMetaObject relative to this type. If the type is a pointer type to a subclass of QObject, flags() contains - QMetaType::PointerToQObject and this function returns the corresponding QMetaObject. This can - be used to in combinaison with QMetaObject::construct to create QObject of this type. + QMetaType::PointerToQObject and this function returns the corresponding QMetaObject. + This can be used in combination with QMetaObject::newInstance() to create QObjects of this type. - If the type is a Q_GADGET, flags() contains QMetaType::IsGadget, and this function returns its - QMetaObject. This can be used to retrieve QMetaMethod and QMetaProperty and use them on a - pointer of this type. (given by QVariant::data for example) + If the type is a Q_GADGET, flags() contains QMetaType::IsGadget. + If the type is a pointer to a Q_GADGET, flags() contains QMetaType::PointerToGadget. + In both cases, this function returns its QMetaObject. + This can be used to retrieve QMetaMethod and QMetaProperty and use them on a + pointer of this type for example, as given by QVariant::data(). - If the type is an enumeration, flags() contains QMetaType::IsEnumeration, and this function - returns the QMetaObject of the enclosing object if the enum was registered as a Q_ENUM or - \nullptr otherwise + If the type is an enumeration, flags() contains QMetaType::IsEnumeration. + In this case, this function returns the QMetaObject of the enclosing + object if the enum was registered as a Q_ENUM or \nullptr otherwise. - \sa QMetaType::metaObjectForType(), QMetaType::flags() + \sa QMetaType::flags() */ /*! - \fn void *QMetaType::create(const void *copy = 0) const + \fn void *QMetaType::create(const void *copy = nullptr) const \since 5.0 Returns a copy of \a copy, assuming it is of the type that this @@ -442,6 +636,20 @@ struct DefinedTypesFilter { \sa QMetaType::destroy() */ +void *QMetaType::create(const void *copy) const +{ + if (copy ? !isCopyConstructible() : !isDefaultConstructible()) + return nullptr; + + std::unique_ptr<void, QMetaTypeDeleter> where(nullptr, {d_ptr}); + if (d_ptr->alignment > __STDCPP_DEFAULT_NEW_ALIGNMENT__) + where.reset(operator new(d_ptr->size, std::align_val_t(d_ptr->alignment))); + else + where.reset(operator new(d_ptr->size)); + + QtMetaTypePrivate::construct(d_ptr, where.get(), copy); + return where.release(); +} /*! \fn void QMetaType::destroy(void *data) const @@ -452,19 +660,16 @@ struct DefinedTypesFilter { \sa QMetaType::create() */ +void QMetaType::destroy(void *data) const +{ + if (data && isDestructible()) { + QtMetaTypePrivate::destruct(d_ptr, data); + QMetaTypeDeleter{d_ptr}(data); + } +} /*! - \fn void *QMetaType::construct(int type, const void *copy) - \deprecated - - Constructs a value of the given type which is a copy of \a copy. - The default value for \a copy is 0. - - Deprecated, use the static function QMetaType::create(int type, - const void *copy) instead. -*/ -/*! - \fn void *QMetaType::construct(void *where, const void *copy = 0) const + \fn void *QMetaType::construct(void *where, const void *copy = nullptr) const \since 5.0 Constructs a value of the type that this QMetaType instance @@ -489,6 +694,16 @@ struct DefinedTypesFilter { special hardware instructions (e.g., aligned SSE loads and stores on x86). */ +void *QMetaType::construct(void *where, const void *copy) const +{ + if (!where) + return nullptr; + if (copy ? !isCopyConstructible() : !isDefaultConstructible()) + return nullptr; + + QtMetaTypePrivate::construct(d_ptr, where, copy); + return where; +} /*! \fn void QMetaType::destruct(void *data) const @@ -501,12 +716,207 @@ struct DefinedTypesFilter { destructor, it doesn't invoke the delete operator. \sa QMetaType::construct() */ +void QMetaType::destruct(void *data) const +{ + if (data && isDestructible()) + QtMetaTypePrivate::destruct(d_ptr, data); +} + +static QPartialOrdering threeWayCompare(const void *ptr1, const void *ptr2) +{ + std::less<const void *> less; + if (less(ptr1, ptr2)) + return QPartialOrdering::Less; + if (less(ptr2, ptr1)) + return QPartialOrdering::Greater; + return QPartialOrdering::Equivalent; +} + +/*! + Compares the objects at \a lhs and \a rhs for ordering. + + Returns QPartialOrdering::Unordered if comparison is not supported + or the values are unordered. Otherwise, returns + QPartialOrdering::Less, QPartialOrdering::Equivalent or + QPartialOrdering::Greater if \a lhs is less than, equivalent + to or greater than \a rhs, respectively. + + Both objects must be of the type described by this metatype. If either \a lhs + or \a rhs is \nullptr, the values are unordered. Comparison is only supported + if the type's less than operator was visible to the metatype declaration. + + If the type's equality operator was also visible, values will only compare equal if the + equality operator says they are. In the absence of an equality operator, when neither + value is less than the other, values are considered equal; if equality is also available + and two such values are not equal, they are considered unordered, just as NaN (not a + number) values of a floating point type lie outside its ordering. + + \note If no less than operator was visible to the metatype declaration, values are + unordered even if an equality operator visible to the declaration considers them equal: + \c{compare() == 0} only agrees with equals() if the less than operator was visible. + + \since 6.0 + \sa equals(), isOrdered() +*/ +QPartialOrdering QMetaType::compare(const void *lhs, const void *rhs) const +{ + if (!lhs || !rhs) + return QPartialOrdering::Unordered; + if (d_ptr && d_ptr->flags & QMetaType::IsPointer) + return threeWayCompare(*reinterpret_cast<const void * const *>(lhs), + *reinterpret_cast<const void * const *>(rhs)); + if (d_ptr && d_ptr->lessThan) { + if (d_ptr->equals && d_ptr->equals(d_ptr, lhs, rhs)) + return QPartialOrdering::Equivalent; + if (d_ptr->lessThan(d_ptr, lhs, rhs)) + return QPartialOrdering::Less; + if (d_ptr->lessThan(d_ptr, rhs, lhs)) + return QPartialOrdering::Greater; + if (!d_ptr->equals) + return QPartialOrdering::Equivalent; + } + return QPartialOrdering::Unordered; +} /*! - \fn QMetaType::~QMetaType() + Compares the objects at \a lhs and \a rhs for equality. + + Both objects must be of the type described by this metatype. Can only compare the + two objects if a less than or equality operator for the type was visible to the + metatype declaration. Otherwise, the metatype never considers values equal. When + an equality operator was visible to the metatype declaration, it is authoritative; + otherwise, if less than is visible, when neither value is less than the other, the + two are considered equal. If values are unordered (see compare() for details) they + are not equal. + + Returns true if the two objects compare equal, otherwise false. - Destructs this object. + \since 6.0 + \sa isEqualityComparable(), compare() */ +bool QMetaType::equals(const void *lhs, const void *rhs) const +{ + if (!lhs || !rhs) + return false; + if (d_ptr) { + if (d_ptr->flags & QMetaType::IsPointer) + return *reinterpret_cast<const void * const *>(lhs) == *reinterpret_cast<const void * const *>(rhs); + + if (d_ptr->equals) + return d_ptr->equals(d_ptr, lhs, rhs); + if (d_ptr->lessThan && !d_ptr->lessThan(d_ptr, lhs, rhs) && !d_ptr->lessThan(d_ptr, rhs, lhs)) + return true; + } + return false; +} + +/*! + \fn bool QMetaType::isDefaultConstructible() const noexcept + \since 6.5 + + Returns true if this type can be default-constructed. If it can be, then + construct() and create() can be used with a \c{copy} parameter that is + null. + + \sa flags(), isCopyConstructible(), isMoveConstructible(), isDestructible() + */ + +/*! + \fn bool QMetaType::isCopyConstructible() const noexcept + \since 6.5 + + Returns true if this type can be copy-constructed. If it can be, then + construct() and create() can be used with a \c{copy} parameter that is + not null. + + \sa flags(), isDefaultConstructible(), isMoveConstructible(), isDestructible() + */ + +/*! + \fn bool QMetaType::isMoveConstructible() const noexcept + \since 6.5 + + Returns true if this type can be move-constructed. QMetaType currently does + not have an API to make use of this trait. + + \sa flags(), isDefaultConstructible(), isCopyConstructible(), isDestructible() + */ + +/*! + \fn bool QMetaType::isDestructible() const noexcept + \since 6.5 + + Returns true if this type can be destroyed. If it can be, then destroy() + and destruct() can be called. + + \sa flags(), isDefaultConstructible(), isCopyConstructible(), isMoveConstructible() + */ + +bool QMetaType::isDefaultConstructible(const QtPrivate::QMetaTypeInterface *iface) noexcept +{ + return !isInterfaceFor<void>(iface) && QtMetaTypePrivate::isDefaultConstructible(iface); +} + +bool QMetaType::isCopyConstructible(const QtPrivate::QMetaTypeInterface *iface) noexcept +{ + return !isInterfaceFor<void>(iface) && QtMetaTypePrivate::isCopyConstructible(iface); +} + +bool QMetaType::isMoveConstructible(const QtPrivate::QMetaTypeInterface *iface) noexcept +{ + return !isInterfaceFor<void>(iface) && QtMetaTypePrivate::isMoveConstructible(iface); +} + +bool QMetaType::isDestructible(const QtPrivate::QMetaTypeInterface *iface) noexcept +{ + return !isInterfaceFor<void>(iface) && QtMetaTypePrivate::isDestructible(iface); +} + +/*! + Returns \c true if a less than or equality operator for the type described by + this metatype was visible to the metatype declaration, otherwise \c false. + + \sa equals(), isOrdered() +*/ +bool QMetaType::isEqualityComparable() const +{ + return d_ptr && (d_ptr->flags & QMetaType::IsPointer || d_ptr->equals != nullptr || d_ptr->lessThan != nullptr); +} + +/*! + Returns \c true if a less than operator for the type described by this metatype + was visible to the metatype declaration, otherwise \c false. + + \sa compare(), isEqualityComparable() +*/ +bool QMetaType::isOrdered() const +{ + return d_ptr && (d_ptr->flags & QMetaType::IsPointer || d_ptr->lessThan != nullptr); +} + + +/*! + \internal +*/ +void QMetaType::unregisterMetaType(QMetaType type) +{ + const QtPrivate::QMetaTypeInterface *d_ptr = type.d_ptr; + if (!d_ptr) + return; + + const int typeId = d_ptr->typeId.loadRelaxed(); + if (typeId < QMetaType::User) + return; + + // this is a custom meta type (not read-only) + + if (auto reg = customTypeRegistry()) { + Q_ASSERT(reg->getCustomType(typeId) == d_ptr); + reg->unregisterDynamicType(typeId); + } + + const_cast<QtPrivate::QMetaTypeInterface *>(d_ptr)->typeId.storeRelease(0); +} /*! \fn template<typename T> QMetaType QMetaType::fromType() @@ -515,56 +925,752 @@ struct DefinedTypesFilter { Returns the QMetaType corresponding to the type in the template parameter. */ -/*! \fn bool operator==(const QMetaType &a, const QMetaType &b) +/*! \fn bool QMetaType::operator==(QMetaType a, QMetaType b) \since 5.15 - \relates QMetaType \overload Returns \c true if the QMetaType \a a represents the same type as the QMetaType \a b, otherwise returns \c false. */ -/*! \fn bool operator!=(const QMetaType &a, const QMetaType &c) +/*! \fn bool QMetaType::operator!=(QMetaType a, QMetaType b) \since 5.15 - \relates QMetaType \overload - Returns \c true if the QMetaType \a a represents a difference type + Returns \c true if the QMetaType \a a represents a different type than the QMetaType \a b, otherwise returns \c false. */ +/*! \internal */ +bool QMetaTypeModuleHelper::convert(const void *, int, void *, int) const +{ + return false; +} + #define QT_ADD_STATIC_METATYPE(MetaTypeName, MetaTypeId, RealName) \ { #RealName, sizeof(#RealName) - 1, MetaTypeId }, #define QT_ADD_STATIC_METATYPE_ALIASES_ITER(MetaTypeName, MetaTypeId, AliasingName, RealNameStr) \ { RealNameStr, sizeof(RealNameStr) - 1, QMetaType::MetaTypeName }, -#define QT_ADD_STATIC_METATYPE_HACKS_ITER(MetaTypeName, TypeId, Name) \ - QT_ADD_STATIC_METATYPE(MetaTypeName, MetaTypeName, Name) + static const struct { const char * typeName; int typeNameLength; int type; } types[] = { QT_FOR_EACH_STATIC_TYPE(QT_ADD_STATIC_METATYPE) QT_FOR_EACH_STATIC_ALIAS_TYPE(QT_ADD_STATIC_METATYPE_ALIASES_ITER) - QT_FOR_EACH_STATIC_HACKS_TYPE(QT_ADD_STATIC_METATYPE_HACKS_ITER) + QT_ADD_STATIC_METATYPE(_, QMetaTypeId2<qreal>::MetaType, qreal) {nullptr, 0, QMetaType::UnknownType} }; -Q_CORE_EXPORT const QMetaTypeInterface *qMetaTypeGuiHelper = nullptr; -Q_CORE_EXPORT const QMetaTypeInterface *qMetaTypeWidgetsHelper = nullptr; -Q_CORE_EXPORT const QMetaObject *qMetaObjectWidgetsHelper = nullptr; - -class QCustomTypeInfo : public QMetaTypeInterface +// NOLINTNEXTLINE(cppcoreguidelines-virtual-class-destructor): this is not a base class +static constexpr struct : QMetaTypeModuleHelper { -public: - QCustomTypeInfo() - : alias(-1) + template<typename T, typename LiteralWrapper = + std::conditional_t<std::is_same_v<T, QString>, QLatin1StringView, const char *>> + static inline bool convertToBool(const T &source) { - QMetaTypeInterface empty = QT_METATYPE_INTERFACE_INIT(void); - *static_cast<QMetaTypeInterface*>(this) = empty; + T str = source.toLower(); + return !(str.isEmpty() || str == LiteralWrapper("0") || str == LiteralWrapper("false")); } - QByteArray typeName; - int alias; -}; + + const QtPrivate::QMetaTypeInterface *interfaceForType(int type) const override { + switch (type) { + QT_FOR_EACH_STATIC_PRIMITIVE_TYPE(QT_METATYPE_CONVERT_ID_TO_TYPE) + QT_FOR_EACH_STATIC_PRIMITIVE_POINTER(QT_METATYPE_CONVERT_ID_TO_TYPE) + QT_FOR_EACH_STATIC_CORE_CLASS(QT_METATYPE_CONVERT_ID_TO_TYPE) + QT_FOR_EACH_STATIC_CORE_POINTER(QT_METATYPE_CONVERT_ID_TO_TYPE) + QT_FOR_EACH_STATIC_CORE_TEMPLATE(QT_METATYPE_CONVERT_ID_TO_TYPE) + default: + return nullptr; + } + } + + bool convert(const void *from, int fromTypeId, void *to, int toTypeId) const override + { + Q_ASSERT(fromTypeId != toTypeId); + + // canConvert calls with two nullptr + bool onlyCheck = (from == nullptr && to == nullptr); + + // other callers must provide two valid pointers + Q_ASSERT(onlyCheck || (bool(from) && bool(to))); + + using Char = char; + using SChar = signed char; + using UChar = unsigned char; + using Short = short; + using UShort = unsigned short; + using Int = int; + using UInt = unsigned int; + using Long = long; + using LongLong = qlonglong; + using ULong = unsigned long; + using ULongLong = qulonglong; + using Float = float; + using Double = double; + using Bool = bool; + using Nullptr = std::nullptr_t; + using Char16 = char16_t; + using Char32 = char32_t; + +#define QMETATYPE_CONVERTER_ASSIGN_DOUBLE(To, From) \ + QMETATYPE_CONVERTER(To, From, result = double(source); return true;) +#define QMETATYPE_CONVERTER_ASSIGN_NUMBER(To, From) \ + QMETATYPE_CONVERTER(To, From, result = To::number(source); return true;) +#ifndef QT_BOOTSTRAPPED +#define CONVERT_CBOR_AND_JSON(To) \ + QMETATYPE_CONVERTER(To, QCborValue, \ + if constexpr(std::is_same_v<To, Bool>) { \ + if (!source.isBool()) \ + return false; \ + result = source.toBool(); \ + } else { \ + if (!source.isInteger() && !source.isDouble()) \ + return false; \ + if constexpr(std::is_integral_v<To>) \ + result = source.toInteger(); \ + else \ + result = source.toDouble(); \ + } \ + return true; \ + ); \ + QMETATYPE_CONVERTER(To, QJsonValue, \ + if constexpr(std::is_same_v<To, Bool>) { \ + if (!source.isBool()) \ + return false; \ + result = source.toBool(); \ + } else { \ + if (!source.isDouble()) \ + return false; \ + if constexpr(std::is_integral_v<To>) \ + result = source.toInteger(); \ + else \ + result = source.toDouble(); \ + } \ + return true; \ + ) +#else +#define CONVERT_CBOR_AND_JSON(To) +#endif + +#define INTEGRAL_CONVERTER(To) \ + QMETATYPE_CONVERTER_ASSIGN(To, Bool); \ + QMETATYPE_CONVERTER_ASSIGN(To, Char); \ + QMETATYPE_CONVERTER_ASSIGN(To, UChar); \ + QMETATYPE_CONVERTER_ASSIGN(To, SChar); \ + QMETATYPE_CONVERTER_ASSIGN(To, Short); \ + QMETATYPE_CONVERTER_ASSIGN(To, UShort); \ + QMETATYPE_CONVERTER_ASSIGN(To, Int); \ + QMETATYPE_CONVERTER_ASSIGN(To, UInt); \ + QMETATYPE_CONVERTER_ASSIGN(To, Long); \ + QMETATYPE_CONVERTER_ASSIGN(To, ULong); \ + QMETATYPE_CONVERTER_ASSIGN(To, LongLong); \ + QMETATYPE_CONVERTER_ASSIGN(To, ULongLong); \ + QMETATYPE_CONVERTER(To, Float, result = qRound64(source); return true;); \ + QMETATYPE_CONVERTER(To, Double, result = qRound64(source); return true;); \ + QMETATYPE_CONVERTER(To, QChar, result = source.unicode(); return true;); \ + QMETATYPE_CONVERTER(To, QString, \ + bool ok = false; \ + if constexpr(std::is_same_v<To, bool>) \ + result = (ok = true, convertToBool(source)); \ + else if constexpr(std::is_signed_v<To>) \ + result = To(source.toLongLong(&ok)); \ + else \ + result = To(source.toULongLong(&ok)); \ + return ok; \ + ); \ + QMETATYPE_CONVERTER(To, QByteArray, \ + bool ok = false; \ + if constexpr(std::is_same_v<To, bool>) \ + result = (ok = true, convertToBool(source)); \ + else if constexpr(std::is_signed_v<To>) \ + result = To(source.toLongLong(&ok)); \ + else \ + result = To(source.toULongLong(&ok)); \ + return ok; \ + ); \ + CONVERT_CBOR_AND_JSON(To) + +#define FLOAT_CONVERTER(To) \ + QMETATYPE_CONVERTER_ASSIGN(To, Bool); \ + QMETATYPE_CONVERTER_ASSIGN(To, Char); \ + QMETATYPE_CONVERTER_ASSIGN(To, UChar); \ + QMETATYPE_CONVERTER_ASSIGN(To, SChar); \ + QMETATYPE_CONVERTER_ASSIGN(To, Short); \ + QMETATYPE_CONVERTER_ASSIGN(To, UShort); \ + QMETATYPE_CONVERTER_ASSIGN(To, Int); \ + QMETATYPE_CONVERTER_ASSIGN(To, UInt); \ + QMETATYPE_CONVERTER_ASSIGN(To, Long); \ + QMETATYPE_CONVERTER_ASSIGN(To, ULong); \ + QMETATYPE_CONVERTER_ASSIGN(To, LongLong); \ + QMETATYPE_CONVERTER_ASSIGN(To, ULongLong); \ + QMETATYPE_CONVERTER_ASSIGN(To, Float); \ + QMETATYPE_CONVERTER_ASSIGN(To, Double); \ + QMETATYPE_CONVERTER(To, QString, \ + bool ok = false; \ + result = source.toDouble(&ok); \ + return ok; \ + ); \ + QMETATYPE_CONVERTER(To, QByteArray, \ + bool ok = false; \ + result = source.toDouble(&ok); \ + return ok; \ + ); \ + CONVERT_CBOR_AND_JSON(To) + + switch (makePair(toTypeId, fromTypeId)) { + + // integral conversions + INTEGRAL_CONVERTER(Bool); + INTEGRAL_CONVERTER(Char); + INTEGRAL_CONVERTER(UChar); + INTEGRAL_CONVERTER(SChar); + INTEGRAL_CONVERTER(Short); + INTEGRAL_CONVERTER(UShort); + INTEGRAL_CONVERTER(Int); + INTEGRAL_CONVERTER(UInt); + INTEGRAL_CONVERTER(Long); + INTEGRAL_CONVERTER(ULong); + INTEGRAL_CONVERTER(LongLong); + INTEGRAL_CONVERTER(ULongLong); + FLOAT_CONVERTER(Float); + FLOAT_CONVERTER(Double); + +#ifndef QT_BOOTSTRAPPED + QMETATYPE_CONVERTER_ASSIGN(QUrl, QString); + QMETATYPE_CONVERTER(QUrl, QCborValue, + if (source.isUrl()) { + result = source.toUrl(); + return true; + } + return false; + ); +#endif +#if QT_CONFIG(itemmodel) + QMETATYPE_CONVERTER_ASSIGN(QModelIndex, QPersistentModelIndex); + QMETATYPE_CONVERTER_ASSIGN(QPersistentModelIndex, QModelIndex); +#endif // QT_CONFIG(itemmodel) + + // QChar methods +#define QMETATYPE_CONVERTER_ASSIGN_QCHAR(From) \ + QMETATYPE_CONVERTER(QChar, From, result = QChar::fromUcs2(source); return true;) + QMETATYPE_CONVERTER_ASSIGN_QCHAR(Char); + QMETATYPE_CONVERTER_ASSIGN_QCHAR(SChar); + QMETATYPE_CONVERTER_ASSIGN_QCHAR(Short); + QMETATYPE_CONVERTER_ASSIGN_QCHAR(Long); + QMETATYPE_CONVERTER_ASSIGN_QCHAR(Int); + QMETATYPE_CONVERTER_ASSIGN_QCHAR(LongLong); + QMETATYPE_CONVERTER_ASSIGN_QCHAR(Float); + QMETATYPE_CONVERTER_ASSIGN_QCHAR(UChar); + QMETATYPE_CONVERTER_ASSIGN_QCHAR(UShort); + QMETATYPE_CONVERTER_ASSIGN_QCHAR(ULong); + QMETATYPE_CONVERTER_ASSIGN_QCHAR(UInt); + QMETATYPE_CONVERTER_ASSIGN_QCHAR(ULongLong); + QMETATYPE_CONVERTER_ASSIGN_QCHAR(Char16); + + QMETATYPE_CONVERTER(Char16, QChar, result = source.unicode(); return true;) + + // conversions to QString + QMETATYPE_CONVERTER_ASSIGN(QString, QChar); + QMETATYPE_CONVERTER(QString, Bool, + result = source ? QStringLiteral("true") : QStringLiteral("false"); + return true; + ); + QMETATYPE_CONVERTER_ASSIGN_NUMBER(QString, Short); + QMETATYPE_CONVERTER_ASSIGN_NUMBER(QString, Long); + QMETATYPE_CONVERTER_ASSIGN_NUMBER(QString, Int); + QMETATYPE_CONVERTER_ASSIGN_NUMBER(QString, LongLong); + QMETATYPE_CONVERTER_ASSIGN_NUMBER(QString, UShort); + QMETATYPE_CONVERTER_ASSIGN_NUMBER(QString, ULong); + QMETATYPE_CONVERTER_ASSIGN_NUMBER(QString, UInt); + QMETATYPE_CONVERTER_ASSIGN_NUMBER(QString, ULongLong); + QMETATYPE_CONVERTER(QString, Float, + result = QString::number(source, 'g', QLocale::FloatingPointShortest); + return true; + ); + QMETATYPE_CONVERTER(QString, Double, + result = QString::number(source, 'g', QLocale::FloatingPointShortest); + return true; + ); + QMETATYPE_CONVERTER(QString, Char, + result = QString::fromLatin1(&source, 1); + return true; + ); + QMETATYPE_CONVERTER(QString, SChar, + char s = source; + result = QString::fromLatin1(&s, 1); + return true; + ); + QMETATYPE_CONVERTER(QString, UChar, + char s = source; + result = QString::fromLatin1(&s, 1); + return true; + ); + QMETATYPE_CONVERTER(QString, Char16, + result = QChar(source); + return true; + ); + QMETATYPE_CONVERTER(QString, Char32, + result = QChar::fromUcs4(source).operator QStringView().toString(); + return true; + ); +#if QT_CONFIG(datestring) + QMETATYPE_CONVERTER(QString, QDate, result = source.toString(Qt::ISODate); return true;); + QMETATYPE_CONVERTER(QString, QTime, result = source.toString(Qt::ISODateWithMs); return true;); + QMETATYPE_CONVERTER(QString, QDateTime, result = source.toString(Qt::ISODateWithMs); return true;); +#endif + QMETATYPE_CONVERTER(QString, QByteArray, result = QString::fromUtf8(source); return true;); + QMETATYPE_CONVERTER(QString, QStringList, + return (source.size() == 1) ? (result = source.at(0), true) : false; + ); +#ifndef QT_BOOTSTRAPPED + QMETATYPE_CONVERTER(QString, QUrl, result = source.toString(); return true;); + QMETATYPE_CONVERTER(QString, QJsonValue, + if (source.isString() || source.isNull()) { + result = source.toString(); + return true; + } + return false; + ); +#endif + QMETATYPE_CONVERTER(QString, Nullptr, Q_UNUSED(source); result = QString(); return true;); + + // QByteArray + QMETATYPE_CONVERTER(QByteArray, QString, result = source.toUtf8(); return true;); + QMETATYPE_CONVERTER(QByteArray, Bool, + result = source ? "true" : "false"; + return true; + ); + QMETATYPE_CONVERTER(QByteArray, Char, result = QByteArray(source, 1); return true;); + QMETATYPE_CONVERTER(QByteArray, SChar, result = QByteArray(source, 1); return true;); + QMETATYPE_CONVERTER(QByteArray, UChar, result = QByteArray(source, 1); return true;); + QMETATYPE_CONVERTER_ASSIGN_NUMBER(QByteArray, Short); + QMETATYPE_CONVERTER_ASSIGN_NUMBER(QByteArray, Long); + QMETATYPE_CONVERTER_ASSIGN_NUMBER(QByteArray, Int); + QMETATYPE_CONVERTER_ASSIGN_NUMBER(QByteArray, LongLong); + QMETATYPE_CONVERTER_ASSIGN_NUMBER(QByteArray, UShort); + QMETATYPE_CONVERTER_ASSIGN_NUMBER(QByteArray, ULong); + QMETATYPE_CONVERTER_ASSIGN_NUMBER(QByteArray, UInt); + QMETATYPE_CONVERTER_ASSIGN_NUMBER(QByteArray, ULongLong); + QMETATYPE_CONVERTER(QByteArray, Float, + result = QByteArray::number(source, 'g', QLocale::FloatingPointShortest); + return true; + ); + QMETATYPE_CONVERTER(QByteArray, Double, + result = QByteArray::number(source, 'g', QLocale::FloatingPointShortest); + return true; + ); + QMETATYPE_CONVERTER(QByteArray, Nullptr, Q_UNUSED(source); result = QByteArray(); return true;); + + QMETATYPE_CONVERTER(QString, QUuid, result = source.toString(); return true;); + QMETATYPE_CONVERTER(QUuid, QString, result = QUuid(source); return true;); + QMETATYPE_CONVERTER(QByteArray, QUuid, result = source.toByteArray(); return true;); + QMETATYPE_CONVERTER(QUuid, QByteArray, result = QUuid(source); return true;); + +#ifndef QT_NO_GEOM_VARIANT + QMETATYPE_CONVERTER(QSize, QSizeF, result = source.toSize(); return true;); + QMETATYPE_CONVERTER_ASSIGN(QSizeF, QSize); + QMETATYPE_CONVERTER(QLine, QLineF, result = source.toLine(); return true;); + QMETATYPE_CONVERTER_ASSIGN(QLineF, QLine); + QMETATYPE_CONVERTER(QRect, QRectF, result = source.toRect(); return true;); + QMETATYPE_CONVERTER_ASSIGN(QRectF, QRect); + QMETATYPE_CONVERTER(QPoint, QPointF, result = source.toPoint(); return true;); + QMETATYPE_CONVERTER_ASSIGN(QPointF, QPoint); +#endif + + QMETATYPE_CONVERTER(QStringList, QString, result = QStringList() << source; return true;); + +#ifndef QT_NO_VARIANT + QMETATYPE_CONVERTER(QByteArrayList, QVariantList, + result.reserve(source.size()); + for (const auto &v: source) + result.append(v.toByteArray()); + return true; + ); + QMETATYPE_CONVERTER(QVariantList, QByteArrayList, + result.reserve(source.size()); + for (const auto &v: source) + result.append(QVariant(v)); + return true; + ); + + QMETATYPE_CONVERTER(QStringList, QVariantList, + result.reserve(source.size()); + for (const auto &v: source) + result.append(v.toString()); + return true; + ); + QMETATYPE_CONVERTER(QVariantList, QStringList, + result.reserve(source.size()); + for (const auto &v: source) + result.append(QVariant(v)); + return true; + ); + + QMETATYPE_CONVERTER(QVariantHash, QVariantMap, + for (auto it = source.begin(); it != source.end(); ++it) + result.insert(it.key(), it.value()); + return true; + ); + QMETATYPE_CONVERTER(QVariantMap, QVariantHash, + for (auto it = source.begin(); it != source.end(); ++it) + result.insert(it.key(), it.value()); + return true; + ); +#endif // !QT_NO_VARIANT +#ifndef QT_BOOTSTRAPPED + QMETATYPE_CONVERTER_ASSIGN(QCborValue, QString); + QMETATYPE_CONVERTER(QString, QCborValue, + if (source.isContainer() || source.isTag()) + return false; + result = source.toVariant().toString(); + return true; + ); + QMETATYPE_CONVERTER_ASSIGN(QCborValue, QByteArray); + QMETATYPE_CONVERTER(QByteArray, QCborValue, + if (source.isByteArray()) { + result = source.toByteArray(); + return true; + } + return false; + ); + QMETATYPE_CONVERTER_ASSIGN(QCborValue, QUuid); + QMETATYPE_CONVERTER(QUuid, QCborValue, + if (!source.isUuid()) + return false; + result = source.toUuid(); + return true; + ); + QMETATYPE_CONVERTER(QCborValue, QVariantList, result = QCborArray::fromVariantList(source); return true;); + QMETATYPE_CONVERTER(QVariantList, QCborValue, + if (!source.isArray()) + return false; + result = source.toArray().toVariantList(); + return true; + ); + QMETATYPE_CONVERTER(QCborValue, QVariantMap, result = QCborMap::fromVariantMap(source); return true;); + QMETATYPE_CONVERTER(QVariantMap, QCborValue, + if (!source.isMap()) + return false; + result = source.toMap().toVariantMap(); + return true; + ); + QMETATYPE_CONVERTER(QCborValue, QVariantHash, result = QCborMap::fromVariantHash(source); return true;); + QMETATYPE_CONVERTER(QVariantHash, QCborValue, + if (!source.isMap()) + return false; + result = source.toMap().toVariantHash(); + return true; + ); +#if QT_CONFIG(regularexpression) + QMETATYPE_CONVERTER(QCborValue, QRegularExpression, result = QCborValue(source); return true;); + QMETATYPE_CONVERTER(QRegularExpression, QCborValue, + if (!source.isRegularExpression()) + return false; + result = source.toRegularExpression(); + return true; + ); +#endif + + QMETATYPE_CONVERTER(QCborValue, Nullptr, + Q_UNUSED(source); + result = QCborValue(QCborValue::Null); + return true; + ); + QMETATYPE_CONVERTER(Nullptr, QCborValue, + result = nullptr; + return source.isNull(); + ); + QMETATYPE_CONVERTER_ASSIGN(QCborValue, Bool); + QMETATYPE_CONVERTER_ASSIGN(QCborValue, Int); + QMETATYPE_CONVERTER_ASSIGN(QCborValue, UInt); + QMETATYPE_CONVERTER(QCborValue, ULong, result = qlonglong(source); return true;); + QMETATYPE_CONVERTER(QCborValue, Long, result = qlonglong(source); return true;); + QMETATYPE_CONVERTER_ASSIGN(QCborValue, LongLong); + QMETATYPE_CONVERTER(QCborValue, ULongLong, result = qlonglong(source); return true;); + QMETATYPE_CONVERTER_ASSIGN(QCborValue, UShort); + QMETATYPE_CONVERTER_ASSIGN(QCborValue, UChar); + QMETATYPE_CONVERTER_ASSIGN(QCborValue, Char); + QMETATYPE_CONVERTER_ASSIGN(QCborValue, SChar); + QMETATYPE_CONVERTER_ASSIGN(QCborValue, Short); + QMETATYPE_CONVERTER_ASSIGN(QCborValue, Double); + QMETATYPE_CONVERTER_ASSIGN(QCborValue, Float); + QMETATYPE_CONVERTER(QCborValue, QStringList, + result = QCborArray::fromStringList(source); + return true; + ); + QMETATYPE_CONVERTER(QCborValue, QDate, + result = QCborValue(source.startOfDay()); + return true; + ); + QMETATYPE_CONVERTER_ASSIGN(QCborValue, QUrl); + QMETATYPE_CONVERTER(QCborValue, QJsonValue, + result = QCborValue::fromJsonValue(source); + return true; + ); + QMETATYPE_CONVERTER(QCborValue, QJsonObject, + result = QCborMap::fromJsonObject(source); + return true; + ); + QMETATYPE_CONVERTER(QCborValue, QJsonArray, + result = QCborArray::fromJsonArray(source); + return true; + ); + QMETATYPE_CONVERTER(QCborValue, QJsonDocument, + QJsonDocument doc = source; + if (doc.isArray()) + result = QCborArray::fromJsonArray(doc.array()); + else + result = QCborMap::fromJsonObject(doc.object()); + return true; + ); + QMETATYPE_CONVERTER_ASSIGN(QCborValue, QCborMap); + QMETATYPE_CONVERTER_ASSIGN(QCborValue, QCborArray); + + QMETATYPE_CONVERTER_ASSIGN(QCborValue, QDateTime); + QMETATYPE_CONVERTER(QDateTime, QCborValue, + if (source.isDateTime()) { + result = source.toDateTime(); + return true; + } + return false; + ); + + QMETATYPE_CONVERTER_ASSIGN(QCborValue, QCborSimpleType); + QMETATYPE_CONVERTER(QCborSimpleType, QCborValue, + if (source.isSimpleType()) { + result = source.toSimpleType(); + return true; + } + return false; + ); + + QMETATYPE_CONVERTER(QCborArray, QVariantList, result = QCborArray::fromVariantList(source); return true;); + QMETATYPE_CONVERTER(QVariantList, QCborArray, result = source.toVariantList(); return true;); + QMETATYPE_CONVERTER(QCborArray, QStringList, result = QCborArray::fromStringList(source); return true;); + QMETATYPE_CONVERTER(QCborMap, QVariantMap, result = QCborMap::fromVariantMap(source); return true;); + QMETATYPE_CONVERTER(QVariantMap, QCborMap, result = source.toVariantMap(); return true;); + QMETATYPE_CONVERTER(QCborMap, QVariantHash, result = QCborMap::fromVariantHash(source); return true;); + QMETATYPE_CONVERTER(QVariantHash, QCborMap, result = source.toVariantHash(); return true;); + + QMETATYPE_CONVERTER(QCborArray, QCborValue, + if (!source.isArray()) + return false; + result = source.toArray(); + return true; + ); + QMETATYPE_CONVERTER(QCborArray, QJsonDocument, + if (!source.isArray()) + return false; + result = QCborArray::fromJsonArray(source.array()); + return true; + ); + QMETATYPE_CONVERTER(QCborArray, QJsonValue, + if (!source.isArray()) + return false; + result = QCborArray::fromJsonArray(source.toArray()); + return true; + ); + QMETATYPE_CONVERTER(QCborArray, QJsonArray, + result = QCborArray::fromJsonArray(source); + return true; + ); + QMETATYPE_CONVERTER(QCborMap, QCborValue, + if (!source.isMap()) + return false; + result = source.toMap(); + return true; + ); + QMETATYPE_CONVERTER(QCborMap, QJsonDocument, + if (source.isArray()) + return false; + result = QCborMap::fromJsonObject(source.object()); + return true; + ); + QMETATYPE_CONVERTER(QCborMap, QJsonValue, + if (!source.isObject()) + return false; + result = QCborMap::fromJsonObject(source.toObject()); + return true; + ); + QMETATYPE_CONVERTER(QCborMap, QJsonObject, + result = QCborMap::fromJsonObject(source); + return true; + ); + + + QMETATYPE_CONVERTER(QVariantList, QJsonValue, + if (!source.isArray()) + return false; + result = source.toArray().toVariantList(); + return true; + ); + QMETATYPE_CONVERTER(QVariantList, QJsonArray, result = source.toVariantList(); return true;); + QMETATYPE_CONVERTER(QVariantMap, QJsonValue, + if (!source.isObject()) + return false; + result = source.toObject().toVariantMap(); + return true; + ); + QMETATYPE_CONVERTER(QVariantMap, QJsonObject, result = source.toVariantMap(); return true;); + QMETATYPE_CONVERTER(QVariantHash, QJsonValue, + if (!source.isObject()) + return false; + result = source.toObject().toVariantHash(); + return true; + ); + QMETATYPE_CONVERTER(QVariantHash, QJsonObject, result = source.toVariantHash(); return true;); + + + QMETATYPE_CONVERTER(QJsonArray, QStringList, result = QJsonArray::fromStringList(source); return true;); + QMETATYPE_CONVERTER(QJsonArray, QVariantList, result = QJsonArray::fromVariantList(source); return true;); + QMETATYPE_CONVERTER(QJsonArray, QJsonValue, + if (!source.isArray()) + return false; + result = source.toArray(); + return true; + ); + QMETATYPE_CONVERTER(QJsonArray, QJsonDocument, + if (!source.isArray()) + return false; + result = source.array(); + return true; + ); + QMETATYPE_CONVERTER(QJsonArray, QCborValue, + if (!source.isArray()) + return false; + result = source.toArray().toJsonArray(); + return true; + ); + QMETATYPE_CONVERTER(QJsonArray, QCborArray, result = source.toJsonArray(); return true;); + QMETATYPE_CONVERTER(QJsonObject, QVariantMap, result = QJsonObject::fromVariantMap(source); return true;); + QMETATYPE_CONVERTER(QJsonObject, QVariantHash, result = QJsonObject::fromVariantHash(source); return true;); + QMETATYPE_CONVERTER(QJsonObject, QJsonValue, + if (!source.isObject()) + return false; + result = source.toObject(); + return true; + ); + QMETATYPE_CONVERTER(QJsonObject, QJsonDocument, + if (source.isArray()) + return false; + result = source.object(); + return true; + ); + QMETATYPE_CONVERTER(QJsonObject, QCborValue, + if (!source.isMap()) + return false; + result = source.toMap().toJsonObject(); + return true; + ); + QMETATYPE_CONVERTER(QJsonObject, QCborMap, result = source.toJsonObject(); return true; ); + + QMETATYPE_CONVERTER(QJsonValue, Nullptr, + Q_UNUSED(source); + result = QJsonValue(QJsonValue::Null); + return true; + ); + QMETATYPE_CONVERTER(Nullptr, QJsonValue, + result = nullptr; + return source.isNull(); + ); + QMETATYPE_CONVERTER(QJsonValue, Bool, + result = QJsonValue(source); + return true;); + QMETATYPE_CONVERTER_ASSIGN_DOUBLE(QJsonValue, Int); + QMETATYPE_CONVERTER_ASSIGN_DOUBLE(QJsonValue, UInt); + QMETATYPE_CONVERTER_ASSIGN_DOUBLE(QJsonValue, Double); + QMETATYPE_CONVERTER_ASSIGN_DOUBLE(QJsonValue, Float); + QMETATYPE_CONVERTER_ASSIGN_DOUBLE(QJsonValue, ULong); + QMETATYPE_CONVERTER_ASSIGN_DOUBLE(QJsonValue, Long); + QMETATYPE_CONVERTER_ASSIGN_DOUBLE(QJsonValue, LongLong); + QMETATYPE_CONVERTER_ASSIGN_DOUBLE(QJsonValue, ULongLong); + QMETATYPE_CONVERTER_ASSIGN_DOUBLE(QJsonValue, UShort); + QMETATYPE_CONVERTER_ASSIGN_DOUBLE(QJsonValue, UChar); + QMETATYPE_CONVERTER_ASSIGN_DOUBLE(QJsonValue, Char); + QMETATYPE_CONVERTER_ASSIGN_DOUBLE(QJsonValue, SChar); + QMETATYPE_CONVERTER_ASSIGN_DOUBLE(QJsonValue, Short); + QMETATYPE_CONVERTER_ASSIGN(QJsonValue, QString); + QMETATYPE_CONVERTER(QJsonValue, QStringList, + result = QJsonValue(QJsonArray::fromStringList(source)); + return true; + ); + QMETATYPE_CONVERTER(QJsonValue, QVariantList, + result = QJsonValue(QJsonArray::fromVariantList(source)); + return true; + ); + QMETATYPE_CONVERTER(QJsonValue, QVariantMap, + result = QJsonValue(QJsonObject::fromVariantMap(source)); + return true; + ); + QMETATYPE_CONVERTER(QJsonValue, QVariantHash, + result = QJsonValue(QJsonObject::fromVariantHash(source)); + return true; + ); + QMETATYPE_CONVERTER(QJsonValue, QJsonObject, + result = source; + return true; + ); + QMETATYPE_CONVERTER(QJsonValue, QJsonArray, + result = source; + return true; + ); + QMETATYPE_CONVERTER(QJsonValue, QJsonDocument, + QJsonDocument doc = source; + result = doc.isArray() ? QJsonValue(doc.array()) : QJsonValue(doc.object()); + return true; + ); + QMETATYPE_CONVERTER(QJsonValue, QCborValue, + result = source.toJsonValue(); + return true; + ); + QMETATYPE_CONVERTER(QJsonValue, QCborMap, + result = source.toJsonObject(); + return true; + ); + QMETATYPE_CONVERTER(QJsonValue, QCborArray, + result = source.toJsonArray(); + return true; + ); + +#endif + + QMETATYPE_CONVERTER(QDate, QDateTime, result = source.date(); return true;); + QMETATYPE_CONVERTER(QTime, QDateTime, result = source.time(); return true;); + QMETATYPE_CONVERTER(QDateTime, QDate, result = source.startOfDay(); return true;); +#if QT_CONFIG(datestring) + QMETATYPE_CONVERTER(QDate, QString, + result = QDate::fromString(source, Qt::ISODate); + return result.isValid(); + ); + QMETATYPE_CONVERTER(QTime, QString, + result = QTime::fromString(source, Qt::ISODate); + return result.isValid(); + ); + QMETATYPE_CONVERTER(QDateTime, QString, + result = QDateTime::fromString(source, Qt::ISODate); + return result.isValid(); + ); +#endif + + } + return false; + } +} metatypeHelper = {}; + +Q_CONSTINIT Q_CORE_EXPORT const QMetaTypeModuleHelper *qMetaTypeGuiHelper = nullptr; +Q_CONSTINIT Q_CORE_EXPORT const QMetaTypeModuleHelper *qMetaTypeWidgetsHelper = nullptr; + +static const QMetaTypeModuleHelper *qModuleHelperForType(int type) +{ + if (type <= QMetaType::LastCoreType) + return &metatypeHelper; + if (type >= QMetaType::FirstGuiType && type <= QMetaType::LastGuiType) + return qMetaTypeGuiHelper; + else if (type >= QMetaType::FirstWidgetsType && type <= QMetaType::LastWidgetsType) + return qMetaTypeWidgetsHelper; + return nullptr; +} template<typename T, typename Key> class QMetaTypeFunctionRegistry @@ -582,20 +1688,22 @@ public: return map.contains(k); } - bool insertIfNotContains(Key k, const T *f) + bool insertIfNotContains(Key k, const T &f) { const QWriteLocker locker(&lock); - const T* &fun = map[k]; - if (fun) + const qsizetype oldSize = map.size(); + auto &e = map[k]; + if (map.size() == oldSize) // already present return false; - fun = f; + e = f; return true; } const T *function(Key k) const { const QReadLocker locker(&lock); - return map.value(k, nullptr); + auto it = map.find(k); + return it == map.end() ? nullptr : std::addressof(*it); } void remove(int from, int to) @@ -606,93 +1714,107 @@ public: } private: mutable QReadWriteLock lock; - QHash<Key, const T *> map; + QHash<Key, T> map; }; -typedef QMetaTypeFunctionRegistry<QtPrivate::AbstractConverterFunction,QPair<int,int> > -QMetaTypeConverterRegistry; -typedef QMetaTypeFunctionRegistry<QtPrivate::AbstractComparatorFunction,int> -QMetaTypeComparatorRegistry; -typedef QMetaTypeFunctionRegistry<QtPrivate::AbstractDebugStreamFunction,int> -QMetaTypeDebugStreamRegistry; +using QMetaTypeConverterRegistry + = QMetaTypeFunctionRegistry<QMetaType::ConverterFunction, std::pair<int,int>>; -Q_STATIC_ASSERT(std::is_pod<QMetaTypeInterface>::value); - -Q_DECLARE_TYPEINFO(QCustomTypeInfo, Q_MOVABLE_TYPE); -Q_GLOBAL_STATIC(QVector<QCustomTypeInfo>, customTypes) -Q_GLOBAL_STATIC(QReadWriteLock, customTypesLock) Q_GLOBAL_STATIC(QMetaTypeConverterRegistry, customTypesConversionRegistry) -Q_GLOBAL_STATIC(QMetaTypeComparatorRegistry, customTypesComparatorRegistry) -Q_GLOBAL_STATIC(QMetaTypeDebugStreamRegistry, customTypesDebugStreamRegistry) + +using QMetaTypeMutableViewRegistry + = QMetaTypeFunctionRegistry<QMetaType::MutableViewFunction, std::pair<int,int>>; +Q_GLOBAL_STATIC(QMetaTypeMutableViewRegistry, customTypesMutableViewRegistry) /*! - \fn bool QMetaType::registerConverter() + \fn template<typename From, typename To> bool QMetaType::registerConverter() \since 5.2 Registers the possibility of an implicit conversion from type From to type To in the meta type system. Returns \c true if the registration succeeded, otherwise false. + + \snippet qmetatype/registerConverters.cpp implicit */ /*! - \fn template<typename MemberFunction, int> bool QMetaType::registerConverter(MemberFunction function) + \fn template<typename From, typename To> static bool QMetaType::registerConverter(To(From::*function)() const) \since 5.2 \overload Registers a method \a function like To From::function() const as converter from type From to type To in the meta type system. Returns \c true if the registration succeeded, otherwise false. + + \snippet qmetatype/registerConverters.cpp member */ /*! - \fn template<typename MemberFunctionOk, char> bool QMetaType::registerConverter(MemberFunctionOk function) + \fn template<typename From, typename To> static bool QMetaType::registerConverter(To(From::*function)(bool*) const) \since 5.2 \overload Registers a method \a function like To From::function(bool *ok) const as converter from type From to type To in the meta type system. Returns \c true if the registration succeeded, otherwise false. + + The \c ok pointer can be used by the function to indicate whether the conversion succeeded. + \snippet qmetatype/registerConverters.cpp memberOk + */ /*! - \fn template<typename UnaryFunction> bool QMetaType::registerConverter(UnaryFunction function) + \fn template<typename From, typename To, typename UnaryFunction> static bool QMetaType::registerConverter(UnaryFunction function) \since 5.2 \overload Registers a unary function object \a function as converter from type From to type To in the meta type system. Returns \c true if the registration succeeded, otherwise false. + + \a function must take an instance of type \c From and return an instance of \c To. It can be a function + pointer, a lambda or a functor object. Since Qt 6.5, the \a function can also return an instance of + \c std::optional<To> to be able to indicate failed conversions. + \snippet qmetatype/registerConverters.cpp unaryfunc */ /*! - \fn bool QMetaType::registerComparators() - \since 5.2 - Registers comparison operators for the user-registered type T. This requires T to have - both an operator== and an operator<. + Registers function \a f as converter function from type id \a from to \a to. + If there's already a conversion registered, this does nothing but deleting \a f. Returns \c true if the registration succeeded, otherwise false. + \since 5.2 + \internal */ +bool QMetaType::registerConverterFunction(const ConverterFunction &f, QMetaType from, QMetaType to) +{ + if (!customTypesConversionRegistry()->insertIfNotContains({from.id(), to.id()}, f)) { + qWarning("Type conversion already registered from type %s to type %s", + from.name(), to.name()); + return false; + } + return true; +} /*! - \fn bool QMetaType::registerEqualsComparator() - \since 5.5 - Registers equals operator for the user-registered type T. This requires T to have - an operator==. - Returns \c true if the registration succeeded, otherwise false. + \fn template<typename From, typename To> static bool QMetaType::registerMutableView(To(From::*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. */ -#ifndef QT_NO_DEBUG_STREAM /*! - \fn bool QMetaType::registerDebugStreamOperator() - Registers the debug stream operator for the user-registered type T. This requires T to have - an operator<<(QDebug dbg, T). - Returns \c true if the registration succeeded, otherwise false. + \fn template<typename From, typename To, typename UnaryFunction> static 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. */ -#endif /*! - Registers function \a f as converter function from type id \a from to \a to. - If there's already a conversion registered, this does nothing but deleting \a f. - Returns \c true if the registration succeeded, otherwise false. - \since 5.2 + 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::registerConverterFunction(const QtPrivate::AbstractConverterFunction *f, int from, int to) +bool QMetaType::registerMutableViewFunction(const MutableViewFunction &f, QMetaType from, QMetaType to) { - if (!customTypesConversionRegistry()->insertIfNotContains(qMakePair(from, to), f)) { - qWarning("Type conversion already registered from type %s to type %s", - QMetaType::typeName(from), QMetaType::typeName(to)); + if (!customTypesMutableViewRegistry()->insertIfNotContains({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; @@ -700,645 +1822,946 @@ bool QMetaType::registerConverterFunction(const QtPrivate::AbstractConverterFunc /*! \internal - - Invoked automatically when a converter function object is destroyed. */ -void QMetaType::unregisterConverterFunction(int from, int to) +void QMetaType::unregisterMutableViewFunction(QMetaType from, QMetaType to) { - if (customTypesConversionRegistry.isDestroyed()) + if (customTypesMutableViewRegistry.isDestroyed()) return; - customTypesConversionRegistry()->remove(from, to); + customTypesMutableViewRegistry()->remove(from.id(), to.id()); } -bool QMetaType::registerComparatorFunction(const QtPrivate::AbstractComparatorFunction *f, int type) +/*! + \internal + + Invoked automatically when a converter function object is destroyed. + */ +void QMetaType::unregisterConverterFunction(QMetaType from, QMetaType to) { - if (!customTypesComparatorRegistry()->insertIfNotContains(type, f)) { - qWarning("Comparators already registered for type %s", QMetaType::typeName(type)); - return false; - } - return true; + if (customTypesConversionRegistry.isDestroyed()) + return; + customTypesConversionRegistry()->remove(from.id(), to.id()); } -/*! - \fn bool QMetaType::hasRegisteredComparators() - Returns \c true, if the meta type system has registered comparators for type T. - \since 5.2 - */ +#ifndef QT_NO_DEBUG_STREAM /*! - Returns \c true, if the meta type system has registered comparators for type id \a typeId. - \since 5.2 - */ -bool QMetaType::hasRegisteredComparators(int typeId) + \fn QDebug QMetaType::operator<<(QDebug d, QMetaType m) + \since 6.5 + Writes the QMetaType \a m to the stream \a d, and returns the stream. +*/ +QDebug operator<<(QDebug d, QMetaType m) { - return customTypesComparatorRegistry()->contains(typeId); + const QDebugStateSaver saver(d); + return d.nospace() << "QMetaType(" << m.name() << ")"; } -#ifndef QT_NO_DEBUG_STREAM -bool QMetaType::registerDebugStreamOperatorFunction(const QtPrivate::AbstractDebugStreamFunction *f, - int type) +/*! + Streams the object at \a rhs to the debug stream \a dbg. Returns \c true + on success, otherwise false. + \since 5.2 +*/ +bool QMetaType::debugStream(QDebug& dbg, const void *rhs) { - if (!customTypesDebugStreamRegistry()->insertIfNotContains(type, f)) { - qWarning("Debug stream operator already registered for type %s", QMetaType::typeName(type)); - return false; + if (d_ptr && d_ptr->flags & QMetaType::IsPointer) { + dbg << *reinterpret_cast<const void * const *>(rhs); + return true; } - return true; + if (d_ptr && d_ptr->debugStream) { + d_ptr->debugStream(d_ptr, dbg, rhs); + return true; + } + return false; } /*! - \fn bool QMetaType::hasRegisteredDebugStreamOperator() - Returns \c true, if the meta type system has a registered debug stream operator for type T. + \fn bool QMetaType::debugStream(QDebug& dbg, const void *rhs, int typeId) + \overload + \deprecated +*/ + +/*! + \fn template<typename T> bool QMetaType::hasRegisteredDebugStreamOperator() + \deprecated \since 5.2 + + Returns \c true, if the meta type system has a registered debug stream operator for type T. */ /*! + \fn bool QMetaType::hasRegisteredDebugStreamOperator(int typeId) + \deprecated Use QMetaType::hasRegisteredDebugStreamOperator() instead. + Returns \c true, if the meta type system has a registered debug stream operator for type id \a typeId. \since 5.2 */ -bool QMetaType::hasRegisteredDebugStreamOperator(int typeId) + +/*! + \since 6.0 + + Returns \c true, if the meta type system has a registered debug stream operator for this + meta type. +*/ +bool QMetaType::hasRegisteredDebugStreamOperator() const { - return customTypesDebugStreamRegistry()->contains(typeId); + return d_ptr && d_ptr->debugStream != nullptr; } #endif +#ifndef QT_NO_QOBJECT /*! - Converts the object at \a from from \a fromTypeId to the preallocated space at \a to - typed \a toTypeId. Returns \c true, if the conversion succeeded, otherwise false. - \since 5.2 -*/ -bool QMetaType::convert(const void *from, int fromTypeId, void *to, int toTypeId) + \internal + returns a QMetaEnum for a given meta tape type id if possible +*/ +static QMetaEnum metaEnumFromType(QMetaType t) +{ + if (t.flags() & QMetaType::IsEnumeration) { + if (const QMetaObject *metaObject = t.metaObject()) { + QByteArrayView qflagsNamePrefix = "QFlags<"; + QByteArray enumName = t.name(); + if (enumName.endsWith('>') && enumName.startsWith(qflagsNamePrefix)) { + // extract the template argument + enumName.chop(1); + enumName = enumName.sliced(qflagsNamePrefix.size()); + } + if (qsizetype lastColon = enumName.lastIndexOf(':'); lastColon != -1) + enumName = enumName.sliced(lastColon + 1); + return metaObject->enumerator(metaObject->indexOfEnumerator(enumName)); + } + } + return QMetaEnum(); +} +#endif + +static bool convertFromEnum(QMetaType fromType, const void *from, QMetaType toType, void *to) { - const QtPrivate::AbstractConverterFunction * const f = - customTypesConversionRegistry()->function(qMakePair(fromTypeId, toTypeId)); - return f && f->convert(f, from, to); + qlonglong ll; + if (fromType.flags() & QMetaType::IsUnsignedEnumeration) { + qulonglong ull; + switch (fromType.sizeOf()) { + case 1: + ull = *static_cast<const unsigned char *>(from); + break; + case 2: + ull = *static_cast<const unsigned short *>(from); + break; + case 4: + ull = *static_cast<const unsigned int *>(from); + break; + case 8: + ull = *static_cast<const quint64 *>(from); + break; + default: + Q_UNREACHABLE(); + } + if (toType.id() == QMetaType::ULongLong) { + *static_cast<qulonglong *>(to) = ull; + return true; + } + if (toType.id() != QMetaType::QString && toType.id() != QMetaType::QByteArray) + return QMetaType::convert(QMetaType::fromType<qulonglong>(), &ull, toType, to); + ll = qlonglong(ull); + } else { + switch (fromType.sizeOf()) { + case 1: + ll = *static_cast<const signed char *>(from); + break; + case 2: + ll = *static_cast<const short *>(from); + break; + case 4: + ll = *static_cast<const int *>(from); + break; + case 8: + ll = *static_cast<const qint64 *>(from); + break; + default: + Q_UNREACHABLE(); + } + if (toType.id() == QMetaType::LongLong) { + *static_cast<qlonglong *>(to) = ll; + return true; + } + if (toType.id() != QMetaType::QString && toType.id() != QMetaType::QByteArray) + return QMetaType::convert(QMetaType::fromType<qlonglong>(), &ll, toType, to); + } +#ifndef QT_NO_QOBJECT + QMetaEnum en = metaEnumFromType(fromType); + if (en.isValid()) { + if (en.isFlag()) { + const QByteArray keys = en.valueToKeys(static_cast<int>(ll)); + if (toType.id() == QMetaType::QString) + *static_cast<QString *>(to) = QString::fromUtf8(keys); + else + *static_cast<QByteArray *>(to) = keys; + } else { + const char *key = en.valueToKey(static_cast<int>(ll)); + if (toType.id() == QMetaType::QString) + *static_cast<QString *>(to) = QString::fromUtf8(key); + else + *static_cast<QByteArray *>(to) = key; + } + return true; + } +#endif + if (toType.id() == QMetaType::QString || toType.id() == QMetaType::QByteArray) + return QMetaType::convert(QMetaType::fromType<qlonglong>(), &ll, toType, to); + return false; } -/*! - Compares the objects at \a lhs and \a rhs. Both objects need to be of type \a typeId. - \a result is set to less than, equal to or greater than zero, if \a lhs is less than, equal to - or greater than \a rhs. Returns \c true, if the comparison succeeded, otherwise \c false. - \since 5.2 -*/ -bool QMetaType::compare(const void *lhs, const void *rhs, int typeId, int* result) +static bool convertToEnum(QMetaType fromType, const void *from, QMetaType toType, void *to) { - const QtPrivate::AbstractComparatorFunction * const f = - customTypesComparatorRegistry()->function(typeId); - if (!f) - return false; - if (f->equals(f, lhs, rhs)) - *result = 0; - else if (f->lessThan) - *result = f->lessThan(f, lhs, rhs) ? -1 : 1; - else + int fromTypeId = fromType.id(); + qlonglong value = -1; + bool ok = false; +#ifndef QT_NO_QOBJECT + if (fromTypeId == QMetaType::QString || fromTypeId == QMetaType::QByteArray) { + QMetaEnum en = metaEnumFromType(toType); + if (en.isValid()) { + QByteArray keys = (fromTypeId == QMetaType::QString) + ? static_cast<const QString *>(from)->toUtf8() + : *static_cast<const QByteArray *>(from); + value = en.keysToValue(keys.constData(), &ok); + } + } +#endif + if (!ok) { + if (fromTypeId == QMetaType::LongLong) { + value = *static_cast<const qlonglong *>(from); + ok = true; + } else { + ok = QMetaType::convert(fromType, from, QMetaType::fromType<qlonglong>(), &value); + } + } + + if (!ok) return false; - return true; + + switch (toType.sizeOf()) { + case 1: + *static_cast<signed char *>(to) = value; + return true; + case 2: + *static_cast<qint16 *>(to) = value; + return true; + case 4: + *static_cast<qint32 *>(to) = value; + return true; + case 8: + *static_cast<qint64 *>(to) = value; + return true; + default: + Q_UNREACHABLE_RETURN(false); + } } -/*! - Compares the objects at \a lhs and \a rhs. Both objects need to be of type \a typeId. - \a result is set to zero, if \a lhs equals to rhs. Returns \c true, if the comparison - succeeded, otherwise \c false. - \since 5.5 -*/ -bool QMetaType::equals(const void *lhs, const void *rhs, int typeId, int *result) +#ifndef QT_BOOTSTRAPPED +static bool convertIterableToVariantList(QMetaType fromType, const void *from, void *to) { - const QtPrivate::AbstractComparatorFunction * const f - = customTypesComparatorRegistry()->function(typeId); - if (!f) + QSequentialIterable list; + if (!QMetaType::convert(fromType, from, QMetaType::fromType<QSequentialIterable>(), &list)) return false; - if (f->equals(f, lhs, rhs)) - *result = 0; - else - *result = -1; + + QVariantList &l = *static_cast<QVariantList *>(to); + l.clear(); + l.reserve(list.size()); + auto end = list.end(); + for (auto it = list.begin(); it != end; ++it) + l << *it; return true; } -/*! - Streams the object at \a rhs of type \a typeId to the debug stream \a dbg. Returns \c true - on success, otherwise false. - \since 5.2 -*/ -bool QMetaType::debugStream(QDebug& dbg, const void *rhs, int typeId) +static bool convertIterableToVariantMap(QMetaType fromType, const void *from, void *to) { - const QtPrivate::AbstractDebugStreamFunction * const f = customTypesDebugStreamRegistry()->function(typeId); - if (!f) + QAssociativeIterable map; + if (!QMetaType::convert(fromType, from, QMetaType::fromType<QAssociativeIterable>(), &map)) return false; - f->stream(f, dbg, rhs); + + QVariantMap &h = *static_cast<QVariantMap *>(to); + h.clear(); + auto end = map.end(); + for (auto it = map.begin(); it != end; ++it) + h.insert(it.key().toString(), it.value()); return true; } -/*! - \fn bool QMetaType::hasRegisteredConverterFunction() - Returns \c true, if the meta type system has a registered conversion from type From to type To. - \since 5.2 - \overload - */ - -/*! - Returns \c true, if the meta type system has a registered conversion from meta type id \a fromTypeId - to \a toTypeId - \since 5.2 -*/ -bool QMetaType::hasRegisteredConverterFunction(int fromTypeId, int toTypeId) +static bool convertIterableToVariantHash(QMetaType fromType, const void *from, void *to) { - return customTypesConversionRegistry()->contains(qMakePair(fromTypeId, toTypeId)); -} + QAssociativeIterable map; + if (!QMetaType::convert(fromType, from, QMetaType::fromType<QAssociativeIterable>(), &map)) + return false; -#ifndef QT_NO_DATASTREAM -/*! - \internal -*/ -void QMetaType::registerStreamOperators(const char *typeName, SaveOperator saveOp, - LoadOperator loadOp) -{ - registerStreamOperators(type(typeName), saveOp, loadOp); + QVariantHash &h = *static_cast<QVariantHash *>(to); + h.clear(); + h.reserve(map.size()); + auto end = map.end(); + for (auto it = map.begin(); it != end; ++it) + h.insert(it.key().toString(), it.value()); + return true; } -/*! - \internal -*/ -void QMetaType::registerStreamOperators(int idx, SaveOperator saveOp, - LoadOperator loadOp) +static bool convertIterableToVariantPair(QMetaType fromType, const void *from, void *to) { - if (idx < User) - return; //builtin types should not be registered; - QVector<QCustomTypeInfo> *ct = customTypes(); - if (!ct) - return; - QWriteLocker locker(customTypesLock()); - QCustomTypeInfo &inf = (*ct)[idx - User]; - inf.saveOp = saveOp; - inf.loadOp = loadOp; -} -#endif // QT_NO_DATASTREAM - -// We don't officially support constexpr in MSVC 2015, but the limited support it -// has is enough for the code below. + const int targetId = qMetaTypeId<QtMetaTypePrivate::QPairVariantInterfaceImpl>(); + const auto f = customTypesConversionRegistry()->function({fromType.id(), targetId}); -#define STRINGIFY_TYPE_NAME(MetaTypeName, TypeId, RealName) \ - #RealName "\0" -#define CALCULATE_TYPE_LEN(MetaTypeName, TypeId, RealName) \ - short(sizeof(#RealName)), -#define MAP_TYPE_ID_TO_IDX(MetaTypeName, TypeId, RealName) \ - TypeId, + if (!f) + return false; -namespace { -// All type names in one long string. -constexpr char metaTypeStrings[] = QT_FOR_EACH_STATIC_TYPE(STRINGIFY_TYPE_NAME); + QtMetaTypePrivate::QPairVariantInterfaceImpl pi; + (*f)(from, &pi); -// The sizes of the strings in the metaTypeStrings string (including terminating null) -constexpr short metaTypeNameSizes[] = { - QT_FOR_EACH_STATIC_TYPE(CALCULATE_TYPE_LEN) -}; + QVariant v1(pi._metaType_first); + void *dataPtr; + if (pi._metaType_first == QMetaType::fromType<QVariant>()) + dataPtr = &v1; + else + dataPtr = v1.data(); + pi.first(dataPtr); -// The type IDs, in the order of the metaTypeStrings data -constexpr short metaTypeIds[] = { - QT_FOR_EACH_STATIC_TYPE(MAP_TYPE_ID_TO_IDX) -}; + QVariant v2(pi._metaType_second); + if (pi._metaType_second == QMetaType::fromType<QVariant>()) + dataPtr = &v2; + else + dataPtr = v2.data(); + pi.second(dataPtr); -constexpr int MetaTypeNameCount = sizeof(metaTypeNameSizes) / sizeof(metaTypeNameSizes[0]); + *static_cast<QVariantPair *>(to) = QVariantPair(v1, v2); + return true; +} -template <typename IntegerSequence> struct MetaTypeOffsets; -template <int... TypeIds> struct MetaTypeOffsets<QtPrivate::IndexesList<TypeIds...>> +static bool convertToSequentialIterable(QMetaType fromType, const void *from, void *to) { - // This would have been a lot easier if the meta types that the macro - // QT_FOR_EACH_STATIC_TYPE declared were in sorted, ascending order, but - // they're not (i.e., the first one declared is QMetaType::Void == 43, - // followed by QMetaType::Bool == 1)... As a consequence, we need to use - // the C++11 constexpr function calculateOffsetForTypeId below in order to - // create the offset array. + using namespace QtMetaTypePrivate; + const int fromTypeId = fromType.id(); - static constexpr int findTypeId(int typeId, int i = 0) - { - return i >= MetaTypeNameCount ? -1 : - metaTypeIds[i] == typeId ? i : findTypeId(typeId, i + 1); + QSequentialIterable &i = *static_cast<QSequentialIterable *>(to); + switch (fromTypeId) { + case QMetaType::QVariantList: + i = QSequentialIterable(reinterpret_cast<const QVariantList *>(from)); + return true; + case QMetaType::QStringList: + i = QSequentialIterable(reinterpret_cast<const QStringList *>(from)); + return true; + case QMetaType::QByteArrayList: + i = QSequentialIterable(reinterpret_cast<const QByteArrayList *>(from)); + return true; + case QMetaType::QString: + i = QSequentialIterable(reinterpret_cast<const QString *>(from)); + return true; + case QMetaType::QByteArray: + i = QSequentialIterable(reinterpret_cast<const QByteArray *>(from)); + return true; + default: { + QSequentialIterable impl; + if (QMetaType::convert( + fromType, from, QMetaType::fromType<QIterable<QMetaSequence>>(), &impl)) { + i = std::move(impl); + return true; + } + } } - static constexpr short calculateOffsetForIdx(int i) - { - return i < 0 ? -1 : - i == 0 ? 0 : metaTypeNameSizes[i - 1] + calculateOffsetForIdx(i - 1); + return false; +} + +static bool canConvertToSequentialIterable(QMetaType fromType) +{ + switch (fromType.id()) { + case QMetaType::QVariantList: + case QMetaType::QStringList: + case QMetaType::QByteArrayList: + case QMetaType::QString: + case QMetaType::QByteArray: + return true; + default: + return QMetaType::canConvert(fromType, QMetaType::fromType<QIterable<QMetaSequence>>()); } +} - static constexpr short calculateOffsetForTypeId(int typeId) - { - return calculateOffsetForIdx(findTypeId(typeId)); -#if 0 - // same as, but this is only valid in C++14: - short offset = 0; - for (int i = 0; i < MetaTypeNameCount; ++i) { - if (metaTypeIds[i] == typeId) - return offset; - offset += metaTypeNameSizes[i]; - } - return -1; -#endif +static bool canImplicitlyViewAsSequentialIterable(QMetaType fromType) +{ + switch (fromType.id()) { + case QMetaType::QVariantList: + case QMetaType::QStringList: + case QMetaType::QByteArrayList: + case QMetaType::QString: + case QMetaType::QByteArray: + return true; + default: + return QMetaType::canView( + fromType, QMetaType::fromType<QIterable<QMetaSequence>>()); } +} - short offsets[sizeof...(TypeIds)]; - constexpr MetaTypeOffsets() : offsets{calculateOffsetForTypeId(TypeIds)...} {} +static bool viewAsSequentialIterable(QMetaType fromType, void *from, void *to) +{ + using namespace QtMetaTypePrivate; + const int fromTypeId = fromType.id(); - const char *operator[](int typeId) const noexcept - { - short o = offsets[typeId]; - return o < 0 ? nullptr : metaTypeStrings + o; + QSequentialIterable &i = *static_cast<QSequentialIterable *>(to); + switch (fromTypeId) { + case QMetaType::QVariantList: + i = QSequentialIterable(reinterpret_cast<QVariantList *>(from)); + return true; + case QMetaType::QStringList: + i = QSequentialIterable(reinterpret_cast<QStringList *>(from)); + return true; + case QMetaType::QByteArrayList: + i = QSequentialIterable(reinterpret_cast<QByteArrayList *>(from)); + return true; + case QMetaType::QString: + i = QSequentialIterable(reinterpret_cast<QString *>(from)); + return true; + case QMetaType::QByteArray: + i = QSequentialIterable(reinterpret_cast<QByteArray *>(from)); + return true; + default: { + QIterable<QMetaSequence> j(QMetaSequence(), nullptr); + if (QMetaType::view( + fromType, from, QMetaType::fromType<QIterable<QMetaSequence>>(), &j)) { + i = std::move(j); + return true; + } + } } -}; -} // anonymous namespace - -constexpr MetaTypeOffsets<QtPrivate::Indexes<QMetaType::HighestInternalId + 1>::Value> metaTypeNames {}; -#undef STRINGIFY_TYPE_NAME -#undef CALCULATE_TYPE_LEN -#undef MAP_TYPE_ID_TO_IDX -/*! - Returns the type name associated with the given \a typeId, or a null - pointer if no matching type was found. The returned pointer must not be - deleted. + return false; +} - \sa type(), isRegistered(), Type, name() -*/ -const char *QMetaType::typeName(int typeId) +static bool convertToAssociativeIterable(QMetaType fromType, const void *from, void *to) { - const uint type = typeId; - if (Q_LIKELY(type <= QMetaType::HighestInternalId)) { - return metaTypeNames[typeId]; - } else if (Q_UNLIKELY(type < QMetaType::User)) { - return nullptr; // It can happen when someone cast int to QVariant::Type, we should not crash... + using namespace QtMetaTypePrivate; + + QAssociativeIterable &i = *static_cast<QAssociativeIterable *>(to); + if (fromType.id() == QMetaType::QVariantMap) { + i = QAssociativeIterable(reinterpret_cast<const QVariantMap *>(from)); + return true; + } + if (fromType.id() == QMetaType::QVariantHash) { + i = QAssociativeIterable(reinterpret_cast<const QVariantHash *>(from)); + return true; } - const QVector<QCustomTypeInfo> * const ct = customTypes(); - QReadLocker locker(customTypesLock()); - return ct && uint(ct->count()) > type - QMetaType::User && !ct->at(type - QMetaType::User).typeName.isEmpty() - ? ct->at(type - QMetaType::User).typeName.constData() - : nullptr; + QAssociativeIterable impl; + if (QMetaType::convert( + fromType, from, QMetaType::fromType<QIterable<QMetaAssociation>>(), &impl)) { + i = std::move(impl); + return true; + } -#undef QT_METATYPE_TYPEID_TYPENAME_CONVERTER + return false; } -/*! - \since 5.15 +static bool canConvertMetaObject(QMetaType fromType, QMetaType toType) +{ + if ((fromType.flags() & QMetaType::IsPointer) != (toType.flags() & QMetaType::IsPointer)) + return false; // Can not convert between pointer and value - Returns the type name associated with this QMetaType, or a null - pointer if no matching type was found. The returned pointer must not be - deleted. + const QMetaObject *f = fromType.metaObject(); + const QMetaObject *t = toType.metaObject(); + if (f && t) { + return f->inherits(t) || (t->inherits(f)); + } + return false; +} - \sa typeName() -*/ -QByteArray QMetaType::name() const +static bool canConvertToAssociativeIterable(QMetaType fromType) { - return QMetaType::typeName(m_typeId); + switch (fromType.id()) { + case QMetaType::QVariantMap: + case QMetaType::QVariantHash: + return true; + default: + return QMetaType::canConvert(fromType, QMetaType::fromType<QIterable<QMetaAssociation>>()); + } } -/* - Similar to QMetaType::type(), but only looks in the static set of types. -*/ -static inline int qMetaTypeStaticType(const char *typeName, int length) +static bool canImplicitlyViewAsAssociativeIterable(QMetaType fromType) { - int i = 0; - while (types[i].typeName && ((length != types[i].typeNameLength) - || memcmp(typeName, types[i].typeName, length))) { - ++i; + switch (fromType.id()) { + case QMetaType::QVariantMap: + case QMetaType::QVariantHash: + return true; + default: + return QMetaType::canView( + fromType, QMetaType::fromType<QIterable<QMetaAssociation>>()); } - return types[i].type; } -/* - Similar to QMetaType::type(), but only looks in the custom set of - types, and doesn't lock the mutex. - The extra \a firstInvalidIndex parameter is an easy way to avoid - iterating over customTypes() a second time in registerNormalizedType(). -*/ -static int qMetaTypeCustomType_unlocked(const char *typeName, int length, int *firstInvalidIndex = nullptr) +static bool viewAsAssociativeIterable(QMetaType fromType, void *from, void *to) { - const QVector<QCustomTypeInfo> * const ct = customTypes(); - if (!ct) - return QMetaType::UnknownType; + using namespace QtMetaTypePrivate; + int fromTypeId = fromType.id(); - if (firstInvalidIndex) - *firstInvalidIndex = -1; - for (int v = 0; v < ct->count(); ++v) { - const QCustomTypeInfo &customInfo = ct->at(v); - if ((length == customInfo.typeName.size()) - && !memcmp(typeName, customInfo.typeName.constData(), length)) { - if (customInfo.alias >= 0) - return customInfo.alias; - return v + QMetaType::User; + 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(QMetaAssociation(), nullptr); + if (QMetaType::view( + fromType, from, QMetaType::fromType<QIterable<QMetaAssociation>>(), &j)) { + i = std::move(j); + return true; + } + + return false; +} + +static bool convertMetaObject(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 if ((fromType.flags() & QMetaType::IsPointer) == (toType.flags() & QMetaType::IsPointer)) { + // fromType and toType are of same 'pointedness' + const QMetaObject *f = fromType.metaObject(); + const QMetaObject *t = toType.metaObject(); + if (f && t && f->inherits(t)) { + toType.destruct(to); + toType.construct(to, from); + return true; } - if (firstInvalidIndex && (*firstInvalidIndex < 0) && customInfo.typeName.isEmpty()) - *firstInvalidIndex = v; } - return QMetaType::UnknownType; + return false; } +#endif // !QT_BOOTSTRAPPED /*! - \internal + \fn bool QMetaType::convert(const void *from, int fromTypeId, void *to, int toTypeId) + \deprecated - This function is needed until existing code outside of qtbase - has been changed to call the new version of registerType(). - */ -int QMetaType::registerType(const char *typeName, Deleter deleter, - Creator creator) -{ - return registerType(typeName, deleter, creator, - QtMetaTypePrivate::QMetaTypeFunctionHelper<void>::Destruct, - QtMetaTypePrivate::QMetaTypeFunctionHelper<void>::Construct, 0, TypeFlags(), nullptr); -} + Converts the object at \a from from \a fromTypeId to the preallocated space at \a to + typed \a toTypeId. Returns \c true, if the conversion succeeded, otherwise false. + + Both \a from and \a to have to be valid pointers. + + \since 5.2 +*/ /*! - \internal - \since 5.5 + Converts the object at \a from from \a fromType to the preallocated space at \a to + typed \a toType. Returns \c true, if the conversion succeeded, otherwise false. - Unregisters the user type with the given \a typeId and all its aliases. - Returns \c true if the type was unregistered or \c false otherwise. + Both \a from and \a to have to be valid pointers. - This function was added for QML to be able to deregister types after - they are unloaded to prevent an infinite increase in custom types for - applications that are unloading/reloading components often. - */ -bool QMetaType::unregisterType(int type) + \since 5.2 +*/ +bool QMetaType::convert(QMetaType fromType, const void *from, QMetaType toType, void *to) { - QWriteLocker locker(customTypesLock()); - QVector<QCustomTypeInfo> *ct = customTypes(); - - // check if user type - if ((type < User) || ((type - User) >= ct->size())) + if (!fromType.isValid() || !toType.isValid()) return false; - // only types without Q_DECLARE_METATYPE can be unregistered - if (ct->data()[type - User].flags & WasDeclaredAsMetaType) - return false; + if (fromType == toType) { + // just make a copy + fromType.destruct(to); + fromType.construct(to, from); + return true; + } - // invalidate type and all its alias entries - for (int v = 0; v < ct->count(); ++v) { - if (((v + User) == type) || (ct->at(v).alias == type)) - ct->data()[v].typeName.clear(); + int fromTypeId = fromType.id(); + int toTypeId = toType.id(); + + if (auto moduleHelper = qModuleHelperForType(qMax(fromTypeId, toTypeId))) { + if (moduleHelper->convert(from, fromTypeId, to, toTypeId)) + return true; + } + const auto f = customTypesConversionRegistry()->function({fromTypeId, toTypeId}); + if (f) + return (*f)(from, to); + + if (fromType.flags() & QMetaType::IsEnumeration) + return convertFromEnum(fromType, from, toType, to); + if (toType.flags() & QMetaType::IsEnumeration) + return convertToEnum(fromType, from, toType, to); + if (toTypeId == Nullptr) { + *static_cast<std::nullptr_t *>(to) = nullptr; + if (fromType.flags() & QMetaType::IsPointer) { + if (*static_cast<const void * const *>(from) == nullptr) + return true; + } } - return true; -} +#ifndef QT_BOOTSTRAPPED +# ifndef QT_NO_VARIANT + if (toTypeId == QVariantPair && convertIterableToVariantPair(fromType, from, to)) + return true; -/*! - \internal - \since 5.0 + // handle iterables + if (toTypeId == QVariantList && convertIterableToVariantList(fromType, from, to)) + return true; - Registers a user type for marshalling, with \a typeName, a \a - deleter, a \a creator, a \a destructor, a \a constructor, and - a \a size. Returns the type's handle, or -1 if the type could - not be registered. - */ -int QMetaType::registerType(const char *typeName, Deleter deleter, - Creator creator, - Destructor destructor, - Constructor constructor, - int size, TypeFlags flags, const QMetaObject *metaObject) -{ -#ifdef QT_NO_QOBJECT - NS(QByteArray) normalizedTypeName = typeName; + if (toTypeId == QVariantMap && convertIterableToVariantMap(fromType, from, to)) + return true; + + if (toTypeId == QVariantHash && convertIterableToVariantHash(fromType, from, to)) + return true; +# endif + + if (toTypeId == qMetaTypeId<QSequentialIterable>()) + return convertToSequentialIterable(fromType, from, to); + + if (toTypeId == qMetaTypeId<QAssociativeIterable>()) + return convertToAssociativeIterable(fromType, from, to); + + return convertMetaObject(fromType, from, toType, to); #else - NS(QByteArray) normalizedTypeName = QMetaObject::normalizedType(typeName); + return false; #endif - - return registerNormalizedType(normalizedTypeName, deleter, creator, destructor, constructor, size, flags, metaObject); } /*! - \internal - \since 5.12 + 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; - Registers a user type for marshalling, with \a typeName, a - \a destructor, a \a constructor, and a \a size. Returns the - type's handle, or -1 if the type could not be registered. - */ -int QMetaType::registerType(const char *typeName, - TypedDestructor destructor, - TypedConstructor constructor, - int size, - TypeFlags flags, - const QMetaObject *metaObject) -{ -#ifdef QT_NO_QOBJECT - NS(QByteArray) normalizedTypeName = typeName; + int fromTypeId = fromType.id(); + int toTypeId = toType.id(); + + const auto f = customTypesMutableViewRegistry()->function({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 convertMetaObject(fromType, from, toType, to); #else - NS(QByteArray) normalizedTypeName = QMetaObject::normalizedType(typeName); + return false; #endif - - return registerNormalizedType(normalizedTypeName, destructor, constructor, size, flags, metaObject); } +/*! + 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(). -static int registerNormalizedType(const NS(QByteArray) &normalizedTypeName, - QMetaType::Destructor destructor, - QMetaType::Constructor constructor, - QMetaType::TypedDestructor typedDestructor, - QMetaType::TypedConstructor typedConstructor, - int size, QMetaType::TypeFlags flags, const QMetaObject *metaObject) + \sa convert(), QSequentialIterable, Q_DECLARE_SEQUENTIAL_CONTAINER_METATYPE(), + QAssociativeIterable, Q_DECLARE_ASSOCIATIVE_CONTAINER_METATYPE() +*/ +bool QMetaType::canView(QMetaType fromType, QMetaType toType) { - QVector<QCustomTypeInfo> *ct = customTypes(); - if (!ct || normalizedTypeName.isEmpty() || (!destructor && !typedDestructor) || (!constructor && !typedConstructor)) - return -1; + int fromTypeId = fromType.id(); + int toTypeId = toType.id(); - int idx = qMetaTypeStaticType(normalizedTypeName.constData(), - normalizedTypeName.size()); + if (fromTypeId == UnknownType || toTypeId == UnknownType) + return false; - int previousSize = 0; - QMetaType::TypeFlags::Int previousFlags = 0; - if (idx == QMetaType::UnknownType) { - QWriteLocker locker(customTypesLock()); - int posInVector = -1; - idx = qMetaTypeCustomType_unlocked(normalizedTypeName.constData(), - normalizedTypeName.size(), - &posInVector); - if (idx == QMetaType::UnknownType) { - QCustomTypeInfo inf; - inf.typeName = normalizedTypeName; -#ifndef QT_NO_DATASTREAM - inf.loadOp = nullptr; - inf.saveOp = nullptr; + const auto f = customTypesMutableViewRegistry()->function({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 - inf.alias = -1; - inf.typedConstructor = typedConstructor; - inf.typedDestructor = typedDestructor; - inf.constructor = constructor; - inf.destructor = destructor; - inf.size = size; - inf.flags = flags; - inf.metaObject = metaObject; - if (posInVector == -1) { - idx = ct->size() + QMetaType::User; - ct->append(inf); - } else { - idx = posInVector + QMetaType::User; - ct->data()[posInVector] = inf; - } - return idx; - } - if (idx >= QMetaType::User) { - previousSize = ct->at(idx - QMetaType::User).size; - previousFlags = ct->at(idx - QMetaType::User).flags; - - // Set new/additional flags in case of old library/app. - // Ensures that older code works in conjunction with new Qt releases - // requiring the new flags. - if (flags != previousFlags) { - QCustomTypeInfo &inf = ct->data()[idx - QMetaType::User]; - inf.flags |= flags; - if (metaObject) - inf.metaObject = metaObject; - } - } - } + return false; +} + +/*! + Returns \c true if QMetaType::convert can convert from \a fromType to + \a toType. + + The following conversions are supported by Qt: + + \table + \header \li Type \li Automatically Cast To + \row \li \l QMetaType::Bool \li \l QMetaType::QChar, \l QMetaType::Double, + \l QMetaType::Int, \l QMetaType::LongLong, \l QMetaType::QString, + \l QMetaType::UInt, \l QMetaType::ULongLong + \row \li \l QMetaType::QByteArray \li \l QMetaType::Double, + \l QMetaType::Int, \l QMetaType::LongLong, \l QMetaType::QString, + \l QMetaType::UInt, \l QMetaType::ULongLong, \l QMetaType::QUuid + \row \li \l QMetaType::QChar \li \l QMetaType::Bool, \l QMetaType::Int, + \l QMetaType::UInt, \l QMetaType::LongLong, \l QMetaType::ULongLong + \row \li \l QMetaType::QColor \li \l QMetaType::QString + \row \li \l QMetaType::QDate \li \l QMetaType::QDateTime, + \l QMetaType::QString + \row \li \l QMetaType::QDateTime \li \l QMetaType::QDate, + \l QMetaType::QString, \l QMetaType::QTime + \row \li \l QMetaType::Double \li \l QMetaType::Bool, \l QMetaType::Int, + \l QMetaType::LongLong, \l QMetaType::QString, \l QMetaType::UInt, + \l QMetaType::ULongLong + \row \li \l QMetaType::QFont \li \l QMetaType::QString + \row \li \l QMetaType::Int \li \l QMetaType::Bool, \l QMetaType::QChar, + \l QMetaType::Double, \l QMetaType::LongLong, \l QMetaType::QString, + \l QMetaType::UInt, \l QMetaType::ULongLong + \row \li \l QMetaType::QKeySequence \li \l QMetaType::Int, + \l QMetaType::QString + \row \li \l QMetaType::QVariantList \li \l QMetaType::QStringList (if the + list's items can be converted to QStrings) + \row \li \l QMetaType::LongLong \li \l QMetaType::Bool, + \l QMetaType::QByteArray, \l QMetaType::QChar, \l QMetaType::Double, + \l QMetaType::Int, \l QMetaType::QString, \l QMetaType::UInt, + \l QMetaType::ULongLong + \row \li \l QMetaType::QPoint \li QMetaType::QPointF + \row \li \l QMetaType::QRect \li QMetaType::QRectF + \row \li \l QMetaType::QString \li \l QMetaType::Bool, + \l QMetaType::QByteArray, \l QMetaType::QChar, \l QMetaType::QColor, + \l QMetaType::QDate, \l QMetaType::QDateTime, \l QMetaType::Double, + \l QMetaType::QFont, \l QMetaType::Int, \l QMetaType::QKeySequence, + \l QMetaType::LongLong, \l QMetaType::QStringList, \l QMetaType::QTime, + \l QMetaType::UInt, \l QMetaType::ULongLong, \l QMetaType::QUuid + \row \li \l QMetaType::QStringList \li \l QMetaType::QVariantList, + \l QMetaType::QString (if the list contains exactly one item) + \row \li \l QMetaType::QTime \li \l QMetaType::QString + \row \li \l QMetaType::UInt \li \l QMetaType::Bool, \l QMetaType::QChar, + \l QMetaType::Double, \l QMetaType::Int, \l QMetaType::LongLong, + \l QMetaType::QString, \l QMetaType::ULongLong + \row \li \l QMetaType::ULongLong \li \l QMetaType::Bool, + \l QMetaType::QChar, \l QMetaType::Double, \l QMetaType::Int, + \l QMetaType::LongLong, \l QMetaType::QString, \l QMetaType::UInt + \row \li \l QMetaType::QUuid \li \l QMetaType::QByteArray, \l QMetaType::QString + \endtable + + Casting between primitive type (int, float, bool etc.) is supported. + + Converting between pointers of types derived from QObject will also 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. + + A cast from a sequential container will also return true for this + function if the \a toType is QVariantList. + + Similarly, a cast from an associative container will also return true for this + function the \a toType is QVariantHash or QVariantMap. + + \sa convert(), QSequentialIterable, Q_DECLARE_SEQUENTIAL_CONTAINER_METATYPE(), QAssociativeIterable, + Q_DECLARE_ASSOCIATIVE_CONTAINER_METATYPE() +*/ +bool QMetaType::canConvert(QMetaType fromType, QMetaType toType) +{ + int fromTypeId = fromType.id(); + int toTypeId = toType.id(); + + if (fromTypeId == UnknownType || toTypeId == UnknownType) + return false; + + if (fromTypeId == toTypeId) + return true; - if (idx < QMetaType::User) { - previousSize = QMetaType::sizeOf(idx); - previousFlags = QMetaType::typeFlags(idx); + if (auto moduleHelper = qModuleHelperForType(qMax(fromTypeId, toTypeId))) { + if (moduleHelper->convert(nullptr, fromTypeId, nullptr, toTypeId)) + return true; } + const ConverterFunction * const f = + customTypesConversionRegistry()->function(std::make_pair(fromTypeId, toTypeId)); + if (f) + return true; - if (Q_UNLIKELY(previousSize != size)) { - qFatal("QMetaType::registerType: Binary compatibility break " - "-- Size mismatch for type '%s' [%i]. Previously registered " - "size %i, now registering size %i.", - normalizedTypeName.constData(), idx, previousSize, size); +#ifndef QT_BOOTSTRAPPED + if (toTypeId == qMetaTypeId<QSequentialIterable>()) + return canConvertToSequentialIterable(fromType); + + if (toTypeId == qMetaTypeId<QAssociativeIterable>()) + return canConvertToAssociativeIterable(fromType); +#endif +#ifndef QT_NO_VARIANT + if (toTypeId == QVariantList + && canConvert(fromType, QMetaType::fromType<QSequentialIterable>())) { + return true; } - // these flags cannot change in a binary compatible way: - const int binaryCompatibilityFlag = QMetaType::PointerToQObject | QMetaType::IsEnumeration | QMetaType::SharedPointerToQObject - | QMetaType::WeakPointerToQObject | QMetaType::TrackingPointerToQObject; - if (Q_UNLIKELY((previousFlags ^ flags) & binaryCompatibilityFlag)) { + if ((toTypeId == QVariantHash || toTypeId == QVariantMap) + && canConvert(fromType, QMetaType::fromType<QAssociativeIterable>())) { + return true; + } - const char *msg = "QMetaType::registerType: Binary compatibility break. " - "\nType flags for type '%s' [%i] don't match. Previously " - "registered TypeFlags(0x%x), now registering TypeFlags(0x%x). "; + if (toTypeId == QVariantPair && hasRegisteredConverterFunction( + fromType, QMetaType::fromType<QtMetaTypePrivate::QPairVariantInterfaceImpl>())) + return true; +#endif - qFatal(msg, normalizedTypeName.constData(), idx, previousFlags, int(flags)); + if (fromType.flags() & IsEnumeration) { + if (toTypeId == QString || toTypeId == QByteArray) + return true; + return canConvert(QMetaType(LongLong), toType); } + if (toType.flags() & IsEnumeration) { + if (fromTypeId == QString || fromTypeId == QByteArray) + return true; + return canConvert(fromType, QMetaType(LongLong)); + } + if (toTypeId == Nullptr && fromType.flags() & IsPointer) + return true; +#ifndef QT_BOOTSTRAPPED + if (canConvertMetaObject(fromType, toType)) + return true; +#endif - return idx; + return false; } /*! - \internal - \since 5.0 - \overload - Don't use, kept for binary compatibility + \fn bool QMetaType::compare(const void *lhs, const void *rhs, int typeId, int* result) + \deprecated Use the non-static compare method instead + + Compares the objects at \a lhs and \a rhs. Both objects need to be of type \a typeId. + \a result is set to less than, equal to or greater than zero, if \a lhs is less than, equal to + or greater than \a rhs. Returns \c true, if the comparison succeeded, otherwise \c false. +*/ + +/*! + \fn template<typename From, typename To> bool QMetaType::hasRegisteredConverterFunction() + Returns \c true, if the meta type system has a registered conversion from type From to type To. + \since 5.2 + \overload + */ - ### TODO Qt6: remove me +/*! + Returns \c true, if the meta type system has a registered conversion from meta type id \a fromType + to \a toType + \since 5.2 */ -int QMetaType::registerNormalizedType(const NS(QByteArray) &normalizedTypeName, Deleter deleter, - Creator creator, - Destructor destructor, - Constructor constructor, - int size, TypeFlags flags, const QMetaObject *metaObject) +bool QMetaType::hasRegisteredConverterFunction(QMetaType fromType, QMetaType toType) { - Q_UNUSED(deleter); - Q_UNUSED(creator); - return registerNormalizedType(normalizedTypeName, destructor, constructor, size, flags, metaObject); + return customTypesConversionRegistry()->contains({fromType.id(), toType.id()}); } +/*! + \fn template<typename From, typename To> 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 +*/ /*! - \internal - \since 5.5 + Returns \c true, if the meta type system has a registered mutable view on meta type id + \a fromType of meta type id \a toType. + \since 5.2 +*/ +bool QMetaType::hasRegisteredMutableViewFunction(QMetaType fromType, QMetaType toType) +{ + return customTypesMutableViewRegistry()->contains({fromType.id(), toType.id()}); +} - Registers a user type for marshalling, with \a normalizedTypeName, - a \a destructor, a \a constructor, and a \a size. Returns the type's - handle, or -1 if the type could not be registered. +/*! + \fn const char *QMetaType::typeName(int typeId) + \deprecated - \note normalizedTypeName is not checked for conformance with - Qt's normalized format, so it must already conform. + Returns the type name associated with the given \a typeId, or a null + pointer if no matching type was found. The returned pointer must not be + deleted. - ### TODO Qt6: remove me - */ -int QMetaType::registerNormalizedType(const NS(QByteArray) &normalizedTypeName, - Destructor destructor, - Constructor constructor, - int size, TypeFlags flags, const QMetaObject *metaObject) -{ - return NS(registerNormalizedType)(normalizedTypeName, destructor, constructor, nullptr, nullptr, size, flags, metaObject); -} + \sa type(), isRegistered(), Type, name() +*/ /*! - \internal - \since 5.12 + \fn constexpr const char *QMetaType::name() const + \since 5.15 - Registers a user type for marshalling, with \a normalizedTypeName, - a \a destructor, a \a constructor, and a \a size. Returns the type's - handle, or -1 if the type could not be registered. + Returns the type name associated with this QMetaType, or a null + pointer if no matching type was found. The returned pointer must not be + deleted. - \note normalizedTypeName is not checked for conformance with - Qt's normalized format, so it must already conform. - */ -int QMetaType::registerNormalizedType(const NS(QByteArray) &normalizedTypeName, - TypedDestructor destructor, - TypedConstructor constructor, - int size, TypeFlags flags, const QMetaObject *metaObject) + \sa typeName() +*/ + +/* + Similar to QMetaType::type(), but only looks in the static set of types. +*/ +static inline int qMetaTypeStaticType(const char *typeName, int length) { - return NS(registerNormalizedType)(normalizedTypeName, nullptr, nullptr, destructor, constructor, size, flags, metaObject); + int i = 0; + while (types[i].typeName && ((length != types[i].typeNameLength) + || memcmp(typeName, types[i].typeName, length))) { + ++i; + } + return types[i].type; } -/*! - \internal - \since 4.7 +/* + Similar to QMetaType::type(), but only looks in the custom set of + types, and doesn't lock the mutex. - Registers a user type for marshalling, as an alias of another type (typedef) */ -int QMetaType::registerTypedef(const char* typeName, int aliasId) +static int qMetaTypeCustomType_unlocked(const char *typeName, int length) { -#ifdef QT_NO_QOBJECT - NS(QByteArray) normalizedTypeName = typeName; -#else - NS(QByteArray) normalizedTypeName = QMetaObject::normalizedType(typeName); + if (customTypeRegistry.exists()) { + auto reg = &*customTypeRegistry; +#if QT_CONFIG(thread) + Q_ASSERT(!reg->lock.tryLockForWrite()); #endif - - return registerNormalizedTypedef(normalizedTypeName, aliasId); + if (auto ti = reg->aliases.value(QByteArray::fromRawData(typeName, length), nullptr)) { + return ti->typeId.loadRelaxed(); + } + } + return QMetaType::UnknownType; } /*! \internal - \since 5.0 Registers a user type for marshalling, as an alias of another type (typedef). Note that normalizedTypeName is not checked for conformance with Qt's normalized format, so it must already conform. */ -int QMetaType::registerNormalizedTypedef(const NS(QByteArray) &normalizedTypeName, int aliasId) +void QMetaType::registerNormalizedTypedef(const NS(QByteArray) & normalizedTypeName, + QMetaType metaType) { - QVector<QCustomTypeInfo> *ct = customTypes(); - if (!ct || normalizedTypeName.isEmpty()) - return -1; - - int idx = qMetaTypeStaticType(normalizedTypeName.constData(), - normalizedTypeName.size()); - - if (idx == UnknownType) { - QWriteLocker locker(customTypesLock()); - int posInVector = -1; - idx = qMetaTypeCustomType_unlocked(normalizedTypeName.constData(), - normalizedTypeName.size(), - &posInVector); - - if (idx == UnknownType) { - QCustomTypeInfo inf; - inf.typeName = normalizedTypeName; - inf.alias = aliasId; - if (posInVector == -1) - ct->append(inf); - else - ct->data()[posInVector] = inf; - return aliasId; - } + if (!metaType.isValid()) + return; + if (auto reg = customTypeRegistry()) { + QWriteLocker lock(®->lock); + auto &al = reg->aliases[normalizedTypeName]; + if (al) + return; + al = metaType.d_ptr; } +} - if (idx != aliasId) { - qWarning("QMetaType::registerTypedef: " - "-- Type name '%s' previously registered as typedef of '%s' [%i], " - "now registering as typedef of '%s' [%i].", - normalizedTypeName.constData(), QMetaType::typeName(idx), idx, - QMetaType::typeName(aliasId), aliasId); + +static const QtPrivate::QMetaTypeInterface *interfaceForTypeNoWarning(int typeId) +{ + const QtPrivate::QMetaTypeInterface *iface = nullptr; + if (typeId >= QMetaType::User) { + if (customTypeRegistry.exists()) + iface = customTypeRegistry->getCustomType(typeId); + } else { + if (auto moduleHelper = qModuleHelperForType(typeId)) + iface = moduleHelper->interfaceForType(typeId); } - return idx; + return iface; } /*! @@ -1349,16 +2772,7 @@ int QMetaType::registerNormalizedTypedef(const NS(QByteArray) &normalizedTypeNam */ bool QMetaType::isRegistered(int type) { - // predefined type - if ((type >= FirstCoreType && type <= LastCoreType) - || (type >= FirstGuiType && type <= LastGuiType) - || (type >= FirstWidgetsType && type <= LastWidgetsType)) { - return true; - } - - QReadLocker locker(customTypesLock()); - const QVector<QCustomTypeInfo> * const ct = customTypes(); - return ((type >= User) && (ct && ct->count() > type - User) && !ct->at(type - User).typeName.isEmpty()); + return interfaceForTypeNoWarning(type) != nullptr; } template <bool tryNormalizedType> @@ -1368,7 +2782,7 @@ static inline int qMetaTypeTypeImpl(const char *typeName, int length) return QMetaType::UnknownType; int type = qMetaTypeStaticType(typeName, length); if (type == QMetaType::UnknownType) { - QReadLocker locker(customTypesLock()); + QReadLocker locker(&customTypeRegistry()->lock); type = qMetaTypeCustomType_unlocked(typeName, length); #ifndef QT_NO_QOBJECT if ((type == QMetaType::UnknownType) && tryNormalizedType) { @@ -1386,349 +2800,219 @@ static inline int qMetaTypeTypeImpl(const char *typeName, int length) } /*! + \fn int QMetaType::type(const char *typeName) + \deprecated + Returns a handle to the type called \a typeName, or QMetaType::UnknownType if there is no such type. \sa isRegistered(), typeName(), Type */ -int QMetaType::type(const char *typeName) -{ - return qMetaTypeTypeImpl</*tryNormalizedType=*/true>(typeName, qstrlen(typeName)); -} /*! - \a internal + \internal Similar to QMetaType::type(); the only difference is that this function doesn't attempt to normalize the type name (i.e., the lookup will fail for type names in non-normalized form). */ -int qMetaTypeTypeInternal(const char *typeName) +Q_CORE_EXPORT int qMetaTypeTypeInternal(const char *typeName) { - return qMetaTypeTypeImpl</*tryNormalizedType=*/false>(typeName, qstrlen(typeName)); + return qMetaTypeTypeImpl</*tryNormalizedType=*/false>(typeName, int(qstrlen(typeName))); } /*! + \fn int QMetaType::type(const QT_PREPEND_NAMESPACE(QByteArray) &typeName) + \since 5.5 \overload + \deprecated Returns a handle to the type called \a typeName, or 0 if there is no such type. \sa isRegistered(), typeName() */ -int QMetaType::type(const QT_PREPEND_NAMESPACE(QByteArray) &typeName) -{ - return qMetaTypeTypeImpl</*tryNormalizedType=*/true>(typeName.constData(), typeName.size()); -} #ifndef QT_NO_DATASTREAM +/*! + Writes the object pointed to by \a data to the given \a stream. + Returns \c true if the object is saved successfully; otherwise + returns \c false. -namespace -{ - -template<typename T> -class HasStreamOperator -{ - struct Yes { char unused[1]; }; - struct No { char unused[2]; }; - Q_STATIC_ASSERT(sizeof(Yes) != sizeof(No)); - - template<class C> static decltype(std::declval<QDataStream&>().operator>>(std::declval<C&>()), Yes()) load(int); - template<class C> static decltype(operator>>(std::declval<QDataStream&>(), std::declval<C&>()), Yes()) load(int); - template<class C> static No load(...); - template<class C> static decltype(operator<<(std::declval<QDataStream&>(), std::declval<const C&>()), Yes()) saveFunction(int); - template<class C> static decltype(std::declval<QDataStream&>().operator<<(std::declval<const C&>()), Yes()) saveMethod(int); - template<class C> static No saveMethod(...); - template<class C> static No saveFunction(...); - static constexpr bool LoadValue = QtMetaTypePrivate::TypeDefinition<T>::IsAvailable && (sizeof(load<T>(0)) == sizeof(Yes)); - static constexpr bool SaveValue = QtMetaTypePrivate::TypeDefinition<T>::IsAvailable && - ((sizeof(saveMethod<T>(0)) == sizeof(Yes)) || (sizeof(saveFunction<T>(0)) == sizeof(Yes))); -public: - static constexpr bool Value = LoadValue && SaveValue; -}; - -// Quick sanity checks -Q_STATIC_ASSERT(HasStreamOperator<NS(QJsonDocument)>::Value); -Q_STATIC_ASSERT(!HasStreamOperator<void*>::Value); -Q_STATIC_ASSERT(HasStreamOperator<qint8>::Value); + Normally, you should not need to call this function directly. + Instead, use QVariant's \c operator<<(), which relies on save() + to stream custom types. -template<typename T, bool IsAcceptedType = DefinedTypesFilter::Acceptor<T>::IsAccepted && HasStreamOperator<T>::Value> -struct FilteredOperatorSwitch -{ - static bool load(QDataStream &stream, T *data, int) - { - stream >> *data; - return true; - } - static bool save(QDataStream &stream, const T *data, int) - { - stream << *data; - return true; - } -}; -template<typename T> -struct FilteredOperatorSwitch<T, /* IsAcceptedType = */ false> + \sa load() +*/ +bool QMetaType::save(QDataStream &stream, const void *data) const { - static const QMetaTypeInterface* getMetaTypeInterface(int type) - { - if (QModulesPrivate::QTypeModuleInfo<T>::IsGui && qMetaTypeGuiHelper) - return &qMetaTypeGuiHelper[type - QMetaType::FirstGuiType]; - else if (QModulesPrivate::QTypeModuleInfo<T>::IsWidget && qMetaTypeWidgetsHelper) - return &qMetaTypeWidgetsHelper[type - QMetaType::FirstWidgetsType]; - return nullptr; - } - static bool save(QDataStream &stream, const T *data, int type) - { - if (auto interface = getMetaTypeInterface(type)) { - interface->saveOp(stream, data); - return true; - } - return false; - } - static bool load(QDataStream &stream, T *data, int type) - { - if (auto interface = getMetaTypeInterface(type)) { - interface->loadOp(stream, data); - return true; - } + if (!data || !isValid()) return false; - } -}; -class SaveOperatorSwitch -{ -public: - QDataStream &stream; - int m_type; - - template<typename T> - bool delegate(const T *data) - { - return FilteredOperatorSwitch<T>::save(stream, data, m_type); - } - bool delegate(const char *data) - { - // force a char to be signed - stream << qint8(*data); + // keep compatibility for long/ulong + if (id() == QMetaType::Long) { + stream << qlonglong(*(long *)data); return true; - } - bool delegate(const long *data) - { - stream << qlonglong(*data); + } else if (id() == QMetaType::ULong) { + stream << qlonglong(*(unsigned long *)data); return true; } - bool delegate(const unsigned long *data) - { - stream << qulonglong(*data); - return true; - } - bool delegate(const QMetaTypeSwitcher::NotBuiltinType *data) - { - const QVector<QCustomTypeInfo> * const ct = customTypes(); - if (!ct) - return false; - QMetaType::SaveOperator saveOp = nullptr; - { - QReadLocker locker(customTypesLock()); - saveOp = ct->at(m_type - QMetaType::User).saveOp; - } - if (!saveOp) - return false; - saveOp(stream, data); - return true; - } - bool delegate(const void*) { return false; } - bool delegate(const QMetaTypeSwitcher::UnknownType*) { return false; } -}; -class LoadOperatorSwitch -{ -public: - QDataStream &stream; - int m_type; - template<typename T> - bool delegate(const T *data) - { - return FilteredOperatorSwitch<T>::load(stream, const_cast<T*>(data), m_type); - } - bool delegate(const char *data) - { - // force a char to be signed - qint8 c; - stream >> c; - *const_cast<char*>(data) = c; - return true; - } - bool delegate(const long *data) - { - qlonglong l; - stream >> l; - *const_cast<long*>(data) = l; - return true; - } - bool delegate(const unsigned long *data) - { - qlonglong l; - stream >> l; - *const_cast<unsigned long*>(data) = l; - return true; - } - bool delegate(const QMetaTypeSwitcher::NotBuiltinType *data) - { - const QVector<QCustomTypeInfo> * const ct = customTypes(); - if (!ct) - return false; - QMetaType::LoadOperator loadOp = nullptr; - { - QReadLocker locker(customTypesLock()); - loadOp = ct->at(m_type - QMetaType::User).loadOp; - } - if (!loadOp) - return false; - loadOp(stream, const_cast<QMetaTypeSwitcher::NotBuiltinType*>(data)); - return true; - } - bool delegate(const void*) { return false; } - bool delegate(const QMetaTypeSwitcher::UnknownType*) { return false; } -}; -} // namespace + if (!d_ptr->dataStreamOut) + return false; + + d_ptr->dataStreamOut(d_ptr, stream, data); + return true; +} /*! - Writes the object pointed to by \a data with the ID \a type to - the given \a stream. Returns \c true if the object is saved - successfully; otherwise returns \c false. + \fn bool QMetaType::save(QDataStream &stream, int type, const void *data) + \overload + \deprecated +*/ - The type must have been registered with qRegisterMetaType() and - qRegisterMetaTypeStreamOperators() beforehand. +/*! + Reads the object of this type from the given \a stream into \a data. + Returns \c true if the object is loaded successfully; otherwise + returns \c false. Normally, you should not need to call this function directly. - Instead, use QVariant's \c operator<<(), which relies on save() + Instead, use QVariant's \c operator>>(), which relies on load() to stream custom types. - \sa load(), qRegisterMetaTypeStreamOperators() + \sa save() */ -bool QMetaType::save(QDataStream &stream, int type, const void *data) +bool QMetaType::load(QDataStream &stream, void *data) const { - if (!data) + if (!data || !isValid()) return false; - SaveOperatorSwitch saveOp{stream, type}; - return QMetaTypeSwitcher::switcher<bool>(saveOp, type, data); + + // keep compatibility for long/ulong + if (id() == QMetaType::Long) { + qlonglong ll; + stream >> ll; + *(long *)data = long(ll); + return true; + } else if (id() == QMetaType::ULong) { + qulonglong ull; + stream >> ull; + *(unsigned long *)data = (unsigned long)(ull); + return true; + } + if (!d_ptr->dataStreamIn) + return false; + + d_ptr->dataStreamIn(d_ptr, stream, data); + return true; } /*! - Reads the object of the specified \a type from the given \a - stream into \a data. Returns \c true if the object is loaded - successfully; otherwise returns \c false. + \since 6.1 - The type must have been registered with qRegisterMetaType() and - qRegisterMetaTypeStreamOperators() beforehand. + Returns \c true, if the meta type system has registered data stream operators for this + meta type. +*/ +bool QMetaType::hasRegisteredDataStreamOperators() const +{ + int type = id(); + if (type == QMetaType::Long || type == QMetaType::ULong) + return true; + return d_ptr && d_ptr->dataStreamIn != nullptr && d_ptr->dataStreamOut != nullptr; +} - Normally, you should not need to call this function directly. - Instead, use QVariant's \c operator>>(), which relies on load() - to stream custom types. +/*! + \since 6.6 + + If this metatype represents an enumeration, this method returns a + metatype of a numeric class of the same signedness and size as the + enums underlying type. + If it represents a QFlags type, it returns QMetaType::Int. + In all other cases an invalid QMetaType is returned. + */ +QMetaType QMetaType::underlyingType() const +{ + if (!d_ptr || !(flags() & IsEnumeration)) + return {}; + /* QFlags has enumeration set so that's handled here (qint32 + case), as QFlags uses int as the underlying type + Note that we do some approximation here, as we cannot + differentiate between different underlying types of the + same size and signedness (consider char <-> (un)signed char, + int <-> long <-> long long). + + ### TODO PENDING: QTBUG-111926 - QFlags supporting >32 bit int + */ + if (flags() & IsUnsignedEnumeration) { + switch (sizeOf()) { + case 1: + return QMetaType::fromType<quint8>(); + case 2: + return QMetaType::fromType<quint16>(); + case 4: + return QMetaType::fromType<quint32>(); + case 8: + return QMetaType::fromType<quint64>(); + default: + break; + } + } else { + switch (sizeOf()) { + case 1: + return QMetaType::fromType<qint8>(); + case 2: + return QMetaType::fromType<qint16>(); + case 4: + return QMetaType::fromType<qint32>(); + case 8: + return QMetaType::fromType<qint64>(); + default: + break; + } + } + // int128 can be handled above once we have qint128 + return QMetaType(); +} - \sa save(), qRegisterMetaTypeStreamOperators() +/*! + \fn bool QMetaType::load(QDataStream &stream, int type, void *data) + \overload + \deprecated */ -bool QMetaType::load(QDataStream &stream, int type, void *data) +#endif // QT_NO_DATASTREAM + +/*! + Returns a QMetaType matching \a typeName. The returned object is + not valid if the typeName is not known to QMetaType + */ +QMetaType QMetaType::fromName(QByteArrayView typeName) { - if (!data) - return false; - LoadOperatorSwitch loadOp{stream, type}; - return QMetaTypeSwitcher::switcher<bool>(loadOp, type, data); + return QMetaType(qMetaTypeTypeImpl</*tryNormalizedType=*/true>(typeName.data(), typeName.size())); } -#endif // QT_NO_DATASTREAM /*! + \fn void *QMetaType::create(int type, const void *copy) + \deprecated + Returns a copy of \a copy, assuming it is of type \a type. If \a copy is zero, creates a default constructed instance. \sa destroy(), isRegistered(), Type */ -void *QMetaType::create(int type, const void *copy) -{ - QMetaType info(type); - if (int size = info.sizeOf()) - return info.construct(operator new(size), copy); - return nullptr; -} /*! + \fn void QMetaType::destroy(int type, void *data) + \deprecated Destroys the \a data, assuming it is of the \a type given. \sa create(), isRegistered(), Type */ -void QMetaType::destroy(int type, void *data) -{ - QMetaType info(type); - info.destruct(data); - operator delete(data); -} - -namespace { -class TypeConstructor { - template<typename T, bool IsAcceptedType = DefinedTypesFilter::Acceptor<T>::IsAccepted> - struct ConstructorImpl { - static void *Construct(const int /*type*/, void *where, const void *copy) { return QtMetaTypePrivate::QMetaTypeFunctionHelper<T>::Construct(where, copy); } - }; - template<typename T> - struct ConstructorImpl<T, /* IsAcceptedType = */ false> { - static void *Construct(const int type, void *where, const void *copy) - { - if (QModulesPrivate::QTypeModuleInfo<T>::IsGui) - return Q_LIKELY(qMetaTypeGuiHelper) - ? qMetaTypeGuiHelper[type - QMetaType::FirstGuiType].constructor(where, copy) - : nullptr; - - if (QModulesPrivate::QTypeModuleInfo<T>::IsWidget) - return Q_LIKELY(qMetaTypeWidgetsHelper) - ? qMetaTypeWidgetsHelper[type - QMetaType::FirstWidgetsType].constructor(where, copy) - : nullptr; - - // This point can be reached only for known types that definition is not available, for example - // in bootstrap mode. We have no other choice then ignore it. - return nullptr; - } - }; -public: - TypeConstructor(const int type, void *where) - : m_type(type) - , m_where(where) - {} - - template<typename T> - void *delegate(const T *copy) { return ConstructorImpl<T>::Construct(m_type, m_where, copy); } - void *delegate(const void *) { return m_where; } - void *delegate(const QMetaTypeSwitcher::UnknownType*) { return m_where; } - void *delegate(const QMetaTypeSwitcher::NotBuiltinType *copy) { return customTypeConstructor(m_type, m_where, copy); } - -private: - static void *customTypeConstructor(const int type, void *where, const void *copy) - { - QMetaType::Constructor ctor; - QMetaType::TypedConstructor tctor; - const QVector<QCustomTypeInfo> * const ct = customTypes(); - { - QReadLocker locker(customTypesLock()); - if (Q_UNLIKELY(type < QMetaType::User || !ct || ct->count() <= type - QMetaType::User)) - return nullptr; - const auto &typeInfo = ct->at(type - QMetaType::User); - ctor = typeInfo.constructor; - tctor = typeInfo.typedConstructor; - } - Q_ASSERT_X((ctor || tctor) , "void *QMetaType::construct(int type, void *where, const void *copy)", "The type was not properly registered"); - if (Q_UNLIKELY(tctor)) - return tctor(type, where, copy); - return ctor(where, copy); - } - - const int m_type; - void *m_where; -}; -} // namespace /*! + \fn void *QMetaType::construct(int type, void *where, const void *copy) \since 5.0 + \deprecated Constructs a value of the given \a type in the existing memory addressed by \a where, that is a copy of \a copy, and returns @@ -1753,79 +3037,12 @@ private: \sa destruct(), sizeOf() */ -void *QMetaType::construct(int type, void *where, const void *copy) -{ - if (!where) - return nullptr; - TypeConstructor constructor(type, where); - return QMetaTypeSwitcher::switcher<void*>(constructor, type, copy); -} -namespace { -class TypeDestructor { - template<typename T, bool IsAcceptedType = DefinedTypesFilter::Acceptor<T>::IsAccepted> - struct DestructorImpl { - static void Destruct(const int /* type */, void *where) { QtMetaTypePrivate::QMetaTypeFunctionHelper<T>::Destruct(where); } - }; - template<typename T> - struct DestructorImpl<T, /* IsAcceptedType = */ false> { - static void Destruct(const int type, void *where) - { - if (QModulesPrivate::QTypeModuleInfo<T>::IsGui) { - if (Q_LIKELY(qMetaTypeGuiHelper)) - qMetaTypeGuiHelper[type - QMetaType::FirstGuiType].destructor(where); - return; - } - if (QModulesPrivate::QTypeModuleInfo<T>::IsWidget) { - if (Q_LIKELY(qMetaTypeWidgetsHelper)) - qMetaTypeWidgetsHelper[type - QMetaType::FirstWidgetsType].destructor(where); - return; - } - // This point can be reached only for known types that definition is not available, for example - // in bootstrap mode. We have no other choice then ignore it. - } - }; -public: - TypeDestructor(const int type) - : m_type(type) - {} - - template<typename T> - void delegate(const T *where) { DestructorImpl<T>::Destruct(m_type, const_cast<T*>(where)); } - // MSVC2013 and earlier can not const_cast a std::nullptr_t pointer. - void delegate(const std::nullptr_t *) {} - void delegate(const void *) {} - void delegate(const QMetaTypeSwitcher::UnknownType*) {} - void delegate(const QMetaTypeSwitcher::NotBuiltinType *where) - { customTypeDestructor(m_type, const_cast<void *>(static_cast<const void *>(where))); } - -private: - static void customTypeDestructor(const int type, void *where) - { - QMetaType::Destructor dtor; - QMetaType::TypedDestructor tdtor; - const QVector<QCustomTypeInfo> * const ct = customTypes(); - { - QReadLocker locker(customTypesLock()); - if (Q_UNLIKELY(type < QMetaType::User || !ct || ct->count() <= type - QMetaType::User)) - return; - const auto &typeInfo = ct->at(type - QMetaType::User); - dtor = typeInfo.destructor; - tdtor = typeInfo.typedDestructor; - } - Q_ASSERT_X((dtor || tdtor), "void QMetaType::destruct(int type, void *where)", "The type was not properly registered"); - if (Q_UNLIKELY(tdtor)) - return tdtor(type, where); - dtor(where); - } - - const int m_type; -}; -} // namespace - /*! + \fn void QMetaType::destruct(int type, void *where) \since 5.0 + \deprecated Destructs the value of the given \a type, located at \a where. @@ -1834,62 +3051,11 @@ private: \sa construct() */ -void QMetaType::destruct(int type, void *where) -{ - if (!where) - return; - TypeDestructor destructor(type); - QMetaTypeSwitcher::switcher<void>(destructor, type, where); -} - - -namespace { -class SizeOf { - template<typename T, bool IsAcceptedType = DefinedTypesFilter::Acceptor<T>::IsAccepted> - struct SizeOfImpl { - static int Size(const int) { return QTypeInfo<T>::sizeOf; } - }; - template<typename T> - struct SizeOfImpl<T, /* IsAcceptedType = */ false> { - static int Size(const int type) - { - if (QModulesPrivate::QTypeModuleInfo<T>::IsGui) - return Q_LIKELY(qMetaTypeGuiHelper) ? qMetaTypeGuiHelper[type - QMetaType::FirstGuiType].size : 0; - - if (QModulesPrivate::QTypeModuleInfo<T>::IsWidget) - return Q_LIKELY(qMetaTypeWidgetsHelper) ? qMetaTypeWidgetsHelper[type - QMetaType::FirstWidgetsType].size : 0; - - // This point can be reached only for known types that definition is not available, for example - // in bootstrap mode. We have no other choice then ignore it. - return 0; - } - }; - -public: - SizeOf(int type) - : m_type(type) - {} - - template<typename T> - int delegate(const T*) { return SizeOfImpl<T>::Size(m_type); } - int delegate(const QMetaTypeSwitcher::UnknownType*) { return 0; } - int delegate(const QMetaTypeSwitcher::NotBuiltinType*) { return customTypeSizeOf(m_type); } -private: - static int customTypeSizeOf(const int type) - { - const QVector<QCustomTypeInfo> * const ct = customTypes(); - QReadLocker locker(customTypesLock()); - if (Q_UNLIKELY(type < QMetaType::User || !ct || ct->count() <= type - QMetaType::User)) - return 0; - return ct->at(type - QMetaType::User).size; - } - - const int m_type; -}; -} // namespace /*! + \fn int QMetaType::sizeOf(int type) \since 5.0 + \deprecated Returns the size of the given \a type in bytes (i.e. sizeof(T), where T is the actual type identified by the \a type argument). @@ -1897,151 +3063,33 @@ private: This function is typically used together with construct() to perform low-level management of the memory used by a type. - \sa construct() + \sa construct(), QMetaType::alignOf() */ -int QMetaType::sizeOf(int type) -{ - SizeOf sizeOf(type); - return QMetaTypeSwitcher::switcher<int>(sizeOf, type); -} - -namespace { -class Flags -{ - template<typename T, bool IsAcceptedType = DefinedTypesFilter::Acceptor<T>::IsAccepted> - struct FlagsImpl - { - static quint32 Flags(const int /* type */) - { - return QtPrivate::QMetaTypeTypeFlags<T>::Flags; - } - }; - template<typename T> - struct FlagsImpl<T, /* IsAcceptedType = */ false> - { - static quint32 Flags(const int type) - { - if (QModulesPrivate::QTypeModuleInfo<T>::IsGui) - return Q_LIKELY(qMetaTypeGuiHelper) ? qMetaTypeGuiHelper[type - QMetaType::FirstGuiType].flags : 0; - - if (QModulesPrivate::QTypeModuleInfo<T>::IsWidget) - return Q_LIKELY(qMetaTypeWidgetsHelper) ? qMetaTypeWidgetsHelper[type - QMetaType::FirstWidgetsType].flags : 0; - - // This point can be reached only for known types that definition is not available, for example - // in bootstrap mode. We have no other choice then ignore it. - return 0; - } - }; -public: - Flags(const int type) - : m_type(type) - {} - template<typename T> - quint32 delegate(const T*) { return FlagsImpl<T>::Flags(m_type); } - quint32 delegate(const void*) { return 0; } - quint32 delegate(const QMetaTypeSwitcher::UnknownType*) { return 0; } - quint32 delegate(const QMetaTypeSwitcher::NotBuiltinType*) { return customTypeFlags(m_type); } -private: - const int m_type; - static quint32 customTypeFlags(const int type) - { - const QVector<QCustomTypeInfo> * const ct = customTypes(); - if (Q_UNLIKELY(!ct || type < QMetaType::User)) - return 0; - QReadLocker locker(customTypesLock()); - if (Q_UNLIKELY(ct->count() <= type - QMetaType::User)) - return 0; - return ct->at(type - QMetaType::User).flags; - } -}; -} // namespace /*! + \fn QMetaType::TypeFlags QMetaType::typeFlags(int type) \since 5.0 + \deprecated Returns flags of the given \a type. \sa QMetaType::TypeFlags */ -QMetaType::TypeFlags QMetaType::typeFlags(int type) -{ - Flags flags(type); - return static_cast<QMetaType::TypeFlags>(QMetaTypeSwitcher::switcher<quint32>(flags, type)); -} - -#ifndef QT_BOOTSTRAPPED -namespace { -class MetaObject -{ -public: - MetaObject(const int type) - : m_type(type) - {} - - template<typename T, bool IsAcceptedType = DefinedTypesFilter::Acceptor<T>::IsAccepted> - struct MetaObjectImpl - { - static const QMetaObject *MetaObject(int /*type*/) - { return QtPrivate::MetaObjectForType<T>::value(); } - }; - template<typename T> - struct MetaObjectImpl<T, /* IsAcceptedType = */ false> - { - static const QMetaObject *MetaObject(int type) { - if (QModulesPrivate::QTypeModuleInfo<T>::IsGui) - return Q_LIKELY(qMetaTypeGuiHelper) - ? qMetaTypeGuiHelper[type - QMetaType::FirstGuiType].metaObject - : nullptr; - if (QModulesPrivate::QTypeModuleInfo<T>::IsWidget) - return Q_LIKELY(qMetaTypeWidgetsHelper) - ? qMetaTypeWidgetsHelper[type - QMetaType::FirstWidgetsType].metaObject - : nullptr; - return nullptr; - } - }; - - template <typename T> - const QMetaObject *delegate(const T *) { return MetaObjectImpl<T>::MetaObject(m_type); } - const QMetaObject *delegate(const void*) { return nullptr; } - const QMetaObject *delegate(const QMetaTypeSwitcher::UnknownType*) { return nullptr; } - const QMetaObject *delegate(const QMetaTypeSwitcher::NotBuiltinType*) { return customMetaObject(m_type); } -private: - const int m_type; - static const QMetaObject *customMetaObject(const int type) - { - const QVector<QCustomTypeInfo> * const ct = customTypes(); - if (Q_UNLIKELY(!ct || type < QMetaType::User)) - return nullptr; - QReadLocker locker(customTypesLock()); - if (Q_UNLIKELY(ct->count() <= type - QMetaType::User)) - return nullptr; - return ct->at(type - QMetaType::User).metaObject; - } -}; -} // namespace -#endif /*! + \fn const QMetaObject *QMetaType::metaObjectForType(int type) \since 5.0 + \deprecated returns QMetaType::metaObject for \a type \sa metaObject() */ -const QMetaObject *QMetaType::metaObjectForType(int type) -{ -#ifndef QT_BOOTSTRAPPED - MetaObject mo(type); - return QMetaTypeSwitcher::switcher<const QMetaObject*>(mo, type); -#else - Q_UNUSED(type); - return nullptr; -#endif -} /*! - \fn int qRegisterMetaType(const char *typeName) + \fn template <typename T> int qRegisterMetaType(const char *typeName) \relates QMetaType + \obsolete \threadsafe Registers the type name \a typeName for the type \c{T}. Returns @@ -2069,86 +3117,67 @@ const QMetaObject *QMetaType::metaObjectForType(int type) \warning This function is useful only for registering an alias (typedef) for every other use case Q_DECLARE_METATYPE and qMetaTypeId() should be used instead. - \sa {QMetaType::}{qRegisterMetaTypeStreamOperators()}, {QMetaType::}{isRegistered()}, - Q_DECLARE_METATYPE() + \sa {QMetaType::}{isRegistered()}, Q_DECLARE_METATYPE() */ /*! - \fn void qRegisterMetaTypeStreamOperators(const char *typeName) + \fn template <typename T> int qRegisterMetaType() \relates QMetaType \threadsafe + \since 4.2 + + Call this function to register the type \c T. Returns the meta type Id. - Registers the stream operators for the type \c{T} called \a - typeName. + Example: - Afterward, the type can be streamed using QMetaType::load() and - QMetaType::save(). These functions are used when streaming a - QVariant. + \snippet code/src_corelib_kernel_qmetatype.cpp 7 - \snippet code/src_corelib_kernel_qmetatype.cpp 5 + This function requires that \c{T} is a fully defined type at the point + where the function is called. For pointer types, it also requires that the + pointed to type is fully defined. Use Q_DECLARE_OPAQUE_POINTER() to be able + to register pointers to forward declared types. - The stream operators should have the following signatures: + To use the type \c T in QMetaType, QVariant, or with the + QObject::property() API, registration is not necessary. - \snippet code/src_corelib_kernel_qmetatype.cpp 6 + To use the type \c T in queued signal and slot connections, + \c{qRegisterMetaType<T>()} must be called before the first connection is + established. That is typically done in the constructor of the class that + uses \c T, or in the \c{main()} function. - \sa qRegisterMetaType(), QMetaType::isRegistered(), Q_DECLARE_METATYPE() -*/ + After a type has been registered, it can be found by its name using + QMetaType::fromName(). -/*! \typedef QMetaType::Deleter - \internal -*/ -/*! \typedef QMetaType::Creator - \internal -*/ -/*! \typedef QMetaType::SaveOperator - \internal -*/ -/*! \typedef QMetaType::LoadOperator - \internal -*/ -/*! \typedef QMetaType::Destructor - \internal -*/ -/*! \typedef QMetaType::Constructor - \internal -*/ + \sa Q_DECLARE_METATYPE() + */ /*! - \fn int qRegisterMetaType() + \fn int qRegisterMetaType(QMetaType meta) \relates QMetaType \threadsafe - \since 4.2 - - Call this function to register the type \c T. \c T must be declared with - Q_DECLARE_METATYPE(). Returns the meta type Id. + \since 6.5 - Example: - - \snippet code/src_corelib_kernel_qmetatype.cpp 7 + Registers the meta type \a meta and returns its type Id. This function requires that \c{T} is a fully defined type at the point where the function is called. For pointer types, it also requires that the pointed to type is fully defined. Use Q_DECLARE_OPAQUE_POINTER() to be able to register pointers to forward declared types. - After a type has been registered, you can create and destroy - objects of that type dynamically at run-time. - - To use the type \c T in QVariant, using Q_DECLARE_METATYPE() is - sufficient. To use the type \c T in queued signal and slot connections, - \c{qRegisterMetaType<T>()} must be called before the first connection - is established. + To use the type \c T in QMetaType, QVariant, or with the + QObject::property() API, registration is not necessary. - Also, to use type \c T with the QObject::property() API, - \c{qRegisterMetaType<T>()} must be called before it is used, typically - in the constructor of the class that uses \c T, or in the \c{main()} - function. + To use the type \c T in queued signal and slot connections, + \c{qRegisterMetaType<T>()} must be called before the first connection is + established. That is typically done in the constructor of the class that + uses \c T, or in the \c{main()} function. - \sa Q_DECLARE_METATYPE() + After a type has been registered, it can be found by its name using + QMetaType::fromName(). */ /*! - \fn int qMetaTypeId() + \fn template <typename T> int qMetaTypeId() \relates QMetaType \threadsafe \since 4.1 @@ -2169,345 +3198,53 @@ const QMetaObject *QMetaType::metaObjectForType(int type) \sa Q_DECLARE_METATYPE(), QMetaType::type() */ -namespace { -class TypeInfo { - template<typename T, bool IsAcceptedType = DefinedTypesFilter::Acceptor<T>::IsAccepted> - struct TypeInfoImpl - { - TypeInfoImpl(const uint /* type */, QMetaTypeInterface &info) - { - QMetaTypeInterface tmp = QT_METATYPE_INTERFACE_INIT_NO_DATASTREAM(T); - info = tmp; - } - }; - - template<typename T> - struct TypeInfoImpl<T, /* IsAcceptedType = */ false> - { - TypeInfoImpl(const uint type, QMetaTypeInterface &info) - { - if (QModulesPrivate::QTypeModuleInfo<T>::IsGui) { - if (Q_LIKELY(qMetaTypeGuiHelper)) - info = qMetaTypeGuiHelper[type - QMetaType::FirstGuiType]; - return; - } - if (QModulesPrivate::QTypeModuleInfo<T>::IsWidget) { - if (Q_LIKELY(qMetaTypeWidgetsHelper)) - info = qMetaTypeWidgetsHelper[type - QMetaType::FirstWidgetsType]; - return; - } - } - }; -public: - QMetaTypeInterface info; - TypeInfo(const uint type) - : m_type(type) - { - QMetaTypeInterface tmp = QT_METATYPE_INTERFACE_INIT_EMPTY(); - info = tmp; - } - template<typename T> - void delegate(const T*) { TypeInfoImpl<T>(m_type, info); } - void delegate(const QMetaTypeSwitcher::UnknownType*) {} - void delegate(const QMetaTypeSwitcher::NotBuiltinType*) { customTypeInfo(m_type); } -private: - void customTypeInfo(const uint type) - { - const QVector<QCustomTypeInfo> * const ct = customTypes(); - if (Q_UNLIKELY(!ct)) - return; - QReadLocker locker(customTypesLock()); - if (Q_LIKELY(uint(ct->count()) > type - QMetaType::User)) - info = ct->at(type - QMetaType::User); - } - - const uint m_type; -}; -} // namespace - -/*! - \fn QMetaType QMetaType::typeInfo(const int type) - \internal -*/ -QMetaType QMetaType::typeInfo(const int type) -{ - TypeInfo typeInfo(type); - QMetaTypeSwitcher::switcher<void>(typeInfo, type); - return (typeInfo.info.constructor || typeInfo.info.typedConstructor) - ? QMetaType(static_cast<ExtensionFlag>(QMetaType::CreateEx | QMetaType::DestroyEx | - (typeInfo.info.typedConstructor ? QMetaType::ConstructEx | QMetaType::DestructEx : 0)) - , static_cast<const QMetaTypeInterface *>(nullptr) // typeInfo::info is a temporary variable, we can't return address of it. - , typeInfo.info.typedConstructor - , typeInfo.info.typedDestructor - , typeInfo.info.saveOp - , typeInfo.info.loadOp - , typeInfo.info.constructor - , typeInfo.info.destructor - , typeInfo.info.size - , typeInfo.info.flags - , type - , typeInfo.info.metaObject) - : QMetaType(UnknownType); -} - -/*! - \fn QMetaType::QMetaType(const int typeId) - \since 5.0 - - Constructs a QMetaType object that contains all information about type \a typeId. - - \note: The default parameter was added in Qt 5.15 -*/ -QMetaType::QMetaType(const int typeId) - : m_typeId(typeId) -{ - if (Q_UNLIKELY(typeId == UnknownType)) { - // Constructs invalid QMetaType instance. - m_extensionFlags = 0xffffffff; - Q_ASSERT(!isValid()); - } else { - // TODO it can be better. - *this = QMetaType::typeInfo(typeId); - if (m_typeId == UnknownType) - m_extensionFlags = 0xffffffff; - else if (m_typeId == QMetaType::Void) - m_extensionFlags = CreateEx | DestroyEx | ConstructEx | DestructEx; - } -} - -/*! - \fn QMetaType::QMetaType(const QMetaType &other) - \since 5.0 - - Copy constructs a QMetaType object. -*/ -QMetaType::QMetaType(const QMetaType &other) - : m_typedConstructor(other.m_typedConstructor) - , m_typedDestructor(other.m_typedDestructor) - , m_saveOp(other.m_saveOp) - , m_loadOp(other.m_loadOp) - , m_constructor(other.m_constructor) - , m_destructor(other.m_destructor) - , m_extension(other.m_extension) // space reserved for future use - , m_size(other.m_size) - , m_typeFlags(other.m_typeFlags) - , m_extensionFlags(other.m_extensionFlags) - , m_typeId(other.m_typeId) - , m_metaObject(other.m_metaObject) -{} - -QMetaType &QMetaType::operator =(const QMetaType &other) -{ - m_typedConstructor = other.m_typedConstructor; - m_typedDestructor = other.m_typedDestructor; - m_saveOp = other.m_saveOp; - m_loadOp = other.m_loadOp; - m_constructor = other.m_constructor; - m_destructor = other.m_destructor; - m_size = other.m_size; - m_typeFlags = other.m_typeFlags; - m_extensionFlags = other.m_extensionFlags; - m_extension = other.m_extension; // space reserved for future use - m_typeId = other.m_typeId; - m_metaObject = other.m_metaObject; - return *this; -} - -/*! - \fn void QMetaType::ctor(const QMetaTypeInterface *info) - \internal - - Method used for future binary compatible extensions. The function may be - called from within QMetaType's constructor to force a library call from - inlined code. -*/ -void QMetaType::ctor(const QMetaTypeInterface *info) -{ - // Special case for Void type, the type is valid but not constructible. - // In future we may consider to remove this assert and extend this function to initialize - // differently m_extensionFlags for different types. Currently it is not needed. - Q_ASSERT(m_typeId == QMetaType::Void); - Q_UNUSED(info); - m_extensionFlags = CreateEx | DestroyEx | ConstructEx | DestructEx; -} - -/*! - \fn void QMetaType::dtor() - \internal - - Method used for future binary compatible extensions. The function may be - called from within QMetaType's destructor to force a library call from - inlined code. -*/ -void QMetaType::dtor() -{} - -/*! - \fn void *QMetaType::createExtended(const void *copy) const - \internal - - Method used for future binary compatible extensions. The function may be called - during QMetaType::create to force library call from inlined code. - - ### TODO Qt6 remove the extension -*/ -void *QMetaType::createExtended(const void *copy) const +static const QtPrivate::QMetaTypeInterface *interfaceForType(int typeId) { - if (m_typeId == QMetaType::UnknownType) - return nullptr; - if (Q_UNLIKELY(m_typedConstructor && !m_constructor)) - return m_typedConstructor(m_typeId, operator new(m_size), copy); - return m_constructor(operator new(m_size), copy); -} - -/*! - \fn void QMetaType::destroyExtended(void *data) const - \internal - - Method used for future binary compatible extensions. The function may be called - during QMetaType::destroy to force library call from inlined code. + const QtPrivate::QMetaTypeInterface *iface = interfaceForTypeNoWarning(typeId); + if (!iface && typeId != QMetaType::UnknownType) + qWarning("Trying to construct an instance of an invalid type, type id: %i", typeId); - ### TODO Qt6 remove the extension -*/ -void QMetaType::destroyExtended(void *data) const -{ - if (m_typeId == QMetaType::UnknownType) - return; - if (Q_UNLIKELY(m_typedDestructor && !m_destructor)) - m_typedDestructor(m_typeId, data); - else - m_destructor(data); - operator delete(data); -} - -/*! - \fn void *QMetaType::constructExtended(void *where, const void *copy) const - \internal - - Method used for future binary compatible extensions. The function may be called - during QMetaType::construct to force library call from inlined code. -*/ -void *QMetaType::constructExtended(void *where, const void *copy) const -{ - if (m_typeId == QMetaType::UnknownType) - return nullptr; - if (m_typedConstructor && !m_constructor) - return m_typedConstructor(m_typeId, where, copy); - return nullptr; + return iface; } /*! - \fn void QMetaType::destructExtended(void *data) const - \internal + \fn QMetaType::QMetaType() + \since 6.0 - Method used for future binary compatible extensions. The function may be called - during QMetaType::destruct to force library call from inlined code. + Constructs a default, invalid, QMetaType object. */ -void QMetaType::destructExtended(void *data) const -{ - if (m_typeId == QMetaType::UnknownType) - return; - if (m_typedDestructor && !m_destructor) - m_typedDestructor(m_typeId, data); -} /*! - \fn uint QMetaType::sizeExtended() const - \internal + \fn QMetaType::QMetaType(int typeId) + \since 5.0 - Method used for future binary compatible extensions. The function may be - called from within QMetaType::size to force a library call from - inlined code. + Constructs a QMetaType object that contains all information about type \a typeId. */ -uint QMetaType::sizeExtended() const -{ - return 0; -} +QMetaType::QMetaType(int typeId) : QMetaType(interfaceForType(typeId)) {} -/*! - \fn QMetaType::TypeFlags QMetaType::flagsExtended() const - \internal - Method used for future binary compatible extensions. The function may be - called from within QMetaType::flags to force a library call from - inlined code. -*/ -QMetaType::TypeFlags QMetaType::flagsExtended() const -{ - return { }; -} - -/*! - \brief QMetaType::metaObjectExtended - \internal +/*! \fn size_t qHash(QMetaType type, size_t seed = 0) + \relates QMetaType + \since 6.4 - Method used for future binary compatible extensions. The function may be - called from within QMetaType::metaObject to force a library call from - inlined code. + Returns the hash value for the \a type, using \a seed to seed the calculation. */ -const QMetaObject *QMetaType::metaObjectExtended() const -{ - return nullptr; -} +namespace QtPrivate { +#if !defined(QT_BOOTSTRAPPED) && !defined(Q_CC_MSVC) && !defined(Q_OS_INTEGRITY) -namespace QtPrivate -{ -const QMetaObject *metaObjectForQWidget() -{ - if (!qMetaTypeWidgetsHelper) - return nullptr; - return qMetaObjectWidgetsHelper; -} - -void qt5CompatibilityHookPostRegister(int id, const QByteArray &normalizedTypeName) -{ - // In Qt6 QList got typedef'ed to QVector. To keep runtime behavior compatibility - // with Qt5 we install corresponding aliases. For example if one register - // QVector<QVector<int>> - // we need to register the type plus all possible aliases: - // QVector<QList<int>> - // QList<QVector<int>> - // QList<QList<int>> - // ### Qt6 TODO This is slow, as it allocates couple of strings we would need to - // if def this call with something like QT_NO_QLIST - const char *vectorName = "QVector<"; - const char *listName = "QList<"; - - auto isSubstringOfAType = [](char c) { return c != ' ' && c != ',' && c != '<'; }; - QVarLengthArray<int> indexes; - - for (auto containerName: {vectorName, listName}) { - for (int i = normalizedTypeName.indexOf(containerName, 0); i != -1; i = normalizedTypeName.indexOf(containerName, i + 1)) { - if (!i || (i > 0 && !isSubstringOfAType(normalizedTypeName[i - 1]))) - indexes.append(i); - } - } - // To avoid problems with the constantly changing size we start replacements - // from the end of normalizedTypeName - std::sort(indexes.rbegin(), indexes.rend()); - - for (quint64 combination = 1; ; ++combination) { - std::bitset<64> bits(combination); - QByteArray name = normalizedTypeName; - for (auto j = 0; j < indexes.size(); ++j) { - if (bits.test(j)) { - auto i = indexes[j]; - auto replaceFrom = normalizedTypeName[i + 1] == 'V' ? vectorName : listName; - auto replaceTo = normalizedTypeName[i + 1] == 'V' ? listName : vectorName; - name.replace(i, sizeof(replaceFrom), replaceTo); - } - } - QMetaType::registerNormalizedTypedef(name, id); - if (bits.count() >= size_t(indexes.size())) - break; - } -} -} +// Explicit instantiation definition +#define QT_METATYPE_DECLARE_TEMPLATE_ITER(TypeName, Id, Name) \ + template class QMetaTypeForType<Name>; \ + template struct QMetaTypeInterfaceWrapper<Name>; +QT_FOR_EACH_STATIC_PRIMITIVE_NON_VOID_TYPE(QT_METATYPE_DECLARE_TEMPLATE_ITER) +QT_FOR_EACH_STATIC_PRIMITIVE_POINTER(QT_METATYPE_DECLARE_TEMPLATE_ITER) +QT_FOR_EACH_STATIC_CORE_CLASS(QT_METATYPE_DECLARE_TEMPLATE_ITER) +QT_FOR_EACH_STATIC_CORE_POINTER(QT_METATYPE_DECLARE_TEMPLATE_ITER) +QT_FOR_EACH_STATIC_CORE_TEMPLATE(QT_METATYPE_DECLARE_TEMPLATE_ITER) -namespace QtMetaTypePrivate { -const bool VectorBoolElements::true_element = true; -const bool VectorBoolElements::false_element = false; +#undef QT_METATYPE_DECLARE_TEMPLATE_ITER +#endif } QT_END_NAMESPACE |