diff options
-rw-r--r-- | src/corelib/doc/snippets/code/src_corelib_kernel_qobject.cpp | 15 | ||||
-rw-r--r-- | src/corelib/kernel/qobject.cpp | 51 | ||||
-rw-r--r-- | src/corelib/kernel/qobject.h | 41 | ||||
-rw-r--r-- | src/corelib/kernel/qobjectdefs_impl.h | 52 | ||||
-rw-r--r-- | tests/auto/corelib/kernel/qobject/tst_qobject.cpp | 209 |
5 files changed, 334 insertions, 34 deletions
diff --git a/src/corelib/doc/snippets/code/src_corelib_kernel_qobject.cpp b/src/corelib/doc/snippets/code/src_corelib_kernel_qobject.cpp index 6a2305cc85..4ecc1758ae 100644 --- a/src/corelib/doc/snippets/code/src_corelib_kernel_qobject.cpp +++ b/src/corelib/doc/snippets/code/src_corelib_kernel_qobject.cpp @@ -472,6 +472,21 @@ if (isSignalConnected(valueChangedSignal)) { } //! [49] +//! [50] +void someFunction(); +QPushButton *button = new QPushButton; +QObject::connect(button, &QPushButton::clicked, this, someFunction, Qt::QueuedConnection); +//! [50] + +//! [51] +QByteArray page = ...; +QTcpSocket *socket = new QTcpSocket; +socket->connectToHost("qt-project.org", 80); +QObject::connect(socket, &QTcpSocket::connected, this, [=] () { + socket->write("GET " + page + "\r\n"); + }, Qt::AutoConnection); +//! [51] + //! [meta data] //: This is a comment for the translator. //= qtn_foo_bar diff --git a/src/corelib/kernel/qobject.cpp b/src/corelib/kernel/qobject.cpp index b914ca812f..cc689657d2 100644 --- a/src/corelib/kernel/qobject.cpp +++ b/src/corelib/kernel/qobject.cpp @@ -854,9 +854,23 @@ QObject::~QObject() senderLists->dirty = true; int signal_index = node->signal_index; + + QtPrivate::QSlotObjectBase *slotObj = Q_NULLPTR; + if (node->isSlotObject) { + slotObj = node->slotObj; + node->isSlotObject = false; + } + node = node->next; if (needToUnlock) m->unlock(); + + if (slotObj) { + locker.unlock(); + slotObj->destroyIfLastRef(); + locker.relock(); + } + sender->disconnectNotify(QMetaObjectPrivate::signal(sender->metaObject(), signal_index)); } } @@ -4273,6 +4287,43 @@ void qDeleteInEventHandler(QObject *o) */ /*! + \fn QMetaObject::Connection QObject::connect(const QObject *sender, PointerToMemberFunction signal, const QObject *context, Functor functor, Qt::ConnectionType type) + + \threadsafe + \overload connect() + + \since 5.2 + + Creates a connection of a given \a type from \a signal in + \a sender object to \a functor to be placed in a specific event + loop of \a context, and returns a handle to the connection + + The signal must be a function declared as a signal in the header. + The slot function can be any function or functor that can be connected + to the signal. + A function can be connected to a given signal if the signal as at + least as many argument as the slot. A functor can be connected to a signal + if they have exactly the same number of arguments. There must exist implicit + conversion between the types of the corresponding arguments in the + signal and the slot. + + Example: + + \snippet code/src_corelib_kernel_qobject.cpp 50 + + If your compiler support C++11 lambda expressions, you can use them: + + \snippet code/src_corelib_kernel_qobject.cpp 51 + + The connection will automatically disconnect if the sender or the context + is destroyed. + + \note If the compiler does not support C++11 variadic templates, the number + of arguments in the signal or slot are limited to 6, and the functor object + must not have an overloaded or templated operator(). + */ + +/*! \internal Implementation of the template version of connect diff --git a/src/corelib/kernel/qobject.h b/src/corelib/kernel/qobject.h index 15b9c8f35e..e2000afc82 100644 --- a/src/corelib/kernel/qobject.h +++ b/src/corelib/kernel/qobject.h @@ -208,6 +208,7 @@ public: #ifdef Q_QDOC static QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, const QObject *receiver, PointerToMemberFunction method, Qt::ConnectionType type); static QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor); + static QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, const QObject *context, Functor functor, Qt::ConnectionType type); #else //Connect a signal to a pointer to qobject member function template <typename Func1, typename Func2> @@ -245,6 +246,16 @@ public: static inline typename QtPrivate::QEnableIf<int(QtPrivate::FunctionPointer<Func2>::ArgumentCount) >= 0, QMetaObject::Connection>::Type connect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal, Func2 slot) { + return connect(sender, signal, sender, slot, Qt::DirectConnection); + } + + //connect to a function pointer (not a member) + template <typename Func1, typename Func2> + static inline typename QtPrivate::QEnableIf<int(QtPrivate::FunctionPointer<Func2>::ArgumentCount) >= 0 && + !QtPrivate::FunctionPointer<Func2>::IsPointerToMemberFunction, QMetaObject::Connection>::Type + connect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal, const QObject *context, Func2 slot, + Qt::ConnectionType type = Qt::AutoConnection) + { typedef QtPrivate::FunctionPointer<Func1> SignalType; typedef QtPrivate::FunctionPointer<Func2> SlotType; @@ -259,11 +270,15 @@ public: Q_STATIC_ASSERT_X((QtPrivate::AreArgumentsCompatible<typename SlotType::ReturnType, typename SignalType::ReturnType>::value), "Return type of the slot is not compatible with the return type of the signal."); - return connectImpl(sender, reinterpret_cast<void **>(&signal), sender, 0, + const int *types = 0; + if (type == Qt::QueuedConnection || type == Qt::BlockingQueuedConnection) + types = QtPrivate::ConnectionTypes<typename SignalType::Arguments>::types(); + + return connectImpl(sender, reinterpret_cast<void **>(&signal), context, 0, new QtPrivate::QStaticSlotObject<Func2, typename QtPrivate::List_Left<typename SignalType::Arguments, SlotType::ArgumentCount>::Value, typename SignalType::ReturnType>(slot), - Qt::DirectConnection, 0, &SignalType::Object::staticMetaObject); + type, types, &SignalType::Object::staticMetaObject); } //connect to a functor @@ -271,6 +286,15 @@ public: static inline typename QtPrivate::QEnableIf<QtPrivate::FunctionPointer<Func2>::ArgumentCount == -1, QMetaObject::Connection>::Type connect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal, Func2 slot) { + return connect(sender, signal, sender, slot, Qt::DirectConnection); + } + + //connect to a functor, with a "context" object defining in which event loop is going to be executed + template <typename Func1, typename Func2> + static inline typename QtPrivate::QEnableIf<QtPrivate::FunctionPointer<Func2>::ArgumentCount == -1, QMetaObject::Connection>::Type + connect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal, const QObject *context, Func2 slot, + Qt::ConnectionType type = Qt::AutoConnection) + { #if defined (Q_COMPILER_DECLTYPE) && defined (Q_COMPILER_VARIADIC_TEMPLATES) typedef QtPrivate::FunctionPointer<Func1> SignalType; const int FunctorArgumentCount = QtPrivate::ComputeFunctorArgumentCount<Func2 , typename SignalType::Arguments>::Value; @@ -292,9 +316,10 @@ public: C++11 variadic templates */ #ifndef Q_COMPILER_DECLTYPE //Workaround the lack of decltype using another function as indirection - return connect_functor(sender, signal, slot, &Func2::operator()); } + return connect_functor(sender, signal, context, slot, &Func2::operator(), type); } template <typename Func1, typename Func2, typename Func2Operator> - static inline QMetaObject::Connection connect_functor(const QObject *sender, Func1 signal, Func2 slot, Func2Operator) { + static inline QMetaObject::Connection connect_functor(const QObject *sender, Func1 signal, const QObject *context, + Func2 slot, Func2Operator, Qt::ConnectionType type) { typedef QtPrivate::FunctionPointer<Func2Operator> SlotType ; #else typedef QtPrivate::FunctionPointer<decltype(&Func2::operator())> SlotType ; @@ -315,11 +340,15 @@ public: Q_STATIC_ASSERT_X(QtPrivate::HasQ_OBJECT_Macro<typename SignalType::Object>::Value, "No Q_OBJECT in the class with the signal"); - return connectImpl(sender, reinterpret_cast<void **>(&signal), sender, 0, + const int *types = 0; + if (type == Qt::QueuedConnection || type == Qt::BlockingQueuedConnection) + types = QtPrivate::ConnectionTypes<typename SignalType::Arguments>::types(); + + return connectImpl(sender, reinterpret_cast<void **>(&signal), context, 0, new QtPrivate::QFunctorSlotObject<Func2, SlotArgumentCount, typename QtPrivate::List_Left<typename SignalType::Arguments, SlotArgumentCount>::Value, typename SignalType::ReturnType>(slot), - Qt::DirectConnection, 0, &SignalType::Object::staticMetaObject); + type, types, &SignalType::Object::staticMetaObject); } #endif //Q_QDOC diff --git a/src/corelib/kernel/qobjectdefs_impl.h b/src/corelib/kernel/qobjectdefs_impl.h index d775ef1b65..fb6601f21b 100644 --- a/src/corelib/kernel/qobjectdefs_impl.h +++ b/src/corelib/kernel/qobjectdefs_impl.h @@ -129,7 +129,7 @@ namespace QtPrivate { its call function is the same as the FunctionPointer::call function. */ #ifndef Q_COMPILER_VARIADIC_TEMPLATES - template<typename Func> struct FunctionPointer { enum {ArgumentCount = -1}; }; + template<typename Func> struct FunctionPointer { enum {ArgumentCount = -1, IsPointerToMemberFunction = false}; }; //Pointers to member functions template<class Obj, typename Ret> struct FunctionPointer<Ret (Obj::*) ()> { @@ -137,7 +137,7 @@ namespace QtPrivate { typedef void Arguments; typedef Ret ReturnType; typedef Ret (Obj::*Function) (); - enum {ArgumentCount = 0}; + enum {ArgumentCount = 0, IsPointerToMemberFunction = true}; template <typename Args, typename R> static void call(Function f, Obj *o, void **arg) { (o->*f)(), ApplyReturnValue<R>(arg[0]); } }; @@ -147,7 +147,7 @@ namespace QtPrivate { typedef List<Arg1, void> Arguments; typedef Ret ReturnType; typedef Ret (Obj::*Function) (Arg1); - enum {ArgumentCount = 1}; + enum {ArgumentCount = 1, IsPointerToMemberFunction = true}; template <typename Args, typename R> static void call(Function f, Obj *o, void **arg) { (o->*f)((*reinterpret_cast<typename RemoveRef<typename Args::Car>::Type *>(arg[1]))), ApplyReturnValue<R>(arg[0]); @@ -159,7 +159,7 @@ namespace QtPrivate { typedef List<Arg1, List<Arg2, void> > Arguments; typedef Ret ReturnType; typedef Ret (Obj::*Function) (Arg1, Arg2); - enum {ArgumentCount = 2}; + enum {ArgumentCount = 2, IsPointerToMemberFunction = true}; template <typename Args, typename R> static void call(Function f, Obj *o, void **arg) { (o->*f)( *reinterpret_cast<typename RemoveRef<typename List_Select<Args, 0>::Value>::Type *>(arg[1]), @@ -172,7 +172,7 @@ namespace QtPrivate { typedef List<Arg1, List<Arg2, List<Arg3, void> > > Arguments; typedef Ret ReturnType; typedef Ret (Obj::*Function) (Arg1, Arg2, Arg3); - enum {ArgumentCount = 3}; + enum {ArgumentCount = 3, IsPointerToMemberFunction = true}; template <typename Args, typename R> static void call(Function f, Obj *o, void **arg) { (o->*f)( *reinterpret_cast<typename RemoveRef<typename List_Select<Args, 0>::Value>::Type *>(arg[1]), @@ -186,7 +186,7 @@ namespace QtPrivate { typedef List<Arg1, List<Arg2, List<Arg3, List<Arg4, void> > > > Arguments; typedef Ret ReturnType; typedef Ret (Obj::*Function) (Arg1, Arg2, Arg3, Arg4); - enum {ArgumentCount = 4}; + enum {ArgumentCount = 4, IsPointerToMemberFunction = true}; template <typename Args, typename R> static void call(Function f, Obj *o, void **arg) { (o->*f)( *reinterpret_cast<typename RemoveRef<typename List_Select<Args, 0>::Value>::Type *>(arg[1]), @@ -201,7 +201,7 @@ namespace QtPrivate { typedef List<Arg1, List<Arg2, List<Arg3, List<Arg4, List<Arg5, void> > > > > Arguments; typedef Ret ReturnType; typedef Ret (Obj::*Function) (Arg1, Arg2, Arg3, Arg4, Arg5); - enum {ArgumentCount = 5}; + enum {ArgumentCount = 5, IsPointerToMemberFunction = true}; template <typename Args, typename R> static void call(Function f, Obj *o, void **arg) { (o->*f)( *reinterpret_cast<typename RemoveRef<typename List_Select<Args, 0>::Value>::Type *>(arg[1]), @@ -218,7 +218,7 @@ namespace QtPrivate { typedef List<Arg1, List<Arg2, List<Arg3, List<Arg4, List<Arg5, List<Arg6, void> > > > > > Arguments; typedef Ret ReturnType; typedef Ret (Obj::*Function) (Arg1, Arg2, Arg3, Arg4, Arg5, Arg6); - enum {ArgumentCount = 6}; + enum {ArgumentCount = 6, IsPointerToMemberFunction = true}; template <typename Args, typename R> static void call(Function f, Obj *o, void **arg) { (o->*f)( *reinterpret_cast<typename RemoveRef<typename List_Select<Args, 0>::Value>::Type *>(arg[1]), @@ -237,7 +237,7 @@ namespace QtPrivate { typedef void Arguments; typedef Ret ReturnType; typedef Ret (Obj::*Function) () const; - enum {ArgumentCount = 0}; + enum {ArgumentCount = 0, IsPointerToMemberFunction = true}; template <typename Args, typename R> static void call(Function f, Obj *o, void **arg) { (o->*f)(), ApplyReturnValue<R>(arg[0]); } }; @@ -247,7 +247,7 @@ namespace QtPrivate { typedef List<Arg1, void> Arguments; typedef Ret ReturnType; typedef Ret (Obj::*Function) (Arg1) const; - enum {ArgumentCount = 1}; + enum {ArgumentCount = 1, IsPointerToMemberFunction = true}; template <typename Args, typename R> static void call(Function f, Obj *o, void **arg) { (o->*f)((*reinterpret_cast<typename RemoveRef<typename Args::Car>::Type *>(arg[1]))), ApplyReturnValue<R>(arg[0]); @@ -259,7 +259,7 @@ namespace QtPrivate { typedef List<Arg1, List<Arg2, void> > Arguments; typedef Ret ReturnType; typedef Ret (Obj::*Function) (Arg1, Arg2) const; - enum {ArgumentCount = 2}; + enum {ArgumentCount = 2, IsPointerToMemberFunction = true}; template <typename Args, typename R> static void call(Function f, Obj *o, void **arg) { (o->*f)( *reinterpret_cast<typename RemoveRef<typename List_Select<Args, 0>::Value>::Type *>(arg[1]), @@ -272,7 +272,7 @@ namespace QtPrivate { typedef List<Arg1, List<Arg2, List<Arg3, void> > > Arguments; typedef Ret ReturnType; typedef Ret (Obj::*Function) (Arg1, Arg2, Arg3) const; - enum {ArgumentCount = 3}; + enum {ArgumentCount = 3, IsPointerToMemberFunction = true}; template <typename Args, typename R> static void call(Function f, Obj *o, void **arg) { (o->*f)( *reinterpret_cast<typename RemoveRef<typename List_Select<Args, 0>::Value>::Type *>(arg[1]), @@ -286,7 +286,7 @@ namespace QtPrivate { typedef List<Arg1, List<Arg2, List<Arg3, List<Arg4, void> > > > Arguments; typedef Ret ReturnType; typedef Ret (Obj::*Function) (Arg1, Arg2, Arg3, Arg4) const; - enum {ArgumentCount = 4}; + enum {ArgumentCount = 4, IsPointerToMemberFunction = true}; template <typename Args, typename R> static void call(Function f, Obj *o, void **arg) { (o->*f)( *reinterpret_cast<typename RemoveRef<typename List_Select<Args, 0>::Value>::Type *>(arg[1]), @@ -301,7 +301,7 @@ namespace QtPrivate { typedef List<Arg1, List<Arg2, List<Arg3, List<Arg4, List<Arg5, void> > > > > Arguments; typedef Ret ReturnType; typedef Ret (Obj::*Function) (Arg1, Arg2, Arg3, Arg4, Arg5) const; - enum {ArgumentCount = 5}; + enum {ArgumentCount = 5, IsPointerToMemberFunction = true}; template <typename Args, typename R> static void call(Function f, Obj *o, void **arg) { (o->*f)( *reinterpret_cast<typename RemoveRef<typename List_Select<Args, 0>::Value>::Type *>(arg[1]), @@ -318,7 +318,7 @@ namespace QtPrivate { typedef List<Arg1, List<Arg2, List<Arg3, List<Arg4, List<Arg5, List<Arg6, void> > > > > > Arguments; typedef Ret ReturnType; typedef Ret (Obj::*Function) (Arg1, Arg2, Arg3, Arg4, Arg5, Arg6) const; - enum {ArgumentCount = 6}; + enum {ArgumentCount = 6, IsPointerToMemberFunction = true}; template <typename Args, typename R> static void call(Function f, Obj *o, void **arg) { (o->*f)( *reinterpret_cast<typename RemoveRef<typename List_Select<Args, 0>::Value>::Type *>(arg[1]), @@ -336,7 +336,7 @@ namespace QtPrivate { typedef void Arguments; typedef Ret (*Function) (); typedef Ret ReturnType; - enum {ArgumentCount = 0}; + enum {ArgumentCount = 0, IsPointerToMemberFunction = false}; template <typename Args, typename R> static void call(Function f, void *, void **arg) { f(), ApplyReturnValue<R>(arg[0]); } }; @@ -345,7 +345,7 @@ namespace QtPrivate { typedef List<Arg1, void> Arguments; typedef Ret ReturnType; typedef Ret (*Function) (Arg1); - enum {ArgumentCount = 1}; + enum {ArgumentCount = 1, IsPointerToMemberFunction = false}; template <typename Args, typename R> static void call(Function f, void *, void **arg) { f(*reinterpret_cast<typename RemoveRef<typename List_Select<Args, 0>::Value>::Type *>(arg[1])), ApplyReturnValue<R>(arg[0]); } @@ -355,7 +355,7 @@ namespace QtPrivate { typedef List<Arg1, List<Arg2, void> > Arguments; typedef Ret ReturnType; typedef Ret (*Function) (Arg1, Arg2); - enum {ArgumentCount = 2}; + enum {ArgumentCount = 2, IsPointerToMemberFunction = false}; template <typename Args, typename R> static void call(Function f, void *, void **arg) { f(*reinterpret_cast<typename RemoveRef<typename List_Select<Args, 0>::Value>::Type *>(arg[1]), @@ -366,7 +366,7 @@ namespace QtPrivate { typedef List<Arg1, List<Arg2, List<Arg3, void> > > Arguments; typedef Ret ReturnType; typedef Ret (*Function) (Arg1, Arg2, Arg3); - enum {ArgumentCount = 3}; + enum {ArgumentCount = 3, IsPointerToMemberFunction = false}; template <typename Args, typename R> static void call(Function f, void *, void **arg) { f( *reinterpret_cast<typename RemoveRef<typename List_Select<Args, 0>::Value>::Type *>(arg[1]), @@ -379,7 +379,7 @@ namespace QtPrivate { typedef List<Arg1, List<Arg2, List<Arg3, List<Arg4, void> > > > Arguments; typedef Ret ReturnType; typedef Ret (*Function) (Arg1, Arg2, Arg3, Arg4); - enum {ArgumentCount = 4}; + enum {ArgumentCount = 4, IsPointerToMemberFunction = false}; template <typename Args, typename R> static void call(Function f, void *, void **arg) { f( *reinterpret_cast<typename RemoveRef<typename List_Select<Args, 0>::Value>::Type *>(arg[1]), @@ -394,7 +394,7 @@ namespace QtPrivate { List<Arg4, List<Arg5, void > > > > > Arguments; typedef Ret ReturnType; typedef Ret (*Function) (Arg1, Arg2, Arg3, Arg4, Arg5); - enum {ArgumentCount = 5}; + enum {ArgumentCount = 5, IsPointerToMemberFunction = false}; template <typename Args, typename R> static void call(Function f, void *, void **arg) { f( *reinterpret_cast<typename RemoveRef<typename List_Select<Args, 0>::Value>::Type *>(arg[1]), @@ -409,7 +409,7 @@ namespace QtPrivate { typedef List<Arg1, List<Arg2, List<Arg3, List<Arg4, List<Arg5, List<Arg6, void> > > > > > Arguments; typedef Ret ReturnType; typedef Ret (*Function) (Arg1, Arg2, Arg3, Arg4, Arg5, Arg6); - enum {ArgumentCount = 6}; + enum {ArgumentCount = 6, IsPointerToMemberFunction = false}; template <typename Args, typename R> static void call(Function f, void *, void **arg) { f( *reinterpret_cast<typename RemoveRef<typename List_Select<Args, 0>::Value>::Type *>(arg[1]), @@ -493,7 +493,7 @@ namespace QtPrivate { template <int N> struct Indexes { typedef typename IndexesAppend<typename Indexes<N - 1>::Value, N - 1>::Value Value; }; template <> struct Indexes<0> { typedef IndexesList<> Value; }; - template<typename Func> struct FunctionPointer { enum {ArgumentCount = -1}; }; + template<typename Func> struct FunctionPointer { enum {ArgumentCount = -1, IsPointerToMemberFunction = false}; }; template <typename, typename, typename, typename> struct FunctorCall; template <int... II, typename... SignalArgs, typename R, typename Function> @@ -521,7 +521,7 @@ namespace QtPrivate { typedef List<Args...> Arguments; typedef Ret ReturnType; typedef Ret (Obj::*Function) (Args...); - enum {ArgumentCount = sizeof...(Args)}; + enum {ArgumentCount = sizeof...(Args), IsPointerToMemberFunction = true}; template <typename SignalArgs, typename R> static void call(Function f, Obj *o, void **arg) { FunctorCall<typename Indexes<ArgumentCount>::Value, SignalArgs, R, Function>::call(f, o, arg); @@ -533,7 +533,7 @@ namespace QtPrivate { typedef List<Args...> Arguments; typedef Ret ReturnType; typedef Ret (Obj::*Function) (Args...) const; - enum {ArgumentCount = sizeof...(Args)}; + enum {ArgumentCount = sizeof...(Args), IsPointerToMemberFunction = true}; template <typename SignalArgs, typename R> static void call(Function f, Obj *o, void **arg) { FunctorCall<typename Indexes<ArgumentCount>::Value, SignalArgs, R, Function>::call(f, o, arg); @@ -545,7 +545,7 @@ namespace QtPrivate { typedef List<Args...> Arguments; typedef Ret ReturnType; typedef Ret (*Function) (Args...); - enum {ArgumentCount = sizeof...(Args)}; + enum {ArgumentCount = sizeof...(Args), IsPointerToMemberFunction = false}; template <typename SignalArgs, typename R> static void call(Function f, void *, void **arg) { FunctorCall<typename Indexes<ArgumentCount>::Value, SignalArgs, R, Function>::call(f, arg); diff --git a/tests/auto/corelib/kernel/qobject/tst_qobject.cpp b/tests/auto/corelib/kernel/qobject/tst_qobject.cpp index 081dc8a857..05d81c2bd1 100644 --- a/tests/auto/corelib/kernel/qobject/tst_qobject.cpp +++ b/tests/auto/corelib/kernel/qobject/tst_qobject.cpp @@ -90,7 +90,7 @@ private slots: void thread(); void thread0(); void moveToThread(); - void sender(); + void senderTest(); void declareInterface(); void qpointerResetBeforeDestroyedSignal(); void testUserData(); @@ -144,7 +144,11 @@ private slots: void connectPrivateSlots(); void connectFunctorArgDifference(); void connectFunctorOverloads(); + void connectFunctorQueued(); + void connectFunctorWithContext(); + void connectStaticSlotWithObject(); void disconnectDoesNotLeakFunctor(); + void contextDoesNotLeakFunctor(); void connectBase(); void qmlConnect(); }; @@ -2152,7 +2156,7 @@ signals: void theSignal(); }; -void tst_QObject::sender() +void tst_QObject::senderTest() { { SuperObject sender; @@ -5602,6 +5606,129 @@ void tst_QObject::connectFunctorArgDifference() QVERIFY(true); } +class ContextObject : public QObject +{ + Q_OBJECT +public: + void compareSender(QObject *s) { QCOMPARE(s, sender()); } +}; + +struct SlotArgFunctor +{ + SlotArgFunctor(int *s) : status(s), context(Q_NULLPTR), sender(Q_NULLPTR) {} + SlotArgFunctor(ContextObject *context, QObject *sender, int *s) : status(s), context(context), sender(sender) {} + void operator()() { *status = 2; if (context) context->compareSender(sender); } + +protected: + int *status; + ContextObject *context; + QObject *sender; +}; + +void tst_QObject::connectFunctorQueued() +{ + int status = 1; + SenderObject obj; + QEventLoop e; + + connect(&obj, &SenderObject::signal1, this, SlotArgFunctor(&status), Qt::QueuedConnection); + connect(&obj, &SenderObject::signal1, &e, &QEventLoop::quit, Qt::QueuedConnection); + + obj.emitSignal1(); + QCOMPARE(status, 1); + e.exec(); + QCOMPARE(status, 2); + +#if defined(Q_COMPILER_LAMBDA) + status = 1; + connect(&obj, &SenderObject::signal1, this, [&status] { status = 2; }, Qt::QueuedConnection); + + obj.emitSignal1(); + QCOMPARE(status, 1); + e.exec(); + QCOMPARE(status, 2); +#endif +} + +void tst_QObject::connectFunctorWithContext() +{ + int status = 1; + SenderObject obj; + QMetaObject::Connection handle; + ContextObject *context = new ContextObject; + QEventLoop e; + + connect(&obj, &SenderObject::signal1, context, SlotArgFunctor(&status)); + connect(&obj, &SenderObject::signal1, &e, &QEventLoop::quit, Qt::QueuedConnection); + + // When the context gets deleted, the connection should decay and the signal shouldn't trigger + // The connection is queued to make sure the destroyed signal propagates correctly and + // cuts the connection. + connect(context, &QObject::destroyed, &obj, &SenderObject::signal1, Qt::QueuedConnection); + context->deleteLater(); + + QCOMPARE(status, 1); + e.exec(); + QCOMPARE(status, 1); + + // Check the sender arg is set correctly in the context + context = new ContextObject; + + connect(&obj, &SenderObject::signal1, context, + SlotArgFunctor(context, &obj, &status), Qt::QueuedConnection); + + obj.emitSignal1(); + QCOMPARE(status, 1); + e.exec(); + QCOMPARE(status, 2); + +#if defined(Q_COMPILER_LAMBDA) + status = 1; + connect(&obj, &SenderObject::signal1, this, [this, &status, &obj] { status = 2; QCOMPARE(sender(), &obj); }, Qt::QueuedConnection); + + obj.emitSignal1(); + QCOMPARE(status, 1); + e.exec(); + QCOMPARE(status, 2); +#endif + + // Free + context->deleteLater(); +} + +static int s_static_slot_checker = 1; + +class StaticSlotChecker : public QObject +{ + Q_OBJECT +public Q_SLOTS: + static void staticSlot() { s_static_slot_checker = 2; } +}; + +void tst_QObject::connectStaticSlotWithObject() +{ + SenderObject sender; + StaticSlotChecker *receiver = new StaticSlotChecker; + QEventLoop e; + + QVERIFY(connect(&sender, &SenderObject::signal1, receiver, &StaticSlotChecker::staticSlot, Qt::QueuedConnection)); + connect(&sender, &SenderObject::signal1, &e, &QEventLoop::quit, Qt::QueuedConnection); + + sender.emitSignal1(); + QCOMPARE(s_static_slot_checker, 1); + e.exec(); + QCOMPARE(s_static_slot_checker, 2); + + s_static_slot_checker = 1; + + connect(receiver, &QObject::destroyed, &sender, &SenderObject::signal1, Qt::QueuedConnection); + receiver->deleteLater(); + + QCOMPARE(s_static_slot_checker, 1); + e.exec(); + QCOMPARE(s_static_slot_checker, 1); +} + struct ComplexFunctor { ComplexFunctor(int &overload, QList<QVariant> &result) : overload(overload), result(result) {} void operator()(int a, int b) { @@ -5774,6 +5901,23 @@ void tst_QObject::disconnectDoesNotLeakFunctor() } QCOMPARE(countedStructObjectsCount, 0); { + GetSenderObject obj; + QMetaObject::Connection c; + { + CountedStruct s(&obj); + QObject context; + QCOMPARE(countedStructObjectsCount, 1); + + c = connect(&obj, &GetSenderObject::aSignal, &context, s); + QVERIFY(c); + QCOMPARE(countedStructObjectsCount, 2); + QVERIFY(QObject::disconnect(c)); + QCOMPARE(countedStructObjectsCount, 1); + } + QCOMPARE(countedStructObjectsCount, 0); + } + QCOMPARE(countedStructObjectsCount, 0); + { QMetaObject::Connection c1, c2; { CountedStruct s; @@ -5857,6 +6001,67 @@ void tst_QObject::disconnectDoesNotLeakFunctor() QCOMPARE(countedStructObjectsCount, 0); } +void tst_QObject::contextDoesNotLeakFunctor() +{ + QCOMPARE(countedStructObjectsCount, 0); + { + QMetaObject::Connection c; + { + QEventLoop e; + ContextObject *context = new ContextObject; + SenderObject obj; + + connect(&obj, &SenderObject::signal1, context, CountedStruct()); + connect(context, &QObject::destroyed, &e, &QEventLoop::quit, Qt::QueuedConnection); + context->deleteLater(); + + QCOMPARE(countedStructObjectsCount, 1); + e.exec(); + QCOMPARE(countedStructObjectsCount, 0); + } + QCOMPARE(countedStructObjectsCount, 0); + } + QCOMPARE(countedStructObjectsCount, 0); + { + GetSenderObject obj; + QMetaObject::Connection c; + { + CountedStruct s(&obj); + QEventLoop e; + ContextObject *context = new ContextObject; + QCOMPARE(countedStructObjectsCount, 1); + + connect(&obj, &GetSenderObject::aSignal, context, s); + QCOMPARE(countedStructObjectsCount, 2); + + connect(context, &QObject::destroyed, &e, &QEventLoop::quit, Qt::QueuedConnection); + context->deleteLater(); + + e.exec(); + QCOMPARE(countedStructObjectsCount, 1); + } + QCOMPARE(countedStructObjectsCount, 0); + } + QCOMPARE(countedStructObjectsCount, 0); + { +#if defined(Q_COMPILER_LAMBDA) + CountedStruct s; + QEventLoop e; + ContextObject *context = new ContextObject; + QCOMPARE(countedStructObjectsCount, 1); + QTimer timer; + + connect(&timer, &QTimer::timeout, context, [s](){}); + QCOMPARE(countedStructObjectsCount, 2); + connect(context, &QObject::destroyed, &e, &QEventLoop::quit, Qt::QueuedConnection); + context->deleteLater(); + e.exec(); + QCOMPARE(countedStructObjectsCount, 1); +#endif // Q_COMPILER_LAMBDA + } + QCOMPARE(countedStructObjectsCount, 0); +} + class SubSender : public SenderObject { Q_OBJECT }; |