diff options
author | Fabian Kosmale <fabian.kosmale@qt.io> | 2023-03-23 10:12:10 +0100 |
---|---|---|
committer | Fabian Kosmale <fabian.kosmale@qt.io> | 2023-06-02 09:13:08 +0200 |
commit | f564e905c1c258691603694aaa70303b029e4598 (patch) | |
tree | ed4e6da24fa75c6df7bf400e01535cdde87b506d /src | |
parent | 18a2c62c073371ab24685d0f98b4b0cc04d11a7e (diff) |
QVariant: Support emplace
[ChangeLog][QtCore][QVariant] Implemented in-place construction for
QVariant. The constructor taking std::in_place_type<Type> constructs
an object of type Type directly inside QVariant's storage, without any
further copy or move operations. QVariant::emplace() does the same
when replacing the content of an existing QVariant and tries to reuse
previously-allocated memory.
Fixes: QTBUG-112187
Change-Id: I16614ad701fa3bb583976ed2001bb312f119a51f
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
Reviewed-by: Ivan Solovev <ivan.solovev@qt.io>
Diffstat (limited to 'src')
-rw-r--r-- | src/corelib/kernel/qvariant.cpp | 63 | ||||
-rw-r--r-- | src/corelib/kernel/qvariant.h | 39 |
2 files changed, 102 insertions, 0 deletions
diff --git a/src/corelib/kernel/qvariant.cpp b/src/corelib/kernel/qvariant.cpp index 882abdf5b4..b5783be83f 100644 --- a/src/corelib/kernel/qvariant.cpp +++ b/src/corelib/kernel/qvariant.cpp @@ -545,6 +545,28 @@ QVariant::QVariant(const QVariant &p) non-initializer list \c{in_place_type_t} overload. */ + +/*! + \fn template <typename Type, typename... Args, if_constructible<Type, Args...> = true> QVariant::emplace(Args&&... args) + + \since 6.6 + Replaces the object currently held in \c{*this} with an object of + type \c{Type}, 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 Type, typename List, typename... Args, if_constructible<Type, std::initializer_list<List> &, Args...> = true> QVariant::emplace(std::initializer_list<List> 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 @@ -556,6 +578,47 @@ QVariant::QVariant(std::in_place_t, QMetaType type) : d(type.iface()) } /*! + \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. diff --git a/src/corelib/kernel/qvariant.h b/src/corelib/kernel/qvariant.h index 9ed6fedd3c..00e2bb0072 100644 --- a/src/corelib/kernel/qvariant.h +++ b/src/corelib/kernel/qvariant.h @@ -444,6 +444,43 @@ public: { return d.storage(); } inline const void *data() const { return constData(); } +private: + template <typename Type> + void verifySuitableForEmplace() + { + static_assert(!std::is_reference_v<Type>, + "QVariant does not support reference types"); + static_assert(!std::is_const_v<Type>, + "QVariant does not support const types"); + static_assert(std::is_copy_constructible_v<Type>, + "QVariant requires that the type is copyable"); + static_assert(std::is_destructible_v<Type>, + "QVariant requires that the type is destructible"); + } + + template <typename Type, typename... Args> + Type &emplaceImpl(Args&&... args) + { + verifySuitableForEmplace<Type>(); + auto data = static_cast<Type *>(prepareForEmplace(QMetaType::fromType<Type>())); + return *q20::construct_at(data, std::forward<Args>(args)...); + } + +public: + template <typename Type, typename... Args, + if_constructible<Type, Args...> = true> + Type &emplace(Args&&... args) + { + return emplaceImpl<Type>(std::forward<Args>(args)...); + } + + template <typename Type, typename List, typename... Args, + if_constructible<Type, std::initializer_list<List> &, Args...> = true> + Type &emplace(std::initializer_list<List> list, Args&&... args) + { + return emplaceImpl<Type>(list, std::forward<Args>(args)...); + } + template<typename T, typename = std::enable_if_t<!std::is_same_v<std::decay_t<T>, QVariant>>> void setValue(T &&avalue) { @@ -580,6 +617,8 @@ private: // used to setup the QVariant internals for the "real" inplace ctor QVariant(std::in_place_t, QMetaType type); + // helper for emplace + void *prepareForEmplace(QMetaType type); // These constructors don't create QVariants of the type associated // with the enum, as expected, but they would create a QVariant of |