Support Qt::UniqueConnection in the new connection syntax
authorOlivier Goffart <ogoffart@woboq.com>
Fri, 25 Nov 2011 21:35:32 +0000 (22:35 +0100)
committerQt by Nokia <qt-info@nokia.com>
Tue, 29 Nov 2011 16:11:59 +0000 (17:11 +0100)
This commit also improves the related documentation a bit.

The test is copied from the test with the old syntax, but all the
connection statement are changed to use the new syntax

Change-Id: Ia5630ca4335b9f8ca6d724ae3c8750d6f0804d8e
Reviewed-by: Bradley T. Hughes <bradley.hughes@nokia.com>
src/corelib/kernel/qobject.cpp
src/corelib/kernel/qobject.h
tests/auto/corelib/kernel/qobject/tst_qobject.cpp

index 5b50f0e..b1d2463 100644 (file)
@@ -2366,7 +2366,7 @@ static inline void check_and_warn_compat(const QMetaObject *sender, const QMetaM
     If you pass the Qt::UniqueConnection \a type, the connection will only
     be made if it is not a duplicate. If there is already a duplicate
     (exact same signal to the exact same slot on the same objects),
-    the connection will fail and connect will return false.
+    the connection will fail and connect will return an invalid QMetaObject::Connection.
 
     The optional \a type parameter describes the type of connection
     to establish. In particular, it determines whether a particular
@@ -4089,6 +4089,14 @@ void qDeleteInEventHandler(QObject *o)
     to verify the existence of \a signal (if it was not declared as a signal)
     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
+    all of these connections with a single disconnect() call.
+    If you pass the Qt::UniqueConnection \a type, the connection will only
+    be made if it is not a duplicate. If there is already a duplicate
+    (exact same signal to the exact same slot on the same objects),
+    the connection will fail and connect will return an invalid QMetaObject::Connection.
+
     The optional \a type parameter describes the type of connection
     to establish. In particular, it determines whether a particular
     signal is delivered to a slot immediately or queued for delivery
@@ -4136,9 +4144,35 @@ void qDeleteInEventHandler(QObject *o)
 
     The connection will automatically disconnect if the sender is destroyed.
  */
-QMetaObject::Connection QObject::connectImpl(const QObject *sender, void **signal, const QObject *receiver, QObject::QSlotObjectBase *slotObj,
-                                             Qt::ConnectionType type, const int* types, const QMetaObject* senderMetaObject)
+
+/** \internal
+
+    Implementation of the template version of connect
+
+    \a sender is the sender object
+    \a signal is a pointer to a pointer to a member signal of the sender
+    \a receiver is the receiver object, may not be null, will be equal to sender when
+                connecting to a static function or a functor
+    \a slot a pointer only used when using Qt::UniqueConnection
+    \a type the Qt::ConnctionType passed as argument to connect
+    \a types an array of integer with the metatype id of the parametter of the signal
+             to be used with queued connection
+             must stay valid at least for the whole time of the connection, this function
+             do not take ownership. typically static data.
+             If null, then the types will be computed when the signal is emit in a queued
+             connection from the types from the signature.
+    \a senderMetaObject is the metaobject used to lookup the signal, the signal must be in
+                        this metaobject
+ */
+QMetaObject::Connection QObject::connectImpl(const QObject *sender, void **signal,
+                                             const QObject *receiver, void **slot,
+                                             QObject::QSlotObjectBase *slotObj, Qt::ConnectionType type,
+                                             const int *types, const QMetaObject *senderMetaObject)
 {
+    if (!sender || !signal || !slotObj || !senderMetaObject) {
+        qWarning("QObject::connect: invalid null parametter");
+        return QMetaObject::Connection();
+    }
     int signal_index = -1;
     void *args[] = { &signal_index, signal };
     senderMetaObject->static_metacall(QMetaObject::IndexOfMethod, 0, args);
@@ -4157,8 +4191,18 @@ QMetaObject::Connection QObject::connectImpl(const QObject *sender, void **signa
                                signalSlotLock(receiver));
 
     if (type & Qt::UniqueConnection) {
-        qWarning() << "QObject::connect: Qt::UniqueConnection not supported when connecting function pointers";
-        type = static_cast<Qt::ConnectionType>(type & (Qt::UniqueConnection - 1));
+        QObjectConnectionListVector *connectionLists = QObjectPrivate::get(s)->connectionLists;
+        if (connectionLists && connectionLists->count() > signal_index) {
+            const QObjectPrivate::Connection *c2 =
+                (*connectionLists)[signal_index].first;
+
+            while (c2) {
+                if (c2->receiver == receiver && c2->isSlotObject && c2->slotObj->compare(slot))
+                    return QMetaObject::Connection();
+                c2 = c2->nextConnectionList;
+            }
+        }
+        type = static_cast<Qt::ConnectionType>(type ^ Qt::UniqueConnection);
     }
 
     QScopedPointer<QObjectPrivate::Connection> c(new QObjectPrivate::Connection);
index 3b8803c..48c0bdb 100644 (file)
@@ -225,9 +225,9 @@ public:
             types = QtPrivate::ConnectionTypes<typename SignalType::Arguments>::types();
 
         return connectImpl(sender, reinterpret_cast<void **>(&signal),
-                           receiver, new QSlotObject<Func2,
-                                                     typename QtPrivate::List_Left<typename SignalType::Arguments, SlotType::ArgumentCount>::Value,
-                                                     typename SignalType::ReturnType>(slot),
+                           receiver, reinterpret_cast<void **>(&slot),
+                           new QSlotObject<Func2, typename QtPrivate::List_Left<typename SignalType::Arguments, SlotType::ArgumentCount>::Value,
+                                           typename SignalType::ReturnType>(slot),
                             type, types, &SignalType::Object::staticMetaObject);
     }
 
@@ -243,7 +243,7 @@ public:
         typedef typename QtPrivate::CheckCompatibleArguments<typename SignalType::Arguments, typename SlotType::Arguments>::IncompatibleSignalSlotArguments EnsureCompatibleArguments;
         typedef typename QtPrivate::QEnableIf<(int(SignalType::ArgumentCount) >= int(SlotType::ArgumentCount))>::Type EnsureArgumentsCount;
 
-        return connectImpl(sender, reinterpret_cast<void **>(&signal), sender,
+        return connectImpl(sender, reinterpret_cast<void **>(&signal), sender, 0,
                            new QStaticSlotObject<Func2,
                                                  typename QtPrivate::List_Left<typename SignalType::Arguments, SlotType::ArgumentCount>::Value,
                                                  typename SignalType::ReturnType>(slot),
@@ -257,8 +257,8 @@ public:
     {
         typedef QtPrivate::FunctionPointer<Func1> SignalType;
 
-        return connectImpl(sender, reinterpret_cast<void **>(&signal),
-                           sender, new QFunctorSlotObject<Func2, SignalType::ArgumentCount, typename SignalType::Arguments, typename SignalType::ReturnType>(slot),
+        return connectImpl(sender, reinterpret_cast<void **>(&signal), sender, 0,
+                           new QFunctorSlotObject<Func2, SignalType::ArgumentCount, typename SignalType::Arguments, typename SignalType::ReturnType>(slot),
                            Qt::DirectConnection, 0, &SignalType::Object::staticMetaObject);
     }
 
@@ -407,8 +407,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 QMetaObject::Connection connectImpl(const QObject *sender, void **signal,
+                                               const QObject *receiver, void **slotPtr,
+                                               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);
index 380e419..5e69a14 100644 (file)
@@ -107,6 +107,7 @@ private slots:
     void connectToSender();
     void qobjectConstCast();
     void uniqConnection();
+    void uniqConnectionPtr();
     void interfaceIid();
     void deleteQObjectWhenDeletingEvent();
     void overloads();
@@ -3269,6 +3270,70 @@ void tst_QObject::uniqConnection()
     delete r2;
 }
 
+void tst_QObject::uniqConnectionPtr()
+{
+    SenderObject *s = new SenderObject;
+    ReceiverObject *r1 = new ReceiverObject;
+    ReceiverObject *r2 = new ReceiverObject;
+    r1->reset();
+    r2->reset();
+    ReceiverObject::sequence = 0;
+
+    QVERIFY( connect( s, &SenderObject::signal1, r1, &ReceiverObject::slot1 , Qt::UniqueConnection) );
+    QVERIFY( connect( s, &SenderObject::signal1, r2, &ReceiverObject::slot1 , Qt::UniqueConnection) );
+    QVERIFY( connect( s, &SenderObject::signal1, r1, &ReceiverObject::slot3 , Qt::UniqueConnection) );
+    QVERIFY( connect( s, &SenderObject::signal3, r1, &ReceiverObject::slot3 , Qt::UniqueConnection) );
+
+    s->emitSignal1();
+    s->emitSignal2();
+    s->emitSignal3();
+    s->emitSignal4();
+
+    QCOMPARE( r1->count_slot1, 1 );
+    QCOMPARE( r1->count_slot2, 0 );
+    QCOMPARE( r1->count_slot3, 2 );
+    QCOMPARE( r1->count_slot4, 0 );
+    QCOMPARE( r2->count_slot1, 1 );
+    QCOMPARE( r2->count_slot2, 0 );
+    QCOMPARE( r2->count_slot3, 0 );
+    QCOMPARE( r2->count_slot4, 0 );
+    QCOMPARE( r1->sequence_slot1, 1 );
+    QCOMPARE( r2->sequence_slot1, 2 );
+    QCOMPARE( r1->sequence_slot3, 4 );
+
+    r1->reset();
+    r2->reset();
+    ReceiverObject::sequence = 0;
+
+    QVERIFY( connect( s, &SenderObject::signal4, r1, &ReceiverObject::slot4 , Qt::UniqueConnection) );
+    QVERIFY( connect( s, &SenderObject::signal4, r2, &ReceiverObject::slot4 , Qt::UniqueConnection) );
+    QVERIFY(!connect( s, &SenderObject::signal4, r2, &ReceiverObject::slot4 , Qt::UniqueConnection) );
+    QVERIFY( connect( s, &SenderObject::signal1, r2, &ReceiverObject::slot4 , Qt::UniqueConnection) );
+    QVERIFY(!connect( s, &SenderObject::signal4, r1, &ReceiverObject::slot4 , Qt::UniqueConnection) );
+
+    s->emitSignal4();
+    QCOMPARE( r1->count_slot4, 1 );
+    QCOMPARE( r2->count_slot4, 1 );
+    QCOMPARE( r1->sequence_slot4, 1 );
+    QCOMPARE( r2->sequence_slot4, 2 );
+
+    r1->reset();
+    r2->reset();
+    ReceiverObject::sequence = 0;
+
+    connect( s, &SenderObject::signal4, r1, &ReceiverObject::slot4 );
+
+    s->emitSignal4();
+    QCOMPARE( r1->count_slot4, 2 );
+    QCOMPARE( r2->count_slot4, 1 );
+    QCOMPARE( r1->sequence_slot4, 3 );
+    QCOMPARE( r2->sequence_slot4, 2 );
+
+    delete s;
+    delete r1;
+    delete r2;
+}
+
 void tst_QObject::interfaceIid()
 {
     QCOMPARE(QByteArray(qobject_interface_iid<Foo::Bleh *>()),