From 8c77a7f77a8111bcbe51532f788679777910ebd5 Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Sat, 16 Aug 2014 11:38:30 +0200 Subject: Make Q_INVOKABLE work on Q_GADGET Methods can be invoked with QMetaMethod::invokeOnGadget Change-Id: Id734868bb530b02587daf0f62bce01798ade2ac2 Reviewed-by: Lars Knoll Reviewed-by: Simon Hausmann --- src/corelib/kernel/qmetaobject.cpp | 106 +++++++++++++++++++++ src/corelib/kernel/qmetaobject.h | 28 ++++++ .../corelib/kernel/qmetamethod/tst_qmetamethod.cpp | 49 ++++++++++ 3 files changed, 183 insertions(+) diff --git a/src/corelib/kernel/qmetaobject.cpp b/src/corelib/kernel/qmetaobject.cpp index 7f37a1ce9a..1b7ced8dc8 100644 --- a/src/corelib/kernel/qmetaobject.cpp +++ b/src/corelib/kernel/qmetaobject.cpp @@ -2298,6 +2298,112 @@ bool QMetaMethod::invoke(QObject *object, connection type Qt::AutoConnection and ignores return values. */ +/*! + \since 5.5 + + Invokes this method on a Q_GADGET. Returns \c true if the member could be invoked. + Returns \c false if there is no such member or the parameters did not match. + + The pointer \a gadget must point to an instance of the gadget class. + + The invocation is always synchronous. + + The return value of this method call is placed in \a + returnValue. You can pass up to ten arguments (\a val0, \a val1, + \a val2, \a val3, \a val4, \a val5, \a val6, \a val7, \a val8, + and \a val9) to this method call. + + \warning this method will not test the validity of the arguments: \a gadget + must be an instance of the class of the QMetaObject of which this QMetaMethod + has been constructed with. The arguments must have the same type as the ones + expected by the method, else, the behavior is undefined. + + \sa Q_ARG(), Q_RETURN_ARG(), qRegisterMetaType(), QMetaObject::invokeMethod() +*/ +bool QMetaMethod::invokeOnGadget(void* gadget, QGenericReturnArgument returnValue, QGenericArgument val0, QGenericArgument val1, QGenericArgument val2, QGenericArgument val3, QGenericArgument val4, QGenericArgument val5, QGenericArgument val6, QGenericArgument val7, QGenericArgument val8, QGenericArgument val9) const +{ + if (!gadget || !mobj) + return false; + + // check return type + if (returnValue.data()) { + const char *retType = typeName(); + if (qstrcmp(returnValue.name(), retType) != 0) { + // normalize the return value as well + QByteArray normalized = QMetaObject::normalizedType(returnValue.name()); + if (qstrcmp(normalized.constData(), retType) != 0) { + // String comparison failed, try compare the metatype. + int t = returnType(); + if (t == QMetaType::UnknownType || t != QMetaType::type(normalized)) + return false; + } + } + } + + // check argument count (we don't allow invoking a method if given too few arguments) + const char *typeNames[] = { + returnValue.name(), + val0.name(), + val1.name(), + val2.name(), + val3.name(), + val4.name(), + val5.name(), + val6.name(), + val7.name(), + val8.name(), + val9.name() + }; + int paramCount; + for (paramCount = 1; paramCount < MaximumParamCount; ++paramCount) { + if (qstrlen(typeNames[paramCount]) <= 0) + break; + } + if (paramCount <= QMetaMethodPrivate::get(this)->parameterCount()) + return false; + + // invoke! + void *param[] = { + returnValue.data(), + val0.data(), + val1.data(), + val2.data(), + val3.data(), + val4.data(), + val5.data(), + val6.data(), + val7.data(), + val8.data(), + val9.data() + }; + int idx_relative = QMetaMethodPrivate::get(this)->ownMethodIndex(); + Q_ASSERT(QMetaObjectPrivate::get(mobj)->revision >= 6); + QObjectPrivate::StaticMetaCallFunction callFunction = mobj->d.static_metacall; + if (!callFunction) + return false; + callFunction(reinterpret_cast(gadget), QMetaObject::InvokeMetaMethod, idx_relative, param); + return true; +} + +/*! + \fn bool QMetaMethod::invokeOnGadget(void *gadget, + QGenericArgument val0 = QGenericArgument(0), + QGenericArgument val1 = QGenericArgument(), + QGenericArgument val2 = QGenericArgument(), + QGenericArgument val3 = QGenericArgument(), + QGenericArgument val4 = QGenericArgument(), + QGenericArgument val5 = QGenericArgument(), + QGenericArgument val6 = QGenericArgument(), + QGenericArgument val7 = QGenericArgument(), + QGenericArgument val8 = QGenericArgument(), + QGenericArgument val9 = QGenericArgument()) const + + \overload + \since 5.5 + + This overload invokes this method for a \a gadget and ignores return values. +*/ + /*! \class QMetaEnum \inmodule QtCore diff --git a/src/corelib/kernel/qmetaobject.h b/src/corelib/kernel/qmetaobject.h index dca920d7ac..e68b899280 100644 --- a/src/corelib/kernel/qmetaobject.h +++ b/src/corelib/kernel/qmetaobject.h @@ -132,6 +132,34 @@ public: val0, val1, val2, val3, val4, val5, val6, val7, val8, val9); } + bool invokeOnGadget(void *gadget, + QGenericReturnArgument returnValue, + QGenericArgument val0 = QGenericArgument(0), + QGenericArgument val1 = QGenericArgument(), + QGenericArgument val2 = QGenericArgument(), + QGenericArgument val3 = QGenericArgument(), + QGenericArgument val4 = QGenericArgument(), + QGenericArgument val5 = QGenericArgument(), + QGenericArgument val6 = QGenericArgument(), + QGenericArgument val7 = QGenericArgument(), + QGenericArgument val8 = QGenericArgument(), + QGenericArgument val9 = QGenericArgument()) const; + inline bool invokeOnGadget(void *gadget, + QGenericArgument val0 = QGenericArgument(0), + QGenericArgument val1 = QGenericArgument(), + QGenericArgument val2 = QGenericArgument(), + QGenericArgument val3 = QGenericArgument(), + QGenericArgument val4 = QGenericArgument(), + QGenericArgument val5 = QGenericArgument(), + QGenericArgument val6 = QGenericArgument(), + QGenericArgument val7 = QGenericArgument(), + QGenericArgument val8 = QGenericArgument(), + QGenericArgument val9 = QGenericArgument()) const + { + return invokeOnGadget(gadget, QGenericReturnArgument(), + val0, val1, val2, val3, val4, val5, val6, val7, val8, val9); + } + inline bool isValid() const { return mobj != 0; } #ifdef Q_QDOC diff --git a/tests/auto/corelib/kernel/qmetamethod/tst_qmetamethod.cpp b/tests/auto/corelib/kernel/qmetamethod/tst_qmetamethod.cpp index 1d39280afb..18de761e2a 100644 --- a/tests/auto/corelib/kernel/qmetamethod/tst_qmetamethod.cpp +++ b/tests/auto/corelib/kernel/qmetamethod/tst_qmetamethod.cpp @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2014 Olivier Goffart ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. @@ -50,6 +51,8 @@ private slots: void comparisonOperators(); void fromSignal(); + + void gadget(); }; struct CustomType { }; @@ -727,5 +730,51 @@ void tst_QMetaMethod::fromSignal() #undef FROMSIGNAL_HELPER } +class MyGadget { + Q_GADGET +public: + QString m_value; + Q_INVOKABLE void setValue(const QString &value) { m_value = value; } + Q_INVOKABLE QString getValue() { return m_value; } +}; + +void tst_QMetaMethod::gadget() +{ + int idx; + + idx = MyGadget::staticMetaObject.indexOfMethod("setValue(QString)"); + QVERIFY(idx >= 0); + QMetaMethod setValueMethod = MyGadget::staticMetaObject.method(idx); + QVERIFY(setValueMethod.isValid()); + + idx = MyGadget::staticMetaObject.indexOfMethod("getValue()"); + QVERIFY(idx >= 0); + QMetaMethod getValueMethod = MyGadget::staticMetaObject.method(idx); + QVERIFY(getValueMethod.isValid()); + + { + MyGadget gadget; + QString string; + + QVERIFY(getValueMethod.invokeOnGadget(&gadget, Q_RETURN_ARG(QString, string))); + QCOMPARE(string, gadget.m_value); + + QVERIFY(setValueMethod.invokeOnGadget(&gadget, Q_ARG(QString, QLatin1String("hello")))); + QCOMPARE(gadget.m_value, QLatin1String("hello")); + + QVERIFY(getValueMethod.invokeOnGadget(&gadget, Q_RETURN_ARG(QString, string))); + QCOMPARE(string, gadget.m_value); + } + + { + // Call with null should not crash + MyGadget *gadget = Q_NULLPTR; + QString string; + QVERIFY(!setValueMethod.invokeOnGadget(gadget, Q_ARG(QString, QLatin1String("hi")))); + QVERIFY(!getValueMethod.invokeOnGadget(gadget, Q_RETURN_ARG(QString, string))); + } +} + + QTEST_MAIN(tst_QMetaMethod) #include "tst_qmetamethod.moc" -- cgit v1.2.3