QVariant: introduce ShouldDeleteVariantData flag

This flag is used in QSequentialIterable and QAssociativeIterable to indicate
that the data pointer in VariantData should be deleted after the variant has
been constructed.

The use case for this is
https://codereview.qt-project.org/c/qt/qtdeclarative/+/284151, where we have
a proxy iterator and cannot easily return a pointer to already owned data, as
it is hard to manage its lifetime in the iterator. In contrast, it is clear
that we can release the memory in the QSequentialIterable functions, as it has
already been copied into the QVariant there.

Change-Id: I2b33497d991cd4f752153e0ebda767b82e4bb851
Reviewed-by: Olivier Goffart (Woboq GmbH) <ogoffart@woboq.com>
This commit is contained in:
Fabian Kosmale 2019-12-10 14:55:14 +01:00
parent 07838840e8
commit c15d6a155c
3 changed files with 117 additions and 20 deletions

View File

@ -4520,15 +4520,24 @@ QSequentialIterable::const_iterator QSequentialIterable::end() const
return it;
}
static const QVariant variantFromVariantDataHelper(const QtMetaTypePrivate::VariantData &d) {
QVariant v;
if (d.metaTypeId == qMetaTypeId<QVariant>())
v = *reinterpret_cast<const QVariant*>(d.data);
else
v = QVariant(d.metaTypeId, d.data, d.flags & ~QVariantConstructionFlags::ShouldDeleteVariantData);
if (d.flags & QVariantConstructionFlags::ShouldDeleteVariantData)
QMetaType::destroy(d.metaTypeId, const_cast<void *>(d.data));
return v;
}
/*!
Returns the element at position \a idx in the container.
*/
QVariant QSequentialIterable::at(int idx) const
{
const QtMetaTypePrivate::VariantData d = m_impl.at(idx);
if (d.metaTypeId == qMetaTypeId<QVariant>())
return *reinterpret_cast<const QVariant*>(d.data);
return QVariant(d.metaTypeId, d.data, d.flags);
return variantFromVariantDataHelper(d);
}
/*!
@ -4605,9 +4614,7 @@ QSequentialIterable::const_iterator::operator=(const const_iterator &other)
const QVariant QSequentialIterable::const_iterator::operator*() const
{
const QtMetaTypePrivate::VariantData d = m_impl.getCurrent();
if (d.metaTypeId == qMetaTypeId<QVariant>())
return *reinterpret_cast<const QVariant*>(d.data);
return QVariant(d.metaTypeId, d.data, d.flags);
return variantFromVariantDataHelper(d);
}
/*!
@ -4939,10 +4946,7 @@ QAssociativeIterable::const_iterator::operator=(const const_iterator &other)
const QVariant QAssociativeIterable::const_iterator::operator*() const
{
const QtMetaTypePrivate::VariantData d = m_impl.getCurrentValue();
QVariant v(d.metaTypeId, d.data, d.flags);
if (d.metaTypeId == qMetaTypeId<QVariant>())
return *reinterpret_cast<const QVariant*>(d.data);
return v;
return variantFromVariantDataHelper(d);
}
/*!
@ -4951,10 +4955,7 @@ const QVariant QAssociativeIterable::const_iterator::operator*() const
const QVariant QAssociativeIterable::const_iterator::key() const
{
const QtMetaTypePrivate::VariantData d = m_impl.getCurrentKey();
QVariant v(d.metaTypeId, d.data, d.flags);
if (d.metaTypeId == qMetaTypeId<QVariant>())
return *reinterpret_cast<const QVariant*>(d.data);
return v;
return variantFromVariantDataHelper(d);
}
/*!
@ -4962,11 +4963,7 @@ const QVariant QAssociativeIterable::const_iterator::key() const
*/
const QVariant QAssociativeIterable::const_iterator::value() const
{
const QtMetaTypePrivate::VariantData d = m_impl.getCurrentValue();
QVariant v(d.metaTypeId, d.data, d.flags);
if (d.metaTypeId == qMetaTypeId<QVariant>())
return *reinterpret_cast<const QVariant*>(d.data);
return v;
return operator*();
}
/*!

View File

@ -105,6 +105,11 @@ inline T *v_cast(QVariant::Private *d, T * = nullptr)
#endif
enum QVariantConstructionFlags : uint {
Default = 0x0,
PointerType = 0x1,
ShouldDeleteVariantData = 0x2 // only used in Q*Iterable
};
//a simple template that avoids to allocate 2 memory chunks when creating a QVariant
template <class T> class QVariantPrivateSharedEx : public QVariant::PrivateShared

View File

@ -31,6 +31,7 @@
#include <QtTest/QtTest>
#include <qvariant.h>
#include <QtCore/private/qvariant_p.h>
#include <qbitarray.h>
#include <qbytearraylist.h>
#include <qdatetime.h>
@ -276,7 +277,8 @@ private slots:
void nullConvert();
void accessSequentialContainerKey();
void shouldDeleteVariantDataWorksForSequential();
void shouldDeleteVariantDataWorksForAssociative();
void fromStdVariant();
void qt4UuidDataStream();
@ -4990,6 +4992,99 @@ void tst_QVariant::accessSequentialContainerKey()
QCOMPARE(nameResult, QStringLiteral("Seven"));
}
void tst_QVariant::shouldDeleteVariantDataWorksForSequential()
{
QCOMPARE(instanceCount, 0);
{
QtMetaTypePrivate::QSequentialIterableImpl iterator {};
iterator._iteratorCapabilities = QtMetaTypePrivate::RandomAccessCapability |
QtMetaTypePrivate::BiDirectionalCapability |
QtMetaTypePrivate::ForwardCapability;
iterator._metaType_flags = QVariantConstructionFlags::ShouldDeleteVariantData;
iterator._size = [](const void *) {return 1;};
iterator._metaType_id = qMetaTypeId<MyType>();
iterator._moveToBegin = [](const void *, void **) {};
iterator._moveToEnd = [](const void *, void **) {};
iterator._advance = [](void **, int) {};
iterator._destroyIter = [](void **){};
iterator._equalIter = [](void * const *, void * const *){return true; /*all iterators are nullptr*/};
iterator._destroyIter = [](void **){};
iterator._at = [](const void *, int ) -> void const * {
MyType mytype {1, "eins"};
return QMetaType::create(qMetaTypeId<MyType>(), &mytype);
};
iterator._get = [](void * const *, int, uint) -> QtMetaTypePrivate::VariantData {
MyType mytype {2, "zwei"};
return {qMetaTypeId<MyType>(), QMetaType::create(qMetaTypeId<MyType>(), &mytype), QVariantConstructionFlags::ShouldDeleteVariantData};
};
QSequentialIterable iterable {iterator};
QVariant value1 = iterable.at(0);
QVERIFY(value1.canConvert<MyType>());
QCOMPARE(value1.value<MyType>().number, 1);
QVariant value2 = *iterable.begin();
QVERIFY(value2.canConvert<MyType>());
QCOMPARE(value2.value<MyType>().number, 2);
}
QCOMPARE(instanceCount, 0);
}
void tst_QVariant::shouldDeleteVariantDataWorksForAssociative()
{
QCOMPARE(instanceCount, 0);
{
QtMetaTypePrivate::QAssociativeIterableImpl iterator {};
iterator._metaType_flags_key = QVariantConstructionFlags::ShouldDeleteVariantData;
iterator._metaType_flags_value = QVariantConstructionFlags::ShouldDeleteVariantData;
iterator._size = [](const void *) {return 1;};
iterator._metaType_id_value = qMetaTypeId<MyType>();
iterator._metaType_id_key = qMetaTypeId<MyType>();
iterator._begin = [](const void *, void **) {};
iterator._end = [](const void *, void **) {};
iterator._advance = [](void **, int) {};
iterator._destroyIter = [](void **){};
iterator._equalIter = [](void * const *, void * const *){return true; /*all iterators are nullptr*/};
iterator._destroyIter = [](void **){};
iterator._find = [](const void *, const void *, void **iterator ) -> void {
(*iterator) = reinterpret_cast<void *>(quintptr(42));
};
iterator._getKey = [](void * const *iterator, int, uint) -> QtMetaTypePrivate::VariantData {
MyType mytype {1, "key"};
if (reinterpret_cast<quintptr>(*iterator) == 42) {
mytype.number = 42;
mytype.text = "find_key";
}
return {qMetaTypeId<MyType>(), QMetaType::create(qMetaTypeId<MyType>(), &mytype), QVariantConstructionFlags::ShouldDeleteVariantData};
};
iterator._getValue = [](void * const *iterator, int, uint) -> QtMetaTypePrivate::VariantData {
MyType mytype {2, "value"};
if (reinterpret_cast<quintptr>(*iterator) == 42) {
mytype.number = 42;
mytype.text = "find_value";
}
return {qMetaTypeId<MyType>(), QMetaType::create(qMetaTypeId<MyType>(), &mytype), QVariantConstructionFlags::ShouldDeleteVariantData};
};
QAssociativeIterable iterable {iterator};
auto it = iterable.begin();
QVariant value1 = it.key();
QVERIFY(value1.canConvert<MyType>());
QCOMPARE(value1.value<MyType>().number, 1);
QCOMPARE(value1.value<MyType>().text, "key");
QVariant value2 = it.value();
QVERIFY(value2.canConvert<MyType>());
QCOMPARE(value2.value<MyType>().number, 2);
auto findIt = iterable.find(QVariant::fromValue(MyType {}));
value1 = findIt.key();
QCOMPARE(value1.value<MyType>().number, 42);
QCOMPARE(value1.value<MyType>().text, "find_key");
value2 = findIt.value();
QCOMPARE(value2.value<MyType>().number, 42);
QCOMPARE(value2.value<MyType>().text, "find_value");
}
QCOMPARE(instanceCount, 0);
}
void tst_QVariant::fromStdVariant()
{
#if __has_include(<variant>) && __cplusplus >= 201703L