summaryrefslogtreecommitdiffstats
path: root/src/corelib/kernel/qvariant.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/kernel/qvariant.cpp')
-rw-r--r--src/corelib/kernel/qvariant.cpp1146
1 files changed, 631 insertions, 515 deletions
diff --git a/src/corelib/kernel/qvariant.cpp b/src/corelib/kernel/qvariant.cpp
index b942ac50c7..92a44c462b 100644
--- a/src/corelib/kernel/qvariant.cpp
+++ b/src/corelib/kernel/qvariant.cpp
@@ -1,50 +1,15 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Copyright (C) 2021 Intel Corporation.
-** Copyright (C) 2015 Olivier Goffart <ogoffart@woboq.com>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qvariant.h"
+// Copyright (C) 2022 The Qt Company Ltd.
+// Copyright (C) 2021 Intel Corporation.
+// 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_p.h"
#include "qbitarray.h"
#include "qbytearray.h"
#include "qdatastream.h"
#include "qdebug.h"
#include "qmap.h"
+#include "qhash.h"
#include "qdatetime.h"
#if QT_CONFIG(easingcurve)
#include "qeasingcurve.h"
@@ -71,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>
@@ -83,32 +47,43 @@
#include "qline.h"
#endif
+#include <memory>
+
#include <cmath>
#include <float.h>
#include <cstring>
QT_BEGIN_NAMESPACE
+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:
@@ -120,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 == QLatin1String("false") || s == QLatin1String("0")) {
- *ok = true;
+ if (s == "false"_L1 || s == "0"_L1)
return 0;
- }
- if (s == QLatin1String("true") || s == QLatin1String("1")) {
- *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
@@ -192,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();
@@ -241,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
@@ -302,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
@@ -399,7 +422,7 @@ static void customClear(QVariant::Private *d)
\value EasingCurve a QEasingCurve
\value Uuid a QUuid
\value ModelIndex a QModelIndex
- \value PersistentModelIndex a QPersistentModelIndex (since 5.5)
+ \value [since 5.5] PersistentModelIndex a QPersistentModelIndex
\value Font a QFont
\value Hash a QVariantHash
\value Icon a QIcon
@@ -494,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);
}
@@ -523,32 +540,121 @@ 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.
*/
/*!
- \fn QVariant::QVariant(QLatin1String val)
+ \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.
*/
/*!
@@ -567,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.
*/
@@ -611,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.
*/
@@ -659,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
@@ -805,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(QLatin1String 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
@@ -1014,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;
}
@@ -1051,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;
@@ -1081,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 = {};
}
@@ -1098,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.
@@ -1243,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());
}
}
@@ -1255,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;
@@ -1334,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)
@@ -1354,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}
*/
@@ -1364,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.
@@ -1419,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()
*/
@@ -1785,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.
@@ -1795,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);
@@ -1832,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);
}
/*!
@@ -1854,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);
}
/*!
@@ -1871,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);
}
/*!
@@ -1888,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);
}
/*!
@@ -1928,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);
}
/*!
@@ -1947,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);
}
/*!
@@ -1966,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()
*/
@@ -2059,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());
@@ -2086,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.
@@ -2112,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.
@@ -2141,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 |
@@ -2156,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))
@@ -2206,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 (t1 == QMetaType::LongLong || t2 == QMetaType::LongLong)
+ if (isUnsigned2 && iface2->size > sizeof(int))
+ return QMetaType::ULongLong;
+
+ // 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)) {
@@ -2354,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 *>());
}
@@ -2375,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;
}
@@ -2392,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.
@@ -2431,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;
}
@@ -2449,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
*/
/*!
@@ -2459,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()
{
@@ -2470,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.
@@ -2493,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() << ", ";
@@ -2507,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
@@ -2526,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
@@ -2539,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.
@@ -2591,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.
@@ -2607,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.
@@ -2624,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
@@ -2644,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
@@ -2654,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