Allow use of template class instances inheriting from a Q_GADGET in Qml

The Q_GADGET macro cannot be used in templates. It can however be useful
to derive a template class from a Q_GADGET enabled base class to benefit
from type safety features in C++ (e.g. the class could represent an id
or handle for some C++ type).

For proper wrapping of a QVariant with a gadget value in a QJSValue, the
QMetaType::IsGadget flag must be set for the registered template
instance type - which does not happen prior to the fix because
IsGadgetHelper requires qt_check_for_QGADGET_macro to be defined in the
registered class but not in an ancestor class - in other words: The
class must declare Q_GADGET.

To overcome this, IsGadgetHelper/IsPointerToGadgetHelper can now
differentiate between a Q_GADGET flagged class (allowing
automatic registration) and a derived class, e.g. a template class
(forcing Q_DECLARE_METATYPE to be used explicitly).

[ChangeLog][QtCore][QMetaObject] It is now possible to use template
class instances inheriting from a Q_GADGET in Qml

Task-number: QTBUG-66744
Change-Id: I7632ad45cff79fa422b3f852ca0b963f35fab155
Reviewed-by: Olivier Goffart (Woboq GmbH) <ogoffart@woboq.com>
This commit is contained in:
Nils Jeisecke 2018-03-02 16:57:11 +01:00
parent b767c7363f
commit fee8944cbe
2 changed files with 35 additions and 10 deletions

View File

