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 <lars.knoll@qt.io>
This commit is contained in:
Ulf Hermann 2022-01-12 17:26:11 +01:00
parent 873a8edff8
commit 657a18c7fa
3 changed files with 41 additions and 7 deletions

View File

@ -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<void **>(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<QAssociativeIterable>())
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<QAssociativeIterable>())
return viewAsAssociativeIterable(fromType, from, to);
return convertQObject(fromType, from, toType, to);
return convertMetaObject(fromType, from, toType, to);
#else
return false;
#endif

View File

@ -217,6 +217,22 @@ struct CustomEqualsOnlyType
static_assert(QTypeTraits::has_operator_equal_v<CustomEqualsOnlyType>);
static_assert(!QTypeTraits::has_operator_less_than_v<CustomEqualsOnlyType>);
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);

View File

@ -164,6 +164,7 @@ void tst_QMetaType::convertCustomType_data()
QTest::addColumn<QLineF>("testQLineF");
QTest::addColumn<QChar>("testQChar");
QTest::addColumn<CustomConvertibleType>("testCustom");
QTest::addColumn<DerivedGadgetType>("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<CustomConvertibleType2>()));
QCOMPARE(v.value<CustomConvertibleType2>().m_foo, testCustom.m_foo);
QFETCH(DerivedGadgetType, testDerived);
v = QVariant::fromValue(testDerived);
QCOMPARE(v.metaType(), QMetaType::fromType<DerivedGadgetType>());
QCOMPARE(v.value<DerivedGadgetType>().m_foo, testDerived.m_foo);
QVERIFY(v.canConvert(QMetaType::fromType<BaseGadgetType>()));
QVERIFY(v.convert(QMetaType::fromType<BaseGadgetType>()));
QCOMPARE(v.metaType(), QMetaType::fromType<BaseGadgetType>());
QCOMPARE(v.value<BaseGadgetType>().m_foo, testDerived.m_foo);
}
void tst_QMetaType::convertConstNonConst()