From 657a18c7faac28d7025350834dc7686e689ac358 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Wed, 12 Jan 2022 17:26:11 +0100 Subject: [PATCH] QMetaType: Allow conversion of derived gadget types to their base types A derived gadget has an is-a relationship with its base type. It should be convertible. In fact, canConvert() already tells us it is. Change-Id: I71a5ac9afd78e88adb23b4d0e757f34077f63207 Reviewed-by: Lars Knoll --- src/corelib/kernel/qmetatype.cpp | 16 +++++++++++----- .../corelib/kernel/qmetatype/tst_qmetatype.h | 16 ++++++++++++++++ .../corelib/kernel/qmetatype/tst_qmetatype2.cpp | 16 ++++++++++++++-- 3 files changed, 41 insertions(+), 7 deletions(-) diff --git a/src/corelib/kernel/qmetatype.cpp b/src/corelib/kernel/qmetatype.cpp index 57a4a3063b8..c380cda0dbd 100644 --- a/src/corelib/kernel/qmetatype.cpp +++ b/src/corelib/kernel/qmetatype.cpp @@ -2181,7 +2181,7 @@ static bool viewAsAssociativeIterable(QMetaType fromType, void *from, void *to) return false; } -static bool convertQObject(QMetaType fromType, const void *from, QMetaType toType, void *to) +static bool convertMetaObject(QMetaType fromType, const void *from, QMetaType toType, void *to) { // handle QObject conversion if ((fromType.flags() & QMetaType::PointerToQObject) && (toType.flags() & QMetaType::PointerToQObject)) { @@ -2194,8 +2194,14 @@ static bool convertQObject(QMetaType fromType, const void *from, QMetaType toTyp // if fromObject is null, use static fromType to check if conversion works *static_cast(to) = nullptr; return fromType.metaObject()->inherits(toType.metaObject()); - } else { - return false; + } + } else { + const QMetaObject *f = fromType.metaObject(); + const QMetaObject *t = toType.metaObject(); + if (f && t && f->inherits(t)) { + toType.destruct(to); + toType.construct(to, from); + return true; } } return false; @@ -2278,7 +2284,7 @@ bool QMetaType::convert(QMetaType fromType, const void *from, QMetaType toType, if (toTypeId == qMetaTypeId()) return convertToAssociativeIterable(fromType, from, to); - return convertQObject(fromType, from, toType, to); + return convertMetaObject(fromType, from, toType, to); #else return false; #endif @@ -2309,7 +2315,7 @@ bool QMetaType::view(QMetaType fromType, void *from, QMetaType toType, void *to) if (toTypeId == qMetaTypeId()) return viewAsAssociativeIterable(fromType, from, to); - return convertQObject(fromType, from, toType, to); + return convertMetaObject(fromType, from, toType, to); #else return false; #endif diff --git a/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.h b/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.h index 65fe3859f03..b126b86475b 100644 --- a/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.h +++ b/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.h @@ -217,6 +217,22 @@ struct CustomEqualsOnlyType static_assert(QTypeTraits::has_operator_equal_v); static_assert(!QTypeTraits::has_operator_less_than_v); +struct BaseGadgetType +{ + Q_GADGET +public: + explicit BaseGadgetType(QVariant foo = QVariant()) : m_foo(std::move(foo)) {} + QVariant m_foo; +}; + +struct DerivedGadgetType : public BaseGadgetType +{ + Q_GADGET +public: + explicit DerivedGadgetType(QVariant foo = QVariant()) : BaseGadgetType(std::move(foo)) {} + int bar = 25; +}; + Q_DECLARE_METATYPE(CustomConvertibleType); Q_DECLARE_METATYPE(CustomConvertibleType2); Q_DECLARE_METATYPE(CustomDebugStreamableType); diff --git a/tests/auto/corelib/kernel/qmetatype/tst_qmetatype2.cpp b/tests/auto/corelib/kernel/qmetatype/tst_qmetatype2.cpp index 8567288c8ba..025353d9c0f 100644 --- a/tests/auto/corelib/kernel/qmetatype/tst_qmetatype2.cpp +++ b/tests/auto/corelib/kernel/qmetatype/tst_qmetatype2.cpp @@ -164,6 +164,7 @@ void tst_QMetaType::convertCustomType_data() QTest::addColumn("testQLineF"); QTest::addColumn("testQChar"); QTest::addColumn("testCustom"); + QTest::addColumn("testDerived"); QTest::newRow("default") << true << QString::fromLatin1("string") << true << 15 @@ -171,14 +172,16 @@ void tst_QMetaType::convertCustomType_data() << QRectF(1.4, 1.9, 10.9, 40.2) << QPoint(12, 34) << QPointF(9.2, 2.7) << QSize(4, 9) << QSizeF(3.3, 9.8) << QLine(3, 9, 29, 4) << QLineF(38.9, 28.9, 102.3, 0.0) - << QChar('Q') << CustomConvertibleType(QString::fromLatin1("test")); + << QChar('Q') << CustomConvertibleType(QString::fromLatin1("test")) + << DerivedGadgetType(QString::fromLatin1("test")); QTest::newRow("not ok") << false << QString::fromLatin1("string") << true << 15 << double(3.14) << float(3.6) << QRect(1, 2, 3, 4) << QRectF(1.4, 1.9, 10.9, 40.2) << QPoint(12, 34) << QPointF(9.2, 2.7) << QSize(4, 9) << QSizeF(3.3, 9.8) << QLine(3, 9, 29, 4) << QLineF() - << QChar('Q') << CustomConvertibleType(42); + << QChar('Q') << CustomConvertibleType(42) + << DerivedGadgetType(42); } void tst_QMetaType::convertCustomType() @@ -276,6 +279,15 @@ void tst_QMetaType::convertCustomType() v = QVariant::fromValue(testCustom); QVERIFY(v.canConvert(::qMetaTypeId())); QCOMPARE(v.value().m_foo, testCustom.m_foo); + + QFETCH(DerivedGadgetType, testDerived); + v = QVariant::fromValue(testDerived); + QCOMPARE(v.metaType(), QMetaType::fromType()); + QCOMPARE(v.value().m_foo, testDerived.m_foo); + QVERIFY(v.canConvert(QMetaType::fromType())); + QVERIFY(v.convert(QMetaType::fromType())); + QCOMPARE(v.metaType(), QMetaType::fromType()); + QCOMPARE(v.value().m_foo, testDerived.m_foo); } void tst_QMetaType::convertConstNonConst()