@ -1381,7 +1381,7 @@ namespace QtPrivate
}; };
template<typename T, typename Enable = void> template<typename T, typename Enable = void>
struct IsGadgetHelper { enum { Value = false }; }; struct IsGadgetHelper { enum { IsRealGadget = false, IsGadgetOrDerivedFrom = false }; };
template<typename T> template<typename T>
struct IsGadgetHelper<T, typename T::QtGadgetHelper> struct IsGadgetHelper<T, typename T::QtGadgetHelper>
@ -1389,11 +1389,14 @@ namespace QtPrivate
template <typename X> template <typename X>
static char checkType(void (X::*)()); static char checkType(void (X::*)());
static void *checkType(void (T::*)()); static void *checkType(void (T::*)());
enum { Value = sizeof(checkType(&T::qt_check_for_QGADGET_macro)) == sizeof(void *) }; enum {
IsRealGadget = sizeof(checkType(&T::qt_check_for_QGADGET_macro)) == sizeof(void *),
IsGadgetOrDerivedFrom = true
};
}; };
template<typename T, typename Enable = void> template<typename T, typename Enable = void>
struct IsPointerToGadgetHelper { enum { Value = false }; }; struct IsPointerToGadgetHelper { enum { IsRealGadget = false, IsGadgetOrDerivedFrom = false }; };
template<typename T> template<typename T>
struct IsPointerToGadgetHelper<T*, typename T::QtGadgetHelper> struct IsPointerToGadgetHelper<T*, typename T::QtGadgetHelper>
@ -1402,7 +1405,10 @@ namespace QtPrivate
template <typename X> template <typename X>
static char checkType(void (X::*)()); static char checkType(void (X::*)());
static void *checkType(void (T::*)()); static void *checkType(void (T::*)());
enum { Value = sizeof(checkType(&T::qt_check_for_QGADGET_macro)) == sizeof(void *) }; enum {
IsRealGadget = sizeof(checkType(&T::qt_check_for_QGADGET_macro)) == sizeof(void *),
IsGadgetOrDerivedFrom = true
};
}; };
@ -1435,12 +1441,12 @@ namespace QtPrivate
static inline const QMetaObject *value() { return &T::staticMetaObject; } static inline const QMetaObject *value() { return &T::staticMetaObject; }
}; };
template<typename T> template<typename T>
struct MetaObjectForType<T, typename std::enable_if<IsGadgetHelper<T>::Value>::type> struct MetaObjectForType<T, typename std::enable_if<IsGadgetHelper<T>::IsGadgetOrDerivedFrom>::type>
{ {
static inline const QMetaObject *value() { return &T::staticMetaObject; } static inline const QMetaObject *value() { return &T::staticMetaObject; }
}; };
template<typename T> template<typename T>
struct MetaObjectForType<T, typename QEnableIf<IsPointerToGadgetHelper<T>::Value>::Type> struct MetaObjectForType<T, typename std::enable_if<IsPointerToGadgetHelper<T>::IsGadgetOrDerivedFrom>::type>
{ {
static inline const QMetaObject *value() { return &IsPointerToGadgetHelper<T>::BaseType::staticMetaObject; } static inline const QMetaObject *value() { return &IsPointerToGadgetHelper<T>::BaseType::staticMetaObject; }
}; };
@ -1599,8 +1605,8 @@ namespace QtPrivate
template <typename T, int = template <typename T, int =
QtPrivate::IsPointerToTypeDerivedFromQObject<T>::Value ? QMetaType::PointerToQObject : QtPrivate::IsPointerToTypeDerivedFromQObject<T>::Value ? QMetaType::PointerToQObject :
QtPrivate::IsGadgetHelper<T>::Value ? QMetaType::IsGadget : QtPrivate::IsGadgetHelper<T>::IsRealGadget ? QMetaType::IsGadget :
QtPrivate::IsPointerToGadgetHelper<T>::Value ? QMetaType::PointerToGadget : QtPrivate::IsPointerToGadgetHelper<T>::IsRealGadget ? QMetaType::PointerToGadget :
QtPrivate::IsQEnumHelper<T>::Value ? QMetaType::IsEnumeration : 0> QtPrivate::IsQEnumHelper<T>::Value ? QMetaType::IsEnumeration : 0>
struct QMetaTypeIdQObject struct QMetaTypeIdQObject
{ {
@ -1653,8 +1659,8 @@ namespace QtPrivate {
| (IsWeakPointerToTypeDerivedFromQObject<T>::Value ? QMetaType::WeakPointerToQObject : 0) | (IsWeakPointerToTypeDerivedFromQObject<T>::Value ? QMetaType::WeakPointerToQObject : 0)
| (IsTrackingPointerToTypeDerivedFromQObject<T>::Value ? QMetaType::TrackingPointerToQObject : 0) | (IsTrackingPointerToTypeDerivedFromQObject<T>::Value ? QMetaType::TrackingPointerToQObject : 0)
| (std::is_enum<T>::value ? QMetaType::IsEnumeration : 0) | (std::is_enum<T>::value ? QMetaType::IsEnumeration : 0)
| (IsGadgetHelper<T>::Value ? QMetaType::IsGadget : 0) | (IsGadgetHelper<T>::IsGadgetOrDerivedFrom ? QMetaType::IsGadget : 0)
| (IsPointerToGadgetHelper<T>::Value ? QMetaType::PointerToGadget : 0) | (IsPointerToGadgetHelper<T>::IsGadgetOrDerivedFrom ? QMetaType::PointerToGadget : 0)
}; };
}; };

View File

