From a912b14c755137ed5529854027b9ab3f7e06c5e9 Mon Sep 17 00:00:00 2001 From: Stephen Kelly Date: Sat, 18 Aug 2012 13:05:16 +0200 Subject: [PATCH] Add automatic metatype registration for invokable methods. 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() before using queued connections with methods using non-built-in metatypes, or before using invokeMethod manually. Change-Id: Ib17d0606b77b0130624b6a88b57c36d26e97d12d Reviewed-by: Kent Hansen --- src/corelib/kernel/qmetaobject.cpp | 33 ++++-- src/corelib/kernel/qobjectdefs.h | 3 +- src/tools/moc/generator.cpp | 52 ++++++++- src/tools/moc/generator.h | 1 + tests/auto/tools/moc/tst_moc.cpp | 234 +++++++++++++++++++++++++++++++++++++ 5 files changed, 312 insertions(+), 11 deletions(-) diff --git a/src/corelib/kernel/qmetaobject.cpp b/src/corelib/kernel/qmetaobject.cpp index ac30b0d..a2da42d 100644 --- a/src/corelib/kernel/qmetaobject.cpp +++ b/src/corelib/kernel/qmetaobject.cpp @@ -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; } } diff --git a/src/corelib/kernel/qobjectdefs.h b/src/corelib/kernel/qobjectdefs.h index 0ed35a2..00eead5 100644 --- a/src/corelib/kernel/qobjectdefs.h +++ b/src/corelib/kernel/qobjectdefs.h @@ -438,7 +438,8 @@ struct Q_CORE_EXPORT QMetaObject QueryPropertyUser, CreateInstance, IndexOfMethod, - RegisterPropertyMetaType + RegisterPropertyMetaType, + RegisterMethodArgumentMetaType }; int static_metacall(Call, int, void **) const; diff --git a/src/tools/moc/generator.cpp b/src/tools/moc/generator.cpp index edf2bf8..b7175f9 100644 --- a/src/tools/moc/generator.cpp +++ b/src/tools/moc/generator.cpp @@ -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(_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 Generator::automaticPropertyMetaTypesHelper() return automaticPropertyMetaTypes; } +QMap > Generator::methodsWithAutomaticTypesHelper(const QList &methodList) +{ + QMap > 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 > methodsWithAutomaticTypes = methodsWithAutomaticTypesHelper(methodList); + + if (!methodsWithAutomaticTypes.isEmpty()) { + fprintf(out, " else if (_c == QMetaObject::RegisterMethodArgumentMetaType) {\n"); + fprintf(out, " switch (_id) {\n"); + fprintf(out, " default: *reinterpret_cast(_a[0]) = -1; break;\n"); + QMap >::const_iterator it = methodsWithAutomaticTypes.constBegin(); + const QMap >::const_iterator end = methodsWithAutomaticTypes.constEnd(); + for ( ; it != end; ++it) { + fprintf(out, " case %d:\n", it.key()); + fprintf(out, " switch (*reinterpret_cast(_a[1])) {\n"); + fprintf(out, " default: *reinterpret_cast(_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(_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"); } diff --git a/src/tools/moc/generator.h b/src/tools/moc/generator.h index 873681a..4fa4d34 100644 --- a/src/tools/moc/generator.h +++ b/src/tools/moc/generator.h @@ -72,6 +72,7 @@ private: void generateSignal(FunctionDef *def, int index); void generatePluginMetaData(); QMultiMap automaticPropertyMetaTypesHelper(); + QMap > methodsWithAutomaticTypesHelper(const QList &methodList); void strreg(const QByteArray &); // registers a string int stridx(const QByteArray &); // returns a string's id diff --git a/tests/auto/tools/moc/tst_moc.cpp b/tests/auto/tools/moc/tst_moc.cpp index c6178c7..795a830 100644 --- a/tests/auto/tools/moc/tst_moc.cpp +++ b/tests/auto/tools/moc/tst_moc.cpp @@ -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) {} + void weakPointerSlot(QWeakPointer) {} + void trackingPointerSlot(QPointer) {} + void listIntSlot(QList) {} + void vectorVariantSlot(QVector) {} + void listCustomObjectSlot(QList) {} + void vectorListIntSlot(QVector >) {} + void vectorListCustomObjectSlot(QVector >) {} + 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 +struct DefaultConstructor +{ + static inline T construct() { return T(); } +}; + +template +struct DefaultConstructor +{ + static inline T* construct() { return 0; } +}; + +void tst_Moc::autoMethodArgumentMetaTypeRegistration() +{ + AutoRegistrationObject aro; + + QVector 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::construct()))); \ + ++i; \ + } + +#define FOR_EACH_SLOT_ARG_TYPE(F) \ + F(QObject*) \ + F(CustomQObject2*) \ + F(QSharedPointer) \ + F(QWeakPointer) \ + F(QPointer) \ + F(QList) \ + F(QVector) \ + F(QList) \ + F(QVector >) \ + F(QVector >) \ + 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 expectedMetaTypeIds = QVector() + << QMetaType::QObjectStar + << qMetaTypeId() + << qMetaTypeId >() + << qMetaTypeId >() + << qMetaTypeId >() + << qMetaTypeId >() + << qMetaTypeId >() + << qMetaTypeId >() + << qMetaTypeId > >() + << qMetaTypeId > >() + << qMetaTypeId() + << qMetaTypeId() + << qMetaTypeId() + << qMetaTypeId() + ; + + QCOMPARE(methodArgMetaTypeIds, expectedMetaTypeIds); + + + QVector 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 expectedMultiMetaTypeIds = QVector() + << QMetaType::Int + << qMetaTypeId() + << qMetaTypeId() + << QMetaType::Int + << qMetaTypeId() + << qMetaTypeId() + << qMetaTypeId() + << QMetaType::Int + << qMetaTypeId() + << QMetaType::Int + << qMetaTypeId() + << qMetaTypeId() + << QMetaType::Int + << qMetaTypeId() + << QMetaType::Int + ; + + QCOMPARE(methodMultiArgMetaTypeIds, expectedMultiMetaTypeIds); + + +} + QTEST_MAIN(tst_Moc) #include "tst_moc.moc" -- 2.7.4