Add automatic metatype registration for invokable methods.
authorStephen Kelly <stephen.kelly@kdab.com>
Sat, 18 Aug 2012 11:05:16 +0000 (13:05 +0200)
committerQt by Nokia <qt-info@nokia.com>
Tue, 28 Aug 2012 16:30:39 +0000 (18:30 +0200)
This works similarly to the automatic registration for Q_PROPERTY types,
but in this case it mostly affects the need for users to
call qRegisterMetaType<T>() before using queued connections
with methods using non-built-in metatypes, or before using invokeMethod
manually.

Change-Id: Ib17d0606b77b0130624b6a88b57c36d26e97d12d
Reviewed-by: Kent Hansen <kent.hansen@nokia.com>
src/corelib/kernel/qmetaobject.cpp
src/corelib/kernel/qobjectdefs.h
src/tools/moc/generator.cpp
src/tools/moc/generator.h
tests/auto/tools/moc/tst_moc.cpp

index ac30b0d..a2da42d 100644 (file)
@@ -1778,7 +1778,16 @@ int QMetaMethod::parameterType(int index) const
         return QMetaType::UnknownType;
     if (index >= QMetaMethodPrivate::get(this)->parameterCount())
         return QMetaType::UnknownType;
-    return QMetaMethodPrivate::get(this)->parameterType(index);
+
+    int type = QMetaMethodPrivate::get(this)->parameterType(index);
+    if (type != QMetaType::UnknownType)
+        return type;
+
+    void *argv[] = { &type, &index };
+    mobj->static_metacall(QMetaObject::RegisterMethodArgumentMetaType, QMetaMethodPrivate::get(this)->ownMethodIndex(), argv);
+    if (type != -1)
+        return type;
+    return QMetaType::UnknownType;
 }
 
 /*!
@@ -2160,15 +2169,21 @@ bool QMetaMethod::invoke(QObject *object,
                 args[i] = QMetaType::create(types[i], param[i]);
                 ++nargs;
             } else if (param[i]) {
-                qWarning("QMetaMethod::invoke: Unable to handle unregistered datatype '%s'",
-                         typeNames[i]);
-                for (int x = 1; x < i; ++x) {
-                    if (types[x] && args[x])
-                        QMetaType::destroy(types[x], args[x]);
+                // Try to register the type and try again before reporting an error.
+                void *argv[] = { &types[i], &i };
+                QMetaObject::metacall(object, QMetaObject::RegisterMethodArgumentMetaType,
+                                      idx_relative + idx_offset, argv);
+                if (types[i] == -1) {
+                    qWarning("QMetaMethod::invoke: Unable to handle unregistered datatype '%s'",
+                            typeNames[i]);
+                    for (int x = 1; x < i; ++x) {
+                        if (types[x] && args[x])
+                            QMetaType::destroy(types[x], args[x]);
+                    }
+                    free(types);
+                    free(args);
+                    return false;
                 }
-                free(types);
-                free(args);
-                return false;
             }
         }
 
index 0ed35a2..00eead5 100644 (file)
@@ -438,7 +438,8 @@ struct Q_CORE_EXPORT QMetaObject
         QueryPropertyUser,
         CreateInstance,
         IndexOfMethod,
-        RegisterPropertyMetaType
+        RegisterPropertyMetaType,
+        RegisterMethodArgumentMetaType
     };
 
     int static_metacall(Call, int, void **) const;
index edf2bf8..b7175f9 100644 (file)
@@ -869,6 +869,16 @@ void Generator::generateMetacall()
         fprintf(out, "        if (_id < %d)\n", methodList.size());
         fprintf(out, "            qt_static_metacall(this, _c, _id, _a);\n");
         fprintf(out, "        _id -= %d;\n    }", methodList.size());
+
+        fprintf(out, " else if (_c == QMetaObject::RegisterMethodArgumentMetaType) {\n");
+        fprintf(out, "        if (_id < %d)\n", methodList.size());
+
+        if (methodsWithAutomaticTypesHelper(methodList).isEmpty())
+            fprintf(out, "            *reinterpret_cast<int*>(_a[0]) = -1;\n");
+        else
+            fprintf(out, "            qt_static_metacall(this, _c, _id, _a);\n");
+        fprintf(out, "        _id -= %d;\n    }", methodList.size());
+
     }
 
     if (cdef->propertyList.size()) {
@@ -1106,6 +1116,20 @@ QMultiMap<QByteArray, int> Generator::automaticPropertyMetaTypesHelper()
     return automaticPropertyMetaTypes;
 }
 
+QMap<int, QMultiMap<QByteArray, int> > Generator::methodsWithAutomaticTypesHelper(const QList<FunctionDef> &methodList)
+{
+    QMap<int, QMultiMap<QByteArray, int> > methodsWithAutomaticTypes;
+    for (int i = 0; i < methodList.size(); ++i) {
+        const FunctionDef &f = methodList.at(i);
+        for (int j = 0; j < f.arguments.count(); ++j) {
+            const QByteArray argType = f.arguments.at(j).normalizedType;
+            if (registerableMetaType(argType) && !isBuiltinType(argType))
+                methodsWithAutomaticTypes[i].insert(argType, j);
+        }
+    }
+    return methodsWithAutomaticTypes;
+}
+
 void Generator::generateStaticMetacall()
 {
     fprintf(out, "void %s::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)\n{\n",
@@ -1197,6 +1221,32 @@ void Generator::generateStaticMetacall()
         fprintf(out, "        }\n");
         fprintf(out, "    }");
         needElse = true;
+
+        QMap<int, QMultiMap<QByteArray, int> > methodsWithAutomaticTypes = methodsWithAutomaticTypesHelper(methodList);
+
+        if (!methodsWithAutomaticTypes.isEmpty()) {
+            fprintf(out, " else if (_c == QMetaObject::RegisterMethodArgumentMetaType) {\n");
+            fprintf(out, "        switch (_id) {\n");
+            fprintf(out, "        default: *reinterpret_cast<int*>(_a[0]) = -1; break;\n");
+            QMap<int, QMultiMap<QByteArray, int> >::const_iterator it = methodsWithAutomaticTypes.constBegin();
+            const QMap<int, QMultiMap<QByteArray, int> >::const_iterator end = methodsWithAutomaticTypes.constEnd();
+            for ( ; it != end; ++it) {
+                fprintf(out, "        case %d:\n", it.key());
+                fprintf(out, "            switch (*reinterpret_cast<int*>(_a[1])) {\n");
+                fprintf(out, "            default: *reinterpret_cast<int*>(_a[0]) = -1; break;\n");
+                foreach (const QByteArray &key, it->uniqueKeys()) {
+                    foreach (int argumentID, it->values(key))
+                        fprintf(out, "            case %d:\n", argumentID);
+                    fprintf(out, "                *reinterpret_cast<int*>(_a[0]) = qRegisterMetaType< %s >(); break;\n", key.constData());
+                }
+                fprintf(out, "            }\n");
+                fprintf(out, "            break;\n");
+            }
+            fprintf(out, "        }\n");
+            fprintf(out, "    }");
+            isUsed_a = true;
+        }
+
     }
     if (!cdef->signalList.isEmpty()) {
         Q_ASSERT(needElse); // if there is signal, there was method.
@@ -1265,7 +1315,7 @@ void Generator::generateStaticMetacall()
 
     if (methodList.isEmpty()) {
         fprintf(out, "    Q_UNUSED(_o);\n");
-        if (cdef->constructorList.isEmpty() && automaticPropertyMetaTypes.isEmpty()) {
+        if (cdef->constructorList.isEmpty() && automaticPropertyMetaTypes.isEmpty() && methodsWithAutomaticTypesHelper(methodList).isEmpty()) {
             fprintf(out, "    Q_UNUSED(_id);\n");
             fprintf(out, "    Q_UNUSED(_c);\n");
         }
index 873681a..4fa4d34 100644 (file)
@@ -72,6 +72,7 @@ private:
     void generateSignal(FunctionDef *def, int index);
     void generatePluginMetaData();
     QMultiMap<QByteArray, int> automaticPropertyMetaTypesHelper();
+    QMap<int, QMultiMap<QByteArray, int> > methodsWithAutomaticTypesHelper(const QList<FunctionDef> &methodList);
 
     void strreg(const QByteArray &); // registers a string
     int stridx(const QByteArray &); // returns a string's id
index c6178c7..795a830 100644 (file)
@@ -550,6 +550,7 @@ private slots:
     void explicitOverrideControl_data();
     void explicitOverrideControl();
     void autoPropertyMetaTypeRegistration();
+    void autoMethodArgumentMetaTypeRegistration();
 
 signals:
     void sigWithUnsignedArg(unsigned foo);
@@ -2332,6 +2333,68 @@ struct NamespacedNonQObject {};
 }
 Q_DECLARE_METATYPE(SomeNamespace::NamespacedNonQObject)
 
+// Need different types for the invokable method tests because otherwise the registration
+// done in the property test would interfere.
+
+class CustomQObject2 : public QObject
+{
+    Q_OBJECT
+    Q_ENUMS(Number)
+public:
+    enum Number {
+      Zero,
+      One,
+      Two
+    };
+    explicit CustomQObject2(QObject *parent = 0)
+      : QObject(parent)
+    {
+    }
+};
+
+Q_DECLARE_METATYPE(CustomQObject2::Number)
+
+typedef CustomQObject2* CustomQObject2Star;
+Q_DECLARE_METATYPE(CustomQObject2Star);
+
+namespace SomeNamespace2 {
+
+class NamespacedQObject2 : public QObject
+{
+    Q_OBJECT
+public:
+    explicit NamespacedQObject2(QObject *parent = 0)
+      : QObject(parent)
+    {
+
+    }
+};
+
+struct NamespacedNonQObject2 {};
+}
+Q_DECLARE_METATYPE(SomeNamespace2::NamespacedNonQObject2)
+
+
+struct CustomObject3 {};
+struct CustomObject4 {};
+struct CustomObject5 {};
+struct CustomObject6 {};
+struct CustomObject7 {};
+struct CustomObject8 {};
+struct CustomObject9 {};
+struct CustomObject10 {};
+struct CustomObject11 {};
+
+Q_DECLARE_METATYPE(CustomObject3)
+Q_DECLARE_METATYPE(CustomObject4)
+Q_DECLARE_METATYPE(CustomObject5)
+Q_DECLARE_METATYPE(CustomObject6)
+Q_DECLARE_METATYPE(CustomObject7)
+Q_DECLARE_METATYPE(CustomObject8)
+Q_DECLARE_METATYPE(CustomObject9)
+Q_DECLARE_METATYPE(CustomObject10)
+Q_DECLARE_METATYPE(CustomObject11)
+
 class AutoRegistrationObject : public QObject
 {
     Q_OBJECT
@@ -2424,6 +2487,29 @@ public:
     {
         return SomeNamespace::NamespacedNonQObject();
     }
+
+public slots:
+    void objectSlot(QObject*) {}
+    void customObjectSlot(CustomQObject2*) {}
+    void sharedPointerSlot(QSharedPointer<CustomQObject2>) {}
+    void weakPointerSlot(QWeakPointer<CustomQObject2>) {}
+    void trackingPointerSlot(QPointer<CustomQObject2>) {}
+    void listIntSlot(QList<int>) {}
+    void vectorVariantSlot(QVector<QVariant>) {}
+    void listCustomObjectSlot(QList<CustomQObject2*>) {}
+    void vectorListIntSlot(QVector<QList<int> >) {}
+    void vectorListCustomObjectSlot(QVector<QList<CustomQObject2*> >) {}
+    void enumSlot(CustomQObject2::Number) {}
+    void typedefSlot(CustomQObject2Star) {}
+    void namespacedQObjectSlot(SomeNamespace2::NamespacedQObject2*) {}
+    void namespacedNonQObjectSlot(SomeNamespace2::NamespacedNonQObject2) {}
+
+    void bu1(int, CustomObject3) {}
+    void bu2(CustomObject4, int) {}
+    void bu3(CustomObject5, CustomObject6) {}
+    void bu4(CustomObject7, int, CustomObject8) {}
+    void bu5(int, CustomObject9, CustomObject10) {}
+    void bu6(int, CustomObject11, int) {}
 };
 
 void tst_Moc::autoPropertyMetaTypeRegistration()
@@ -2465,6 +2551,154 @@ void tst_Moc::autoPropertyMetaTypeRegistration()
     QCOMPARE(propertyMetaTypeIds, expectedMetaTypeIds);
 }
 
+template<typename T>
+struct DefaultConstructor
+{
+  static inline T construct() { return T(); }
+};
+
+template<typename T>
+struct DefaultConstructor<T*>
+{
+  static inline T* construct() { return 0; }
+};
+
+void tst_Moc::autoMethodArgumentMetaTypeRegistration()
+{
+    AutoRegistrationObject aro;
+
+    QVector<int> methodArgMetaTypeIds;
+
+    const QMetaObject *metaObject = aro.metaObject();
+
+    int i = metaObject->methodOffset(); // Start after QObject built-in slots;
+
+#define TYPE_LOOP(TYPE) \
+    { \
+        const QMetaMethod method = metaObject->method(i); \
+        for (int j = 0; j < method.parameterCount(); ++j) \
+            methodArgMetaTypeIds.append(method.parameterType(j)); \
+        QVERIFY(method.invoke(&aro, Q_ARG(TYPE, DefaultConstructor<TYPE>::construct()))); \
+        ++i; \
+    }
+
+#define FOR_EACH_SLOT_ARG_TYPE(F) \
+    F(QObject*) \
+    F(CustomQObject2*) \
+    F(QSharedPointer<CustomQObject2>) \
+    F(QWeakPointer<CustomQObject2>) \
+    F(QPointer<CustomQObject2>) \
+    F(QList<int>) \
+    F(QVector<QVariant>) \
+    F(QList<CustomQObject2*>) \
+    F(QVector<QList<int> >) \
+    F(QVector<QList<CustomQObject2*> >) \
+    F(CustomQObject2::Number) \
+    F(CustomQObject2Star) \
+    F(SomeNamespace2::NamespacedQObject2*) \
+    F(SomeNamespace2::NamespacedNonQObject2)
+
+    // Note: mulit-arg slots are tested below.
+
+    FOR_EACH_SLOT_ARG_TYPE(TYPE_LOOP)
+
+#undef TYPE_LOOP
+#undef FOR_EACH_SLOT_ARG_TYPE
+
+    QVector<int> expectedMetaTypeIds = QVector<int>()
+        << QMetaType::QObjectStar
+        << qMetaTypeId<CustomQObject2*>()
+        << qMetaTypeId<QSharedPointer<CustomQObject2> >()
+        << qMetaTypeId<QWeakPointer<CustomQObject2> >()
+        << qMetaTypeId<QPointer<CustomQObject2> >()
+        << qMetaTypeId<QList<int> >()
+        << qMetaTypeId<QVector<QVariant> >()
+        << qMetaTypeId<QList<CustomQObject2*> >()
+        << qMetaTypeId<QVector<QList<int> > >()
+        << qMetaTypeId<QVector<QList<CustomQObject2*> > >()
+        << qMetaTypeId<CustomQObject2::Number>()
+        << qMetaTypeId<CustomQObject2Star>()
+        << qMetaTypeId<SomeNamespace2::NamespacedQObject2*>()
+        << qMetaTypeId<SomeNamespace2::NamespacedNonQObject2>()
+        ;
+
+    QCOMPARE(methodArgMetaTypeIds, expectedMetaTypeIds);
+
+
+    QVector<int> methodMultiArgMetaTypeIds;
+
+    {
+        const QMetaMethod method = metaObject->method(i);
+        QCOMPARE(method.name(), QByteArray("bu1"));
+        for (int j = 0; j < method.parameterCount(); ++j)
+            methodMultiArgMetaTypeIds.append(method.parameterType(j));
+        QVERIFY(method.invoke(&aro, Q_ARG(int, 42), Q_ARG(CustomObject3, CustomObject3())));
+        ++i;
+    }
+    {
+        const QMetaMethod method = metaObject->method(i);
+        QCOMPARE(method.name(), QByteArray("bu2"));
+        for (int j = 0; j < method.parameterCount(); ++j)
+            methodMultiArgMetaTypeIds.append(method.parameterType(j));
+        QVERIFY(method.invoke(&aro, Q_ARG(CustomObject4, CustomObject4()), Q_ARG(int, 42)));
+        ++i;
+    }
+    {
+        const QMetaMethod method = metaObject->method(i);
+        QCOMPARE(method.name(), QByteArray("bu3"));
+        for (int j = 0; j < method.parameterCount(); ++j)
+            methodMultiArgMetaTypeIds.append(method.parameterType(j));
+        QVERIFY(method.invoke(&aro, Q_ARG(CustomObject5, CustomObject5()), Q_ARG(CustomObject6, CustomObject6())));
+        ++i;
+    }
+    {
+        const QMetaMethod method = metaObject->method(i);
+        QCOMPARE(method.name(), QByteArray("bu4"));
+        for (int j = 0; j < method.parameterCount(); ++j)
+            methodMultiArgMetaTypeIds.append(method.parameterType(j));
+        QVERIFY(method.invoke(&aro, Q_ARG(CustomObject7, CustomObject7()), Q_ARG(int, 42), Q_ARG(CustomObject8, CustomObject8())));
+        ++i;
+    }
+    {
+        const QMetaMethod method = metaObject->method(i);
+        QCOMPARE(method.name(), QByteArray("bu5"));
+        for (int j = 0; j < method.parameterCount(); ++j)
+            methodMultiArgMetaTypeIds.append(method.parameterType(j));
+        QVERIFY(method.invoke(&aro, Q_ARG(int, 42), Q_ARG(CustomObject9, CustomObject9()), Q_ARG(CustomObject10, CustomObject10())));
+        ++i;
+    }
+    {
+        const QMetaMethod method = metaObject->method(i);
+        QCOMPARE(method.name(), QByteArray("bu6"));
+        for (int j = 0; j < method.parameterCount(); ++j)
+            methodMultiArgMetaTypeIds.append(method.parameterType(j));
+        QVERIFY(method.invoke(&aro, Q_ARG(int, 42), Q_ARG(CustomObject11, CustomObject11()), Q_ARG(int, 42)));
+        ++i;
+    }
+
+    QVector<int> expectedMultiMetaTypeIds = QVector<int>()
+        << QMetaType::Int
+        << qMetaTypeId<CustomObject3>()
+        << qMetaTypeId<CustomObject4>()
+        << QMetaType::Int
+        << qMetaTypeId<CustomObject5>()
+        << qMetaTypeId<CustomObject6>()
+        << qMetaTypeId<CustomObject7>()
+        << QMetaType::Int
+        << qMetaTypeId<CustomObject8>()
+        << QMetaType::Int
+        << qMetaTypeId<CustomObject9>()
+        << qMetaTypeId<CustomObject10>()
+        << QMetaType::Int
+        << qMetaTypeId<CustomObject11>()
+        << QMetaType::Int
+        ;
+
+    QCOMPARE(methodMultiArgMetaTypeIds, expectedMultiMetaTypeIds);
+
+
+}
+
 QTEST_MAIN(tst_Moc)
 
 #include "tst_moc.moc"