@ -141,6 +141,14 @@ public:
class CustomNonQObject {}; class CustomNonQObject {};
class GadgetDerived : public CustomGadget {}; class GadgetDerived : public CustomGadget {};
// cannot use Q_GADGET due to moc limitations but wants to behave like
// a Q_GADGET in Qml land
template<typename T>
class GadgetDerivedAndTyped : public CustomGadget {};
Q_DECLARE_METATYPE(GadgetDerivedAndTyped<int>)
Q_DECLARE_METATYPE(GadgetDerivedAndTyped<int>*)
void tst_QMetaType::defined() void tst_QMetaType::defined()
{ {
QCOMPARE(int(QMetaTypeId2<QString>::Defined), 1); QCOMPARE(int(QMetaTypeId2<QString>::Defined), 1);
@ -157,6 +165,10 @@ void tst_QMetaType::defined()
QVERIFY(!QMetaTypeId2<CustomNonQObject>::Defined); QVERIFY(!QMetaTypeId2<CustomNonQObject>::Defined);
QVERIFY(!QMetaTypeId2<CustomNonQObject*>::Defined); QVERIFY(!QMetaTypeId2<CustomNonQObject*>::Defined);
QVERIFY(!QMetaTypeId2<CustomGadget_NonDefaultConstructible>::Defined); QVERIFY(!QMetaTypeId2<CustomGadget_NonDefaultConstructible>::Defined);
// registered with Q_DECLARE_METATYPE
QVERIFY(QMetaTypeId2<GadgetDerivedAndTyped<int>>::Defined);
QVERIFY(QMetaTypeId2<GadgetDerivedAndTyped<int>*>::Defined);
} }
struct Bar struct Bar
@ -396,6 +408,10 @@ void tst_QMetaType::typeName_data()
QTest::newRow("CustomGadget*") << ::qMetaTypeId<CustomGadget*>() << QString::fromLatin1("CustomGadget*"); QTest::newRow("CustomGadget*") << ::qMetaTypeId<CustomGadget*>() << QString::fromLatin1("CustomGadget*");
QTest::newRow("CustomQObject::CustomQEnum") << ::qMetaTypeId<CustomQObject::CustomQEnum>() << QString::fromLatin1("CustomQObject::CustomQEnum"); QTest::newRow("CustomQObject::CustomQEnum") << ::qMetaTypeId<CustomQObject::CustomQEnum>() << QString::fromLatin1("CustomQObject::CustomQEnum");
QTest::newRow("Qt::ArrowType") << ::qMetaTypeId<Qt::ArrowType>() << QString::fromLatin1("Qt::ArrowType"); QTest::newRow("Qt::ArrowType") << ::qMetaTypeId<Qt::ArrowType>() << QString::fromLatin1("Qt::ArrowType");
// template instance class derived from Q_GADGET enabled class
QTest::newRow("GadgetDerivedAndTyped<int>") << ::qMetaTypeId<GadgetDerivedAndTyped<int>>() << QString::fromLatin1("GadgetDerivedAndTyped<int>");
QTest::newRow("GadgetDerivedAndTyped<int>*") << ::qMetaTypeId<GadgetDerivedAndTyped<int>*>() << QString::fromLatin1("GadgetDerivedAndTyped<int>*");
} }
void tst_QMetaType::typeName() void tst_QMetaType::typeName()
@ -1703,6 +1719,9 @@ void tst_QMetaType::metaObject_data()
QTest::newRow("MyGadget*") << ::qMetaTypeId<MyGadget*>() << &MyGadget::staticMetaObject << false << true << false; QTest::newRow("MyGadget*") << ::qMetaTypeId<MyGadget*>() << &MyGadget::staticMetaObject << false << true << false;
QTest::newRow("MyEnum") << ::qMetaTypeId<MyGadget::MyEnum>() << &MyGadget::staticMetaObject << false << false << false; QTest::newRow("MyEnum") << ::qMetaTypeId<MyGadget::MyEnum>() << &MyGadget::staticMetaObject << false << false << false;
QTest::newRow("Qt::ScrollBarPolicy") << ::qMetaTypeId<Qt::ScrollBarPolicy>() << &QObject::staticQtMetaObject << false << false << false; QTest::newRow("Qt::ScrollBarPolicy") << ::qMetaTypeId<Qt::ScrollBarPolicy>() << &QObject::staticQtMetaObject << false << false << false;
QTest::newRow("GadgetDerivedAndTyped<int>") << ::qMetaTypeId<GadgetDerivedAndTyped<int>>() << &GadgetDerivedAndTyped<int>::staticMetaObject << true << false << false;
QTest::newRow("GadgetDerivedAndTyped<int>*") << ::qMetaTypeId<GadgetDerivedAndTyped<int>*>() << &GadgetDerivedAndTyped<int>::staticMetaObject << false << true << false;
} }