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 /tests/auto/corelib/kernel/qvariant | |
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 'tests/auto/corelib/kernel/qvariant')
-rw-r--r-- | tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp | 98 |
1 files changed, 98 insertions, 0 deletions
diff --git a/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp b/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp index 4fa42a1d52..feb03a029f 100644 --- a/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp +++ b/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp @@ -380,6 +380,7 @@ private slots: void copyNonDefaultConstructible(); void inplaceConstruct(); + void emplace(); void getIf_int() { getIf_impl(42); } void getIf_QString() { getIf_impl(u"string"_s); }; @@ -5767,6 +5768,103 @@ void tst_QVariant::inplaceConstruct() } } +struct LargerThanInternalQVariantStorage { + char data[6 * sizeof(void *)]; +}; + +struct alignas(256) LargerThanInternalQVariantStorageOveraligned { + char data[6 * sizeof(void *)]; +}; + +struct alignas(128) SmallerAlignmentEvenLargerSize { + char data[17 * sizeof(void *)]; +}; + +void tst_QVariant::emplace() +{ + { + // can emplace non default constructible + can emplace on null variant + NonDefaultConstructible ndc(42); + QVariant var; + var.emplace<NonDefaultConstructible>(42); + QVERIFY(get_if<NonDefaultConstructible>(&var)); + QCOMPARE(get<NonDefaultConstructible>(var), ndc); + } + { + // can emplace using ctor taking initializer_list + QVariant var; + var.emplace<std::vector<int>>({0, 1, 2, 3, 4}); + auto vecPtr = get_if<std::vector<int>>(&var); + QVERIFY(vecPtr); + QCOMPARE(vecPtr->size(), 5U); + for (int i = 0; i < 5; ++i) + QCOMPARE(vecPtr->at(size_t(i)), i); + } + // prequisites for the test + QCOMPARE_LE(sizeof(std::vector<int>), sizeof(std::string)); + QCOMPARE(alignof(std::vector<int>), alignof(std::string)); + { + // emplace can reuse storage + auto var = QVariant::fromValue(std::string{}); + QVERIFY(var.data_ptr().is_shared); + auto data = var.constData(); + std::vector<int> &vec = var.emplace<std::vector<int>>(3, 42); + /* alignment is the same, so the pointer is exactly the same; + no offset change */ + auto expected = std::vector<int>{42, 42, 42}; + QCOMPARE(get_if<std::vector<int>>(&var), &vec); + QCOMPARE(get<std::vector<int>>(var), expected); + QCOMPARE(var.constData(), data); + } + { + // emplace can't reuse storage if the variant is shared + auto var = QVariant::fromValue(std::string{}); + [[maybe_unused]] QVariant causesSharing = var; + QVERIFY(var.data_ptr().is_shared); + auto data = var.constData(); + var.emplace<std::vector<int>>(3, 42); + auto expected = std::vector<int>{42, 42, 42}; + QVERIFY(get_if<std::vector<int>>(&var)); + QCOMPARE(get<std::vector<int>>(var), expected); + QCOMPARE_NE(var.constData(), data); + } + { + // emplace puts element into the correct place - non-shared + QVERIFY(QVariant::Private::canUseInternalSpace(QMetaType::fromType<QString>().iface())); + QVariant var; + var.emplace<QString>(QChar('x')); + QVERIFY(!var.data_ptr().is_shared); + } + { + // emplace puts element into the correct place - shared + QVERIFY(!QVariant::Private::canUseInternalSpace(QMetaType::fromType<std::string>().iface())); + QVariant var; + var.emplace<std::string>(42, 'x'); + QVERIFY(var.data_ptr().is_shared); + } + { + // emplace does not reuse the storage if alignment is too large + auto iface = QMetaType::fromType<LargerThanInternalQVariantStorage>().iface(); + QVERIFY(!QVariant::Private::canUseInternalSpace(iface)); + auto var = QVariant::fromValue(LargerThanInternalQVariantStorage{}); + auto data = var.constData(); + var.emplace<LargerThanInternalQVariantStorageOveraligned>(); + QCOMPARE_NE(var.constData(), data); + } + { + // emplace does reuse the storage if new alignment and size are together small enough + auto iface = QMetaType::fromType<LargerThanInternalQVariantStorageOveraligned>().iface(); + QVERIFY(!QVariant::Private::canUseInternalSpace(iface)); + auto var = QVariant::fromValue(LargerThanInternalQVariantStorageOveraligned{}); + auto data = var.constData(); + var.emplace<SmallerAlignmentEvenLargerSize>(); + // no exact match below - the alignment is after all different + QCOMPARE_LE(quintptr(var.constData()), quintptr(data)); + QCOMPARE_LE(quintptr(var.constData()), + quintptr(data) + sizeof(LargerThanInternalQVariantStorageOveraligned)); + } +} + void tst_QVariant::getIf_NonDefaultConstructible() { getIf_impl(NonDefaultConstructible{42}); |