Add template specialization of QMetaType for QObject derived pointers.
authorStephen Kelly <stephen.kelly@kdab.com>
Fri, 2 Mar 2012 13:48:09 +0000 (14:48 +0100)
committerQt by Nokia <qt-info@nokia.com>
Tue, 6 Mar 2012 12:37:17 +0000 (13:37 +0100)
This makes it possible to do things like

QVariant::fromValue(new SomeObject);

without first using Q_DECLARE_METATYPE(Something*)

This functionality was originally part of
http://codereview.qt-project.org/#change,11710 but was rejected
because the functionality was based on specialization of
QVariant::fromValue which could be dangerous.

This new implementation doesn't have such danger.

Change-Id: I83fe941b6984be54469bc6b9191f6eacaceaa036
Reviewed-by: Jędrzej Nowacki <jedrzej.nowacki@nokia.com>
Reviewed-by: Olivier Goffart <ogoffart@woboq.com>
src/corelib/kernel/qmetatype.h
tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp
tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp

index f969875..06ada13 100644 (file)
@@ -364,33 +364,11 @@ void qMetaTypeLoadHelper(QDataStream &stream, void *t)
 template <> inline void qMetaTypeLoadHelper<void>(QDataStream &, void *) {}
 #endif // QT_NO_DATASTREAM
 
-template <typename T>
-struct QMetaTypeId
-{
-    enum { Defined = 0 };
-};
-
-template <typename T>
-struct QMetaTypeId2
-{
-    enum { Defined = QMetaTypeId<T>::Defined };
-    static inline int qt_metatype_id() { return QMetaTypeId<T>::qt_metatype_id(); }
-};
-
 class QObject;
 class QWidget;
 
-namespace QtPrivate {
-    template <typename T, bool Defined = QMetaTypeId2<T>::Defined>
-    struct QMetaTypeIdHelper {
-        static inline int qt_metatype_id()
-        { return QMetaTypeId2<T>::qt_metatype_id(); }
-    };
-    template <typename T> struct QMetaTypeIdHelper<T, false> {
-        static inline int qt_metatype_id()
-        { return -1; }
-    };
-
+namespace QtPrivate
+{
     template<typename T>
     struct IsPointerToTypeDerivedFromQObject
     {
@@ -427,6 +405,38 @@ namespace QtPrivate {
         Q_STATIC_ASSERT_X(sizeof(T), "Type argument of Q_DECLARE_METATYPE(T*) must be fully defined");
         enum { Value = sizeof(checkType(static_cast<T*>(0))) == sizeof(yes_type) };
     };
+}
+
+template <typename T, bool = QtPrivate::IsPointerToTypeDerivedFromQObject<T>::Value>
+struct QMetaTypeIdQObject
+{
+    enum {
+        Defined = 0
+    };
+};
+
+template <typename T>
+struct QMetaTypeId : public QMetaTypeIdQObject<T>
+{
+};
+
+template <typename T>
+struct QMetaTypeId2
+{
+    enum { Defined = QMetaTypeId<T>::Defined };
+    static inline int qt_metatype_id() { return QMetaTypeId<T>::qt_metatype_id(); }
+};
+
+namespace QtPrivate {
+    template <typename T, bool Defined = QMetaTypeId2<T>::Defined>
+    struct QMetaTypeIdHelper {
+        static inline int qt_metatype_id()
+        { return QMetaTypeId2<T>::qt_metatype_id(); }
+    };
+    template <typename T> struct QMetaTypeIdHelper<T, false> {
+        static inline int qt_metatype_id()
+        { return -1; }
+    };
 
     // Function pointers don't derive from QObject
     template <class Result> struct IsPointerToTypeDerivedFromQObject<Result(*)()> { enum { Value = false }; };
@@ -501,6 +511,23 @@ inline int qRegisterMetaType(
 #endif
 }
 
+template <typename T>
+struct QMetaTypeIdQObject<T*, /* isPointerToTypeDerivedFromQObject */ true>
+{
+    enum {
+        Defined = 1
+    };
+
+    static int qt_metatype_id()
+    {
+        static QBasicAtomicInt metatype_id = Q_BASIC_ATOMIC_INITIALIZER(0);
+        if (!metatype_id.load())
+            metatype_id.storeRelease(qRegisterMetaType<T*>(QByteArray(T::staticMetaObject.className() + QByteArrayLiteral("*")).constData(),
+                        reinterpret_cast<T**>(quintptr(-1))));
+        return metatype_id.loadAcquire();
+    }
+};
+
 #ifndef QT_NO_DATASTREAM
 template <typename T>
 inline int qRegisterMetaTypeStreamOperators()
index f8403f1..572ecc9 100644 (file)
@@ -100,12 +100,29 @@ private slots:
 
 struct Foo { int i; };
 
+
+class CustomQObject : public QObject
+{
+    Q_OBJECT
+public:
+    CustomQObject(QObject *parent = 0)
+      : QObject(parent)
+    {
+    }
+};
+
+class CustomNonQObject {};
+
 void tst_QMetaType::defined()
 {
     QCOMPARE(int(QMetaTypeId2<QString>::Defined), 1);
     QCOMPARE(int(QMetaTypeId2<Foo>::Defined), 0);
     QCOMPARE(int(QMetaTypeId2<void*>::Defined), 1);
     QCOMPARE(int(QMetaTypeId2<int*>::Defined), 0);
+    QVERIFY(QMetaTypeId2<CustomQObject*>::Defined);
+    QVERIFY(!QMetaTypeId2<CustomQObject>::Defined);
+    QVERIFY(!QMetaTypeId2<CustomNonQObject>::Defined);
+    QVERIFY(!QMetaTypeId2<CustomNonQObject*>::Defined);
 }
 
 struct Bar
index ccdab17..5aa1389 100644 (file)
@@ -2541,9 +2541,24 @@ public:
 };
 Q_DECLARE_METATYPE(CustomQObjectDerived*)
 
+class CustomQObjectDerivedNoMetaType : public CustomQObject {
+    Q_OBJECT
+public:
+    CustomQObjectDerivedNoMetaType(QObject *parent = 0) : CustomQObject(parent) {}
+};
+
 void tst_QVariant::qvariant_cast_QObject_derived()
 {
     {
+        CustomQObjectDerivedNoMetaType *object = new CustomQObjectDerivedNoMetaType(this);
+        QVariant data = QVariant::fromValue(object);
+        QVERIFY(data.userType() == qMetaTypeId<CustomQObjectDerivedNoMetaType*>());
+        QCOMPARE(data.value<QObject *>(), object);
+        QCOMPARE(data.value<CustomQObjectDerivedNoMetaType *>(), object);
+        QCOMPARE(data.value<CustomQObject *>(), object);
+        QVERIFY(data.value<CustomQWidget*>() == 0);
+    }
+    {
         CustomQObjectDerived *object = new CustomQObjectDerived(this);
         QVariant data = QVariant::fromValue(object);