QObject::disconnect with new syntax

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>
This commit is contained in:
Olivier Goffart 2011-11-18 10:57:04 +01:00 committed by Qt by Nokia
parent a482487b9f
commit ed0b262de9
5 changed files with 249 additions and 13 deletions

View File

@ -470,7 +470,14 @@ QObject::connect(socket, &QTcpSocket::connected, [=] () {
}); });
//! [46] //! [46]
//! [47]
disconnect(myObject, &MyObject::mySignal(), 0, 0);
//! [47]
//! [48]
QObject::disconnect(lineEdit, &QLineEdit::textChanged,
label, &QLabel::setText);
//! [48]
//! [meta data] //! [meta data]
//: This is a comment for the translator. //: This is a comment for the translator.

View File

@ -142,11 +142,11 @@ struct QMetaObjectPrivate
const QMetaObject *rmeta = 0, const QMetaObject *rmeta = 0,
int type = 0, int *types = 0); int type = 0, int *types = 0);
static bool disconnect(const QObject *sender, int signal_index, 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); DisconnectType = DisconnectAll);
static inline bool disconnectHelper(QObjectPrivate::Connection *c, static inline bool disconnectHelper(QObjectPrivate::Connection *c,
const QObject *receiver, int method_index, const QObject *receiver, int method_index, void **slot,
QMutex *senderMutex, DisconnectType); QMutex *senderMutex, DisconnectType = DisconnectAll);
#endif #endif
}; };

View File

