summaryrefslogtreecommitdiffstats
path: root/tests/auto/corelib/kernel/qvariant
diff options
context:
space:
mode:
authorFabian Kosmale <fabian.kosmale@qt.io>2023-03-23 10:12:10 +0100
committerFabian Kosmale <fabian.kosmale@qt.io>2023-06-02 09:13:08 +0200
commitf564e905c1c258691603694aaa70303b029e4598 (patch)
treeed4e6da24fa75c6df7bf400e01535cdde87b506d /tests/auto/corelib/kernel/qvariant
parent18a2c62c073371ab24685d0f98b4b0cc04d11a7e (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.cpp98
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});