QObject::disconnect with new syntax
authorOlivier Goffart <ogoffart@woboq.com>
Fri, 18 Nov 2011 09:57:04 +0000 (10:57 +0100)
committerQt by Nokia <qt-info@nokia.com>
Tue, 29 Nov 2011 16:08:39 +0000 (17:08 +0100)
This add an overload to disconnect which is symetrical to the new
syntax of connect.

It is possible to diconnect connection like this:

QObject::connect( sender, &Sender::valueChanged,
                  receiver, &Receiver::updateValue );
QObject::disconnect( sender, &Sender::valueChanged,
                     receiver, &Receiver::updateValue );

This overload only work with pointer to member function, and not static
functions or functors.

The test is copied from tst_QObject::disconnect(), just
changed the syntax of the connection and disconnection

Change-Id: Ia8f819100cb12098e32877522b97b732b1e676a8
Reviewed-by: Bradley T. Hughes <bradley.hughes@nokia.com>
doc/src/snippets/code/src_corelib_kernel_qobject.cpp
src/corelib/kernel/qmetaobject_p.h
src/corelib/kernel/qobject.cpp
src/corelib/kernel/qobject.h
tests/auto/corelib/kernel/qobject/tst_qobject.cpp

index 11b70cc..b6d0e39 100644 (file)
@@ -470,7 +470,14 @@ QObject::connect(socket, &QTcpSocket::connected, [=] () {
     });
 //! [46]
 
+//! [47]
+disconnect(myObject, &MyObject::mySignal(), 0, 0);
+//! [47]
 
+//! [48]
+QObject::disconnect(lineEdit, &QLineEdit::textChanged,
+                 label,  &QLabel::setText);
+//! [48]
 
 //! [meta data]
 //: This is a comment for the translator.
index 0476558..b99907e 100644 (file)
@@ -142,11 +142,11 @@ struct QMetaObjectPrivate
                         const QMetaObject *rmeta = 0,
                         int type = 0, int *types = 0);
     static bool disconnect(const QObject *sender, int signal_index,
-                           const QObject *receiver, int method_index,
+                           const QObject *receiver, int method_index, void **slot,
                            DisconnectType = DisconnectAll);
     static inline bool disconnectHelper(QObjectPrivate::Connection *c,
-                                        const QObject *receiver, int method_index,
-                                        QMutex *senderMutex, DisconnectType);
+                                        const QObject *receiver, int method_index, void **slot,
+                                        QMutex *senderMutex, DisconnectType = DisconnectAll);
 #endif
 };
 