@ -2758,7 +2758,7 @@ bool QObject::disconnect(const QObject *sender, const char *signal,
} }
if (!method) { if (!method) {
res |= QMetaObjectPrivate::disconnect(sender, signal_index, receiver, -1); res |= QMetaObjectPrivate::disconnect(sender, signal_index, receiver, -1, 0);
} else { } else {
const QMetaObject *rmeta = receiver->metaObject(); const QMetaObject *rmeta = receiver->metaObject();
do { do {
@ -2768,7 +2768,7 @@ bool QObject::disconnect(const QObject *sender, const char *signal,
rmeta = rmeta->superClass(); rmeta = rmeta->superClass();
if (method_index < 0) if (method_index < 0)
break; break;
res |= QMetaObjectPrivate::disconnect(sender, signal_index, receiver, method_index); res |= QMetaObjectPrivate::disconnect(sender, signal_index, receiver, method_index, 0);
method_found = true; method_found = true;
} while ((rmeta = rmeta->superClass())); } while ((rmeta = rmeta->superClass()));
} }
@ -2881,7 +2881,7 @@ bool QObject::disconnect(const QObject *sender, const QMetaMethod &signal,
return false; return false;
} }
if (!QMetaObjectPrivate::disconnect(sender, signal_index, receiver, method_index)) if (!QMetaObjectPrivate::disconnect(sender, signal_index, receiver, method_index, 0))
return false; return false;
const_cast<QObject*>(sender)->disconnectNotify(method.mobj ? signalSignature.constData() : 0); 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); signal_index = methodIndexToSignalIndex(sender->metaObject(), signal_index);
return QMetaObjectPrivate::disconnect(sender, signal_index, return QMetaObjectPrivate::disconnect(sender, signal_index,
receiver, method_index); receiver, method_index, 0);
} }
/*!\internal /*!\internal
@ -3086,7 +3086,7 @@ bool QMetaObject::disconnectOne(const QObject *sender, int signal_index,
{ {
signal_index = methodIndexToSignalIndex(sender->metaObject(), signal_index); signal_index = methodIndexToSignalIndex(sender->metaObject(), signal_index);
return QMetaObjectPrivate::disconnect(sender, signal_index, return QMetaObjectPrivate::disconnect(sender, signal_index,
receiver, method_index, receiver, method_index, 0,
QMetaObjectPrivate::DisconnectOne); 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 Helper function to remove the connection from the senders list and setting the receivers to 0
*/ */
bool QMetaObjectPrivate::disconnectHelper(QObjectPrivate::Connection *c, bool QMetaObjectPrivate::disconnectHelper(QObjectPrivate::Connection *c,
const QObject *receiver, int method_index, const QObject *receiver, int method_index, void **slot,
QMutex *senderMutex, DisconnectType disconnectType) QMutex *senderMutex, DisconnectType disconnectType)
{ {
bool success = false; bool success = false;
while (c) { while (c) {
if (c->receiver if (c->receiver
&& (receiver == 0 || (c->receiver == 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; bool needToUnlock = false;
QMutex *receiverMutex = 0; QMutex *receiverMutex = 0;
if (!receiver) { 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 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, 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) DisconnectType disconnectType)
{ {
if (!sender) 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) { for (signal_index = -1; signal_index < connectionLists->count(); ++signal_index) {
QObjectPrivate::Connection *c = QObjectPrivate::Connection *c =
(*connectionLists)[signal_index].first; (*connectionLists)[signal_index].first;
if (disconnectHelper(c, receiver, method_index, senderMutex, disconnectType)) { if (disconnectHelper(c, receiver, method_index, slot, senderMutex, disconnectType)) {
success = true; success = true;
connectionLists->dirty = true; connectionLists->dirty = true;
} }
@ -3167,7 +3168,7 @@ bool QMetaObjectPrivate::disconnect(const QObject *sender, int signal_index,
} else if (signal_index < connectionLists->count()) { } else if (signal_index < connectionLists->count()) {
QObjectPrivate::Connection *c = QObjectPrivate::Connection *c =
(*connectionLists)[signal_index].first; (*connectionLists)[signal_index].first;
if (disconnectHelper(c, receiver, method_index, senderMutex, disconnectType)) { if (disconnectHelper(c, receiver, method_index, slot, senderMutex, disconnectType)) {
success = true; success = true;
connectionLists->dirty = true; connectionLists->dirty = true;
} }
@ -4212,6 +4213,88 @@ bool QObject::disconnect(const QMetaObject::Connection &connection)
return true; 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 /*! \class QMetaObject::Connection
Represents a handle to a signal-slot connection. Represents a handle to a signal-slot connection.
It can be used to disconnect that connection, or check if 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 QT_END_NAMESPACE
#include "moc_qobject.cpp" #include "moc_qobject.cpp"

View File

@ -273,6 +273,33 @@ public:
{ return disconnect(this, 0, receiver, member); } { return disconnect(this, 0, receiver, member); }
static bool disconnect(const QMetaObject::Connection &); 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 dumpObjectTree();
void dumpObjectInfo(); void dumpObjectInfo();
@ -340,6 +367,7 @@ private:
QSlotObjectBase() : ref(1) {} QSlotObjectBase() : ref(1) {}
virtual ~QSlotObjectBase(); virtual ~QSlotObjectBase();
virtual void call(QObject *receiver, void **a) = 0; 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 // 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. // 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) { virtual void call(QObject *receiver, void **a) {
FuncType::template call<Args, R>(function, static_cast<typename FuncType::Object *>(receiver), 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 // 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. // 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, static QMetaObject::Connection connectImpl(const QObject *sender, void **signal, const QObject *receiver, QSlotObjectBase *slot,
Qt::ConnectionType type, const int *types, const QMetaObject *senderMetaObject); 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, inline QMetaObject::Connection QObject::connect(const QObject *asender, const char *asignal,

View File

@ -122,6 +122,7 @@ private slots:
void autoConnectionBehavior(); void autoConnectionBehavior();
void baseDestroyed(); void baseDestroyed();
void pointerConnect(); void pointerConnect();
void pointerDisconnect();
void emitInDefinedOrderPointer(); void emitInDefinedOrderPointer();
void customTypesPointer(); void customTypesPointer();
void connectCxx0x(); void connectCxx0x();
@ -4104,6 +4105,110 @@ void tst_QObject::pointerConnect()
delete r2; 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() void tst_QObject::emitInDefinedOrderPointer()
{ {
SenderObject sender; SenderObject sender;