summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStephen Kelly <stephen.kelly@kdab.com>2012-08-18 13:05:16 +0200
committerQt by Nokia <qt-info@nokia.com>2012-08-28 18:30:39 +0200
commita912b14c755137ed5529854027b9ab3f7e06c5e9 (patch)
tree8593866f1a795d6a3168473e75a56bcb74c920cb
parent62c2061a501563e7f2929c5883f01955af0f2fc1 (diff)
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<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>
-rw-r--r--src/corelib/kernel/qmetaobject.cpp33
-rw-r--r--src/corelib/kernel/qobjectdefs.h3
-rw-r--r--src/tools/moc/generator.cpp52
-rw-r--r--src/tools/moc/generator.h1
-rw-r--r--tests/auto/tools/moc/tst_moc.cpp234
5 files changed, 312 insertions, 11 deletions
diff --git a/src/corelib/kernel/qmetaobject.cpp b/src/corelib/kernel/qmetaobject.cpp
index ac30b0dde1..a2da42d73e 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 0ed35a2faf..00eead5d2f 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 edf2bf8889..b7175f9bc9 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<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");
}
diff --git a/src/tools/moc/generator.h b/src/tools/moc/generator.h
index 873681ab88..4fa4d3440c 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<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
diff --git a/tests/auto/tools/moc/tst_moc.cpp b/tests/auto/tools/moc/tst_moc.cpp
index c6178c7c2e..795a8307f3 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<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"