diff options
Diffstat (limited to 'src/corelib/kernel/qvariant.cpp')
-rw-r--r-- | src/corelib/kernel/qvariant.cpp | 1093 |
1 files changed, 621 insertions, 472 deletions
diff --git a/src/corelib/kernel/qvariant.cpp b/src/corelib/kernel/qvariant.cpp index 55fe9d3c3f..92a44c462b 100644 --- a/src/corelib/kernel/qvariant.cpp +++ b/src/corelib/kernel/qvariant.cpp @@ -3,7 +3,7 @@ // Copyright (C) 2015 Olivier Goffart <ogoffart@woboq.com> // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only -#include "qvariant.h" +#include "qvariant_p.h" #include "qbitarray.h" #include "qbytearray.h" #include "qdatastream.h" @@ -36,7 +36,6 @@ #include "qjsondocument.h" #include "qbytearraylist.h" #endif -#include "private/qvariant_p.h" #include "private/qlocale_p.h" #include "qmetatype_p.h" #include <qmetaobject.h> @@ -48,6 +47,8 @@ #include "qline.h" #endif +#include <memory> + #include <cmath> #include <float.h> #include <cstring> @@ -58,24 +59,31 @@ using namespace Qt::StringLiterals; namespace { // anonymous used to hide QVariant handlers -/*! - \internal - */ +static qlonglong qMetaTypeNumberBySize(const QVariant::Private *d) +{ + switch (d->typeInterface()->size) { + case 1: + return d->get<signed char>(); + case 2: + return d->get<short>(); + case 4: + return d->get<int>(); + case 8: + return d->get<qlonglong>(); + } + Q_UNREACHABLE_RETURN(0); +} + static qlonglong qMetaTypeNumber(const QVariant::Private *d) { - switch (d->typeId()) { + switch (d->typeInterface()->typeId) { case QMetaType::Int: - return d->get<int>(); case QMetaType::LongLong: - return d->get<qlonglong>(); case QMetaType::Char: - return qlonglong(d->get<char>()); case QMetaType::SChar: - return qlonglong(d->get<signed char>()); case QMetaType::Short: - return qlonglong(d->get<short>()); case QMetaType::Long: - return qlonglong(d->get<long>()); + return qMetaTypeNumberBySize(d); case QMetaType::Float: return qRound64(d->get<float>()); case QMetaType::Double: @@ -87,54 +95,46 @@ static qlonglong qMetaTypeNumber(const QVariant::Private *d) return d->get<QCborValue>().toInteger(); #endif } - Q_ASSERT(false); - return 0; + Q_UNREACHABLE_RETURN(0); } static qulonglong qMetaTypeUNumber(const QVariant::Private *d) { - switch (d->typeId()) { - case QMetaType::UInt: - return d->get<unsigned int>(); - case QMetaType::ULongLong: - return d->get<qulonglong>(); - case QMetaType::UChar: + switch (d->typeInterface()->size) { + case 1: return d->get<unsigned char>(); - case QMetaType::UShort: + case 2: return d->get<unsigned short>(); - case QMetaType::ULong: - return d->get<unsigned long>(); + case 4: + return d->get<unsigned int>(); + case 8: + return d->get<qulonglong>(); } - Q_ASSERT(false); - return 0; + Q_UNREACHABLE_RETURN(0); } -static qlonglong qConvertToNumber(const QVariant::Private *d, bool *ok, bool allowStringToBool = false) +static std::optional<qlonglong> qConvertToNumber(const QVariant::Private *d, bool allowStringToBool = false) { - *ok = true; - - switch (uint(d->typeId())) { + bool ok; + switch (d->typeInterface()->typeId) { case QMetaType::QString: { const QString &s = d->get<QString>(); - qlonglong l = s.toLongLong(ok); - if (*ok) + if (qlonglong l = s.toLongLong(&ok); ok) return l; if (allowStringToBool) { - if (s == "false"_L1 || s == "0"_L1) { - *ok = true; + if (s == "false"_L1 || s == "0"_L1) return 0; - } - if (s == "true"_L1 || s == "1"_L1) { - *ok = true; + if (s == "true"_L1 || s == "1"_L1) return 1; - } } - return 0; + return std::nullopt; } case QMetaType::QChar: return d->get<QChar>().unicode(); case QMetaType::QByteArray: - return d->get<QByteArray>().toLongLong(ok); + if (qlonglong l = d->get<QByteArray>().toLongLong(&ok); ok) + return l; + return std::nullopt; case QMetaType::Bool: return qlonglong(d->get<bool>()); #ifndef QT_BOOTSTRAPPED @@ -159,47 +159,42 @@ static qlonglong qConvertToNumber(const QVariant::Private *d, bool *ok, bool all case QMetaType::ULongLong: case QMetaType::UInt: case QMetaType::UChar: + case QMetaType::Char16: + case QMetaType::Char32: case QMetaType::UShort: case QMetaType::ULong: - return qlonglong(qMetaTypeUNumber(d)); } - QMetaType typeInfo = d->type(); - if (typeInfo.flags() & QMetaType::IsEnumeration - || d->typeId() == QMetaType::QCborSimpleType) { - switch (typeInfo.sizeOf()) { - case 1: - return d->get<signed char>(); - case 2: - return d->get<short>(); - case 4: - return d->get<int>(); - case 8: - return d->get<qlonglong>(); - } - } + if (d->typeInterface()->flags & QMetaType::IsEnumeration + || d->typeInterface()->typeId == QMetaType::QCborSimpleType) + return qMetaTypeNumberBySize(d); - *ok = false; - return Q_INT64_C(0); + return std::nullopt; } -static qreal qConvertToRealNumber(const QVariant::Private *d, bool *ok) +static std::optional<double> qConvertToRealNumber(const QVariant::Private *d) { - *ok = true; - switch (uint(d->typeId())) { + bool ok; + switch (d->typeInterface()->typeId) { case QMetaType::QString: - return d->get<QString>().toDouble(ok); + if (double r = d->get<QString>().toDouble(&ok); ok) + return r; + return std::nullopt; case QMetaType::Double: - return qreal(d->get<double>()); + return d->get<double>(); case QMetaType::Float: - return qreal(d->get<float>()); + return double(d->get<float>()); + case QMetaType::Float16: + return double(d->get<qfloat16>()); case QMetaType::ULongLong: case QMetaType::UInt: case QMetaType::UChar: + case QMetaType::Char16: + case QMetaType::Char32: case QMetaType::UShort: case QMetaType::ULong: - return qreal(qMetaTypeUNumber(d)); + return double(qMetaTypeUNumber(d)); #ifndef QT_BOOTSTRAPPED case QMetaType::QCborValue: return d->get<QCborValue>().toDouble(); @@ -208,56 +203,116 @@ static qreal qConvertToRealNumber(const QVariant::Private *d, bool *ok) #endif default: // includes enum conversion as well as invalid types - return qreal(qConvertToNumber(d, ok)); + if (std::optional<qlonglong> l = qConvertToNumber(d)) + return double(*l); + return std::nullopt; } } -// the type of d has already been set, but other field are not set -static void customConstruct(QVariant::Private *d, const void *copy) +static bool isValidMetaTypeForVariant(const QtPrivate::QMetaTypeInterface *iface, const void *copy) { - QtPrivate::QMetaTypeInterface *iface = d->typeInterface(); - if (!(iface && iface->size)) { - *d = QVariant::Private(); - return; + using namespace QtMetaTypePrivate; + if (!iface || iface->size == 0) + return false; + + Q_ASSERT(!isInterfaceFor<void>(iface)); // only void should have size 0 + if (!isCopyConstructible(iface) || !isDestructible(iface)) { + // all meta types must be copyable (because QVariant is) and + // destructible (because QVariant owns it) + qWarning("QVariant: Provided metatype for '%s' does not support destruction and " + "copy construction", iface->name); + return false; + } + if (!copy && !isDefaultConstructible(iface)) { + // non-default-constructible types are acceptable, but not if you're + // asking us to construct from nothing + qWarning("QVariant: Cannot create type '%s' without a default constructor", iface->name); + return false; } + return true; +} + +enum CustomConstructMoveOptions { + UseCopy, // custom construct uses the copy ctor unconditionally + // future option: TryMove: uses move ctor if available, else copy ctor + ForceMove, // custom construct use the move ctor (which must exist) +}; + +enum CustomConstructNullabilityOption { + MaybeNull, // copy might be null, might be non-null + NonNull, // copy is guarantueed to be non-null + // future option: AlwaysNull? +}; + +// the type of d has already been set, but other field are not set +template <CustomConstructMoveOptions moveOption = UseCopy, CustomConstructNullabilityOption nullability = MaybeNull> +static void customConstruct(const QtPrivate::QMetaTypeInterface *iface, QVariant::Private *d, + std::conditional_t<moveOption == ForceMove, void *, const void *> copy) +{ + using namespace QtMetaTypePrivate; + Q_ASSERT(iface); + Q_ASSERT(iface->size); + Q_ASSERT(!isInterfaceFor<void>(iface)); + Q_ASSERT(isCopyConstructible(iface)); + Q_ASSERT(isDestructible(iface)); + Q_ASSERT(copy || isDefaultConstructible(iface)); + if constexpr (moveOption == ForceMove) + Q_ASSERT(isMoveConstructible(iface)); + if constexpr (nullability == NonNull) + Q_ASSERT(copy != nullptr); + + // need to check for nullptr_t here, as this can get called by fromValue(nullptr). fromValue() uses + // std::addressof(value) which in this case returns the address of the nullptr object. + // ### Qt 7: remove nullptr_t special casing + d->is_null = !copy QT6_ONLY(|| isInterfaceFor<std::nullptr_t>(iface)); + if (QVariant::Private::canUseInternalSpace(iface)) { - // QVariant requires type to be copy and default constructible - Q_ASSERT(iface->copyCtr); - Q_ASSERT(iface->defaultCtr); - if (copy) - iface->copyCtr(iface, &d->data, copy); - else - iface->defaultCtr(iface, &d->data); d->is_shared = false; - } else { - d->data.shared = QVariant::PrivateShared::create(iface); - if (copy) - iface->copyCtr(iface, d->data.shared->data(), copy); + if (!copy && !iface->defaultCtr) + return; // trivial default constructor and it's OK to build in 0-filled storage, which we've already done + if constexpr (moveOption == ForceMove && nullability == NonNull) + moveConstruct(iface, d->data.data, copy); else - iface->defaultCtr(iface, d->data.shared->data()); + construct(iface, d->data.data, copy); + } else { + d->data.shared = customConstructShared(iface->size, iface->alignment, [=](void *where) { + if constexpr (moveOption == ForceMove && nullability == NonNull) + moveConstruct(iface, where, copy); + else + construct(iface, where, copy); + }); d->is_shared = true; } - // need to check for nullptr_t here, as this can get called by fromValue(nullptr). fromValue() uses - // std::addressof(value) which in this case returns the address of the nullptr object. - d->is_null = !copy || QMetaType(iface) == QMetaType::fromType<std::nullptr_t>(); } static void customClear(QVariant::Private *d) { - auto iface = reinterpret_cast<QtPrivate::QMetaTypeInterface *>(d->packedType << 2); + const QtPrivate::QMetaTypeInterface *iface = d->typeInterface(); if (!iface) return; if (!d->is_shared) { - if (iface->dtor) - iface->dtor(iface, &d->data); + QtMetaTypePrivate::destruct(iface, d->data.data); } else { - if (iface->dtor) - iface->dtor(iface, d->data.shared->data()); + QtMetaTypePrivate::destruct(iface, d->data.shared->data()); QVariant::PrivateShared::free(d->data.shared); } } +static QVariant::Private clonePrivate(const QVariant::Private &other) +{ + QVariant::Private d = other; + if (d.is_shared) { + d.data.shared->ref.ref(); + } else if (const QtPrivate::QMetaTypeInterface *iface = d.typeInterface()) { + Q_ASSERT(d.canUseInternalSpace(iface)); + + // if not trivially copyable, ask to copy + if (iface->copyCtr) + QtMetaTypePrivate::copyConstruct(iface, d.data.data, other.data.data); + } + return d; +} } // anonymous used to hide QVariant handlers @@ -269,6 +324,7 @@ static void customClear(QVariant::Private *d) \ingroup objectmodel \ingroup shared + \compares equality Because C++ forbids unions from including types that have non-default constructors or destructors, most interesting Qt @@ -461,24 +517,18 @@ void QVariant::create(int type, const void *copy) */ void QVariant::create(QMetaType type, const void *copy) { - d = Private(type); - customConstruct(&d, copy); + *this = QVariant::fromMetaType(type, copy); } /*! \fn QVariant::~QVariant() Destroys the QVariant and the contained object. - - Note that subclasses that reimplement clear() should reimplement - the destructor to call clear(). This destructor calls clear(), but - because it is the destructor, QVariant::clear() is called rather - than a subclass's clear(). */ QVariant::~QVariant() { - if ((d.is_shared && !d.data.shared->ref.deref()) || (!d.is_shared)) + if (!d.is_shared || !d.data.shared->ref.deref()) customClear(&d); } @@ -490,24 +540,112 @@ QVariant::~QVariant() */ QVariant::QVariant(const QVariant &p) - : d(p.d) + : d(clonePrivate(p.d)) { - if (d.is_shared) { - d.data.shared->ref.ref(); - return; +} + +/*! + \fn template <typename T, typename... Args, QVariant::if_constructible<T, Args...> = true> QVariant::QVariant(std::in_place_type_t<T>, Args&&... args) noexcept(is_noexcept_constructible<q20::remove_cvref_t<T>, Args...>::value) + + \since 6.6 + Constructs a new variant containing a value of type \c T. The contained + value is is initialized with the arguments + \c{std::forward<Args>(args)...}. + + This overload only participates in overload resolution if \c T can be + constructed from \a args. + + This constructor is provided for STL/std::any compatibility. + + \overload + */ + +/*! + + \fn template <typename T, typename U, typename... Args, QVariant::if_constructible<T, std::initializer_list<U> &, Args...> = true> explicit QVariant::QVariant(std::in_place_type_t<T>, std::initializer_list<U> il, Args&&... args) noexcept(is_noexcept_constructible<q20::remove_cvref_t<T>, std::initializer_list<U> &, Args... >::value) + + \since 6.6 + \overload + This overload exists to support types with constructors taking an + \c initializer_list. It behaves otherwise equivalent to the + non-initializer list \c{in_place_type_t} overload. +*/ + + +/*! + \fn template <typename T, typename... Args, QVariant::if_constructible<T, Args...> = true> QVariant::emplace(Args&&... args) + + \since 6.6 + Replaces the object currently held in \c{*this} with an object of + type \c{T}, constructed from \a{args}\c{...}. If \c{*this} was non-null, + the previously held object is destroyed first. + If possible, this method will reuse memory allocated by the QVariant. + Returns a reference to the newly-created object. + */ + +/*! + \fn template <typename T, typename U, typename... Args, QVariant::if_constructible<T, std::initializer_list<U> &, Args...> = true> QVariant::emplace(std::initializer_list<U> list, Args&&... args) + + \since 6.6 + \overload + This overload exists to support types with constructors taking an + \c initializer_list. It behaves otherwise equivalent to the + non-initializer list overload. +*/ + +QVariant::QVariant(std::in_place_t, QMetaType type) : d(type.iface()) +{ + // we query the metatype instead of detecting it at compile time + // so that we can change relocatability of internal types + if (!Private::canUseInternalSpace(type.iface())) { + d.data.shared = PrivateShared::create(type.sizeOf(), type.alignOf()); + d.is_shared = true; } - QtPrivate::QMetaTypeInterface *iface = d.typeInterface(); - auto other = p.constData(); - if (iface) { - if (other) - iface->copyCtr(iface, &d.data, other); - else - iface->defaultCtr(iface, &d.data); +} + +/*! + \internal + Returns a pointer to data suitable for placement new + of an object of type \a type + Changes the variant's metatype to \a type + */ +void *QVariant::prepareForEmplace(QMetaType type) +{ + /* There are two cases where we can reuse the existing storage + (1) The new type fits in QVariant's SBO storage + (2) We are using the externally allocated storage, the variant is + detached, and the new type fits into the existing storage. + In all other cases (3), we cannot reuse the storage. + */ + auto typeFits = [&] { + auto newIface = type.iface(); + auto oldIface = d.typeInterface(); + auto newSize = PrivateShared::computeAllocationSize(newIface->size, newIface->alignment); + auto oldSize = PrivateShared::computeAllocationSize(oldIface->size, oldIface->alignment); + return newSize <= oldSize; + }; + if (Private::canUseInternalSpace(type.iface())) { // (1) + clear(); + d.packedType = quintptr(type.iface()) >> 2; + return d.data.data; + } else if (d.is_shared && isDetached() && typeFits()) { // (2) + QtMetaTypePrivate::destruct(d.typeInterface(), d.data.shared->data()); + // compare QVariant::PrivateShared::create + const auto ps = d.data.shared; + const auto align = type.alignOf(); + ps->offset = PrivateShared::computeOffset(ps, align); + d.packedType = quintptr(type.iface()) >> 2; + return ps->data(); } + // (3) + QVariant newVariant(std::in_place, type); + swap(newVariant); + // const cast is safe, we're in a non-const method + return const_cast<void *>(d.storage()); } /*! - \fn QVariant::QVariant(const QString &val) + \fn QVariant::QVariant(const QString &val) noexcept Constructs a new variant with a string value, \a val. */ @@ -515,7 +653,8 @@ QVariant::QVariant(const QVariant &p) /*! \fn QVariant::QVariant(QLatin1StringView val) - Constructs a new variant with a string value, \a val. + Constructs a new variant with a QString value from the Latin-1 + string viewed by \a val. */ /*! @@ -534,37 +673,37 @@ QVariant::QVariant(const QVariant &p) */ /*! - \fn QVariant::QVariant(const QStringList &val) + \fn QVariant::QVariant(const QStringList &val) noexcept Constructs a new variant with a string list value, \a val. */ /*! - \fn QVariant::QVariant(const QMap<QString, QVariant> &val) + \fn QVariant::QVariant(const QMap<QString, QVariant> &val) noexcept Constructs a new variant with a map of \l {QVariant}s, \a val. */ /*! - \fn QVariant::QVariant(const QHash<QString, QVariant> &val) + \fn QVariant::QVariant(const QHash<QString, QVariant> &val) noexcept Constructs a new variant with a hash of \l {QVariant}s, \a val. */ /*! - \fn QVariant::QVariant(QDate val) + \fn QVariant::QVariant(QDate val) noexcept Constructs a new variant with a date value, \a val. */ /*! - \fn QVariant::QVariant(QTime val) + \fn QVariant::QVariant(QTime val) noexcept Constructs a new variant with a time value, \a val. */ /*! - \fn QVariant::QVariant(const QDateTime &val) + \fn QVariant::QVariant(const QDateTime &val) noexcept Constructs a new variant with a date/time value, \a val. */ @@ -578,14 +717,14 @@ QVariant::QVariant(const QVariant &p) /*! \since 5.0 - \fn QVariant::QVariant(const QUuid &val) + \fn QVariant::QVariant(QUuid val) noexcept Constructs a new variant with an uuid value, \a val. */ /*! \since 5.0 - \fn QVariant::QVariant(const QModelIndex &val) + \fn QVariant::QVariant(const QModelIndex &val) noexcept Constructs a new variant with a QModelIndex value, \a val. */ @@ -626,135 +765,135 @@ QVariant::QVariant(const QVariant &p) */ /*! - \fn QVariant::QVariant(const QByteArray &val) + \fn QVariant::QVariant(const QByteArray &val) noexcept Constructs a new variant with a bytearray value, \a val. */ /*! - \fn QVariant::QVariant(const QBitArray &val) + \fn QVariant::QVariant(const QBitArray &val) noexcept Constructs a new variant with a bitarray value, \a val. */ /*! - \fn QVariant::QVariant(const QPoint &val) + \fn QVariant::QVariant(QPoint val) noexcept Constructs a new variant with a point value of \a val. */ /*! - \fn QVariant::QVariant(const QPointF &val) + \fn QVariant::QVariant(QPointF val) noexcept Constructs a new variant with a point value of \a val. */ /*! - \fn QVariant::QVariant(const QRectF &val) + \fn QVariant::QVariant(QRectF val) Constructs a new variant with a rect value of \a val. */ /*! - \fn QVariant::QVariant(const QLineF &val) + \fn QVariant::QVariant(QLineF val) noexcept Constructs a new variant with a line value of \a val. */ /*! - \fn QVariant::QVariant(const QLine &val) + \fn QVariant::QVariant(QLine val) noexcept Constructs a new variant with a line value of \a val. */ /*! - \fn QVariant::QVariant(const QRect &val) + \fn QVariant::QVariant(QRect val) noexcept Constructs a new variant with a rect value of \a val. */ /*! - \fn QVariant::QVariant(const QSize &val) + \fn QVariant::QVariant(QSize val) noexcept Constructs a new variant with a size value of \a val. */ /*! - \fn QVariant::QVariant(const QSizeF &val) + \fn QVariant::QVariant(QSizeF val) noexcept Constructs a new variant with a size value of \a val. */ /*! - \fn QVariant::QVariant(const QUrl &val) + \fn QVariant::QVariant(const QUrl &val) noexcept Constructs a new variant with a url value of \a val. */ /*! - \fn QVariant::QVariant(int val) + \fn QVariant::QVariant(int val) noexcept Constructs a new variant with an integer value, \a val. */ /*! - \fn QVariant::QVariant(uint val) + \fn QVariant::QVariant(uint val) noexcept Constructs a new variant with an unsigned integer value, \a val. */ /*! - \fn QVariant::QVariant(qlonglong val) + \fn QVariant::QVariant(qlonglong val) noexcept Constructs a new variant with a long long integer value, \a val. */ /*! - \fn QVariant::QVariant(qulonglong val) + \fn QVariant::QVariant(qulonglong val) noexcept Constructs a new variant with an unsigned long long integer value, \a val. */ /*! - \fn QVariant::QVariant(bool val) + \fn QVariant::QVariant(bool val) noexcept Constructs a new variant with a boolean value, \a val. */ /*! - \fn QVariant::QVariant(double val) + \fn QVariant::QVariant(double val) noexcept Constructs a new variant with a floating point value, \a val. */ /*! - \fn QVariant::QVariant(float val) + \fn QVariant::QVariant(float val) noexcept Constructs a new variant with a floating point value, \a val. \since 4.6 */ /*! - \fn QVariant::QVariant(const QList<QVariant> &val) + \fn QVariant::QVariant(const QList<QVariant> &val) noexcept Constructs a new variant with a list value, \a val. */ /*! - \fn QVariant::QVariant(QChar c) + \fn QVariant::QVariant(QChar c) noexcept Constructs a new variant with a char value, \a c. */ /*! - \fn QVariant::QVariant(const QLocale &l) + \fn QVariant::QVariant(const QLocale &l) noexcept Constructs a new variant with a locale value, \a l. */ /*! - \fn QVariant::QVariant(const QRegularExpression &re) + \fn QVariant::QVariant(const QRegularExpression &re) noexcept \since 5.0 @@ -772,149 +911,96 @@ QVariant::QVariant(const QVariant &p) */ /*! - Constructs variant of type \a type, and initializes with - \a copy if \a copy is not \nullptr. + Constructs a variant of type \a type, and initializes it with + a copy of \c{*copy} if \a copy is not \nullptr (in which case, \a copy + must point to an object of type \a type). - Note that you have to pass the address of the variable you want stored. + Note that you have to pass the address of the object you want stored. Usually, you never have to use this constructor, use QVariant::fromValue() instead to construct variants from the pointer types represented by \c QMetaType::VoidStar, and \c QMetaType::QObjectStar. - \sa QVariant::fromValue(), QMetaType::Type + If \a type does not support copy construction and \a copy is not \nullptr, + the variant will be invalid. Similarly, if \a copy is \nullptr and + \a type does not support default construction, the variant will be + invalid. + + \sa QVariant::fromMetaType, QVariant::fromValue(), QMetaType::Type */ -QVariant::QVariant(QMetaType type, const void *copy) : d(type) -{ - customConstruct(&d, copy); -} - -QVariant::QVariant(int val) - : d(QMetaType::fromType<int>()) -{ d.set(val); } -QVariant::QVariant(uint val) - : d(QMetaType::fromType<uint>()) -{ d.set(val); } -QVariant::QVariant(qlonglong val) - : d(QMetaType::fromType<qlonglong>()) -{ d.set(val); } -QVariant::QVariant(qulonglong val) - : d(QMetaType::fromType<qulonglong>()) -{ d.set(val); } -QVariant::QVariant(bool val) - : d(QMetaType::fromType<bool>()) -{ d.set(val); } -QVariant::QVariant(double val) - : d(QMetaType::fromType<double>()) -{ d.set(val); } -QVariant::QVariant(float val) - : d(QMetaType::fromType<float>()) -{ d.set(val); } - -QVariant::QVariant(const QByteArray &val) - : d(QMetaType::fromType<QByteArray>()) -{ v_construct<QByteArray>(&d, val); } -QVariant::QVariant(const QBitArray &val) - : d(QMetaType::fromType<QBitArray>()) -{ v_construct<QBitArray>(&d, val); } -QVariant::QVariant(const QString &val) - : d(QMetaType::fromType<QString>()) -{ v_construct<QString>(&d, val); } -QVariant::QVariant(QChar val) - : d(QMetaType::fromType<QChar>()) -{ v_construct<QChar>(&d, val); } -QVariant::QVariant(QLatin1StringView val) - : d(QMetaType::fromType<QString>()) -{ v_construct<QString>(&d, val); } -QVariant::QVariant(const QStringList &val) - : d(QMetaType::fromType<QStringList>()) -{ v_construct<QStringList>(&d, val); } - -QVariant::QVariant(QDate val) - : d(QMetaType::fromType<QDate>()) -{ v_construct<QDate>(&d, val); } -QVariant::QVariant(QTime val) - : d(QMetaType::fromType<QTime>()) -{ v_construct<QTime>(&d, val); } -QVariant::QVariant(const QDateTime &val) - : d(QMetaType::fromType<QDateTime>()) -{ v_construct<QDateTime>(&d, val); } +QVariant::QVariant(QMetaType type, const void *copy) + : d() +{ + *this = fromMetaType(type, copy); +} + +QVariant::QVariant(int val) noexcept : d(std::piecewise_construct_t{}, val) {} +QVariant::QVariant(uint val) noexcept : d(std::piecewise_construct_t{}, val) {} +QVariant::QVariant(qlonglong val) noexcept : d(std::piecewise_construct_t{}, val) {} +QVariant::QVariant(qulonglong val) noexcept : d(std::piecewise_construct_t{}, val) {} +QVariant::QVariant(bool val) noexcept : d(std::piecewise_construct_t{}, val) {} +QVariant::QVariant(double val) noexcept : d(std::piecewise_construct_t{}, val) {} +QVariant::QVariant(float val) noexcept : d(std::piecewise_construct_t{}, val) {} + +QVariant::QVariant(const QByteArray &val) noexcept : d(std::piecewise_construct_t{}, val) {} +#ifndef QT_BOOTSTRAPPED +QVariant::QVariant(const QBitArray &val) noexcept : d(std::piecewise_construct_t{}, val) {} +#endif +QVariant::QVariant(const QString &val) noexcept : d(std::piecewise_construct_t{}, val) {} +QVariant::QVariant(QChar val) noexcept : d(std::piecewise_construct_t{}, val) {} +QVariant::QVariant(const QStringList &val) noexcept : d(std::piecewise_construct_t{}, val) {} + +QVariant::QVariant(QDate val) noexcept : d(std::piecewise_construct_t{}, val) {} +QVariant::QVariant(QTime val) noexcept : d(std::piecewise_construct_t{}, val) {} +QVariant::QVariant(const QDateTime &val) noexcept : d(std::piecewise_construct_t{}, val) {} + +QVariant::QVariant(const QList<QVariant> &list) noexcept : d(std::piecewise_construct_t{}, list) {} +QVariant::QVariant(const QMap<QString, QVariant> &map) noexcept : d(std::piecewise_construct_t{}, map) {} +QVariant::QVariant(const QHash<QString, QVariant> &hash) noexcept : d(std::piecewise_construct_t{}, hash) {} + +QVariant::QVariant(QLatin1StringView val) : QVariant(QString(val)) {} + #if QT_CONFIG(easingcurve) -QVariant::QVariant(const QEasingCurve &val) - : d(QMetaType::fromType<QEasingCurve>()) -{ v_construct<QEasingCurve>(&d, val); } +QVariant::QVariant(const QEasingCurve &val) : d(std::piecewise_construct_t{}, val) {} #endif -QVariant::QVariant(const QList<QVariant> &list) - : d(QMetaType::fromType<QList<QVariant>>()) -{ v_construct<QVariantList>(&d, list); } -QVariant::QVariant(const QMap<QString, QVariant> &map) - : d(QMetaType::fromType<QMap<QString, QVariant>>()) -{ v_construct<QVariantMap>(&d, map); } -QVariant::QVariant(const QHash<QString, QVariant> &hash) - : d(QMetaType::fromType<QHash<QString, QVariant>>()) -{ v_construct<QVariantHash>(&d, hash); } #ifndef QT_NO_GEOM_VARIANT -QVariant::QVariant(const QPoint &pt) - : d(QMetaType::fromType<QPoint>()) -{ v_construct<QPoint>(&d, pt); } -QVariant::QVariant(const QPointF &pt) - : d(QMetaType::fromType<QPointF>()) -{ v_construct<QPointF>(&d, pt); } -QVariant::QVariant(const QRectF &r) - : d(QMetaType::fromType<QRectF>()) -{ v_construct<QRectF>(&d, r); } -QVariant::QVariant(const QLineF &l) - : d(QMetaType::fromType<QLineF>()) -{ v_construct<QLineF>(&d, l); } -QVariant::QVariant(const QLine &l) - : d(QMetaType::fromType<QLine>()) -{ v_construct<QLine>(&d, l); } -QVariant::QVariant(const QRect &r) - : d(QMetaType::fromType<QRect>()) -{ v_construct<QRect>(&d, r); } -QVariant::QVariant(const QSize &s) - : d(QMetaType::fromType<QSize>()) -{ v_construct<QSize>(&d, s); } -QVariant::QVariant(const QSizeF &s) - : d(QMetaType::fromType<QSizeF>()) -{ v_construct<QSizeF>(&d, s); } +QVariant::QVariant(QPoint pt) noexcept + : d(std::piecewise_construct_t{}, pt) {} +QVariant::QVariant(QPointF pt) noexcept(Private::FitsInInternalSize<sizeof(qreal) * 2>) + : d(std::piecewise_construct_t{}, pt) {} +QVariant::QVariant(QRect r) noexcept(Private::FitsInInternalSize<sizeof(int) * 4>) + : d(std::piecewise_construct_t{}, r) {} +QVariant::QVariant(QRectF r) noexcept(Private::FitsInInternalSize<sizeof(qreal) * 4>) + : d(std::piecewise_construct_t{}, r) {} +QVariant::QVariant(QLine l) noexcept(Private::FitsInInternalSize<sizeof(int) * 4>) + : d(std::piecewise_construct_t{}, l) {} +QVariant::QVariant(QLineF l) noexcept(Private::FitsInInternalSize<sizeof(qreal) * 4>) + : d(std::piecewise_construct_t{}, l) {} +QVariant::QVariant(QSize s) noexcept + : d(std::piecewise_construct_t{}, s) {} +QVariant::QVariant(QSizeF s) noexcept(Private::FitsInInternalSize<sizeof(qreal) * 2>) + : d(std::piecewise_construct_t{}, s) {} #endif #ifndef QT_BOOTSTRAPPED -QVariant::QVariant(const QUrl &u) - : d(QMetaType::fromType<QUrl>()) -{ v_construct<QUrl>(&d, u); } +QVariant::QVariant(const QUrl &u) noexcept : d(std::piecewise_construct_t{}, u) {} #endif -QVariant::QVariant(const QLocale &l) - : d(QMetaType::fromType<QLocale>()) -{ v_construct<QLocale>(&d, l); } +QVariant::QVariant(const QLocale &l) noexcept : d(std::piecewise_construct_t{}, l) {} #if QT_CONFIG(regularexpression) -QVariant::QVariant(const QRegularExpression &re) - : d(QMetaType::fromType<QRegularExpression>()) -{ v_construct<QRegularExpression>(&d, re); } +QVariant::QVariant(const QRegularExpression &re) noexcept : d(std::piecewise_construct_t{}, re) {} #endif // QT_CONFIG(regularexpression) -QVariant::QVariant(const QUuid &uuid) - : d(QMetaType::fromType<QUuid>()) -{ v_construct<QUuid>(&d, uuid); } +QVariant::QVariant(QUuid uuid) noexcept(Private::FitsInInternalSize<16>) : d(std::piecewise_construct_t{}, uuid) {} #ifndef QT_BOOTSTRAPPED -QVariant::QVariant(const QJsonValue &jsonValue) - : d(QMetaType::fromType<QJsonValue>()) -{ v_construct<QJsonValue>(&d, jsonValue); } -QVariant::QVariant(const QJsonObject &jsonObject) - : d(QMetaType::fromType<QJsonObject>()) -{ v_construct<QJsonObject>(&d, jsonObject); } -QVariant::QVariant(const QJsonArray &jsonArray) - : d(QMetaType::fromType<QJsonArray>()) -{ v_construct<QJsonArray>(&d, jsonArray); } -QVariant::QVariant(const QJsonDocument &jsonDocument) - : d(QMetaType::fromType<QJsonDocument>()) -{ v_construct<QJsonDocument>(&d, jsonDocument); } +QVariant::QVariant(const QJsonValue &jsonValue) noexcept(Private::FitsInInternalSize<sizeof(CborValueStandIn)>) + : d(std::piecewise_construct_t{}, jsonValue) +{ static_assert(sizeof(CborValueStandIn) == sizeof(QJsonValue)); } +QVariant::QVariant(const QJsonObject &jsonObject) noexcept : d(std::piecewise_construct_t{}, jsonObject) {} +QVariant::QVariant(const QJsonArray &jsonArray) noexcept : d(std::piecewise_construct_t{}, jsonArray) {} +QVariant::QVariant(const QJsonDocument &jsonDocument) : d(std::piecewise_construct_t{}, jsonDocument) {} #endif // QT_BOOTSTRAPPED #if QT_CONFIG(itemmodel) -QVariant::QVariant(const QModelIndex &modelIndex) - : d(QMetaType::fromType<QModelIndex>()) -{ v_construct<QModelIndex>(&d, modelIndex); } -QVariant::QVariant(const QPersistentModelIndex &modelIndex) - : d(QMetaType::fromType<QPersistentModelIndex>()) -{ v_construct<QPersistentModelIndex>(&d, modelIndex); } +QVariant::QVariant(const QModelIndex &modelIndex) noexcept(Private::FitsInInternalSize<8 + 2 * sizeof(quintptr)>) + : d(std::piecewise_construct_t{}, modelIndex) {} +QVariant::QVariant(const QPersistentModelIndex &modelIndex) : d(std::piecewise_construct_t{}, modelIndex) {} #endif /*! \fn QVariant::Type QVariant::type() const @@ -981,21 +1067,7 @@ QVariant &QVariant::operator=(const QVariant &variant) return *this; clear(); - if (variant.d.is_shared) { - variant.d.data.shared->ref.ref(); - d = variant.d; - } else { - d = variant.d; - QtPrivate::QMetaTypeInterface *iface = d.typeInterface(); - const void *other = variant.constData(); - if (iface) { - if (other) - iface->copyCtr(iface, &d, other); - else - iface->defaultCtr(iface, &d); - } - } - + d = clonePrivate(variant.d); return *this; } @@ -1018,8 +1090,10 @@ void QVariant::detach() if (!d.is_shared || d.data.shared->ref.loadRelaxed() == 1) return; - Private dd(d.type()); - customConstruct(&dd, constData()); + Q_ASSERT(isValidMetaTypeForVariant(d.typeInterface(), constData())); + Private dd(d.typeInterface()); + // null variant is never shared; anything else is NonNull + customConstruct<UseCopy, NonNull>(d.typeInterface(), &dd, constData()); if (!d.data.shared->ref.deref()) customClear(&d); d.data.shared = dd.data.shared; @@ -1048,7 +1122,7 @@ const char *QVariant::typeName() const */ void QVariant::clear() { - if ((d.is_shared && !d.data.shared->ref.deref()) || (!d.is_shared)) + if (!d.is_shared || !d.data.shared->ref.deref()) customClear(&d); d = {}; } @@ -1065,7 +1139,7 @@ void QVariant::clear() /*! \fn QVariant::Type QVariant::nameToType(const char *name) - \deprecated [6.0] Use \c QMetaType.fromName(name).id() instead + \deprecated [6.0] Use \c QMetaType::fromName(name).id() instead Converts the string representation of the storage type given in \a name, to its enum representation. @@ -1210,7 +1284,7 @@ void QVariant::load(QDataStream &s) void *data = const_cast<void *>(constData()); if (!d.type().load(s, data)) { s.setStatus(QDataStream::ReadCorruptData); - qWarning("QVariant::load: unable to load type %d.", d.typeId()); + qWarning("QVariant::load: unable to load type %d.", d.type().id()); } } @@ -1222,7 +1296,7 @@ void QVariant::load(QDataStream &s) */ void QVariant::save(QDataStream &s) const { - quint32 typeId = d.typeId(); + quint32 typeId = d.type().id(); bool saveAsUserType = false; if (typeId >= QMetaType::User) { typeId = QMetaType::User; @@ -1301,16 +1375,28 @@ void QVariant::save(QDataStream &s) const if (!d.type().save(s, constData())) { qWarning("QVariant::save: unable to save type '%s' (type id: %d).\n", - d.type().name(), d.typeId()); + d.type().name(), d.type().id()); Q_ASSERT_X(false, "QVariant::save", "Invalid type to save"); } } /*! \since 4.4 + \relates QVariant Reads a variant \a p from the stream \a s. + \note If the stream contains types that aren't the built-in ones (see \l + QMetaType::Type), those types must be registered using qRegisterMetaType() + or QMetaType::registerType() before the variant can be properly loaded. If + an unregistered type is found, QVariant will set the corrupt flag in the + stream, stop processing and print a warning. For example, for QList<int> + it would print the following: + + \quotation + QVariant::load: unknown user type with name QList<int> + \endquotation + \sa{Serializing Qt Data Types}{Format of the QDataStream operators} */ QDataStream &operator>>(QDataStream &s, QVariant &p) @@ -1321,6 +1407,7 @@ QDataStream &operator>>(QDataStream &s, QVariant &p) /*! Writes a variant \a p to the stream \a s. + \relates QVariant \sa{Serializing Qt Data Types}{Format of the QDataStream operators} */ @@ -1331,12 +1418,14 @@ QDataStream &operator<<(QDataStream &s, const QVariant &p) } /*! \fn QDataStream& operator>>(QDataStream &s, QVariant::Type &p) + \relates QVariant \deprecated [6.0] Stream QMetaType::Type instead. Reads a variant type \a p in enum representation from the stream \a s. */ /*! \fn QDataStream& operator<<(QDataStream &s, const QVariant::Type p) + \relates QVariant \deprecated [6.0] Stream QMetaType::Type instead. Writes a variant type \a p to the stream \a s. @@ -1386,8 +1475,13 @@ QString QVariant::toString() const } /*! - Returns the variant as a QMap<QString, QVariant> if the variant - has type() \l QMetaType::QVariantMap; otherwise returns an empty map. + Returns the variant as a QVariantMap if the variant has type() \l + QMetaType::QVariantMap. If it doesn't, QVariant will attempt to + convert the type to a map and then return it. This will succeed for + any type that has registered a converter to QVariantMap or which was + declared as a associative container using + \l{Q_DECLARE_ASSOCIATIVE_CONTAINER_METATYPE}. If none of those + conditions are true, this function will return an empty map. \sa canConvert(), convert() */ @@ -1752,6 +1846,7 @@ QChar QVariant::toChar() const return qvariant_cast<QChar>(*this); } +#ifndef QT_BOOTSTRAPPED /*! Returns the variant as a QBitArray if the variant has userType() \l QMetaType::QBitArray; otherwise returns an empty bit array. @@ -1762,16 +1857,17 @@ QBitArray QVariant::toBitArray() const { return qvariant_cast<QBitArray>(*this); } +#endif // QT_BOOTSTRAPPED template <typename T> -inline T qNumVariantToHelper(const QVariant::Private &d, bool *ok, const T& val) +inline T qNumVariantToHelper(const QVariant::Private &d, bool *ok) { QMetaType t = QMetaType::fromType<T>(); if (ok) *ok = true; if (d.type() == t) - return val; + return d.get<T>(); T ret = 0; bool success = QMetaType::convert(d.type(), d.storage(), t, &ret); @@ -1799,7 +1895,7 @@ inline T qNumVariantToHelper(const QVariant::Private &d, bool *ok, const T& val) */ int QVariant::toInt(bool *ok) const { - return qNumVariantToHelper<int>(d, ok, d.get<int>()); + return qNumVariantToHelper<int>(d, ok); } /*! @@ -1821,7 +1917,7 @@ int QVariant::toInt(bool *ok) const */ uint QVariant::toUInt(bool *ok) const { - return qNumVariantToHelper<uint>(d, ok, d.get<unsigned int>()); + return qNumVariantToHelper<uint>(d, ok); } /*! @@ -1838,7 +1934,7 @@ uint QVariant::toUInt(bool *ok) const */ qlonglong QVariant::toLongLong(bool *ok) const { - return qNumVariantToHelper<qlonglong>(d, ok, d.get<qlonglong>()); + return qNumVariantToHelper<qlonglong>(d, ok); } /*! @@ -1855,7 +1951,7 @@ qlonglong QVariant::toLongLong(bool *ok) const */ qulonglong QVariant::toULongLong(bool *ok) const { - return qNumVariantToHelper<qulonglong>(d, ok, d.get<qulonglong>()); + return qNumVariantToHelper<qulonglong>(d, ok); } /*! @@ -1895,7 +1991,7 @@ bool QVariant::toBool() const */ double QVariant::toDouble(bool *ok) const { - return qNumVariantToHelper<double>(d, ok, d.get<double>()); + return qNumVariantToHelper<double>(d, ok); } /*! @@ -1914,7 +2010,7 @@ double QVariant::toDouble(bool *ok) const */ float QVariant::toFloat(bool *ok) const { - return qNumVariantToHelper<float>(d, ok, d.get<float>()); + return qNumVariantToHelper<float>(d, ok); } /*! @@ -1933,13 +2029,17 @@ float QVariant::toFloat(bool *ok) const */ qreal QVariant::toReal(bool *ok) const { - return qNumVariantToHelper<qreal>(d, ok, d.get<qreal>()); + return qNumVariantToHelper<qreal>(d, ok); } /*! - Returns the variant as a QVariantList if the variant has userType() - \l QMetaType::QVariantList or \l QMetaType::QStringList; otherwise returns - an empty list. + Returns the variant as a QVariantList if the variant has userType() \l + QMetaType::QVariantList. If it doesn't, QVariant will attempt to convert + the type to a list and then return it. This will succeed for any type that + has registered a converter to QVariantList or which was declared as a + sequential container using \l{Q_DECLARE_SEQUENTIAL_CONTAINER_METATYPE}. If + none of those conditions are true, this function will return an empty + list. \sa canConvert(), convert() */ @@ -2026,7 +2126,7 @@ bool QVariant::convert(QMetaType targetType) return false; // Fail if the value is not initialized or was forced null by a previous failed convert. - if (oldValue.d.is_null && oldValue.d.typeId() != QMetaType::Nullptr) + if (oldValue.d.is_null && oldValue.d.type().id() != QMetaType::Nullptr) return false; bool ok = QMetaType::convert(oldValue.d.type(), oldValue.constData(), targetType, data()); @@ -2053,9 +2153,9 @@ bool QVariant::view(int type, void *ptr) } /*! - \fn bool QVariant::operator==(const QVariant &v1, const QVariant &v2) + \fn bool QVariant::operator==(const QVariant &lhs, const QVariant &rhs) - Returns \c true if \a v1 and \a v2 are equal; otherwise returns \c false. + Returns \c true if \a lhs and \a rhs are equal; otherwise returns \c false. QVariant uses the equality operator of the type() contained to check for equality. @@ -2079,9 +2179,9 @@ bool QVariant::view(int type, void *ptr) */ /*! - \fn bool QVariant::operator!=(const QVariant &v1, const QVariant &v2) + \fn bool QVariant::operator!=(const QVariant &lhs, const QVariant &rhs) - Returns \c false if \a v1 and \a v2 are equal; otherwise returns \c true. + Returns \c false if \a lhs and \a rhs are equal; otherwise returns \c true. QVariant uses the equality operator of the type() contained to check for equality. @@ -2108,6 +2208,8 @@ static bool qIsNumericType(uint tp) Q_UINT64_C(1) << QMetaType::Double | Q_UINT64_C(1) << QMetaType::Float | Q_UINT64_C(1) << QMetaType::Char | + Q_UINT64_C(1) << QMetaType::Char16 | + Q_UINT64_C(1) << QMetaType::Char32 | Q_UINT64_C(1) << QMetaType::SChar | Q_UINT64_C(1) << QMetaType::UChar | Q_UINT64_C(1) << QMetaType::Short | @@ -2123,33 +2225,52 @@ static bool qIsNumericType(uint tp) static bool qIsFloatingPoint(uint tp) { - return tp == QMetaType::Double || tp == QMetaType::Float; + return tp == QMetaType::Double || tp == QMetaType::Float || tp == QMetaType::Float16; } -static int normalizeLowerRanks(uint tp) +static bool canBeNumericallyCompared(const QtPrivate::QMetaTypeInterface *iface1, + const QtPrivate::QMetaTypeInterface *iface2) { - static const qulonglong numericTypeBits = - Q_UINT64_C(1) << QMetaType::Bool | - Q_UINT64_C(1) << QMetaType::Char | - Q_UINT64_C(1) << QMetaType::SChar | - Q_UINT64_C(1) << QMetaType::UChar | - Q_UINT64_C(1) << QMetaType::Short | - Q_UINT64_C(1) << QMetaType::UShort; - return numericTypeBits & (Q_UINT64_C(1) << tp) ? uint(QMetaType::Int) : tp; -} + if (!iface1 || !iface2) + return false; -static int normalizeLong(uint tp) -{ - const uint IntType = sizeof(long) == sizeof(int) ? QMetaType::Int : QMetaType::LongLong; - const uint UIntType = sizeof(ulong) == sizeof(uint) ? QMetaType::UInt : QMetaType::ULongLong; - return tp == QMetaType::Long ? IntType : - tp == QMetaType::ULong ? UIntType : tp; + // We don't need QMetaType::id() here because the type Id is always stored + // directly for all built-in types. + bool isNumeric1 = qIsNumericType(iface1->typeId); + bool isNumeric2 = qIsNumericType(iface2->typeId); + + // if they're both numeric (or QString), then they can be compared + if (isNumeric1 && isNumeric2) + return true; + + bool isEnum1 = iface1->flags & QMetaType::IsEnumeration; + bool isEnum2 = iface2->flags & QMetaType::IsEnumeration; + + // if both are enums, we can only compare if they are the same enum + // (the language does allow comparing two different enum types, but that's + // usually considered poor coding and produces a warning) + if (isEnum1 && isEnum2) + return QMetaType(iface1) == QMetaType(iface2); + + // if one is an enum and the other is a numeric, we can compare too + if (isEnum1 && isNumeric2) + return true; + if (isNumeric1 && isEnum2) + return true; + + // we need at least one enum and one numeric... + return false; } -static int numericTypePromotion(uint t1, uint t2) +static int numericTypePromotion(const QtPrivate::QMetaTypeInterface *iface1, + const QtPrivate::QMetaTypeInterface *iface2) { - Q_ASSERT(qIsNumericType(t1)); - Q_ASSERT(qIsNumericType(t2)); + Q_ASSERT(canBeNumericallyCompared(iface1, iface2)); + + // We don't need QMetaType::id() here because the type Id is always stored + // directly for the types we're comparing against below. + uint t1 = iface1->typeId; + uint t2 = iface2->typeId; if ((t1 == QMetaType::Bool && t2 == QMetaType::QString) || (t2 == QMetaType::Bool && t1 == QMetaType::QString)) @@ -2173,144 +2294,86 @@ static int numericTypePromotion(uint t1, uint t2) if (qIsFloatingPoint(t1) || qIsFloatingPoint(t2)) return QMetaType::QReal; + auto isUnsigned = [](uint tp) { + // only types for which sizeof(T) >= sizeof(int); lesser ones promote to int + return tp == QMetaType::ULongLong || tp == QMetaType::ULong || + tp == QMetaType::UInt || tp == QMetaType::Char32; + }; + bool isUnsigned1 = isUnsigned(t1); + bool isUnsigned2 = isUnsigned(t2); + // integral rules: - // for all platforms we support, int can always hold the values of lower-ranked types - t1 = normalizeLowerRanks(t1); - t2 = normalizeLowerRanks(t2); - - // normalize long / ulong: in all platforms we run, they're either the same as int or as long long - t1 = normalizeLong(t1); - t2 = normalizeLong(t2); - - // implement the other rules - // the four possibilities are Int, UInt, LongLong and ULongLong - // if any of the two is ULongLong, then it wins (highest rank, unsigned) - // otherwise, if one of the two is LongLong, then the other is either LongLong too or lower-ranked - // otherwise, if one of the two is UInt, then the other is either UInt too or Int - if (t1 == QMetaType::ULongLong || t2 == QMetaType::ULongLong) + // 1) if either type is a 64-bit unsigned, compare as 64-bit unsigned + if (isUnsigned1 && iface1->size > sizeof(int)) + return QMetaType::ULongLong; + if (isUnsigned2 && iface2->size > sizeof(int)) return QMetaType::ULongLong; - if (t1 == QMetaType::LongLong || t2 == QMetaType::LongLong) + + // 2) if either type is 64-bit, compare as 64-bit signed + if (iface1->size > sizeof(int) || iface2->size > sizeof(int)) return QMetaType::LongLong; - if (t1 == QMetaType::UInt || t2 == QMetaType::UInt) + + // 3) if either type is 32-bit unsigned, compare as 32-bit unsigned + if (isUnsigned1 || isUnsigned2) return QMetaType::UInt; + + // 4) otherwise, just do int promotion return QMetaType::Int; } -static bool integralEquals(uint promotedType, const QVariant::Private *d1, const QVariant::Private *d2) +template <typename Numeric> static QPartialOrdering spaceShip(Numeric lhs, Numeric rhs) { - // use toLongLong to retrieve the data, it gets us all the bits - bool ok; - qlonglong l1 = qConvertToNumber(d1, &ok, promotedType == QMetaType::Bool); - if (!ok) - return false; - - qlonglong l2 = qConvertToNumber(d2, &ok, promotedType == QMetaType::Bool); - if (!ok) - return false; - - if (promotedType == QMetaType::Bool) - return bool(l1) == bool(l2); - if (promotedType == QMetaType::Int) - return int(l1) == int(l2); - if (promotedType == QMetaType::UInt) - return uint(l1) == uint(l2); - if (promotedType == QMetaType::LongLong) - return l1 == l2; - if (promotedType == QMetaType::ULongLong) - return qulonglong(l1) == qulonglong(l2); - - Q_UNREACHABLE(); - return 0; -} + if (lhs == rhs) + return QPartialOrdering::Equivalent; + if constexpr (std::numeric_limits<Numeric>::has_quiet_NaN) { + if (std::isnan(lhs) || std::isnan(rhs)) + return QPartialOrdering::Unordered; + } -namespace { -template<typename Numeric> -int spaceShip(Numeric lhs, Numeric rhs) -{ bool smaller; if constexpr (std::is_same_v<Numeric, QObject *>) smaller = std::less<QObject *>()(lhs, rhs); // can't use less all the time because of bool else smaller = lhs < rhs; - if (smaller) - return -1; - else if (lhs == rhs) - return 0; - else - return 1; -} + return smaller ? QPartialOrdering::Less : QPartialOrdering::Greater; } -static std::optional<int> integralCompare(uint promotedType, const QVariant::Private *d1, const QVariant::Private *d2) +static QPartialOrdering integralCompare(uint promotedType, const QVariant::Private *d1, const QVariant::Private *d2) { // use toLongLong to retrieve the data, it gets us all the bits - bool ok; - qlonglong l1 = qConvertToNumber(d1, &ok, promotedType == QMetaType::Bool); - if (!ok) - return std::nullopt; - - qlonglong l2 = qConvertToNumber(d2, &ok, promotedType == QMetaType::Bool); - if (!ok) - return std::nullopt; - - if (promotedType == QMetaType::Bool) - return spaceShip<bool>(l1, l2); - if (promotedType == QMetaType::Int) - return spaceShip<int>(l1, l2); + std::optional<qlonglong> l1 = qConvertToNumber(d1, promotedType == QMetaType::Bool); + std::optional<qlonglong> l2 = qConvertToNumber(d2, promotedType == QMetaType::Bool); + if (!l1 || !l2) + return QPartialOrdering::Unordered; if (promotedType == QMetaType::UInt) - return spaceShip<uint>(l1, l2); + return spaceShip<uint>(*l1, *l2); if (promotedType == QMetaType::LongLong) - return spaceShip<qlonglong>(l1, l2); + return spaceShip<qlonglong>(*l1, *l2); if (promotedType == QMetaType::ULongLong) - return spaceShip<qulonglong>(l1, l2); + return spaceShip<qulonglong>(*l1, *l2); - Q_UNREACHABLE(); - return 0; + return spaceShip<int>(*l1, *l2); } -static std::optional<int> numericCompare(const QVariant::Private *d1, const QVariant::Private *d2) +static QPartialOrdering numericCompare(const QVariant::Private *d1, const QVariant::Private *d2) { - uint promotedType = numericTypePromotion(d1->typeId(), d2->typeId()); + uint promotedType = numericTypePromotion(d1->typeInterface(), d2->typeInterface()); if (promotedType != QMetaType::QReal) return integralCompare(promotedType, d1, d2); - // qreal comparisons - bool ok; - qreal r1 = qConvertToRealNumber(d1, &ok); - if (!ok) - return std::nullopt; - qreal r2 = qConvertToRealNumber(d2, &ok); - if (!ok) - return std::nullopt; - if (r1 == r2) - return 0; - - if (std::isnan(r1) || std::isnan(r2)) - return std::nullopt; - return spaceShip<qreal>(r1, r2); -} - -static bool numericEquals(const QVariant::Private *d1, const QVariant::Private *d2) -{ - uint promotedType = numericTypePromotion(d1->typeId(), d2->typeId()); - if (promotedType != QMetaType::QReal) - return integralEquals(promotedType, d1, d2); - // qreal comparisons - bool ok; - qreal r1 = qConvertToRealNumber(d1, &ok); - if (!ok) - return false; - qreal r2 = qConvertToRealNumber(d2, &ok); - if (!ok) - return false; - if (r1 == r2) - return true; + // floating point comparison + const auto r1 = qConvertToRealNumber(d1); + const auto r2 = qConvertToRealNumber(d2); + if (!r1 || !r2) + return QPartialOrdering::Unordered; + if (*r1 == *r2) + return QPartialOrdering::Equivalent; - return false; + return spaceShip(*r1, *r2); } #ifndef QT_BOOTSTRAPPED -static bool canConvertMetaObject(QMetaType fromType, QMetaType toType) +static bool qvCanConvertMetaObject(QMetaType fromType, QMetaType toType) { if ((fromType.flags() & QMetaType::PointerToQObject) && (toType.flags() & QMetaType::PointerToQObject)) { @@ -2321,13 +2384,7 @@ static bool canConvertMetaObject(QMetaType fromType, QMetaType toType) return false; } -static bool pointerEquals(const QVariant::Private *d1, const QVariant::Private *d2) -{ - // simply check whether both types point to the same data - return d1->get<QObject *>() == d2->get<QObject *>(); -} - -static int pointerCompare(const QVariant::Private *d1, const QVariant::Private *d2) +static QPartialOrdering pointerCompare(const QVariant::Private *d1, const QVariant::Private *d2) { return spaceShip<QObject *>(d1->get<QObject *>(), d2->get<QObject *>()); } @@ -2342,12 +2399,12 @@ bool QVariant::equals(const QVariant &v) const if (metatype != v.metaType()) { // try numeric comparisons, with C++ type promotion rules (no conversion) - if (qIsNumericType(metatype.id()) && qIsNumericType(v.d.typeId())) - return numericEquals(&d, &v.d); + if (canBeNumericallyCompared(metatype.iface(), v.d.type().iface())) + return numericCompare(&d, &v.d) == QPartialOrdering::Equivalent; #ifndef QT_BOOTSTRAPPED // if both types are related pointers to QObjects, check if they point to the same object - if (canConvertMetaObject(metatype, v.metaType())) - return pointerEquals(&d, &v.d); + if (qvCanConvertMetaObject(metatype, v.metaType())) + return pointerCompare(&d, &v.d) == QPartialOrdering::Equivalent; #endif return false; } @@ -2359,18 +2416,6 @@ bool QVariant::equals(const QVariant &v) const return metatype.equals(d.storage(), v.d.storage()); } -static QPartialOrdering convertOptionalToPartialOrdering(const std::optional<int> &opt) -{ - if (!opt) - return QPartialOrdering::Unordered; - else if (*opt < 0) - return QPartialOrdering::Less; - else if (*opt == 0) - return QPartialOrdering::Equivalent; - else - return QPartialOrdering::Greater; -} - /*! Compares the objects at \a lhs and \a rhs for ordering. @@ -2398,11 +2443,11 @@ QPartialOrdering QVariant::compare(const QVariant &lhs, const QVariant &rhs) QMetaType t = lhs.d.type(); if (t != rhs.d.type()) { // try numeric comparisons, with C++ type promotion rules (no conversion) - if (qIsNumericType(lhs.d.typeId()) && qIsNumericType(rhs.d.typeId())) - return convertOptionalToPartialOrdering(numericCompare(&lhs.d, &rhs.d)); + if (canBeNumericallyCompared(lhs.d.type().iface(), rhs.d.type().iface())) + return numericCompare(&lhs.d, &rhs.d); #ifndef QT_BOOTSTRAPPED - if (canConvertMetaObject(lhs.metaType(), rhs.metaType())) - return convertOptionalToPartialOrdering(pointerCompare(&lhs.d, &rhs.d)); + if (qvCanConvertMetaObject(lhs.metaType(), rhs.metaType())) + return pointerCompare(&lhs.d, &rhs.d); #endif return QPartialOrdering::Unordered; } @@ -2416,7 +2461,7 @@ QPartialOrdering QVariant::compare(const QVariant &lhs, const QVariant &rhs) Returns a pointer to the contained object as a generic void* that cannot be written to. - \sa QMetaType + \sa get_if(), QMetaType */ /*! @@ -2426,7 +2471,7 @@ QPartialOrdering QVariant::compare(const QVariant &lhs, const QVariant &rhs) This function detaches the QVariant. When called on a \l{isNull}{null-QVariant}, the QVariant will not be null after the call. - \sa QMetaType + \sa get_if(), QMetaType */ void *QVariant::data() { @@ -2437,6 +2482,42 @@ void *QVariant::data() } /*! + \since 6.6 + \fn template <typename T> const T* QVariant::get_if(const QVariant *v) + \fn template <typename T> T* QVariant::get_if(QVariant *v) + + If \a v contains an object of type \c T, returns a pointer to the contained + object, otherwise returns \nullptr. + + The overload taking a mutable \a v detaches \a v: When called on a + \l{isNull()}{null} \a v with matching type \c T, \a v will not be null + after the call. + + These functions are provided for compatibility with \c{std::variant}. + + \sa data() +*/ + +/*! + \since 6.6 + \fn template <typename T> T &QVariant::get(QVariant &v) + \fn template <typename T> const T &QVariant::get(const QVariant &v) + \fn template <typename T> T &&QVariant::get(QVariant &&v) + \fn template <typename T> const T &&QVariant::get(const QVariant &&v) + + If \a v contains an object of type \c T, returns a reference to the contained + object, otherwise the call has undefined behavior. + + The overloads taking a mutable \a v detach \a v: When called on a + \l{isNull()}{null} \a v with matching type \c T, \a v will not be null + after the call. + + These functions are provided for compatibility with \c{std::variant}. + + \sa get_if(), data() +*/ + +/*! Returns \c true if this is a null variant, false otherwise. A variant is considered null if it contains no initialized value or a null pointer. @@ -2460,7 +2541,7 @@ bool QVariant::isNull() const QDebug QVariant::qdebugHelper(QDebug dbg) const { QDebugStateSaver saver(dbg); - const uint typeId = d.typeId(); + const uint typeId = d.type().id(); dbg.nospace() << "QVariant("; if (typeId != QMetaType::UnknownType) { dbg << d.type().name() << ", "; @@ -2474,6 +2555,22 @@ QDebug QVariant::qdebugHelper(QDebug dbg) const return dbg; } +QVariant QVariant::moveConstruct(QMetaType type, void *data) +{ + QVariant var; + var.d = QVariant::Private(type.d_ptr); + customConstruct<ForceMove, NonNull>(type.d_ptr, &var.d, data); + return var; +} + +QVariant QVariant::copyConstruct(QMetaType type, const void *data) +{ + QVariant var; + var.d = QVariant::Private(type.d_ptr); + customConstruct<UseCopy, NonNull>(type.d_ptr, &var.d, data); + return var; +} + #if QT_DEPRECATED_SINCE(6, 0) QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED @@ -2493,7 +2590,7 @@ QT_WARNING_POP #endif -/*! \fn template<typename T> void QVariant::setValue(T &&value) +/*! \fn template<typename T, typename = std::enable_if_t<!std::is_same_v<std::decay_t<T>, QVariant>>> void QVariant::setValue(T &&value) Stores a copy of \a value. If \c{T} is a type that QVariant doesn't support, QMetaType is used to store the value. A compile @@ -2506,19 +2603,19 @@ QT_WARNING_POP \sa value(), fromValue(), canConvert() */ -/*! \fn template<typename T> void QVariant::setValue(const QVariant &value) +/*! \fn void QVariant::setValue(const QVariant &value) Copies \a value over this QVariant. It is equivalent to simply assigning \a value to this QVariant. */ -/*! \fn template<typename T> void QVariant::setValue(QVariant &&value) +/*! \fn void QVariant::setValue(QVariant &&value) Moves \a value over this QVariant. It is equivalent to simply move assigning \a value to this QVariant. */ -/*! \fn template<typename T> T QVariant::value() const +/*! \fn template<typename T> T QVariant::value() const & Returns the stored value converted to the template type \c{T}. Call canConvert() to find out whether a type can be converted. @@ -2558,7 +2655,7 @@ QT_WARNING_POP \sa canView(), Q_DECLARE_SEQUENTIAL_CONTAINER_METATYPE() */ -/*! \fn bool QVariant::canConvert() const +/*! \fn template<typename T> bool QVariant::canConvert() const Returns \c true if the variant can be converted to the template type \c{T}, otherwise false. @@ -2574,7 +2671,7 @@ QT_WARNING_POP \sa convert() */ -/*! \fn bool QVariant::canView() const +/*! \fn template<typename T> bool QVariant::canView() const Returns \c true if a mutable view of the template type \c{T} can be created on this variant, otherwise \c false. @@ -2591,12 +2688,15 @@ QT_WARNING_POP \snippet code/src_corelib_kernel_qvariant.cpp 7 - \note If you are working with custom types, you should use - the Q_DECLARE_METATYPE() macro to register your custom type. - \sa setValue(), value() */ +/*! \fn template<typename T, QVariant::if_rvalue<T> = true> static QVariant QVariant::fromValue(T &&value) + + \since 6.6 + \overload +*/ + /*! \fn template<typename... Types> QVariant QVariant::fromStdVariant(const std::variant<Types...> &value) \since 5.11 @@ -2611,6 +2711,47 @@ QT_WARNING_POP */ /*! + \fn template<typename... Types> QVariant QVariant::fromStdVariant(std::variant<Types...> &&value) + \since 6.6 + \overload +*/ + + +/*! + \since 6.7 + + Creates a variant of type \a type, and initializes it with + a copy of \c{*copy} if \a copy is not \nullptr (in which case, \a copy + must point to an object of type \a type). + + Note that you have to pass the address of the object you want stored. + + Usually, you never have to use this constructor, use QVariant::fromValue() + instead to construct variants from the pointer types represented by + \c QMetaType::VoidStar, and \c QMetaType::QObjectStar. + + If \a type does not support copy construction and \a copy is not \nullptr, + the variant will be invalid. Similarly, if \a copy is \nullptr and + \a type does not support default construction, the variant will be + invalid. + + Returns the QVariant created as described above. + + \sa QVariant::fromValue(), QMetaType::Type +*/ +QVariant QVariant::fromMetaType(QMetaType type, const void *copy) +{ + QVariant result; + type.registerType(); + const auto iface = type.iface(); + if (isValidMetaTypeForVariant(iface, copy)) { + result.d = Private(iface); + customConstruct(iface, &result.d, copy); + } + return result; +} + +/*! \fn template<typename T> T qvariant_cast(const QVariant &value) \relates QVariant @@ -2621,6 +2762,14 @@ QT_WARNING_POP \sa QVariant::value() */ +/*! + \fn template<typename T> T QVariant::qvariant_cast(QVariant &&value) + \overload + \since 6.7 + + Returns the given \a value converted to the template type \c{T}. +*/ + /*! \fn template<typename T> T qVariantValue(const QVariant &value) \relates QVariant \deprecated |