diff options
-rw-r--r-- | src/corelib/kernel/qmetaobject.cpp | 83 | ||||
-rw-r--r-- | src/corelib/kernel/qobject_impl.h | 78 | ||||
-rw-r--r-- | src/corelib/kernel/qobjectdefs.h | 89 | ||||
-rw-r--r-- | src/corelib/kernel/qobjectdefs_impl.h | 94 | ||||
-rw-r--r-- | tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp | 176 |
5 files changed, 437 insertions, 83 deletions
diff --git a/src/corelib/kernel/qmetaobject.cpp b/src/corelib/kernel/qmetaobject.cpp index 2e0dd8e5d2..68ee2bb241 100644 --- a/src/corelib/kernel/qmetaobject.cpp +++ b/src/corelib/kernel/qmetaobject.cpp @@ -1489,6 +1489,51 @@ bool QMetaObject::invokeMethod(QObject *obj, val0, val1, val2, val3, val4, val5, val6, val7, val8, val9); } +bool QMetaObject::invokeMethodImpl(QObject *object, QtPrivate::QSlotObjectBase *slot, Qt::ConnectionType type, void *ret) +{ + if (! object) + return false; + + QThread *currentThread = QThread::currentThread(); + QThread *objectThread = object->thread(); + if (type == Qt::AutoConnection) + type = (currentThread == objectThread) ? Qt::DirectConnection : Qt::QueuedConnection; + + void *argv[] = { ret }; + + if (type == Qt::DirectConnection) { + slot->call(object, argv); + } else if (type == Qt::QueuedConnection) { + if (argv[0]) { + qWarning("QMetaObject::invokeMethod: Unable to invoke methods with return values in " + "queued connections"); + return false; + } + + // args and typesCopy will be deallocated by ~QMetaCallEvent() using free() + void **args = static_cast<void **>(calloc(1, sizeof(void *))); + Q_CHECK_PTR(args); + + int *types = static_cast<int *>(calloc(1, sizeof(int))); + Q_CHECK_PTR(types); + + QCoreApplication::postEvent(object, new QMetaCallEvent(slot, 0, -1, 1, types, args)); + } else if (type == Qt::BlockingQueuedConnection) { +#ifndef QT_NO_THREAD + if (currentThread == objectThread) + qWarning("QMetaObject::invokeMethod: Dead lock detected"); + + QSemaphore semaphore; + QCoreApplication::postEvent(object, new QMetaCallEvent(slot, 0, -1, 0, 0, argv, &semaphore)); + semaphore.acquire(); +#endif // QT_NO_THREAD + } else { + qWarning("QMetaObject::invokeMethod: Unknown connection type"); + return false; + } + return true; +} + /*! \fn bool QMetaObject::invokeMethod(QObject *obj, const char *member, QGenericReturnArgument ret, QGenericArgument val0 = QGenericArgument(0), @@ -1544,6 +1589,44 @@ bool QMetaObject::invokeMethod(QObject *obj, */ /*! + \fn bool QMetaObject::invokeMethod(QObject *receiver, PointerToMemberFunction function, Qt::ConnectionType type = Qt::AutoConnection, MemberFunctionReturnType *ret = Q_NULLPTR) + + \since 5.10 + + \overload +*/ + +/*! + \fn bool QMetaObject::invokeMethod(QObject *receiver, PointerToMemberFunction function, MemberFunctionReturnType *ret) + + \since 5.10 + + \overload + + This overload invokes the member function using the connection type Qt::AutoConnection. +*/ + +/*! + \fn bool QMetaObject::invokeMethod(QObject *context, Functor function, Qt::ConnectionType type = Qt::AutoConnection, FunctorReturnType *ret = Q_NULLPTR) + + \since 5.10 + + \overload + + Call the functor in the event loop of \a context. +*/ + +/*! + \fn bool QMetaObject::invokeMethod(QObject *context, Functor function, FunctorReturnType *ret = Q_NULLPTR) + + \since 5.10 + + \overload + + Call the functor in the event loop of \a context using the connection type Qt::AutoConnection. +*/ + +/*! \fn QMetaObject::Connection::Connection(const Connection &other) Constructs a copy of \a other. diff --git a/src/corelib/kernel/qobject_impl.h b/src/corelib/kernel/qobject_impl.h index d7ae63a98c..c775d807b1 100644 --- a/src/corelib/kernel/qobject_impl.h +++ b/src/corelib/kernel/qobject_impl.h @@ -74,60 +74,6 @@ namespace QtPrivate { template <typename... Args> struct ConnectionTypes<List<Args...>, true> { static const int *types() { static const int t[sizeof...(Args) + 1] = { (QtPrivate::QMetaTypeIdHelper<Args>::qt_metatype_id())..., 0 }; return t; } }; - // internal base class (interface) containing functions required to call a slot managed by a pointer to function. - class QSlotObjectBase { - QAtomicInt m_ref; - // don't use virtual functions here; we don't want the - // compiler to create tons of per-polymorphic-class stuff that - // we'll never need. We just use one function pointer. - typedef void (*ImplFn)(int which, QSlotObjectBase* this_, QObject *receiver, void **args, bool *ret); - const ImplFn m_impl; - protected: - enum Operation { - Destroy, - Call, - Compare, - - NumOperations - }; - public: - explicit QSlotObjectBase(ImplFn fn) : m_ref(1), m_impl(fn) {} - - inline int ref() Q_DECL_NOTHROW { return m_ref.ref(); } - inline void destroyIfLastRef() Q_DECL_NOTHROW - { if (!m_ref.deref()) m_impl(Destroy, this, Q_NULLPTR, Q_NULLPTR, Q_NULLPTR); } - - inline bool compare(void **a) { bool ret = false; m_impl(Compare, this, Q_NULLPTR, a, &ret); return ret; } - inline void call(QObject *r, void **a) { m_impl(Call, this, r, a, Q_NULLPTR); } - protected: - ~QSlotObjectBase() {} - private: - Q_DISABLE_COPY(QSlotObjectBase) - }; - // implementation of QSlotObjectBase for which the slot is a pointer to member function of a QObject - // Args and R are the List of arguments and the returntype of the signal to which the slot is connected. - template<typename Func, typename Args, typename R> class QSlotObject : public QSlotObjectBase - { - typedef QtPrivate::FunctionPointer<Func> FuncType; - Func function; - static void impl(int which, QSlotObjectBase *this_, QObject *r, void **a, bool *ret) - { - switch (which) { - case Destroy: - delete static_cast<QSlotObject*>(this_); - break; - case Call: - FuncType::template call<Args, R>(static_cast<QSlotObject*>(this_)->function, static_cast<typename FuncType::Object *>(r), a); - break; - case Compare: - *ret = *reinterpret_cast<Func *>(a) == static_cast<QSlotObject*>(this_)->function; - break; - case NumOperations: ; - } - } - public: - explicit QSlotObject(Func f) : QSlotObjectBase(&impl), function(f) {} - }; // implementation of QSlotObjectBase for which the slot is a static function // Args and R are the List of arguments and the returntype of the signal to which the slot is connected. template<typename Func, typename Args, typename R> class QStaticSlotObject : public QSlotObjectBase @@ -151,30 +97,6 @@ namespace QtPrivate { public: explicit QStaticSlotObject(Func f) : QSlotObjectBase(&impl), function(f) {} }; - // implementation of QSlotObjectBase for which the slot is a functor (or lambda) - // N is the number of arguments - // Args and R are the List of arguments and the returntype of the signal to which the slot is connected. - template<typename Func, int N, typename Args, typename R> class QFunctorSlotObject : public QSlotObjectBase - { - typedef QtPrivate::Functor<Func, N> FuncType; - Func function; - static void impl(int which, QSlotObjectBase *this_, QObject *r, void **a, bool *ret) - { - switch (which) { - case Destroy: - delete static_cast<QFunctorSlotObject*>(this_); - break; - case Call: - FuncType::template call<Args, R>(static_cast<QFunctorSlotObject*>(this_)->function, r, a); - break; - case Compare: // not implemented - case NumOperations: - Q_UNUSED(ret); - } - } - public: - explicit QFunctorSlotObject(const Func &f) : QSlotObjectBase(&impl), function(f) {} - }; } diff --git a/src/corelib/kernel/qobjectdefs.h b/src/corelib/kernel/qobjectdefs.h index ea4046df55..b6307fcfcf 100644 --- a/src/corelib/kernel/qobjectdefs.h +++ b/src/corelib/kernel/qobjectdefs.h @@ -57,7 +57,6 @@ struct QArrayData; typedef QArrayData QByteArrayData; class QString; - #ifndef Q_MOC_OUTPUT_REVISION #define Q_MOC_OUTPUT_REVISION 67 #endif @@ -466,6 +465,91 @@ struct Q_CORE_EXPORT QMetaObject val1, val2, val3, val4, val5, val6, val7, val8, val9); } +#ifdef Q_QDOC + template<typename PointerToMemberFunction, typename MemberFunctionReturnType> + static bool invokeMethod(QObject *receiver, PointerToMemberFunction function, Qt::ConnectionType type = Qt::AutoConnection, MemberFunctionReturnType *ret = nullptr); + template<typename PointerToMemberFunction, typename MemberFunctionReturnType> + static bool invokeMethod(QObject *receiver, PointerToMemberFunction function, MemberFunctionReturnType *ret); + template<typename Functor, typename FunctorReturnType> + static bool invokeMethod(QObject *context, Functor function, Qt::ConnectionType type = Qt::AutoConnection, FunctorReturnType *ret = nullptr); + template<typename Functor, typename FunctorReturnType> + static bool invokeMethod(QObject *context, Functor function, FunctorReturnType *ret); +#else + + // invokeMethod() for member function pointer + template <typename Func> + static typename std::enable_if<QtPrivate::FunctionPointer<Func>::IsPointerToMemberFunction + && !std::is_convertible<Func, const char*>::value + && QtPrivate::FunctionPointer<Func>::ArgumentCount == 0, bool>::type + invokeMethod(typename QtPrivate::FunctionPointer<Func>::Object *object, + Func function, + Qt::ConnectionType type = Qt::AutoConnection, + typename QtPrivate::FunctionPointer<Func>::ReturnType *ret = nullptr) + { + return invokeMethodImpl(object, new QtPrivate::QSlotObjectWithNoArgs<Func>(function), type, ret); + } + + template <typename Func> + static typename std::enable_if<QtPrivate::FunctionPointer<Func>::IsPointerToMemberFunction + && !std::is_convertible<Func, const char*>::value + && QtPrivate::FunctionPointer<Func>::ArgumentCount == 0, bool>::type + invokeMethod(typename QtPrivate::FunctionPointer<Func>::Object *object, + Func function, + typename QtPrivate::FunctionPointer<Func>::ReturnType *ret) + { + return invokeMethodImpl(object, new QtPrivate::QSlotObjectWithNoArgs<Func>(function), Qt::AutoConnection, ret); + } + + // invokeMethod() for function pointer (not member) + template <typename Func> + static typename std::enable_if<!QtPrivate::FunctionPointer<Func>::IsPointerToMemberFunction + && !std::is_convertible<Func, const char*>::value + && QtPrivate::FunctionPointer<Func>::ArgumentCount == 0, bool>::type + invokeMethod(QObject *context, Func function, + Qt::ConnectionType type = Qt::AutoConnection, + typename QtPrivate::FunctionPointer<Func>::ReturnType *ret = nullptr) + { + return invokeMethodImpl(context, new QtPrivate::QFunctorSlotObjectWithNoArgsImplicitReturn<Func>(function), type, ret); + } + + template <typename Func> + static typename std::enable_if<!QtPrivate::FunctionPointer<Func>::IsPointerToMemberFunction + && !std::is_convertible<Func, const char*>::value + && QtPrivate::FunctionPointer<Func>::ArgumentCount == 0, bool>::type + invokeMethod(QObject *context, Func function, + typename QtPrivate::FunctionPointer<Func>::ReturnType *ret) + { + return invokeMethodImpl(context, new QtPrivate::QFunctorSlotObjectWithNoArgsImplicitReturn<Func>(function), Qt::AutoConnection, ret); + } + + // invokeMethod() for Functor + template <typename Func> + static typename std::enable_if<!QtPrivate::FunctionPointer<Func>::IsPointerToMemberFunction + && QtPrivate::FunctionPointer<Func>::ArgumentCount == -1 + && !std::is_convertible<Func, const char*>::value, bool>::type + invokeMethod(QObject *context, Func function, + Qt::ConnectionType type = Qt::AutoConnection, decltype(function()) *ret = nullptr) + { + return invokeMethodImpl(context, + new QtPrivate::QFunctorSlotObjectWithNoArgs<Func, decltype(function())>(function), + type, + ret); + } + + template <typename Func> + static typename std::enable_if<!QtPrivate::FunctionPointer<Func>::IsPointerToMemberFunction + && QtPrivate::FunctionPointer<Func>::ArgumentCount == -1 + && !std::is_convertible<Func, const char*>::value, bool>::type + invokeMethod(QObject *context, Func function, typename std::result_of<Func()>::type *ret) + { + return invokeMethodImpl(context, + new QtPrivate::QFunctorSlotObjectWithNoArgs<Func, decltype(function())>(function), + Qt::AutoConnection, + ret); + } + +#endif + QObject *newInstance(QGenericArgument val0 = QGenericArgument(Q_NULLPTR), QGenericArgument val1 = QGenericArgument(), QGenericArgument val2 = QGenericArgument(), @@ -505,6 +589,9 @@ struct Q_CORE_EXPORT QMetaObject const QMetaObject * const *relatedMetaObjects; void *extradata; //reserved for future use } d; + +private: + static bool invokeMethodImpl(QObject *object, QtPrivate::QSlotObjectBase *slot, Qt::ConnectionType type, void *ret); }; class Q_CORE_EXPORT QMetaObject::Connection { diff --git a/src/corelib/kernel/qobjectdefs_impl.h b/src/corelib/kernel/qobjectdefs_impl.h index 3f5f2e78bb..ef68050529 100644 --- a/src/corelib/kernel/qobjectdefs_impl.h +++ b/src/corelib/kernel/qobjectdefs_impl.h @@ -51,7 +51,7 @@ #endif QT_BEGIN_NAMESPACE - +class QObject; namespace QtPrivate { template <typename T> struct RemoveRef { typedef T Type; }; @@ -352,6 +352,98 @@ namespace QtPrivate { template <typename D> static D dummy(); typedef decltype(dummy<Functor>().operator()((dummy<ArgList>())...)) Value; }; + + // internal base class (interface) containing functions required to call a slot managed by a pointer to function. + class QSlotObjectBase { + QAtomicInt m_ref; + // don't use virtual functions here; we don't want the + // compiler to create tons of per-polymorphic-class stuff that + // we'll never need. We just use one function pointer. + typedef void (*ImplFn)(int which, QSlotObjectBase* this_, QObject *receiver, void **args, bool *ret); + const ImplFn m_impl; + protected: + enum Operation { + Destroy, + Call, + Compare, + + NumOperations + }; + public: + explicit QSlotObjectBase(ImplFn fn) : m_ref(1), m_impl(fn) {} + + inline int ref() Q_DECL_NOTHROW { return m_ref.ref(); } + inline void destroyIfLastRef() Q_DECL_NOTHROW + { if (!m_ref.deref()) m_impl(Destroy, this, Q_NULLPTR, Q_NULLPTR, Q_NULLPTR); } + + inline bool compare(void **a) { bool ret = false; m_impl(Compare, this, Q_NULLPTR, a, &ret); return ret; } + inline void call(QObject *r, void **a) { m_impl(Call, this, r, a, Q_NULLPTR); } + protected: + ~QSlotObjectBase() {} + private: + Q_DISABLE_COPY(QSlotObjectBase) + }; + + // implementation of QSlotObjectBase for which the slot is a pointer to member function of a QObject + // Args and R are the List of arguments and the returntype of the signal to which the slot is connected. + template<typename Func, typename Args, typename R> class QSlotObject : public QSlotObjectBase + { + typedef QtPrivate::FunctionPointer<Func> FuncType; + Func function; + static void impl(int which, QSlotObjectBase *this_, QObject *r, void **a, bool *ret) + { + switch (which) { + case Destroy: + delete static_cast<QSlotObject*>(this_); + break; + case Call: + FuncType::template call<Args, R>(static_cast<QSlotObject*>(this_)->function, static_cast<typename FuncType::Object *>(r), a); + break; + case Compare: + *ret = *reinterpret_cast<Func *>(a) == static_cast<QSlotObject*>(this_)->function; + break; + case NumOperations: ; + } + } + public: + explicit QSlotObject(Func f) : QSlotObjectBase(&impl), function(f) {} + }; + // implementation of QSlotObjectBase for which the slot is a functor (or lambda) + // N is the number of arguments + // Args and R are the List of arguments and the returntype of the signal to which the slot is connected. + template<typename Func, int N, typename Args, typename R> class QFunctorSlotObject : public QSlotObjectBase + { + typedef QtPrivate::Functor<Func, N> FuncType; + Func function; + static void impl(int which, QSlotObjectBase *this_, QObject *r, void **a, bool *ret) + { + switch (which) { + case Destroy: + delete static_cast<QFunctorSlotObject*>(this_); + break; + case Call: + FuncType::template call<Args, R>(static_cast<QFunctorSlotObject*>(this_)->function, r, a); + break; + case Compare: // not implemented + case NumOperations: + Q_UNUSED(ret); + } + } + public: + explicit QFunctorSlotObject(const Func &f) : QSlotObjectBase(&impl), function(f) {} + }; + + // typedefs for readability for when there are no parameters + template <typename Func> + using QSlotObjectWithNoArgs = QSlotObject<Func, + QtPrivate::List<>, + typename QtPrivate::FunctionPointer<Func>::ReturnType>; + + template <typename Func, typename R> + using QFunctorSlotObjectWithNoArgs = QFunctorSlotObject<Func, 0, QtPrivate::List<>, R>; + + template <typename Func> + using QFunctorSlotObjectWithNoArgsImplicitReturn = QFunctorSlotObjectWithNoArgs<Func, typename QtPrivate::FunctionPointer<Func>::ReturnType>; } QT_END_NAMESPACE diff --git a/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp b/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp index fd32dc1ef8..14c1b29836 100644 --- a/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp +++ b/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp @@ -200,8 +200,11 @@ public: private slots: void connectSlotsByName(); void invokeMetaMember(); + void invokePointer(); void invokeQueuedMetaMember(); + void invokeQueuedPointer(); void invokeBlockingQueuedMetaMember(); + void invokeBlockingQueuedPointer(); void invokeCustomTypes(); void invokeMetaConstructor(); void invokeTypedefTypes(); @@ -416,6 +419,10 @@ public slots: return s2; } +public: + static void staticFunction0(); + static qint64 staticFunction1(); + signals: void sig0(); QString sig1(QString s1); @@ -429,8 +436,11 @@ private: public: QString slotResult; + static QString staticResult; }; +QString QtTestObject::staticResult; + QtTestObject::QtTestObject() { connect(this, SIGNAL(sig0()), this, SLOT(sl0())); @@ -489,6 +499,13 @@ void QtTestObject::testSender() void QtTestObject::slotWithUnregisteredParameterType(MyUnregisteredType) { slotResult = "slotWithUnregisteredReturnType"; } +void QtTestObject::staticFunction0() +{ + staticResult = "staticFunction0"; +} + +qint64 QtTestObject::staticFunction1() +{ staticResult = "staticFunction1"; return Q_INT64_C(123456789)*123456789; } void tst_QMetaObject::invokeMetaMember() { @@ -497,9 +514,18 @@ void tst_QMetaObject::invokeMetaMember() QString t1("1"); QString t2("2"); QString t3("3"); QString t4("4"); QString t5("5"); QString t6("6"); QString t7("7"); QString t8("8"); QString t9("9"); QString t10("X"); - QVERIFY(!QMetaObject::invokeMethod(0, 0)); - QVERIFY(!QMetaObject::invokeMethod(0, "sl0")); - QVERIFY(!QMetaObject::invokeMethod(&obj, 0)); + // Test nullptr + char *nullCharArray = nullptr; + const char *nullConstCharArray = nullptr; + QVERIFY(!QMetaObject::invokeMethod(nullptr, nullCharArray)); + QVERIFY(!QMetaObject::invokeMethod(nullptr, nullConstCharArray)); + QVERIFY(!QMetaObject::invokeMethod(nullptr, "sl0")); + QVERIFY(!QMetaObject::invokeMethod(&obj, nullCharArray)); + QVERIFY(!QMetaObject::invokeMethod(&obj, nullConstCharArray)); + QVERIFY(!QMetaObject::invokeMethod(&obj, nullCharArray, Qt::AutoConnection)); + QVERIFY(!QMetaObject::invokeMethod(&obj, nullConstCharArray, Qt::AutoConnection)); + QVERIFY(!QMetaObject::invokeMethod(&obj, nullCharArray, Qt::AutoConnection, QGenericReturnArgument())); + QVERIFY(!QMetaObject::invokeMethod(&obj, nullConstCharArray, Qt::AutoConnection, QGenericReturnArgument())); QVERIFY(QMetaObject::invokeMethod(&obj, "sl0")); QCOMPARE(obj.slotResult, QString("sl0")); @@ -628,6 +654,56 @@ void tst_QMetaObject::invokeMetaMember() QCOMPARE(obj.slotResult, QString("sl1:hehe")); } +void testFunction(){} + + +void tst_QMetaObject::invokePointer() +{ + QtTestObject obj; + QtTestObject *const nullTestObject = nullptr; + + QString t1("1"); + + // Test member functions + QVERIFY(!QMetaObject::invokeMethod(nullTestObject, &QtTestObject::sl0)); + QVERIFY(QMetaObject::invokeMethod(&obj, &QtTestObject::sl0)); + QCOMPARE(obj.slotResult, QString("sl0")); + + QVERIFY(QMetaObject::invokeMethod(&obj, &QtTestObject::testSender)); + QCOMPARE(obj.slotResult, QString("0x0")); + + qint64 return64 = 0; + QVERIFY(QMetaObject::invokeMethod(&obj, &QtTestObject::sl14, &return64)); + QCOMPARE(return64, Q_INT64_C(123456789)*123456789); + QCOMPARE(obj.slotResult, QString("sl14")); + + // signals + QVERIFY(QMetaObject::invokeMethod(&obj, &QtTestObject::sig0)); + QCOMPARE(obj.slotResult, QString("sl0")); + + // Test function pointers + QVERIFY(!QMetaObject::invokeMethod(0, &testFunction)); + QVERIFY(QMetaObject::invokeMethod(&obj, &testFunction)); + + QVERIFY(!QMetaObject::invokeMethod(0, &QtTestObject::staticFunction0)); + QVERIFY(QMetaObject::invokeMethod(&obj, &QtTestObject::staticFunction0)); + QCOMPARE(QtTestObject::staticResult, QString("staticFunction0")); + + return64 = 0; + QVERIFY(QMetaObject::invokeMethod(&obj, &QtTestObject::staticFunction1, &return64)); + QCOMPARE(return64, Q_INT64_C(123456789)*123456789); + QCOMPARE(QtTestObject::staticResult, QString("staticFunction1")); + + // Test lambdas + QVERIFY(QMetaObject::invokeMethod(&obj, [&](){obj.sl1(t1);})); + QCOMPARE(obj.slotResult, QString("sl1:1")); + + QString exp; + QVERIFY(QMetaObject::invokeMethod(&obj, [&]()->QString{return obj.sl1("bubu");}, &exp)); + QCOMPARE(exp, QString("yessir")); + QCOMPARE(obj.slotResult, QString("sl1:bubu")); +} + void tst_QMetaObject::invokeQueuedMetaMember() { QtTestObject obj; @@ -688,6 +764,44 @@ void tst_QMetaObject::invokeQueuedMetaMember() } } +void tst_QMetaObject::invokeQueuedPointer() +{ + QtTestObject obj; + + // Test member function + QVERIFY(QMetaObject::invokeMethod(&obj, &QtTestObject::sl0, Qt::QueuedConnection)); + QVERIFY(obj.slotResult.isEmpty()); + qApp->processEvents(QEventLoop::AllEvents); + QCOMPARE(obj.slotResult, QString("sl0")); + + // signals + obj.slotResult.clear(); + QVERIFY(QMetaObject::invokeMethod(&obj, &QtTestObject::sig0, Qt::QueuedConnection)); + QVERIFY(obj.slotResult.isEmpty()); + qApp->processEvents(QEventLoop::AllEvents); + QCOMPARE(obj.slotResult, QString("sl0")); + + // Test function pointers + QtTestObject::staticResult.clear(); + QVERIFY(QMetaObject::invokeMethod(&obj, &QtTestObject::staticFunction0, Qt::QueuedConnection)); + QVERIFY(QtTestObject::staticResult.isEmpty()); + qApp->processEvents(QEventLoop::AllEvents); + QCOMPARE(QtTestObject::staticResult, QString("staticFunction0")); + + // Test lambda + obj.slotResult.clear(); + QVERIFY(QMetaObject::invokeMethod(&obj, [&](){obj.sl0();}, Qt::QueuedConnection)); + QVERIFY(obj.slotResult.isEmpty()); + qApp->processEvents(QEventLoop::AllEvents); + QCOMPARE(obj.slotResult, QString("sl0")); + + qint32 var = 0; + QTest::ignoreMessage(QtWarningMsg, "QMetaObject::invokeMethod: Unable to invoke methods with return values in queued connections"); + QVERIFY(!QMetaObject::invokeMethod(&obj, []()->qint32{return 1;}, Qt::QueuedConnection, &var)); + QCOMPARE(var, 0); +} + + void tst_QMetaObject::invokeBlockingQueuedMetaMember() { QThread t; @@ -821,6 +935,62 @@ void tst_QMetaObject::invokeBlockingQueuedMetaMember() } +void tst_QMetaObject::invokeBlockingQueuedPointer() +{ + QtTestObject *const nullTestObject = nullptr; + + QThread t; + t.start(); + QtTestObject obj; + obj.moveToThread(&t); + + QString t1("1"); + + // Test member functions + QVERIFY(!QMetaObject::invokeMethod(nullTestObject, &QtTestObject::sl0, Qt::BlockingQueuedConnection)); + QVERIFY(QMetaObject::invokeMethod(&obj, &QtTestObject::sl0, Qt::BlockingQueuedConnection)); + QCOMPARE(obj.slotResult, QString("sl0")); + + QVERIFY(QMetaObject::invokeMethod(&obj, &QtTestObject::testSender, Qt::BlockingQueuedConnection)); + QCOMPARE(obj.slotResult, QString("0x0")); + + // return qint64 + qint64 return64 = 0; + QVERIFY(QMetaObject::invokeMethod(&obj, &QtTestObject::sl14, Qt::BlockingQueuedConnection, + &return64)); + QCOMPARE(return64, Q_INT64_C(123456789)*123456789); + QCOMPARE(obj.slotResult, QString("sl14")); + + //test signals + QVERIFY(QMetaObject::invokeMethod(&obj, &QtTestObject::sig0, Qt::BlockingQueuedConnection)); + QCOMPARE(obj.slotResult, QString("sl0")); + + // Test function pointers + QVERIFY(!QMetaObject::invokeMethod(0, &testFunction, Qt::BlockingQueuedConnection)); + QVERIFY(QMetaObject::invokeMethod(&obj, &testFunction, Qt::BlockingQueuedConnection)); + + QVERIFY(!QMetaObject::invokeMethod(0, &QtTestObject::staticFunction0, Qt::BlockingQueuedConnection)); + QVERIFY(QMetaObject::invokeMethod(&obj, &QtTestObject::staticFunction0, Qt::BlockingQueuedConnection)); + QCOMPARE(QtTestObject::staticResult, QString("staticFunction0")); + + return64 = 0; + QVERIFY(QMetaObject::invokeMethod(&obj, &QtTestObject::staticFunction1, Qt::BlockingQueuedConnection, &return64)); + QCOMPARE(return64, Q_INT64_C(123456789)*123456789); + QCOMPARE(QtTestObject::staticResult, QString("staticFunction1")); + + // Test lambdas + QVERIFY(QMetaObject::invokeMethod(&obj, [&](){obj.sl1(t1);}, Qt::BlockingQueuedConnection)); + QCOMPARE(obj.slotResult, QString("sl1:1")); + + QString exp; + QVERIFY(QMetaObject::invokeMethod(&obj, [&]()->QString{return obj.sl1("bubu");}, Qt::BlockingQueuedConnection, &exp)); + QCOMPARE(exp, QString("yessir")); + QCOMPARE(obj.slotResult, QString("sl1:bubu")); + + QVERIFY(QMetaObject::invokeMethod(&obj, [&](){obj.moveToThread(QThread::currentThread());}, Qt::BlockingQueuedConnection)); + t.quit(); + QVERIFY(t.wait()); +} void tst_QMetaObject::qtMetaObjectInheritance() |