diff options
Diffstat (limited to 'src/corelib/kernel/qmetatype.cpp')
-rw-r--r-- | src/corelib/kernel/qmetatype.cpp | 2957 |
1 files changed, 2243 insertions, 714 deletions
diff --git a/src/corelib/kernel/qmetatype.cpp b/src/corelib/kernel/qmetatype.cpp index 9841758c80..1c2665e53c 100644 --- a/src/corelib/kernel/qmetatype.cpp +++ b/src/corelib/kernel/qmetatype.cpp @@ -1,41 +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$ -** -****************************************************************************/ +// 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" @@ -43,23 +9,25 @@ #include "qdatetime.h" #include "qbytearray.h" #include "qreadwritelock.h" +#include "qhash.h" +#include "qmap.h" #include "qstring.h" #include "qstringlist.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 "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" @@ -71,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) @@ -84,52 +56,71 @@ # include "qline.h" #endif -#include <bitset> #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 { - QReadWriteLock lock; - QList<QtPrivate::QMetaTypeInterface *> registry; - QHash<QByteArray, QtPrivate::QMetaTypeInterface *> aliases; -#ifndef QT_NO_DATASTREAM - struct DataStreamOps + +#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0) && !defined(QT_BOOTSTRAPPED) + QMetaTypeCustomRegistry() { - QMetaType::SaveOperator saveOp; - QMetaType::LoadOperator loadOp; - }; - QHash<int, DataStreamOps> dataStreamOp; + /* 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(QtPrivate::QMetaTypeInterface *ti) + 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 (ti->typeId) - return ti->typeId; + 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)) { - ti->typeId.storeRelaxed(ti2->typeId.loadRelaxed()); - return ti2->typeId; + const auto id = ti2->typeId.loadRelaxed(); + ti->typeId.storeRelaxed(id); + return id; } aliases[name] = ti; int size = registry.size(); @@ -142,11 +133,11 @@ struct QMetaTypeCustomRegistry registry.append(ti); firstEmpty = registry.size(); } - ti->typeId = firstEmpty + QMetaType::User; + ti->typeId.storeRelaxed(firstEmpty + QMetaType::User); } if (ti->legacyRegisterOp) ti->legacyRegisterOp(); - return ti->typeId; + return ti->typeId.loadRelaxed(); }; void unregisterDynamicType(int id) @@ -159,20 +150,14 @@ struct QMetaTypeCustomRegistry auto &ti = registry[idx]; // We must unregister all names. - auto it = aliases.begin(); - while (it != aliases.end()) { - if (it.value() == ti) - it = aliases.erase(it); - else - ++it; - } + aliases.removeIf([ti] (const auto &kv) { return kv.value() == ti; }); ti = nullptr; firstEmpty = std::min(firstEmpty, idx); } - QtPrivate::QMetaTypeInterface *getCustomType(int id) + const QtPrivate::QMetaTypeInterface *getCustomType(int id) { QReadLocker l(&lock); return registry.value(id - QMetaType::User - 1); @@ -183,6 +168,44 @@ 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 @@ -239,13 +262,23 @@ Q_GLOBAL_STATIC(QMetaTypeCustomRegistry, customTypeRegistry) \li Pointers to classes derived from QObject \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() */ @@ -319,14 +352,20 @@ Q_GLOBAL_STATIC(QMetaTypeCustomRegistry, customTypeRegistry) \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 @@ -344,6 +383,7 @@ Q_GLOBAL_STATIC(QMetaTypeCustomRegistry, customTypeRegistry) \value QStringList QStringList \value QVariantMap QVariantMap \value QVariantHash QVariantHash + \value QVariantPair QVariantPair \value QIcon QIcon \value QPen QPen \value QLineF QLineF @@ -384,13 +424,13 @@ Q_GLOBAL_STATIC(QMetaTypeCustomRegistry, customTypeRegistry) \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() */ @@ -400,17 +440,31 @@ Q_GLOBAL_STATIC(QMetaTypeCustomRegistry, customTypeRegistry) 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. */ /*! @@ -420,21 +474,24 @@ Q_GLOBAL_STATIC(QMetaTypeCustomRegistry, customTypeRegistry) \ingroup objectmodel \threadsafe + \compares equality 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 @@ -452,6 +509,8 @@ Q_GLOBAL_STATIC(QMetaTypeCustomRegistry, customTypeRegistry) Returns \c true if this QMetaType object contains valid information about a type, false otherwise. + + \sa isRegistered() */ bool QMetaType::isValid() const { @@ -462,35 +521,49 @@ bool QMetaType::isValid() const \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; + 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. */ -int QMetaType::id() 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) { - if (d_ptr) { - if (d_ptr->typeId) - return d_ptr->typeId; - auto reg = customTypeRegistry(); - if (reg) { - return reg->registerCustomType(d_ptr); - } + Q_ASSERT(iface); + auto reg = customTypeRegistry(); + if (reg) { + return reg->registerCustomType(iface); } return 0; } /*! - \fn bool QMetaType::sizeOf() const + \fn constexpr qsizetype QMetaType::sizeOf() const \since 5.0 Returns the size of the type in bytes (i.e. sizeof(T), @@ -500,57 +573,62 @@ int QMetaType::id() const 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() */ -int QMetaType::sizeOf() const -{ - if (d_ptr) - return d_ptr->size; - return 0; -} /*! - \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() */ -QMetaType::TypeFlags QMetaType::flags() const -{ - if (d_ptr) - return TypeFlags(d_ptr->flags); - return {}; -} /*! - \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() */ -const QMetaObject *QMetaType::metaObject() const -{ - return d_ptr ? d_ptr->metaObject : nullptr; -} /*! - \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 @@ -561,16 +639,17 @@ const QMetaObject *QMetaType::metaObject() const */ void *QMetaType::create(const void *copy) const { - if (d_ptr) { - void *where = -#ifdef __STDCPP_DEFAULT_NEW_ALIGNMENT__ - d_ptr->alignment > __STDCPP_DEFAULT_NEW_ALIGNMENT__ ? - operator new(d_ptr->size, std::align_val_t(d_ptr->alignment)) : -#endif - operator new(d_ptr->size); - return construct(where, copy); - } - return nullptr; + 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(); } /*! @@ -584,18 +663,14 @@ void *QMetaType::create(const void *copy) const */ void QMetaType::destroy(void *data) const { - if (d_ptr && d_ptr->dtor) { - d_ptr->dtor(d_ptr, data); - if (d_ptr->alignment > __STDCPP_DEFAULT_NEW_ALIGNMENT__) { - operator delete(data, std::align_val_t(d_ptr->alignment)); - } else { - operator delete(data); - } + if (data && isDestructible()) { + QtMetaTypePrivate::destruct(d_ptr, data); + QMetaTypeDeleter{d_ptr}(data); } } /*! - \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 @@ -624,16 +699,11 @@ void *QMetaType::construct(void *where, const void *copy) const { if (!where) return nullptr; - if (d_ptr) { - if (copy && d_ptr->copyCtr) { - d_ptr->copyCtr(d_ptr, where, copy); - return where; - } else if (!copy && d_ptr->defaultCtr) { - d_ptr->defaultCtr(d_ptr, where); - return where; - } - } - return nullptr; + if (copy ? !isCopyConstructible() : !isDefaultConstructible()) + return nullptr; + + QtMetaTypePrivate::construct(d_ptr, where, copy); + return where; } /*! @@ -649,47 +719,204 @@ void *QMetaType::construct(void *where, const void *copy) const */ void QMetaType::destruct(void *data) const { - if (!data) - return; - if (d_ptr && d_ptr->dtor) { - d_ptr->dtor(d_ptr, data); - return; + 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; } -void QtMetaTypePrivate::derefAndDestroy(NS(QtPrivate::QMetaTypeInterface) *d_ptr) +/*! + 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. + + \since 6.0 + \sa isEqualityComparable(), compare() +*/ +bool QMetaType::equals(const void *lhs, const void *rhs) const { - if (d_ptr && !d_ptr->ref.deref()) { - if (auto reg = customTypeRegistry()) - reg->unregisterDynamicType(d_ptr->typeId.loadRelaxed()); - Q_ASSERT(d_ptr->deleteSelf); - d_ptr->deleteSelf(d_ptr); + 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); } /*! - \fn QMetaType::~QMetaType() + 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. - Destructs this object. + \sa equals(), isOrdered() */ -QMetaType::~QMetaType() +bool QMetaType::isEqualityComparable() const { - QtMetaTypePrivate::derefAndDestroy(d_ptr); + return d_ptr && (d_ptr->flags & QMetaType::IsPointer || d_ptr->equals != nullptr || d_ptr->lessThan != nullptr); } -QMetaType::QMetaType(QtPrivate::QMetaTypeInterface *d) : d_ptr(d) +/*! + 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 { - if (d_ptr) - d_ptr->ref.ref(); + return d_ptr && (d_ptr->flags & QMetaType::IsPointer || d_ptr->lessThan != nullptr); } -QMetaType &QMetaType::operator=(const QMetaType &other) + +/*! + \internal +*/ +void QMetaType::unregisterMetaType(QMetaType type) { - if (d_ptr != other.d_ptr) { - this->~QMetaType(); - new (this) QMetaType(other.d_ptr); + 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); } - return *this; + + const_cast<QtPrivate::QMetaTypeInterface *>(d_ptr)->typeId.storeRelease(0); } /*! @@ -699,24 +926,28 @@ QMetaType &QMetaType::operator=(const QMetaType &other) Returns the QMetaType corresponding to the type in the template parameter. */ -/*! \fn bool operator==(const QMetaType &a, const QMetaType &b) +/*! \fn bool QMetaType::operator==(const QMetaType &lhs, const QMetaType &rhs) \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. + Returns \c true if the QMetaType \a lhs represents the same type + as the QMetaType \a rhs, otherwise returns \c false. */ -/*! \fn bool operator!=(const QMetaType &a, const QMetaType &b) +/*! \fn bool QMetaType::operator!=(const QMetaType &lhs, const QMetaType &rhs) \since 5.15 - \relates QMetaType \overload - Returns \c true if the QMetaType \a a represents a different type - than the QMetaType \a b, otherwise returns \c false. + Returns \c true if the QMetaType \a lhs represents a different type + than the QMetaType \a rhs, 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 }, @@ -732,11 +963,709 @@ static const struct { const char * typeName; int typeNameLength; int type; } typ {nullptr, 0, QMetaType::UnknownType} }; -Q_CORE_EXPORT const QMetaTypeModuleHelper *qMetaTypeGuiHelper = nullptr; -Q_CORE_EXPORT const QMetaTypeModuleHelper *qMetaTypeWidgetsHelper = nullptr; +// NOLINTNEXTLINE(cppcoreguidelines-virtual-class-destructor): this is not a base class +static constexpr struct : QMetaTypeModuleHelper +{ + template<typename T, typename LiteralWrapper = + std::conditional_t<std::is_same_v<T, QString>, QLatin1StringView, const char *>> + static inline bool convertToBool(const T &source) + { + T str = source.toLower(); + return !(str.isEmpty() || str == LiteralWrapper("0") || str == LiteralWrapper("false")); + } + + 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) @@ -760,20 +1689,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) @@ -784,88 +1715,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_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; @@ -873,280 +1823,922 @@ 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 + \internal + returns a QMetaEnum for a given meta tape type id if possible */ -bool QMetaType::convert(const void *from, int fromTypeId, void *to, int toTypeId) +static QMetaEnum metaEnumFromType(QMetaType t) { - const QtPrivate::AbstractConverterFunction * const f = - customTypesConversionRegistry()->function(qMakePair(fromTypeId, toTypeId)); - return f && f->convert(f, from, to); + 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 -/*! - 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 convertFromEnum(QMetaType fromType, const void *from, QMetaType toType, void *to) { - const QtPrivate::AbstractComparatorFunction * const f = - customTypesComparatorRegistry()->function(typeId); - if (!f) + 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; +} + +static bool convertToEnum(QMetaType fromType, const void *from, QMetaType toType, void *to) +{ + 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; - if (f->equals(f, lhs, rhs)) - *result = 0; - else if (f->lessThan) - *result = f->lessThan(f, lhs, rhs) ? -1 : 1; - else + + 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); + } +} + +#ifndef QT_BOOTSTRAPPED +static bool convertIterableToVariantList(QMetaType fromType, const void *from, void *to) +{ + QSequentialIterable list; + if (!QMetaType::convert(fromType, from, QMetaType::fromType<QSequentialIterable>(), &list)) return false; + + 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; } -/*! - 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) +static bool convertIterableToVariantMap(QMetaType fromType, const void *from, void *to) { - const QtPrivate::AbstractComparatorFunction * const f - = customTypesComparatorRegistry()->function(typeId); - if (!f) + QAssociativeIterable map; + if (!QMetaType::convert(fromType, from, QMetaType::fromType<QAssociativeIterable>(), &map)) return false; - if (f->equals(f, lhs, rhs)) - *result = 0; - else - *result = -1; + + 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; } -/*! - 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 convertIterableToVariantHash(QMetaType fromType, const void *from, void *to) +{ + QAssociativeIterable map; + if (!QMetaType::convert(fromType, from, QMetaType::fromType<QAssociativeIterable>(), &map)) + return false; + + 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; +} + +static bool convertIterableToVariantPair(QMetaType fromType, const void *from, void *to) { - const QtPrivate::AbstractDebugStreamFunction * const f = customTypesDebugStreamRegistry()->function(typeId); + const int targetId = qMetaTypeId<QtMetaTypePrivate::QPairVariantInterfaceImpl>(); + const auto f = customTypesConversionRegistry()->function({fromType.id(), targetId}); + if (!f) return false; - f->stream(f, dbg, rhs); + + QtMetaTypePrivate::QPairVariantInterfaceImpl pi; + (*f)(from, &pi); + + QVariant v1(pi._metaType_first); + void *dataPtr; + if (pi._metaType_first == QMetaType::fromType<QVariant>()) + dataPtr = &v1; + else + dataPtr = v1.data(); + pi.first(dataPtr); + + QVariant v2(pi._metaType_second); + if (pi._metaType_second == QMetaType::fromType<QVariant>()) + dataPtr = &v2; + else + dataPtr = v2.data(); + pi.second(dataPtr); + + *static_cast<QVariantPair *>(to) = QVariantPair(v1, v2); return true; } +static bool convertToSequentialIterable(QMetaType fromType, const void *from, void *to) +{ + using namespace QtMetaTypePrivate; + const int fromTypeId = fromType.id(); + + 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; + } + } + } + + 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 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>>()); + } +} + +static bool viewAsSequentialIterable(QMetaType fromType, void *from, void *to) +{ + using namespace QtMetaTypePrivate; + const int fromTypeId = fromType.id(); + + 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; + } + } + } + + return false; +} + +static bool convertToAssociativeIterable(QMetaType fromType, const void *from, void *to) +{ + 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; + } + + QAssociativeIterable impl; + if (QMetaType::convert( + fromType, from, QMetaType::fromType<QIterable<QMetaAssociation>>(), &impl)) { + i = std::move(impl); + return true; + } + + return false; +} + +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 + + const QMetaObject *f = fromType.metaObject(); + const QMetaObject *t = toType.metaObject(); + if (f && t) { + return f->inherits(t) || (t->inherits(f)); + } + return false; +} + +static bool canConvertToAssociativeIterable(QMetaType fromType) +{ + switch (fromType.id()) { + case QMetaType::QVariantMap: + case QMetaType::QVariantHash: + return true; + default: + return QMetaType::canConvert(fromType, QMetaType::fromType<QIterable<QMetaAssociation>>()); + } +} + +static bool canImplicitlyViewAsAssociativeIterable(QMetaType fromType) +{ + switch (fromType.id()) { + case QMetaType::QVariantMap: + case QMetaType::QVariantHash: + return true; + default: + return QMetaType::canView( + fromType, QMetaType::fromType<QIterable<QMetaAssociation>>()); + } +} + +static bool viewAsAssociativeIterable(QMetaType fromType, void *from, void *to) +{ + using namespace QtMetaTypePrivate; + int fromTypeId = fromType.id(); + + QAssociativeIterable &i = *static_cast<QAssociativeIterable *>(to); + if (fromTypeId == QMetaType::QVariantMap) { + i = QAssociativeIterable(reinterpret_cast<QVariantMap *>(from)); + return true; + } + if (fromTypeId == QMetaType::QVariantHash) { + i = QAssociativeIterable(reinterpret_cast<QVariantHash *>(from)); + return true; + } + + QIterable<QMetaAssociation> j(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; + } + } + return false; +} +#endif // !QT_BOOTSTRAPPED + /*! - \fn bool QMetaType::hasRegisteredConverterFunction() - Returns \c true, if the meta type system has a registered conversion from type From to type To. + \fn bool QMetaType::convert(const void *from, int fromTypeId, void *to, int toTypeId) + \deprecated + + 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 - \overload - */ +*/ /*! - Returns \c true, if the meta type system has a registered conversion from meta type id \a fromTypeId - to \a toTypeId + 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. + + Both \a from and \a to have to be valid pointers. + \since 5.2 */ -bool QMetaType::hasRegisteredConverterFunction(int fromTypeId, int toTypeId) +bool QMetaType::convert(QMetaType fromType, const void *from, QMetaType toType, void *to) { - return customTypesConversionRegistry()->contains(qMakePair(fromTypeId, toTypeId)); + if (!fromType.isValid() || !toType.isValid()) + return false; + + if (fromType == toType) { + // just make a copy + fromType.destruct(to); + fromType.construct(to, from); + return true; + } + + 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; + } + } + +#ifndef QT_BOOTSTRAPPED +# ifndef QT_NO_VARIANT + if (toTypeId == QVariantPair && convertIterableToVariantPair(fromType, from, to)) + return true; + + // handle iterables + if (toTypeId == QVariantList && convertIterableToVariantList(fromType, from, to)) + return true; + + 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 + return false; +#endif } -#ifndef QT_NO_DATASTREAM /*! - \internal + 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 */ -void QMetaType::registerStreamOperators(const char *typeName, SaveOperator saveOp, - LoadOperator loadOp) +bool QMetaType::view(QMetaType fromType, void *from, QMetaType toType, void *to) { - registerStreamOperators(type(typeName), saveOp, loadOp); + if (!fromType.isValid() || !toType.isValid()) + return false; + + 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 + return false; +#endif } /*! - \internal + Returns \c true if QMetaType::view can create a mutable view of type \a toType + on type \a fromType. + + Converting between pointers of types derived from QObject will return true for this + function if a qobject_cast from the type described by \a fromType to the type described + by \a toType would succeed. + + You can create a mutable view of type QSequentialIterable on any container registered with + Q_DECLARE_SEQUENTIAL_CONTAINER_METATYPE(). + + Similarly you can create a mutable view of type QAssociativeIterable on any container + registered with Q_DECLARE_ASSOCIATIVE_CONTAINER_METATYPE(). + + \sa convert(), QSequentialIterable, Q_DECLARE_SEQUENTIAL_CONTAINER_METATYPE(), + QAssociativeIterable, Q_DECLARE_ASSOCIATIVE_CONTAINER_METATYPE() */ -void QMetaType::registerStreamOperators(int idx, SaveOperator saveOp, - LoadOperator loadOp) +bool QMetaType::canView(QMetaType fromType, QMetaType toType) { - if (idx < User) - return; //builtin types should not be registered; - - if (auto reg = customTypeRegistry()) { - QWriteLocker locker(®->lock); - reg->dataStreamOp[idx] = { saveOp, loadOp }; - } -} -#endif // QT_NO_DATASTREAM + int fromTypeId = fromType.id(); + int toTypeId = toType.id(); -// We don't officially support constexpr in MSVC 2015, but the limited support it -// has is enough for the code below. + if (fromTypeId == UnknownType || toTypeId == UnknownType) + return false; -#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, + const auto f = customTypesMutableViewRegistry()->function({fromTypeId, toTypeId}); + if (f) + return true; -namespace { -// All type names in one long string. -constexpr char metaTypeStrings[] = QT_FOR_EACH_STATIC_TYPE(STRINGIFY_TYPE_NAME); +#ifndef QT_BOOTSTRAPPED + if (toTypeId == qMetaTypeId<QSequentialIterable>()) + return canImplicitlyViewAsSequentialIterable(fromType); -// The sizes of the strings in the metaTypeStrings string (including terminating null) -constexpr short metaTypeNameSizes[] = { - QT_FOR_EACH_STATIC_TYPE(CALCULATE_TYPE_LEN) -}; + if (toTypeId == qMetaTypeId<QAssociativeIterable>()) + return canImplicitlyViewAsAssociativeIterable(fromType); -// The type IDs, in the order of the metaTypeStrings data -constexpr short metaTypeIds[] = { - QT_FOR_EACH_STATIC_TYPE(MAP_TYPE_ID_TO_IDX) -}; + if (canConvertMetaObject(fromType, toType)) + return true; +#endif -constexpr int MetaTypeNameCount = sizeof(metaTypeNameSizes) / sizeof(metaTypeNameSizes[0]); + return false; +} -template <typename IntegerSequence> struct MetaTypeOffsets; -template <int... TypeIds> struct MetaTypeOffsets<QtPrivate::IndexesList<TypeIds...>> +/*! + 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) { - // 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. + int fromTypeId = fromType.id(); + int toTypeId = toType.id(); - static constexpr int findTypeId(int typeId, int i = 0) - { - return i >= MetaTypeNameCount ? -1 : - metaTypeIds[i] == typeId ? i : findTypeId(typeId, i + 1); - } + if (fromTypeId == UnknownType || toTypeId == UnknownType) + return false; - static constexpr short calculateOffsetForIdx(int i) - { - return i < 0 ? -1 : - i == 0 ? 0 : metaTypeNameSizes[i - 1] + calculateOffsetForIdx(i - 1); + if (fromTypeId == toTypeId) + return true; + + 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; - 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; +#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; } - short offsets[sizeof...(TypeIds)]; - constexpr MetaTypeOffsets() : offsets{calculateOffsetForTypeId(TypeIds)...} {} + if ((toTypeId == QVariantHash || toTypeId == QVariantMap) + && canConvert(fromType, QMetaType::fromType<QAssociativeIterable>())) { + return true; + } - const char *operator[](int typeId) const noexcept - { - short o = offsets[typeId]; - return o < 0 ? nullptr : metaTypeStrings + o; + if (toTypeId == QVariantPair && hasRegisteredConverterFunction( + fromType, QMetaType::fromType<QtMetaTypePrivate::QPairVariantInterfaceImpl>())) + return true; +#endif + + if (fromType.flags() & IsEnumeration) { + if (toTypeId == QString || toTypeId == QByteArray) + return true; + return canConvert(QMetaType(LongLong), toType); } -}; -} // anonymous namespace + 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 false; +} + +/*! + \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. +*/ -constexpr MetaTypeOffsets<QtPrivate::Indexes<QMetaType::HighestInternalId + 1>::Value> metaTypeNames {}; -#undef STRINGIFY_TYPE_NAME -#undef CALCULATE_TYPE_LEN -#undef MAP_TYPE_ID_TO_IDX +/*! + \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 + */ + +/*! + Returns \c true, if the meta type system has a registered conversion from meta type id \a fromType + to \a toType + \since 5.2 +*/ +bool QMetaType::hasRegisteredConverterFunction(QMetaType fromType, QMetaType toType) +{ + return customTypesConversionRegistry()->contains({fromType.id(), toType.id()}); +} + +/*! + \internal + Non-template helper ("SCARY") for IsMetaTypePair::registerConverter(). +*/ +bool QtPrivate::hasRegisteredConverterFunctionToPairVariantInterface(QMetaType m) +{ + const QMetaType to = QMetaType::fromType<QtMetaTypePrivate::QPairVariantInterfaceImpl>(); + return QMetaType::hasRegisteredConverterFunction(m, to); +} + +/*! + \internal + Non-template helper ("SCARY") for SequentialValueTypeIsMetaType::registerConverter(). +*/ +bool QtPrivate::hasRegisteredConverterFunctionToIterableMetaSequence(QMetaType m) +{ + const QMetaType to = QMetaType::fromType<QIterable<QMetaSequence>>(); + return QMetaType::hasRegisteredConverterFunction(m, to); +} + +/*! + \internal + Non-template helper ("SCARY") for AssociativeKeyTypeIsMetaType::registerConverter(). +*/ +bool QtPrivate::hasRegisteredConverterFunctionToIterableMetaAssociation(QMetaType m) +{ + const QMetaType to = QMetaType::fromType<QIterable<QMetaAssociation>>(); + return QMetaType::hasRegisteredConverterFunction(m, to); +} + +/*! + \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 +*/ + +/*! + 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()}); +} + +/*! + \internal + Non-template helper ("SCARY") for SequentialValueTypeIsMetaType::registerMutableView(). +*/ +bool QtPrivate::hasRegisteredMutableViewFunctionToIterableMetaSequence(QMetaType m) +{ + const QMetaType to = QMetaType::fromType<QIterable<QMetaSequence>>(); + return QMetaType::hasRegisteredMutableViewFunction(m, to); +} + +/*! + \internal + Non-template helper ("SCARY") for AssociativeKeyTypeIsMetaType::registerMutableView(). +*/ +bool QtPrivate::hasRegisteredMutableViewFunctionToIterableMetaAssociation(QMetaType m) +{ + const QMetaType to = QMetaType::fromType<QIterable<QMetaAssociation>>(); + return QMetaType::hasRegisteredMutableViewFunction(m, to); +} /*! + \fn const char *QMetaType::typeName(int typeId) + \deprecated + 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. \sa type(), isRegistered(), Type, name() */ -const char *QMetaType::typeName(int typeId) -{ - 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... - } - - if (auto reg = customTypeRegistry()) { - if (auto ti = reg->getCustomType(typeId)) - return ti->name; - } - return nullptr; -} /*! + \fn constexpr const char *QMetaType::name() const \since 5.15 Returns the type name associated with this QMetaType, or a null @@ -1155,10 +2747,6 @@ const char *QMetaType::typeName(int typeId) \sa typeName() */ -QByteArray QMetaType::name() const -{ - return d_ptr ? d_ptr->name : nullptr; -} /* Similar to QMetaType::type(), but only looks in the static set of types. @@ -1180,12 +2768,13 @@ static inline int qMetaTypeStaticType(const char *typeName, int length) */ static int qMetaTypeCustomType_unlocked(const char *typeName, int length) { - if (auto reg = customTypeRegistry()) { + if (customTypeRegistry.exists()) { + auto reg = &*customTypeRegistry; #if QT_CONFIG(thread) Q_ASSERT(!reg->lock.tryLockForWrite()); #endif - if (auto ti = reg->aliases.value(QByteArray(typeName, length), nullptr)) { - return ti->typeId; + if (auto ti = reg->aliases.value(QByteArray::fromRawData(typeName, length), nullptr)) { + return ti->typeId.loadRelaxed(); } } return QMetaType::UnknownType; @@ -1212,6 +2801,20 @@ void QMetaType::registerNormalizedTypedef(const NS(QByteArray) & normalizedTypeN } } + +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 iface; +} + /*! Returns \c true if the datatype with ID \a type is registered; otherwise returns \c false. @@ -1220,7 +2823,7 @@ void QMetaType::registerNormalizedTypedef(const NS(QByteArray) & normalizedTypeN */ bool QMetaType::isRegistered(int type) { - return QMetaType(type).isRegistered(); + return interfaceForTypeNoWarning(type) != nullptr; } template <bool tryNormalizedType> @@ -1248,280 +2851,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 -namespace -{ - -template<typename T> -class HasStreamOperator -{ - struct Yes { char unused[1]; }; - struct No { char unused[2]; }; - 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 -static_assert(HasStreamOperator<NS(QJsonDocument)>::Value); -static_assert(!HasStreamOperator<void*>::Value); -static_assert(HasStreamOperator<qint8>::Value); +/*! + 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. -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; - } -}; + 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> -struct FilteredOperatorSwitch<T, /* IsAcceptedType = */ false> + \sa load() +*/ +bool QMetaType::save(QDataStream &stream, const void *data) const { - static const QMetaTypeModuleHelper *getMetaTypeInterface() - { - if (QModulesPrivate::QTypeModuleInfo<T>::IsGui) - return qMetaTypeGuiHelper; - else if (QModulesPrivate::QTypeModuleInfo<T>::IsWidget) - return qMetaTypeWidgetsHelper; - return nullptr; - } - static bool save(QDataStream &stream, const T *data, int type) - { - if (auto interface = getMetaTypeInterface()) { - return interface->save(stream, type, data); - } + if (!data || !isValid()) return false; - } - static bool load(QDataStream &stream, T *data, int type) - { - if (auto interface = getMetaTypeInterface()) { - return interface->load(stream, type, data); - } - 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); - return true; - } - bool delegate(const long *data) - { - stream << qlonglong(*data); - return true; - } - bool delegate(const unsigned long *data) - { - stream << qulonglong(*data); + // keep compatibility for long/ulong + if (id() == QMetaType::Long) { + stream << qlonglong(*(long *)data); return true; - } - bool delegate(const QMetaTypeSwitcher::NotBuiltinType *data) - { - auto ct = customTypeRegistry(); - if (!ct) - return false; - QMetaType::SaveOperator op = nullptr; - { - QReadLocker lock(&ct->lock); - op = ct->dataStreamOp.value(m_type).saveOp; - } - if (!op) - return false; - op(stream, data); + } else if (id() == QMetaType::ULong) { + stream << qlonglong(*(unsigned long *)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) - { - auto ct = customTypeRegistry(); - if (!ct) - return false; - QMetaType::LoadOperator op = nullptr; - { - QReadLocker lock(&ct->lock); - op = ct->dataStreamOp.value(m_type).loadOp; - } - if (!op) - return false; - op(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 - \sa save(), qRegisterMetaTypeStreamOperators() -*/ -bool QMetaType::load(QDataStream &stream, int type, void *data) + 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 (!data) - return false; - LoadOperatorSwitch loadOp{stream, type}; - return QMetaTypeSwitcher::switcher<bool>(loadOp, type, data); + 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(); } + +/*! + \fn bool QMetaType::load(QDataStream &stream, int type, void *data) + \overload + \deprecated +*/ #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) +{ + return QMetaType(qMetaTypeTypeImpl</*tryNormalizedType=*/true>(typeName.data(), typeName.size())); +} + +/*! + \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) -{ - return QMetaType(type).create(copy); -} /*! + \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(type).destroy(data); -} /*! + \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 @@ -1546,14 +3088,12 @@ void QMetaType::destroy(int type, void *data) \sa destruct(), sizeOf() */ -void *QMetaType::construct(int type, void *where, const void *copy) -{ - return QMetaType(type).construct(where, copy); -} /*! + \fn void QMetaType::destruct(int type, void *where) \since 5.0 + \deprecated Destructs the value of the given \a type, located at \a where. @@ -1562,13 +3102,11 @@ void *QMetaType::construct(int type, void *where, const void *copy) \sa construct() */ -void QMetaType::destruct(int type, void *where) -{ - return QMetaType(type).destruct(where); -} /*! + \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). @@ -1576,41 +3114,33 @@ void QMetaType::destruct(int type, void *where) 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) -{ - return QMetaType(type).sizeOf(); -} /*! + \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) -{ - return QMetaType(type).flags(); -} - /*! + \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) -{ - return QMetaType(type).metaObject(); -} /*! - \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 @@ -1638,67 +3168,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 - Registers the stream operators for the type \c{T} called \a - typeName. + Call this function to register the type \c T. Returns the meta type Id. - Afterward, the type can be streamed using QMetaType::load() and - QMetaType::save(). These functions are used when streaming a - QVariant. + Example: - \snippet code/src_corelib_kernel_qmetatype.cpp 5 + \snippet code/src_corelib_kernel_qmetatype.cpp 7 - The stream operators should have the following signatures: + 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. - \snippet code/src_corelib_kernel_qmetatype.cpp 6 + To use the type \c T in QMetaType, QVariant, or with the + QObject::property() API, registration is not necessary. - \sa qRegisterMetaType(), QMetaType::isRegistered(), Q_DECLARE_METATYPE() -*/ + 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. + + After a type has been registered, it can be found by its name using + QMetaType::fromName(). + + \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 @@ -1719,52 +3249,51 @@ const QMetaObject *QMetaType::metaObjectForType(int type) \sa Q_DECLARE_METATYPE(), QMetaType::type() */ -static QtPrivate::QMetaTypeInterface *interfaceForType(int typeId) +static const QtPrivate::QMetaTypeInterface *interfaceForType(int typeId) { - if (typeId >= QMetaType::User) { - if (auto reg = customTypeRegistry()) - return reg->getCustomType(typeId); - } - if (auto moduleHelper = qModuleHelperForType(typeId)) - return moduleHelper->interfaceForType(typeId); - - switch (typeId) { - 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: - if (typeId != QMetaType::UnknownType) - qWarning("Trying to construct an instance of an invalid type, type id: %i", typeId); - return nullptr; - } + 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); + + return iface; } /*! - \fn QMetaType::QMetaType(const int typeId) + \fn QMetaType::QMetaType() + \since 6.0 + + Constructs a default, invalid, QMetaType object. +*/ + +/*! + \fn QMetaType::QMetaType(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(int typeId) : QMetaType(interfaceForType(typeId)) {} -namespace QtMetaTypePrivate { -const bool VectorBoolElements::true_element = true; -const bool VectorBoolElements::false_element = false; -} + +/*! \fn size_t qHash(QMetaType type, size_t seed = 0) + \relates QMetaType + \since 6.4 + + Returns the hash value for the \a type, using \a seed to seed the calculation. +*/ namespace QtPrivate { -#ifndef QT_BOOTSTRAPPED +#if !defined(QT_BOOTSTRAPPED) && !defined(Q_CC_MSVC) && !defined(Q_OS_INTEGRITY) + // Explicit instantiation definition -#define QT_METATYPE_DECLARE_TEMPLATE_ITER(TypeName, Id, Name) template class QMetaTypeForType<Name>; -QT_FOR_EACH_STATIC_PRIMITIVE_TYPE(QT_METATYPE_DECLARE_TEMPLATE_ITER) +#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) + #undef QT_METATYPE_DECLARE_TEMPLATE_ITER #endif } |