index 2be0a7c..fc7df79 100644 (file)
@@ -2758,7 +2758,7 @@ bool QObject::disconnect(const QObject *sender, const char *signal,
         }
 
         if (!method) {
-            res |= QMetaObjectPrivate::disconnect(sender, signal_index, receiver, -1);
+            res |= QMetaObjectPrivate::disconnect(sender, signal_index, receiver, -1, 0);
         } else {
             const QMetaObject *rmeta = receiver->metaObject();
             do {
@@ -2768,7 +2768,7 @@ bool QObject::disconnect(const QObject *sender, const char *signal,
                             rmeta = rmeta->superClass();
                 if (method_index < 0)
                     break;
-                res |= QMetaObjectPrivate::disconnect(sender, signal_index, receiver, method_index);
+                res |= QMetaObjectPrivate::disconnect(sender, signal_index, receiver, method_index, 0);
                 method_found = true;
             } while ((rmeta = rmeta->superClass()));
         }
@@ -2881,7 +2881,7 @@ bool QObject::disconnect(const QObject *sender, const QMetaMethod &signal,
         return false;
     }
 
-    if (!QMetaObjectPrivate::disconnect(sender, signal_index, receiver, method_index))
+    if (!QMetaObjectPrivate::disconnect(sender, signal_index, receiver, method_index, 0))
         return false;
 
     const_cast<QObject*>(sender)->disconnectNotify(method.mobj ? signalSignature.constData() : 0);
@@ -3072,7 +3072,7 @@ bool QMetaObject::disconnect(const QObject *sender, int signal_index,
 {
     signal_index = methodIndexToSignalIndex(sender->metaObject(), signal_index);
     return QMetaObjectPrivate::disconnect(sender, signal_index,
-                                          receiver, method_index);
+                                          receiver, method_index, 0);
 }
 
 /*!\internal
@@ -3086,7 +3086,7 @@ bool QMetaObject::disconnectOne(const QObject *sender, int signal_index,
 {
     signal_index = methodIndexToSignalIndex(sender->metaObject(), signal_index);
     return QMetaObjectPrivate::disconnect(sender, signal_index,
-                                          receiver, method_index,
+                                          receiver, method_index, 0,
                                           QMetaObjectPrivate::DisconnectOne);
 }
 
@@ -3094,14 +3094,15 @@ bool QMetaObject::disconnectOne(const QObject *sender, int signal_index,
     Helper function to remove the connection from the senders list and setting the receivers to 0
  */
 bool QMetaObjectPrivate::disconnectHelper(QObjectPrivate::Connection *c,
-                                          const QObject *receiver, int method_index,
+                                          const QObject *receiver, int method_index, void **slot,
                                           QMutex *senderMutex, DisconnectType disconnectType)
 {
     bool success = false;
     while (c) {
         if (c->receiver
             && (receiver == 0 || (c->receiver == receiver
-                           && (method_index < 0 || c->method() == method_index)))) {
+                           && (method_index < 0 || c->method() == method_index)
+                           && (slot == 0 || (c->isSlotObject && c->slotObj->compare(slot)))))) {
             bool needToUnlock = false;
             QMutex *receiverMutex = 0;
             if (!receiver) {
@@ -3134,7 +3135,7 @@ bool QMetaObjectPrivate::disconnectHelper(QObjectPrivate::Connection *c,
     Same as the QMetaObject::disconnect, but \a signal_index must be the result of QObjectPrivate::signalIndex
  */
 bool QMetaObjectPrivate::disconnect(const QObject *sender, int signal_index,
-                                    const QObject *receiver, int method_index,
+                                    const QObject *receiver, int method_index, void **slot,
                                     DisconnectType disconnectType)
 {
     if (!sender)
@@ -3159,7 +3160,7 @@ bool QMetaObjectPrivate::disconnect(const QObject *sender, int signal_index,
         for (signal_index = -1; signal_index < connectionLists->count(); ++signal_index) {
             QObjectPrivate::Connection *c =
                 (*connectionLists)[signal_index].first;
-            if (disconnectHelper(c, receiver, method_index, senderMutex, disconnectType)) {
+            if (disconnectHelper(c, receiver, method_index, slot, senderMutex, disconnectType)) {
                 success = true;
                 connectionLists->dirty = true;
             }
@@ -3167,7 +3168,7 @@ bool QMetaObjectPrivate::disconnect(const QObject *sender, int signal_index,
     } else if (signal_index < connectionLists->count()) {
         QObjectPrivate::Connection *c =
             (*connectionLists)[signal_index].first;
-        if (disconnectHelper(c, receiver, method_index, senderMutex, disconnectType)) {
+        if (disconnectHelper(c, receiver, method_index, slot, senderMutex, disconnectType)) {
             success = true;
             connectionLists->dirty = true;
         }
@@ -4212,6 +4213,88 @@ bool QObject::disconnect(const QMetaObject::Connection &connection)
     return true;
 }
 
+/*! \fn bool QObject::disconnect(const QObject *sender, (T::*signal)(...), const Qbject *receiver, (T::*method)(...))
+    \threadsafe
+    \overload
+
+    Disconnects \a signal in object \a sender from \a method in object
+    \a receiver. Returns true if the connection is successfully broken;
+    otherwise returns false.
+
+    A signal-slot connection is removed when either of the objects
+    involved are destroyed.
+
+    disconnect() is typically used in three ways, as the following
+    examples demonstrate.
+    \list 1
+    \i Disconnect everything connected to an object's signals:
+
+       \snippet doc/src/snippets/code/src_corelib_kernel_qobject.cpp 26
+
+    \i Disconnect everything connected to a specific signal:
+
+       \snippet doc/src/snippets/code/src_corelib_kernel_qobject.cpp 47
+
+    \i Disconnect a specific receiver:
+
+       \snippet doc/src/snippets/code/src_corelib_kernel_qobject.cpp 30
+
+    \i Disconnect a connection from one specific signal to a specific slot:
+
+       \snippet doc/src/snippets/code/src_corelib_kernel_qobject.cpp 48
+
+
+    \endlist
+
+    0 may be used as a wildcard, meaning "any signal", "any receiving
+    object", or "any slot in the receiving object", respectively.
+
+    The \a sender may never be 0. (You cannot disconnect signals from
+    more than one object in a single call.)
+
+    If \a signal is 0, it disconnects \a receiver and \a method from
+    any signal. If not, only the specified signal is disconnected.
+
+    If \a receiver is 0, it disconnects anything connected to \a
+    signal. If not, slots in objects other than \a receiver are not
+    disconnected.
+
+    If \a method is 0, it disconnects anything that is connected to \a
+    receiver. If not, only slots named \a method will be disconnected,
+    and all other slots are left alone. The \a method must be 0 if \a
+    receiver is left out, so you cannot disconnect a
+    specifically-named slot on all objects.
+
+    \note It is not possible to use this overload to diconnect signals
+    connected to functors or lambda expressions. That is because it is not
+    possible to compare them. Instead, use the olverload that take a
+    QMetaObject::Connection
+
+    \sa connect()
+*/
+bool QObject::disconnectImpl(const QObject *sender, void **signal, const QObject *receiver, void **slot, const QMetaObject *senderMetaObject)
+{
+    if (sender == 0 || (receiver == 0 && slot != 0)) {
+        qWarning("Object::disconnect: Unexpected null parameter");
+        return false;
+    }
+
+    int signal_index = -1;
+    if (signal) {
+        void *args[] = { &signal_index, signal };
+        senderMetaObject->static_metacall(QMetaObject::IndexOfMethod, 0, args);
+        if (signal_index < 0 || signal_index >= QMetaObjectPrivate::get(senderMetaObject)->signalCount) {
+            qWarning("QObject::disconnect: signal not found in %s", senderMetaObject->className());
+            return false;
+        }
+        int signalOffset, methodOffset;
+        computeOffsets(senderMetaObject, &signalOffset, &methodOffset);
+        signal_index += signalOffset;
+    }
+
+    return QMetaObjectPrivate::disconnect(sender, signal_index, receiver, -1, slot);
+}
+
 /*! \class QMetaObject::Connection
      Represents a handle to a signal-slot connection.
      It can be used to disconnect that connection, or check if
@@ -4263,6 +4346,12 @@ QObject::QSlotObjectBase::~QSlotObjectBase()
 {
 }
 
+bool QObject::QSlotObjectBase::compare(void** )
+{
+    return false;
+}
+
+
 QT_END_NAMESPACE
 
 #include "moc_qobject.cpp"
index 27165cd..3b8803c 100644 (file)
@@ -273,6 +273,33 @@ public:
         { return disconnect(this, 0, receiver, member); }
     static bool disconnect(const QMetaObject::Connection &);
 
+    template <typename Func1, typename Func2>
+    static inline bool disconnect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal,
+                                  const typename QtPrivate::FunctionPointer<Func2>::Object *receiver, Func2 slot)
+    {
+        typedef QtPrivate::FunctionPointer<Func1> SignalType;
+        typedef QtPrivate::FunctionPointer<Func2> SlotType;
+        reinterpret_cast<typename SignalType::Object *>(0)->qt_check_for_QOBJECT_macro(*reinterpret_cast<typename SignalType::Object *>(0));
+
+        //compilation error if the arguments does not match.
+        typedef typename QtPrivate::CheckCompatibleArguments<typename SignalType::Arguments, typename SlotType::Arguments>::IncompatibleSignalSlotArguments EnsureCompatibleArguments;
+        return disconnectImpl(sender, reinterpret_cast<void **>(&signal), receiver, reinterpret_cast<void **>(&slot),
+                              &SignalType::Object::staticMetaObject);
+    }
+    template <typename Func1>
+    static inline bool disconnect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal,
+                                  const QObject *receiver, void **zero)
+    {
+        // This is the overload for when one wish to disconnect a signal from any slot. (slot=0)
+        // Since the function template parametter cannot be deduced from '0', we use a
+        // dummy void ** parametter that must be equal to 0
+        Q_ASSERT(!zero);
+        typedef QtPrivate::FunctionPointer<Func1> SignalType;
+        return disconnectImpl(sender, reinterpret_cast<void **>(&signal), receiver, zero,
+                              &SignalType::Object::staticMetaObject);
+    }
+
+
     void dumpObjectTree();
     void dumpObjectInfo();
 
@@ -340,6 +367,7 @@ private:
         QSlotObjectBase() : ref(1) {}
         virtual ~QSlotObjectBase();
         virtual void call(QObject *receiver, void **a) = 0;
+        virtual bool compare(void **);
     };
     // 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.
@@ -351,6 +379,9 @@ private:
         virtual void call(QObject *receiver, void **a) {
             FuncType::template call<Args, R>(function, static_cast<typename FuncType::Object *>(receiver), a);
         }
+        virtual bool compare(void **f) {
+            return *reinterpret_cast<Func *>(f) == function;
+        }
     };
     // 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.
@@ -378,6 +409,10 @@ private:
 
     static QMetaObject::Connection connectImpl(const QObject *sender, void **signal, const QObject *receiver, QSlotObjectBase *slot,
                                                Qt::ConnectionType type, const int *types, const QMetaObject *senderMetaObject);
+
+    static bool disconnectImpl(const QObject *sender, void **signal, const QObject *receiver, void **slot,
+                               const QMetaObject *senderMetaObject);
+
 };
 
 inline QMetaObject::Connection QObject::connect(const QObject *asender, const char *asignal,
index 77e9a9e..f5d06d2 100644 (file)
@@ -122,6 +122,7 @@ private slots:
     void autoConnectionBehavior();
     void baseDestroyed();
     void pointerConnect();
+    void pointerDisconnect();
     void emitInDefinedOrderPointer();
     void customTypesPointer();
     void connectCxx0x();
@@ -4104,6 +4105,110 @@ void tst_QObject::pointerConnect()
     delete r2;
 }
 
+void tst_QObject::pointerDisconnect()
+{
+    SenderObject *s = new SenderObject;
+    ReceiverObject *r1 = new ReceiverObject;
+    ReceiverObject *r2 = new ReceiverObject;
+
+    connect( s, &SenderObject::signal1, r1, &ReceiverObject::slot1 );
+
+    connect( s, &SenderObject::signal2, r1, &ReceiverObject::slot2 );
+    connect( s, &SenderObject::signal3, r1, &ReceiverObject::slot3 );
+    connect( s, &SenderObject::signal4, r1, &ReceiverObject::slot4 );
+
+    s->emitSignal1();
+    s->emitSignal2();
+    s->emitSignal3();
+    s->emitSignal4();
+
+    QCOMPARE( r1->called(1), TRUE );
+    QCOMPARE( r1->called(2), TRUE );
+    QCOMPARE( r1->called(3), TRUE );
+    QCOMPARE( r1->called(4), TRUE );
+    r1->reset();
+
+    // usual disconnect with all parameters given
+    bool ret = QObject::disconnect( s, &SenderObject::signal1, r1, &ReceiverObject::slot1 );
+
+    s->emitSignal1();
+
+    QCOMPARE( r1->called(1), FALSE );
+    r1->reset();
+
+    QCOMPARE( ret, TRUE );
+    ret = QObject::disconnect( s, &SenderObject::signal1, r1, &ReceiverObject::slot1 );
+    QCOMPARE( ret, FALSE  );
+
+    // disconnect all signals from s from all slots from r1
+    QObject::disconnect( s, 0, r1, 0 );
+
+    s->emitSignal2();
+    s->emitSignal3();
+    s->emitSignal4();
+
+    QCOMPARE( r1->called(2), FALSE );
+    QCOMPARE( r1->called(3), FALSE );
+    QCOMPARE( r1->called(4), FALSE );
+    r1->reset();
+
+    connect( s, &SenderObject::signal1, r1, &ReceiverObject::slot1 );
+    connect( s, &SenderObject::signal1, r1, &ReceiverObject::slot2 );
+    connect( s, &SenderObject::signal1, r1, &ReceiverObject::slot3 );
+    connect( s, &SenderObject::signal2, r1, &ReceiverObject::slot4 );
+
+    // disconnect s's signal1() from all slots of r1
+    QObject::disconnect( s, &SenderObject::signal1, r1, 0 );
+
+    s->emitSignal1();
+    s->emitSignal2();
+
+    QCOMPARE( r1->called(1), FALSE );
+    QCOMPARE( r1->called(2), FALSE );
+    QCOMPARE( r1->called(3), FALSE );
+    QCOMPARE( r1->called(4), TRUE );
+    r1->reset();
+    // make sure all is disconnected again
+    QObject::disconnect( s, 0, r1, 0 );
+
+    connect( s, &SenderObject::signal1, r1, &ReceiverObject::slot1 );
+    connect( s, &SenderObject::signal1, r2, &ReceiverObject::slot1 );
+    connect( s, &SenderObject::signal2, r1, &ReceiverObject::slot2 );
+    connect( s, &SenderObject::signal2, r2, &ReceiverObject::slot2 );
+    connect( s, &SenderObject::signal3, r1, &ReceiverObject::slot3 );
+    connect( s, &SenderObject::signal3, r2, &ReceiverObject::slot3 );
+
+    // disconnect signal1() from all receivers
+    QObject::disconnect( s, &SenderObject::signal1, 0, 0 );
+    s->emitSignal1();
+    s->emitSignal2();
+    s->emitSignal3();
+
+    QCOMPARE( r1->called(1), FALSE );
+    QCOMPARE( r2->called(1), FALSE );
+    QCOMPARE( r1->called(2), TRUE );
+    QCOMPARE( r2->called(2), TRUE );
+    QCOMPARE( r1->called(2), TRUE );
+    QCOMPARE( r2->called(2), TRUE );
+
+    r1->reset();
+    r2->reset();
+
+    // disconnect all signals of s from all receivers
+    QObject::disconnect( s, 0, 0, 0 );
+
+    QCOMPARE( r1->called(2), FALSE );
+    QCOMPARE( r2->called(2), FALSE );
+    QCOMPARE( r1->called(2), FALSE );
+    QCOMPARE( r2->called(2), FALSE );
+
+    delete r2;
+    delete r1;
+    delete s;
+
+}
+
+
 void tst_QObject::emitInDefinedOrderPointer()
 {
     SenderObject sender;