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>
This commit is contained in:
Stephen Kelly 2012-08-18 13:05:16 +02:00 committed by Qt by Nokia
parent 62c2061a50
commit a912b14c75
5 changed files with 312 additions and 11 deletions

View 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;
}
}

View File

@ -438,7 +438,8 @@ struct Q_CORE_EXPORT QMetaObject
QueryPropertyUser,
CreateInstance,
IndexOfMethod,
RegisterPropertyMetaType
RegisterPropertyMetaType,
RegisterMethodArgumentMetaType
};
int static_metacall(Call, int, void **) const;

View 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");
}

View 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

View 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"