summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOlivier Goffart <ogoffart@woboq.com>2011-11-11 17:01:06 +0100
committerQt by Nokia <qt-info@nokia.com>2011-11-25 01:12:14 +0100
commit79f675a1e0f628bbc25345ebc1eb1f5809166c6b (patch)
tree998a846e047fcadb588d9922f6aa8aaa6bd562e0
parente759f9580ef0a76131c7540015a87968742fce9f (diff)
Change the return value of QObject::connect
From a bool to a handle to to connection. Also added a new overload of disconnect that disconnect a handle This is required because with the new syntax taking lambda or functors, it is the only way to disconnect a connection (as it is impossible to compare functors) The new return value is QMetaObject::Connection, it is a wrapper around the internal QObjectPrivate::Connection. QObjectPrivate::Connection is now reference counted. tst_qglobal.cpp: This test set up an internal callback, and the callback do not set any proper connection handle (and tbh, it would be hard for it to do so). So the returned QMetaObject::Connection is invalid, and ok is false (Internal callbacks are only used for jambi and should probably be removed) Change-Id: I111626fb4f47efc4db5e2ea5bff9da15f08fea7b Reviewed-by: Bradley T. Hughes <bradley.hughes@nokia.com> Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
-rw-r--r--src/corelib/kernel/qmetaobject_p.h2
-rw-r--r--src/corelib/kernel/qobject.cpp190
-rw-r--r--src/corelib/kernel/qobject.h14
-rw-r--r--src/corelib/kernel/qobject_p.h11
-rw-r--r--src/corelib/kernel/qobjectdefs.h27
-rw-r--r--tests/auto/corelib/global/qglobal/tst_qglobal.cpp2
6 files changed, 184 insertions, 62 deletions
diff --git a/src/corelib/kernel/qmetaobject_p.h b/src/corelib/kernel/qmetaobject_p.h
index 919273a605..0476558a92 100644
--- a/src/corelib/kernel/qmetaobject_p.h
+++ b/src/corelib/kernel/qmetaobject_p.h
@@ -137,7 +137,7 @@ struct QMetaObjectPrivate
enum DisconnectType { DisconnectAll, DisconnectOne };
static void memberIndexes(const QObject *obj, const QMetaMethod &member,
int *signalIndex, int *methodIndex);
- static bool connect(const QObject *sender, int signal_index,
+ static QObjectPrivate::Connection *connect(const QObject *sender, int signal_index,
const QObject *receiver, int method_index_relative,
const QMetaObject *rmeta = 0,
int type = 0, int *types = 0);
diff --git a/src/corelib/kernel/qobject.cpp b/src/corelib/kernel/qobject.cpp
index 6bca22b6df..59282d3464 100644
--- a/src/corelib/kernel/qobject.cpp
+++ b/src/corelib/kernel/qobject.cpp
@@ -373,7 +373,7 @@ void QObjectPrivate::cleanConnectionLists()
} else {
QObjectPrivate::Connection *next = c->nextConnectionList;
*prev = next;
- delete c;
+ c->deref();
c = next;
}
}
@@ -854,7 +854,7 @@ QObject::~QObject()
while (QObjectPrivate::Connection *c = connectionList.first) {
if (!c->receiver) {
connectionList.first = c->nextConnectionList;
- delete c;
+ c->deref();
continue;
}
@@ -865,11 +865,12 @@ QObject::~QObject()
*c->prev = c->next;
if (c->next) c->next->prev = c->prev;
}
+ c->receiver = 0;
if (needToUnlock)
m->unlock();
connectionList.first = c->nextConnectionList;
- delete c;
+ c->deref();
}
}
@@ -2280,7 +2281,8 @@ static inline void check_and_warn_compat(const QMetaObject *sender, const QMetaM
Creates a connection of the given \a type from the \a signal in
the \a sender object to the \a method in the \a receiver object.
- Returns true if the connection succeeds; otherwise returns false.
+ Returns a handle to the connection that can be used to disconnect
+ it later.
You must use the \c SIGNAL() and \c SLOT() macros when specifying
the \a signal and the \a method, for example:
@@ -2309,11 +2311,12 @@ static inline void check_and_warn_compat(const QMetaObject *sender, const QMetaM
in the same order as the order the connection was made, when the
signal is emitted.
- The function returns true if it successfully connects the signal
- to the slot. It will return false if it cannot create the
- connection, for example, if QObject is unable to verify the
- existence of either \a signal or \a method, or if their signatures
- aren't compatible.
+ The function returns a handle to a connection if it successfully
+ connects the signal to the slot. The Connection handle will be invalid
+ if it cannot create the connection, for example, if QObject is unable
+ to verify the existence of either \a signal or \a method, or if their
+ signatures aren't compatible.
+ You can check if the QMetaObject::Connection is valid by casting it to a bool.
By default, a signal is emitted for every connection you make;
two signals are emitted for duplicate connections. You can break
@@ -2339,15 +2342,15 @@ static inline void check_and_warn_compat(const QMetaObject *sender, const QMetaM
\sa disconnect(), sender(), qRegisterMetaType()
*/
-
-bool QObject::connect(const QObject *sender, const char *signal,
- const QObject *receiver, const char *method,
- Qt::ConnectionType type)
+QMetaObject::Connection QObject::connect(const QObject *sender, const char *signal,
+ const QObject *receiver, const char *method,
+ Qt::ConnectionType type)
{
{
- const void *cbdata[] = { sender, signal, receiver, method, &type };
+ void *result = 0;
+ const void *cbdata[] = { sender, signal, receiver, method, &type , &result};
if (QInternal::activateCallbacks(QInternal::ConnectCallback, (void **) cbdata))
- return true;
+ return QMetaObject::Connection(result);
}
#ifndef QT_NO_DEBUG
@@ -2366,12 +2369,12 @@ bool QObject::connect(const QObject *sender, const char *signal,
(signal && *signal) ? signal+1 : "(null)",
receiver ? receiver->metaObject()->className() : "(null)",
(method && *method) ? method+1 : "(null)");
- return false;
+ return QMetaObject::Connection(0);
}
QByteArray tmp_signal_name;
if (!check_signal_macro(sender, signal, "connect", "bind"))
- return false;
+ return QMetaObject::Connection(0);
const QMetaObject *smeta = sender->metaObject();
const char *signal_arg = signal;
++signal; //skip code
@@ -2393,7 +2396,7 @@ bool QObject::connect(const QObject *sender, const char *signal,
if (signal_index < 0) {
err_method_notfound(sender, signal_arg, "connect");
err_info_about_objects("connect", sender, receiver);
- return false;
+ return QMetaObject::Connection(0);
}
signal_index = QMetaObjectPrivate::originalClone(smeta, signal_index);
int signalOffset, methodOffset;
@@ -2405,7 +2408,7 @@ bool QObject::connect(const QObject *sender, const char *signal,
int membcode = extract_code(method);
if (!check_method_code(membcode, receiver, method, "connect"))
- return false;
+ return QMetaObject::Connection(0);
const char *method_arg = method;
++method; // skip code
@@ -2444,7 +2447,7 @@ bool QObject::connect(const QObject *sender, const char *signal,
if (method_index_relative < 0) {
err_method_notfound(receiver, method_arg, "connect");
err_info_about_objects("connect", sender, receiver);
- return false;
+ return QMetaObject::Connection(0);
}
if (!QMetaObject::checkConnectArgs(signal, method)) {
@@ -2452,13 +2455,13 @@ bool QObject::connect(const QObject *sender, const char *signal,
"\n %s::%s --> %s::%s",
sender->metaObject()->className(), signal,
receiver->metaObject()->className(), method);
- return false;
+ return QMetaObject::Connection(0);
}
int *types = 0;
if ((type == Qt::QueuedConnection)
&& !(types = queuedConnectionTypes(smeta->method(signal_absolute_index).parameterTypes())))
- return false;
+ return QMetaObject::Connection(0);
#ifndef QT_NO_DEBUG
if (warnCompat) {
@@ -2467,10 +2470,11 @@ bool QObject::connect(const QObject *sender, const char *signal,
check_and_warn_compat(smeta, smethod, rmeta, rmethod);
}
#endif
- if (!QMetaObjectPrivate::connect(sender, signal_index, receiver, method_index_relative, rmeta ,type, types))
- return false;
- const_cast<QObject*>(sender)->connectNotify(signal - 1);
- return true;
+ QMetaObject::Connection handle = QMetaObject::Connection(QMetaObjectPrivate::connect(
+ sender, signal_index, receiver, method_index_relative, rmeta ,type, types));
+ if (handle)
+ const_cast<QObject*>(sender)->connectNotify(signal - 1);
+ return handle;
}
/*!
@@ -2478,7 +2482,12 @@ bool QObject::connect(const QObject *sender, const char *signal,
Creates a connection of the given \a type from the \a signal in
the \a sender object to the \a method in the \a receiver object.
- Returns true if the connection succeeds; otherwise returns false.
+ Returns a handle to the connection that can be used to disconnect
+ it later.
+
+ The Connection handle will be invalid if it cannot create the
+ connection, for example, the parameters were invalid.
+ You can check if the QMetaObject::Connection is valid by casting it to a bool.
This function works in the same way as
connect(const QObject *sender, const char *signal,
@@ -2490,9 +2499,9 @@ bool QObject::connect(const QObject *sender, const char *signal,
const QObject *receiver, const char *method,
Qt::ConnectionType type)
*/
-bool QObject::connect(const QObject *sender, const QMetaMethod &signal,
- const QObject *receiver, const QMetaMethod &method,
- Qt::ConnectionType type)
+QMetaObject::Connection QObject::connect(const QObject *sender, const QMetaMethod &signal,
+ const QObject *receiver, const QMetaMethod &method,
+ Qt::ConnectionType type)
{
#ifndef QT_NO_DEBUG
bool warnCompat = true;
@@ -2513,7 +2522,7 @@ bool QObject::connect(const QObject *sender, const QMetaMethod &signal,
signal.signature(),
receiver ? receiver->metaObject()->className() : "(null)",
method.signature() );
- return false;
+ return QMetaObject::Connection(0);
}
// Reconstructing SIGNAL() macro result for signal.signature() string
@@ -2528,12 +2537,12 @@ bool QObject::connect(const QObject *sender, const QMetaMethod &signal,
methodSignature.append((char)(method.methodType() == QMetaMethod::Slot ? QSLOT_CODE
: method.methodType() == QMetaMethod::Signal ? QSIGNAL_CODE : 0 + '0'));
methodSignature.append(method.signature());
- const void *cbdata[] = { sender, signalSignature.constData(), receiver, methodSignature.constData(), &type };
+ void *result = 0;
+ const void *cbdata[] = { sender, signalSignature.constData(), receiver, methodSignature.constData(), &type, &result };
if (QInternal::activateCallbacks(QInternal::ConnectCallback, (void **) cbdata))
- return true;
+ return QMetaObject::Connection(result);
}
-
int signal_index;
int method_index;
{
@@ -2547,36 +2556,36 @@ bool QObject::connect(const QObject *sender, const QMetaMethod &signal,
if (signal_index == -1) {
qWarning("QObject::connect: Can't find signal %s on instance of class %s",
signal.signature(), smeta->className());
- return false;
+ return QMetaObject::Connection(0);
}
if (method_index == -1) {
qWarning("QObject::connect: Can't find method %s on instance of class %s",
method.signature(), rmeta->className());
- return false;
+ return QMetaObject::Connection(0);
}
-
+
if (!QMetaObject::checkConnectArgs(signal.signature(), method.signature())) {
qWarning("QObject::connect: Incompatible sender/receiver arguments"
"\n %s::%s --> %s::%s",
smeta->className(), signal.signature(),
rmeta->className(), method.signature());
- return false;
+ return QMetaObject::Connection(0);
}
int *types = 0;
if ((type == Qt::QueuedConnection)
&& !(types = queuedConnectionTypes(signal.parameterTypes())))
- return false;
+ return QMetaObject::Connection(0);
#ifndef QT_NO_DEBUG
if (warnCompat)
check_and_warn_compat(smeta, signal, rmeta, method);
#endif
- if (!QMetaObjectPrivate::connect(sender, signal_index, receiver, method_index, 0, type, types))
- return false;
-
- const_cast<QObject*>(sender)->connectNotify(signalSignature.constData());
- return true;
+ QMetaObject::Connection handle = QMetaObject::Connection(QMetaObjectPrivate::connect(
+ sender, signal_index, receiver, method_index, 0, type, types));
+ if (handle)
+ const_cast<QObject*>(sender)->connectNotify(signalSignature.constData());
+ return handle;
}
/*!
@@ -2958,22 +2967,24 @@ static int methodIndexToSignalIndex(const QMetaObject *metaObject, int signal_in
if \a signal_index is -1, then we effectively connect *all* signals
from the sender to the receiver's slot
*/
-bool QMetaObject::connect(const QObject *sender, int signal_index,
- const QObject *receiver, int method_index, int type, int *types)
+QMetaObject::Connection QMetaObject::connect(const QObject *sender, int signal_index,
+ const QObject *receiver, int method_index, int type, int *types)
{
signal_index = methodIndexToSignalIndex(sender->metaObject(), signal_index);
- return QMetaObjectPrivate::connect(sender, signal_index,
+ return Connection(QMetaObjectPrivate::connect(sender, signal_index,
receiver, method_index,
0, //FIXME, we could speed this connection up by computing the relative index
- type, types);
+ type, types));
}
/*! \internal
Same as the QMetaObject::connect, but \a signal_index must be the result of QObjectPrivate::signalIndex
method_index is relative to the rmeta metaobject, if rmeta is null, then it is absolute index
+
+ the QObjectPrivate::Connection* has a refcount of 2, so it must be passed to a QMetaObject::Connection
*/
-bool QMetaObjectPrivate::connect(const QObject *sender, int signal_index,
+QObjectPrivate::Connection *QMetaObjectPrivate::connect(const QObject *sender, int signal_index,
const QObject *receiver, int method_index,
const QMetaObject *rmeta, int type, int *types)
{
@@ -2998,7 +3009,7 @@ bool QMetaObjectPrivate::connect(const QObject *sender, int signal_index,
while (c2) {
if (c2->receiver == receiver && c2->method() == method_index_absolute)
- return false;
+ return 0;
c2 = c2->nextConnectionList;
}
}
@@ -3030,8 +3041,7 @@ bool QMetaObjectPrivate::connect(const QObject *sender, int signal_index,
sender_d->connectedSignals[signal_index >> 5] |= (1 << (signal_index & 0x1f));
}
- c.take(); // stop tracking
- return true;
+ return c.take();
}
/*!\internal
@@ -4032,6 +4042,82 @@ void qDeleteInEventHandler(QObject *o)
delete o;
}
+/*!
+ Disconnect a connection.
+
+ If the \a connection is invalid or has already been disconnected, do nothing
+ and return false.
+
+ \sa connect()
+ */
+bool QObject::disconnect(const QMetaObject::Connection &connection)
+{
+ QObjectPrivate::Connection *c = static_cast<QObjectPrivate::Connection *>(connection.d_ptr);
+
+ if (!c || !c->receiver)
+ return false;
+
+ QMutex *senderMutex = signalSlotLock(c->sender);
+ QMutex *receiverMutex = signalSlotLock(c->receiver);
+ QOrderedMutexLocker locker(senderMutex, receiverMutex);
+
+ QObjectConnectionListVector *connectionLists = QObjectPrivate::get(c->sender)->connectionLists;
+ Q_ASSERT(connectionLists);
+ connectionLists->dirty = true;
+
+ *c->prev = c->next;
+ if (c->next)
+ c->next->prev = c->prev;
+ c->receiver = 0;
+ return true;
+}
+
+/*! \class QMetaObject::Connection
+ Represents a handle to a signal-slot connection.
+ It can be used to disconnect that connection, or check if
+ the connection was successful
+
+ \sa QObject::disconnect
+ */
+
+/*!
+ Create a copy of the handle to the connection
+ */
+QMetaObject::Connection::Connection(const QMetaObject::Connection &other) : d_ptr(other.d_ptr)
+{
+ if (d_ptr)
+ static_cast<QObjectPrivate::Connection *>(d_ptr)->ref();
+}
+
+QMetaObject::Connection& QMetaObject::Connection::operator=(const QMetaObject::Connection& other)
+{
+ if (other.d_ptr != d_ptr) {
+ if (d_ptr)
+ static_cast<QObjectPrivate::Connection *>(d_ptr)->deref();
+ d_ptr = other.d_ptr;
+ if (other.d_ptr)
+ static_cast<QObjectPrivate::Connection *>(other.d_ptr)->ref();
+ }
+ return *this;
+}
+
+QMetaObject::Connection::Connection() : d_ptr(0) {}
+
+QMetaObject::Connection::~Connection()
+{
+ if (d_ptr)
+ static_cast<QObjectPrivate::Connection *>(d_ptr)->deref();
+}
+
+/*!
+ \fn bool QMetaObject::Connection::operator bool()
+
+ Returns true if the connection is valid.
+
+ The connection is valid if the call to QObject::connect succeeded.
+ The connection is invalid if QObject::connect was not able to find
+ the signal or the slot, or if the arguments do not match.
+ */
QT_END_NAMESPACE
diff --git a/src/corelib/kernel/qobject.h b/src/corelib/kernel/qobject.h
index 591c5c88bf..038b59042b 100644
--- a/src/corelib/kernel/qobject.h
+++ b/src/corelib/kernel/qobject.h
@@ -194,15 +194,14 @@ public:
void installEventFilter(QObject *);
void removeEventFilter(QObject *);
-
- static bool connect(const QObject *sender, const char *signal,
+ static QMetaObject::Connection connect(const QObject *sender, const char *signal,
const QObject *receiver, const char *member, Qt::ConnectionType = Qt::AutoConnection);
-
- static bool connect(const QObject *sender, const QMetaMethod &signal,
+
+ static QMetaObject::Connection connect(const QObject *sender, const QMetaMethod &signal,
const QObject *receiver, const QMetaMethod &method,
Qt::ConnectionType type = Qt::AutoConnection);
- inline bool connect(const QObject *sender, const char *signal,
+ inline QMetaObject::Connection connect(const QObject *sender, const char *signal,
const char *member, Qt::ConnectionType type = Qt::AutoConnection) const;
static bool disconnect(const QObject *sender, const char *signal,
@@ -214,6 +213,7 @@ public:
{ return disconnect(this, signal, receiver, member); }
inline bool disconnect(const QObject *receiver, const char *member = 0)
{ return disconnect(this, 0, receiver, member); }
+ static bool disconnect(const QMetaObject::Connection &);
void dumpObjectTree();
void dumpObjectInfo();
@@ -275,8 +275,8 @@ private:
Q_PRIVATE_SLOT(d_func(), void _q_reregisterTimers(void *))
};
-inline bool QObject::connect(const QObject *asender, const char *asignal,
- const char *amember, Qt::ConnectionType atype) const
+inline QMetaObject::Connection QObject::connect(const QObject *asender, const char *asignal,
+ const char *amember, Qt::ConnectionType atype) const
{ return connect(asender, asignal, this, amember, atype); }
#ifndef QT_NO_USERDATA
diff --git a/src/corelib/kernel/qobject_p.h b/src/corelib/kernel/qobject_p.h
index b4c30bd149..2a9334ae8f 100644
--- a/src/corelib/kernel/qobject_p.h
+++ b/src/corelib/kernel/qobject_p.h
@@ -121,11 +121,22 @@ public:
Connection *next;
Connection **prev;
QAtomicPointer<int> argumentTypes;
+ QAtomicInt ref_;
ushort method_offset;
ushort method_relative;
ushort connectionType : 3; // 0 == auto, 1 == direct, 2 == queued, 4 == blocking
+ Connection() : nextConnectionList(0), ref_(2) {
+ //ref_ is 2 for the use in the internal lists, and for the use in QMetaObject::Connection
+ }
~Connection();
int method() const { return method_offset + method_relative; }
+ void ref() { ref_.ref(); }
+ void deref() {
+ if (!ref_.deref()) {
+ Q_ASSERT(!receiver);
+ delete this;
+ }
+ }
};
// ConnectionList is a singly-linked list
struct ConnectionList {
diff --git a/src/corelib/kernel/qobjectdefs.h b/src/corelib/kernel/qobjectdefs.h
index 31f3067829..1ad24387e0 100644
--- a/src/corelib/kernel/qobjectdefs.h
+++ b/src/corelib/kernel/qobjectdefs.h
@@ -291,6 +291,7 @@ public:
struct Q_CORE_EXPORT QMetaObject
{
+ class Connection;
const char *className() const;
const QMetaObject *superClass() const;
@@ -336,7 +337,7 @@ struct Q_CORE_EXPORT QMetaObject
static QByteArray normalizedType(const char *type);
// internal index-based connect
- static bool connect(const QObject *sender, int signal_index,
+ static Connection connect(const QObject *sender, int signal_index,
const QObject *receiver, int method_index,
int type = 0, int *types = 0);
// internal index-based disconnect
@@ -458,6 +459,30 @@ struct Q_CORE_EXPORT QMetaObject
} d;
};
+class Q_CORE_EXPORT QMetaObject::Connection {
+ void *d_ptr; //QObjectPrivate::Connection*
+ explicit Connection(void *data) : d_ptr(data) { }
+ friend class QObject;
+ friend struct QMetaObject;
+public:
+ ~Connection();
+ Connection();
+ Connection(const Connection &other);
+ Connection &operator=(const Connection &other);
+#ifdef qdoc
+ operator bool() const;
+#else
+ typedef void *Connection::*RestrictedBool;
+ operator RestrictedBool() const { return d_ptr ? &Connection::d_ptr : 0; }
+#endif
+
+#ifdef Q_COMPILER_RVALUE_REFS
+ inline Connection(Connection &&o) : d_ptr(o.d_ptr) { o.d_ptr = 0; }
+ inline Connection &operator=(Connection &&other)
+ { qSwap(d_ptr, other.d_ptr); return *this; }
+#endif
+};
+
typedef const QMetaObject& (*QMetaObjectAccessor)();
struct QMetaObjectExtraData
diff --git a/tests/auto/corelib/global/qglobal/tst_qglobal.cpp b/tests/auto/corelib/global/qglobal/tst_qglobal.cpp
index c0a14faac3..b3115e036e 100644
--- a/tests/auto/corelib/global/qglobal/tst_qglobal.cpp
+++ b/tests/auto/corelib/global/qglobal/tst_qglobal.cpp
@@ -110,7 +110,7 @@ void tst_QGlobal::qInternalCallbacks()
// Test that connect works as expected...
connect_info.reset();
bool ok = QObject::connect(&a, signal.toLatin1(), &b, slot.toLatin1(), Qt::AutoConnection);
- QVERIFY(ok);
+ QVERIFY(!ok); // our dummy callback do not return a valid QMetaObject::Connection
QCOMPARE(&a, connect_info.sender);
QCOMPARE(&b, connect_info.receiver);
QCOMPARE(signal, connect_info.signal);