Add QMetaObject::invokeMethod() overloads for function pointers

The new overloads do not accept parameters for the invoked function, this
use case is handled by using lambda.
Overloads for non member function pointers and functors are separated as
the return type is not retrieved in the same way.
Move QSlotObjectBase, QSlotObject and QFunctorSlotObject from
qobject_impl.h to qobjectdefs_impl.h in order to make them available in
qobjectdefs.h.
Update autotests of previous overloads because of a soft break in source
compatibility: passing null literals (0, NULL, nullptr, etc.) for the
second parameter of invokeMethod() is not supported anymore.

[ChangeLog][QtCore] Added QMetaObject::invokeMethod() overloads for function
pointers.

Task-number: QTBUG-37253
Change-Id: I6fb67e086d315ae393ce32743c4eb1abd6cc9139
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
Benjamin Terrier 2017-01-14 17:01:50 +01:00 committed by Aapo Keskimolo
parent 0c4eb097ed
commit d2388f15e7
5 changed files with 437 additions and 83 deletions

View File

@ -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),
@ -1543,6 +1588,44 @@ bool QMetaObject::invokeMethod(QObject *obj,
ignores return values.
*/
/*!
\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)

View File

@ -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) {}
};
}

View File

@ -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 {

View File

@ -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

View File

@ -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()