From c81e8f8ff24d30cc4c137d6f071954958e984ce9 Mon Sep 17 00:00:00 2001 From: Fabian Kosmale Date: Wed, 22 Mar 2023 15:21:15 +0100 Subject: [PATCH] QVariant: Add support for in-place construction This avoids constructing an object just to copy (later: move) it into a QVariant. ChangeLog will be in a follow-up change adding emplace support. Task-number: QTBUG-112187 Change-Id: I444e580c7d8927d41b3d21d5a521e7c475119e4c Reviewed-by: Marc Mutz --- src/corelib/kernel/qvariant.cpp | 37 ++++++++++++++++ src/corelib/kernel/qvariant.h | 43 +++++++++++++++++++ .../corelib/kernel/qvariant/tst_qvariant.cpp | 19 ++++++++ 3 files changed, 99 insertions(+) diff --git a/src/corelib/kernel/qvariant.cpp b/src/corelib/kernel/qvariant.cpp index 28761f9792..d4a7c91640 100644 --- a/src/corelib/kernel/qvariant.cpp +++ b/src/corelib/kernel/qvariant.cpp @@ -523,6 +523,43 @@ QVariant::QVariant(const QVariant &p) { } +/*! + \fn template = true> QVariant::QVariant(std::in_place_type_t, Args&&... args) noexcept(is_noexcept_constructible, Args...>::value) + + \since 6.6 + Constructs a new variant containing a value of type \c Type. The contained + value is is initialized with the arguments + \c{std::forward(args)...}. + + This overload only participates in overload resolution if \c Type can be + constructed from \a args. + + This constructor is provided for STL/std::any compatibility. + + \overload + */ + +/*! + + \fn template &, Args...> = true> explicit QVariant::QVariant(std::in_place_type_t, std::initializer_list il, Args&&... args) noexcept(is_noexcept_constructible, std::initializer_list &, 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. +*/ + +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; + } +} + /*! \fn QVariant::QVariant(const QString &val) noexcept diff --git a/src/corelib/kernel/qvariant.h b/src/corelib/kernel/qvariant.h index 50ede63987..1fd304e772 100644 --- a/src/corelib/kernel/qvariant.h +++ b/src/corelib/kernel/qvariant.h @@ -62,6 +62,15 @@ template<> constexpr inline bool qIsRelocatable = true; } class Q_CORE_EXPORT QVariant { + template + using if_constructible = std::enable_if_t< + std::conjunction_v< + std::is_copy_constructible>, + std::is_destructible>, + std::is_constructible, Args...> + >, + bool>; + struct CborValueStandIn { qint64 n; void *c; int t; }; public: struct PrivateShared @@ -204,6 +213,37 @@ public: explicit QVariant(QMetaType type, const void *copy = nullptr); QVariant(const QVariant &other); +private: + template + using is_noexcept_constructible = std::conjunction< + std::bool_constant>, + std::is_nothrow_constructible + >; + +public: + template = true> + explicit QVariant(std::in_place_type_t, Args&&... args) + noexcept(is_noexcept_constructible, Args...>::value) + : QVariant(std::in_place, QMetaType::fromType>() ) + { + void *data = const_cast(constData()); + new (data) Type(std::forward(args)...); + } + + template &, Args...> = true> + explicit QVariant(std::in_place_type_t, std::initializer_list il, Args&&... args) + noexcept(is_noexcept_constructible, + std::initializer_list &, + Args... + >::value) + : QVariant(std::in_place, QMetaType::fromType>()) + { + char *data = static_cast(const_cast(constData())); + new (data) Type(il, std::forward(args)...); + } + // primitives QVariant(int i) noexcept; QVariant(uint ui) noexcept; @@ -535,6 +575,9 @@ private: // int variant, so delete this constructor: QVariant(QMetaType::Type) = delete; + // used to setup the QVariant internals for the "real" inplace ctor + QVariant(std::in_place_t, QMetaType type); + // These constructors don't create QVariants of the type associated // with the enum, as expected, but they would create a QVariant of // type int with the value of the enum value. diff --git a/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp b/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp index 6667d95e0d..698d117f70 100644 --- a/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp +++ b/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp @@ -378,6 +378,8 @@ private slots: void constructFromIncompatibleMetaType(); void copyNonDefaultConstructible(); + void inplaceConstruct(); + void getIf_int() { getIf_impl(42); } void getIf_QString() { getIf_impl(u"string"_s); }; void getIf_NonDefaultConstructible(); @@ -5720,6 +5722,23 @@ void tst_QVariant::copyNonDefaultConstructible() QCOMPARE(var2, var); } +void tst_QVariant::inplaceConstruct() +{ + { + NonDefaultConstructible ndc(42); + QVariant var(std::in_place_type, 42); + QVERIFY(get_if(&var)); + QCOMPARE(get(var), ndc); + } + + { + std::vector vec {1, 2, 3, 4}; + QVariant var(std::in_place_type>, {1, 2, 3, 4}); + QVERIFY(get_if>(&var)); + QCOMPARE(get>(var), vec); + } +} + void tst_QVariant::getIf_NonDefaultConstructible() { getIf_impl(NonDefaultConstructible{42});