diff options
Diffstat (limited to 'src/corelib/kernel/qvariant.cpp')
-rw-r--r-- | src/corelib/kernel/qvariant.cpp | 289 |
1 files changed, 243 insertions, 46 deletions
diff --git a/src/corelib/kernel/qvariant.cpp b/src/corelib/kernel/qvariant.cpp index 28761f9792..92a44c462b 100644 --- a/src/corelib/kernel/qvariant.cpp +++ b/src/corelib/kernel/qvariant.cpp @@ -47,6 +47,8 @@ #include "qline.h" #endif +#include <memory> + #include <cmath> #include <float.h> #include <cstring> @@ -171,20 +173,20 @@ static std::optional<qlonglong> qConvertToNumber(const QVariant::Private *d, boo return std::nullopt; } -static std::optional<qreal> qConvertToRealNumber(const QVariant::Private *d) +static std::optional<double> qConvertToRealNumber(const QVariant::Private *d) { bool ok; switch (d->typeInterface()->typeId) { case QMetaType::QString: if (double r = d->get<QString>().toDouble(&ok); ok) - return qreal(r); + 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 qreal(d->get<qfloat16>()); + return double(d->get<qfloat16>()); case QMetaType::ULongLong: case QMetaType::UInt: case QMetaType::UChar: @@ -192,7 +194,7 @@ static std::optional<qreal> qConvertToRealNumber(const QVariant::Private *d) 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(); @@ -202,7 +204,7 @@ static std::optional<qreal> qConvertToRealNumber(const QVariant::Private *d) default: // includes enum conversion as well as invalid types if (std::optional<qlonglong> l = qConvertToNumber(d)) - return qreal(*l); + return double(*l); return std::nullopt; } } @@ -231,9 +233,22 @@ static bool isValidMetaTypeForVariant(const QtPrivate::QMetaTypeInterface *iface 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, - const void *copy) + std::conditional_t<moveOption == ForceMove, void *, const void *> copy) { using namespace QtMetaTypePrivate; Q_ASSERT(iface); @@ -242,6 +257,10 @@ static void customConstruct(const QtPrivate::QMetaTypeInterface *iface, QVariant 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. @@ -251,11 +270,17 @@ static void customConstruct(const QtPrivate::QMetaTypeInterface *iface, QVariant if (QVariant::Private::canUseInternalSpace(iface)) { d->is_shared = false; if (!copy && !iface->defaultCtr) - return; // default constructor and it's OK to build in 0-filled storage, which we've already done - construct(iface, d->data.data, copy); + 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 + construct(iface, d->data.data, copy); } else { d->data.shared = customConstructShared(iface->size, iface->alignment, [=](void *where) { - construct(iface, where, copy); + if constexpr (moveOption == ForceMove && nullability == NonNull) + moveConstruct(iface, where, copy); + else + construct(iface, where, copy); }); d->is_shared = true; } @@ -299,6 +324,7 @@ static QVariant::Private clonePrivate(const QVariant::Private &other) \ingroup objectmodel \ingroup shared + \compares equality Because C++ forbids unions from including types that have non-default constructors or destructors, most interesting Qt @@ -491,23 +517,18 @@ void QVariant::create(int type, const void *copy) */ void QVariant::create(QMetaType type, const void *copy) { - *this = QVariant(type, 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); } @@ -524,6 +545,106 @@ QVariant::QVariant(const QVariant &p) } /*! + \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; + } +} + +/*! + \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) noexcept Constructs a new variant with a string value, \a val. @@ -790,27 +911,27 @@ 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. - If \a type does not support copy and default construction, the variant will - be invalid. + 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::fromValue(), QMetaType::Type + \sa QVariant::fromMetaType, QVariant::fromValue(), QMetaType::Type */ -QVariant::QVariant(QMetaType type, const void *copy) : d(type.iface()) +QVariant::QVariant(QMetaType type, const void *copy) + : d() { - type.registerType(); - if (isValidMetaTypeForVariant(type.iface(), copy)) - customConstruct(type.iface(), &d, copy); - else - d = {}; + *this = fromMetaType(type, copy); } QVariant::QVariant(int val) noexcept : d(std::piecewise_construct_t{}, val) {} @@ -822,7 +943,9 @@ 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) {} @@ -969,7 +1092,8 @@ void QVariant::detach() Q_ASSERT(isValidMetaTypeForVariant(d.typeInterface(), constData())); Private dd(d.typeInterface()); - customConstruct(d.typeInterface(), &dd, constData()); + // 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; @@ -998,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 = {}; } @@ -1722,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. @@ -1732,6 +1857,7 @@ QBitArray QVariant::toBitArray() const { return qvariant_cast<QBitArray>(*this); } +#endif // QT_BOOTSTRAPPED template <typename T> inline T qNumVariantToHelper(const QVariant::Private &d, bool *ok) @@ -2027,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. @@ -2053,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. @@ -2235,15 +2361,15 @@ static QPartialOrdering numericCompare(const QVariant::Private *d1, const QVaria if (promotedType != QMetaType::QReal) return integralCompare(promotedType, d1, d2); - // qreal comparisons - std::optional<qreal> r1 = qConvertToRealNumber(d1); - std::optional<qreal> r2 = qConvertToRealNumber(d2); + // 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 spaceShip<qreal>(*r1, *r2); + return spaceShip(*r1, *r2); } #ifndef QT_BOOTSTRAPPED @@ -2429,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 @@ -2448,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 @@ -2461,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. @@ -2513,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. @@ -2529,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. @@ -2549,6 +2691,12 @@ QT_WARNING_POP \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 @@ -2563,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 @@ -2573,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 |