From d2388f15e7fa420415d119b0e422ea03da050a60 Mon Sep 17 00:00:00 2001 From: Benjamin Terrier Date: Sat, 14 Jan 2017 17:01:50 +0100 Subject: 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 --- src/corelib/kernel/qmetaobject.cpp | 83 +++++++++++++++++++++++++++++++ src/corelib/kernel/qobject_impl.h | 78 ----------------------------- src/corelib/kernel/qobjectdefs.h | 89 ++++++++++++++++++++++++++++++++- src/corelib/kernel/qobjectdefs_impl.h | 94 ++++++++++++++++++++++++++++++++++- 4 files changed, 264 insertions(+), 80 deletions(-) (limited to 'src') 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(calloc(1, sizeof(void *))); + Q_CHECK_PTR(args); + + int *types = static_cast(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) 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 struct ConnectionTypes, true> { static const int *types() { static const int t[sizeof...(Args) + 1] = { (QtPrivate::QMetaTypeIdHelper::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 class QSlotObject : public QSlotObjectBase - { - typedef QtPrivate::FunctionPointer FuncType; - Func function; - static void impl(int which, QSlotObjectBase *this_, QObject *r, void **a, bool *ret) - { - switch (which) { - case Destroy: - delete static_cast(this_); - break; - case Call: - FuncType::template call(static_cast(this_)->function, static_cast(r), a); - break; - case Compare: - *ret = *reinterpret_cast(a) == static_cast(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 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 class QFunctorSlotObject : public QSlotObjectBase - { - typedef QtPrivate::Functor FuncType; - Func function; - static void impl(int which, QSlotObjectBase *this_, QObject *r, void **a, bool *ret) - { - switch (which) { - case Destroy: - delete static_cast(this_); - break; - case Call: - FuncType::template call(static_cast(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 + static bool invokeMethod(QObject *receiver, PointerToMemberFunction function, Qt::ConnectionType type = Qt::AutoConnection, MemberFunctionReturnType *ret = nullptr); + template + static bool invokeMethod(QObject *receiver, PointerToMemberFunction function, MemberFunctionReturnType *ret); + template + static bool invokeMethod(QObject *context, Functor function, Qt::ConnectionType type = Qt::AutoConnection, FunctorReturnType *ret = nullptr); + template + static bool invokeMethod(QObject *context, Functor function, FunctorReturnType *ret); +#else + + // invokeMethod() for member function pointer + template + static typename std::enable_if::IsPointerToMemberFunction + && !std::is_convertible::value + && QtPrivate::FunctionPointer::ArgumentCount == 0, bool>::type + invokeMethod(typename QtPrivate::FunctionPointer::Object *object, + Func function, + Qt::ConnectionType type = Qt::AutoConnection, + typename QtPrivate::FunctionPointer::ReturnType *ret = nullptr) + { + return invokeMethodImpl(object, new QtPrivate::QSlotObjectWithNoArgs(function), type, ret); + } + + template + static typename std::enable_if::IsPointerToMemberFunction + && !std::is_convertible::value + && QtPrivate::FunctionPointer::ArgumentCount == 0, bool>::type + invokeMethod(typename QtPrivate::FunctionPointer::Object *object, + Func function, + typename QtPrivate::FunctionPointer::ReturnType *ret) + { + return invokeMethodImpl(object, new QtPrivate::QSlotObjectWithNoArgs(function), Qt::AutoConnection, ret); + } + + // invokeMethod() for function pointer (not member) + template + static typename std::enable_if::IsPointerToMemberFunction + && !std::is_convertible::value + && QtPrivate::FunctionPointer::ArgumentCount == 0, bool>::type + invokeMethod(QObject *context, Func function, + Qt::ConnectionType type = Qt::AutoConnection, + typename QtPrivate::FunctionPointer::ReturnType *ret = nullptr) + { + return invokeMethodImpl(context, new QtPrivate::QFunctorSlotObjectWithNoArgsImplicitReturn(function), type, ret); + } + + template + static typename std::enable_if::IsPointerToMemberFunction + && !std::is_convertible::value + && QtPrivate::FunctionPointer::ArgumentCount == 0, bool>::type + invokeMethod(QObject *context, Func function, + typename QtPrivate::FunctionPointer::ReturnType *ret) + { + return invokeMethodImpl(context, new QtPrivate::QFunctorSlotObjectWithNoArgsImplicitReturn(function), Qt::AutoConnection, ret); + } + + // invokeMethod() for Functor + template + static typename std::enable_if::IsPointerToMemberFunction + && QtPrivate::FunctionPointer::ArgumentCount == -1 + && !std::is_convertible::value, bool>::type + invokeMethod(QObject *context, Func function, + Qt::ConnectionType type = Qt::AutoConnection, decltype(function()) *ret = nullptr) + { + return invokeMethodImpl(context, + new QtPrivate::QFunctorSlotObjectWithNoArgs(function), + type, + ret); + } + + template + static typename std::enable_if::IsPointerToMemberFunction + && QtPrivate::FunctionPointer::ArgumentCount == -1 + && !std::is_convertible::value, bool>::type + invokeMethod(QObject *context, Func function, typename std::result_of::type *ret) + { + return invokeMethodImpl(context, + new QtPrivate::QFunctorSlotObjectWithNoArgs(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 struct RemoveRef { typedef T Type; }; @@ -352,6 +352,98 @@ namespace QtPrivate { template static D dummy(); typedef decltype(dummy().operator()((dummy())...)) 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 class QSlotObject : public QSlotObjectBase + { + typedef QtPrivate::FunctionPointer FuncType; + Func function; + static void impl(int which, QSlotObjectBase *this_, QObject *r, void **a, bool *ret) + { + switch (which) { + case Destroy: + delete static_cast(this_); + break; + case Call: + FuncType::template call(static_cast(this_)->function, static_cast(r), a); + break; + case Compare: + *ret = *reinterpret_cast(a) == static_cast(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 class QFunctorSlotObject : public QSlotObjectBase + { + typedef QtPrivate::Functor FuncType; + Func function; + static void impl(int which, QSlotObjectBase *this_, QObject *r, void **a, bool *ret) + { + switch (which) { + case Destroy: + delete static_cast(this_); + break; + case Call: + FuncType::template call(static_cast(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 + using QSlotObjectWithNoArgs = QSlotObject, + typename QtPrivate::FunctionPointer::ReturnType>; + + template + using QFunctorSlotObjectWithNoArgs = QFunctorSlotObject, R>; + + template + using QFunctorSlotObjectWithNoArgsImplicitReturn = QFunctorSlotObjectWithNoArgs::ReturnType>; } QT_END_NAMESPACE -- cgit v1.2.3