QVariant: Prefer direct conversion to QVariant{List,Map,Hash}

If a type has both a converter to QVariantList and to
QSequentialIterableImpl registered, we would have chosen the
QSequentialIterableImpl version. In the case of types like QJSValue,
this is more costly. With this change  we therefore uses the direct
conversion if it has been registered.

The same applies to QAssociativeIterableImpl and
QVariantHash/QVariantMap.

Change-Id: I9c0b5068efe4bfbc5e0598a200e6db59201e9974
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
Reviewed-by: Olivier Goffart (Woboq GmbH) <ogoffart@woboq.com>
bb10
Fabian Kosmale 2019-12-11 10:54:57 +01:00
parent 1e89c132e1
commit 8669b8e60f
2 changed files with 49 additions and 6 deletions

View File

@ -801,7 +801,8 @@ namespace QtPrivate {
static QVariantList invoke(const QVariant &v)
{
const int typeId = v.userType();
if (typeId == qMetaTypeId<QStringList>() || typeId == qMetaTypeId<QByteArrayList>() || QMetaType::hasRegisteredConverterFunction(typeId, qMetaTypeId<QtMetaTypePrivate::QSequentialIterableImpl>())) {
if (typeId == qMetaTypeId<QStringList>() || typeId == qMetaTypeId<QByteArrayList>() ||
(QMetaType::hasRegisteredConverterFunction(typeId, qMetaTypeId<QtMetaTypePrivate::QSequentialIterableImpl>()) && !QMetaType::hasRegisteredConverterFunction(typeId, qMetaTypeId<QVariantList>()))) {
QSequentialIterable iter = QVariantValueHelperInterface<QSequentialIterable>::invoke(v);
QVariantList l;
l.reserve(iter.size());
@ -818,7 +819,7 @@ namespace QtPrivate {
static QVariantHash invoke(const QVariant &v)
{
const int typeId = v.userType();
if (typeId == qMetaTypeId<QVariantMap>() || QMetaType::hasRegisteredConverterFunction(typeId, qMetaTypeId<QtMetaTypePrivate::QAssociativeIterableImpl>())) {
if (typeId == qMetaTypeId<QVariantMap>() || ((QMetaType::hasRegisteredConverterFunction(typeId, qMetaTypeId<QtMetaTypePrivate::QAssociativeIterableImpl>())) && !QMetaType::hasRegisteredConverterFunction(typeId, qMetaTypeId<QVariantHash>()))) {
QAssociativeIterable iter = QVariantValueHelperInterface<QAssociativeIterable>::invoke(v);
QVariantHash l;
l.reserve(iter.size());
@ -835,7 +836,7 @@ namespace QtPrivate {
static QVariantMap invoke(const QVariant &v)
{
const int typeId = v.userType();
if (typeId == qMetaTypeId<QVariantHash>() || QMetaType::hasRegisteredConverterFunction(typeId, qMetaTypeId<QtMetaTypePrivate::QAssociativeIterableImpl>())) {
if (typeId == qMetaTypeId<QVariantHash>() || (QMetaType::hasRegisteredConverterFunction(typeId, qMetaTypeId<QtMetaTypePrivate::QAssociativeIterableImpl>()) && !QMetaType::hasRegisteredConverterFunction(typeId, qMetaTypeId<QVariantMap>()))) {
QAssociativeIterable iter = QVariantValueHelperInterface<QAssociativeIterable>::invoke(v);
QVariantMap l;
for (QAssociativeIterable::const_iterator it = iter.begin(), end = iter.end(); it != end; ++it)
@ -851,10 +852,8 @@ namespace QtPrivate {
static QPair<QVariant, QVariant> invoke(const QVariant &v)
{
const int typeId = v.userType();
if (typeId == qMetaTypeId<QPair<QVariant, QVariant> >())
return QVariantValueHelper<QPair<QVariant, QVariant> >::invoke(v);
if (QMetaType::hasRegisteredConverterFunction(typeId, qMetaTypeId<QtMetaTypePrivate::QPairVariantInterfaceImpl>())) {
if (QMetaType::hasRegisteredConverterFunction(typeId, qMetaTypeId<QtMetaTypePrivate::QPairVariantInterfaceImpl>()) && !(typeId == qMetaTypeId<QPair<QVariant, QVariant> >())) {
QtMetaTypePrivate::QPairVariantInterfaceImpl pi = v.value<QtMetaTypePrivate::QPairVariantInterfaceImpl>();
const QtMetaTypePrivate::VariantData d1 = pi.first();

View File

@ -282,6 +282,8 @@ private slots:
void fromStdVariant();
void qt4UuidDataStream();
void preferDirectConversionOverInterfaces();
private:
void dataStream_data(QDataStream::Version version);
void loadQVariantFromDataStream(QDataStream::Version version);
@ -5140,5 +5142,47 @@ void tst_QVariant::qt4UuidDataStream()
QCOMPARE(result.value<QUuid>(), source);
}
void tst_QVariant::preferDirectConversionOverInterfaces()
{
using namespace QtMetaTypePrivate;
bool calledCorrectConverter = false;
QMetaType::registerConverter<MyType, QSequentialIterableImpl>([](const MyType &) {
return QSequentialIterableImpl {};
});
QMetaType::registerConverter<MyType, QVariantList>([&calledCorrectConverter](const MyType &) {
calledCorrectConverter = true;
return QVariantList {};
});
QMetaType::registerConverter<MyType, QAssociativeIterableImpl>([](const MyType &) {
return QAssociativeIterableImpl {};
});
QMetaType::registerConverter<MyType, QVariantHash>([&calledCorrectConverter](const MyType &) {
calledCorrectConverter = true;
return QVariantHash {};
});
QMetaType::registerConverter<MyType, QVariantMap>([&calledCorrectConverter](const MyType &) {
calledCorrectConverter = true;
return QVariantMap {};
});
auto holder = QVariant::fromValue(MyType {});
QVERIFY(holder.canConvert<QSequentialIterableImpl>());
QVERIFY(holder.canConvert<QVariantList>());
QVERIFY(holder.canConvert<QAssociativeIterableImpl>());
QVERIFY(holder.canConvert<QVariantHash>());
QVERIFY(holder.canConvert<QVariantMap>());
holder.value<QVariantList>();
QVERIFY(calledCorrectConverter);
calledCorrectConverter = false;
holder.value<QVariantHash>();
QVERIFY(calledCorrectConverter);
calledCorrectConverter = false;
holder.value<QVariantMap>();
QVERIFY(calledCorrectConverter);
}
QTEST_MAIN(tst_QVariant)
#include "tst_qvariant.moc"