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:
parent
62c2061a50
commit
a912b14c75
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -438,7 +438,8 @@ struct Q_CORE_EXPORT QMetaObject
|
||||
QueryPropertyUser,
|
||||
CreateInstance,
|
||||
IndexOfMethod,
|
||||
RegisterPropertyMetaType
|
||||
RegisterPropertyMetaType,
|
||||
RegisterMethodArgumentMetaType
|
||||
};
|
||||
|
||||
int static_metacall(Call, int, void **) const;
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
Loading…
x
Reference in New Issue
Block a user