Add support for QueuedConnection when connecting using the new syntax
authorOlivier Goffart <ogoffart@woboq.com>
Wed, 23 Nov 2011 14:03:04 +0000 (15:03 +0100)
committerQt by Nokia <qt-info@nokia.com>
Fri, 25 Nov 2011 00:12:14 +0000 (01:12 +0100)
QMetaCallEvent now can handle a pointer to QSlotObjectBase

Change-Id: I94da1e68ce9bb1fd96a9ae013a389552eb625faa
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
src/corelib/kernel/qobject.cpp
src/corelib/kernel/qobject.h
src/corelib/kernel/qobject_impl.h
src/corelib/kernel/qobject_p.h
tests/auto/corelib/kernel/qobject/tst_qobject.cpp

index e8735d0..0adbc9c 100644 (file)
@@ -498,13 +498,25 @@ void QObjectPrivate::clearGuards(QObject *object)
 QMetaCallEvent::QMetaCallEvent(ushort method_offset, ushort method_relative, QObjectPrivate::StaticMetaCallFunction callFunction,
                                const QObject *sender, int signalId,
                                int nargs, int *types, void **args, QSemaphore *semaphore)
-    : QEvent(MetaCall), sender_(sender), signalId_(signalId),
+    : QEvent(MetaCall), slotObj_(0), sender_(sender), signalId_(signalId),
       nargs_(nargs), types_(types), args_(args), semaphore_(semaphore),
       callFunction_(callFunction), method_offset_(method_offset), method_relative_(method_relative)
 { }
 
 /*! \internal
  */
+QMetaCallEvent::QMetaCallEvent(QObject::QSlotObjectBase *slotO, const QObject *sender, int signalId,
+                               int nargs, int *types, void **args, QSemaphore *semaphore)
+    : QEvent(MetaCall), slotObj_(slotO), sender_(sender), signalId_(signalId),
+      nargs_(nargs), types_(types), args_(args), semaphore_(semaphore),
+      callFunction_(0), method_offset_(0), method_relative_(-1)
+{
+    if (slotObj_)
+        slotObj_->ref.ref();
+}
+
+/*! \internal
+ */
 QMetaCallEvent::~QMetaCallEvent()
 {
     if (types_) {
@@ -519,13 +531,17 @@ QMetaCallEvent::~QMetaCallEvent()
     if (semaphore_)
         semaphore_->release();
 #endif
+    if (slotObj_ && !slotObj_->ref.deref())
+        delete slotObj_;
 }
 
 /*! \internal
  */
 void QMetaCallEvent::placeMetaCall(QObject *object)
 {
-    if (callFunction_) {
+    if (slotObj_) {
+        slotObj_->call(object, args_);
+    } else if (callFunction_) {
         callFunction_(object, QMetaObject::InvokeMetaMethod, method_relative_, args_);
     } else {
         QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, method_offset_ + method_relative_, args_);
@@ -3261,11 +3277,10 @@ static void queued_activate(QObject *sender, int signal, QObjectPrivate::Connect
     args[0] = 0; // return value
     for (int n = 1; n < nargs; ++n)
         args[n] = QMetaType::create((types[n] = argumentTypes[n-1]), argv[n]);
-    QCoreApplication::postEvent(c->receiver, new QMetaCallEvent(c->method_offset,
-                                                                c->method_relative,
-                                                                c->callFunction,
-                                                                sender, signal, nargs,
-                                                                types, args));
+    QMetaCallEvent *ev = c->isSlotObject ?
+        new QMetaCallEvent(c->slotObj, sender, signal, nargs, types, args) :
+        new QMetaCallEvent(c->method_offset, c->method_relative, c->callFunction, sender, signal, nargs, types, args);
+    QCoreApplication::postEvent(c->receiver, ev);
 }
 
 
@@ -3378,12 +3393,10 @@ void QMetaObject::activate(QObject *sender, const QMetaObject *m, int local_sign
                     receiver->metaObject()->className(), receiver);
                 }
                 QSemaphore semaphore;
-                QCoreApplication::postEvent(receiver, new QMetaCallEvent(c->method_offset, c->method_relative,
-                                                                         c->callFunction,
-                                                                         sender, signal_absolute_index,
-                                                                         0, 0,
-                                                                         argv ? argv : empty_argv,
-                                                                         &semaphore));
+                QMetaCallEvent *ev = c->isSlotObject ?
+                    new QMetaCallEvent(c->slotObj, sender, signal_absolute_index, 0, 0, argv ? argv : empty_argv, &semaphore) :
+                    new QMetaCallEvent(c->method_offset, c->method_relative, c->callFunction, sender, signal_absolute_index, 0, 0, argv ? argv : empty_argv, &semaphore);
+                QCoreApplication::postEvent(receiver, ev);
                 semaphore.acquire();
                 locker.relock();
                 continue;
index 93448b5..d4a532a 100644 (file)
@@ -221,6 +221,9 @@ public:
         typedef typename QtPrivate::CheckCompatibleArguments<typename SignalType::Arguments, typename SlotType::Arguments>::IncompatibleSignalSlotArguments EnsureCompatibleArguments;
 
         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),
                            receiver, new QSlotObject<Func2,
                                                      typename QtPrivate::List_Left<typename SignalType::Arguments, SlotType::ArgumentCount>::Value,
@@ -318,6 +321,7 @@ protected:
     static const QMetaObject staticQtMetaObject;
 
     friend struct QMetaObject;
+    friend class QMetaCallEvent;
     friend class QApplication;
     friend class QApplicationPrivate;
     friend class QCoreApplication;
index 7d3dc16..9918b1f 100644 (file)
@@ -344,6 +344,43 @@ namespace QtPrivate {
 
 #endif
 
+    /*
+        Logic to statically generate the array of qMetaTypeId
+        ConnectionTypes<FunctionPointer<Signal>::Arguments>::types() returns an array
+        of int that is suitable for the types arguments of the connection functions.
+
+        The array only exist of all the types are declared as a metatype
+        (detected using the TypesAreDeclaredMetaType helper struct)
+        If one of the type is not declared, the function return 0 and the signal
+        cannot be used in queued connection.
+    */
+#ifndef Q_COMPILER_VARIADIC_TEMPLATES
+    template <typename ArgList> struct TypesAreDeclaredMetaType { enum { Value = false }; };
+    template <> struct TypesAreDeclaredMetaType<void> { enum { Value = true }; };
+    template <typename Arg, typename Tail> struct TypesAreDeclaredMetaType<List<Arg, Tail> > { enum { Value = QMetaTypeId2<Arg>::Defined && TypesAreDeclaredMetaType<Tail>::Value }; };
+
+    template <typename ArgList, bool Declared = TypesAreDeclaredMetaType<ArgList>::Value > struct ConnectionTypes
+    { static const int *types() { return 0; } };
+    template <> struct ConnectionTypes<void, true>
+    { static const int *types() { static const int t[1] = { 0 }; return t; } };
+    template <typename Arg1> struct ConnectionTypes<List<Arg1, void>, true>
+    { static const int *types() { static const int t[2] = { QtPrivate::QMetaTypeIdHelper<Arg1>::qt_metatype_id(), 0 }; return t; } };
+    template <typename Arg1, typename Arg2> struct ConnectionTypes<List<Arg1, List<Arg2, void> >, true>
+    { static const int *types() { static const int t[3] = { QtPrivate::QMetaTypeIdHelper<Arg1>::qt_metatype_id(), QtPrivate::QMetaTypeIdHelper<Arg2>::qt_metatype_id(), 0 }; return t; } };
+    template <typename Arg1, typename Arg2, typename Arg3> struct ConnectionTypes<List<Arg1, List<Arg2,  List<Arg3, void> > >, true>
+    { static const int *types() { static const int t[4] = { QtPrivate::QMetaTypeIdHelper<Arg1>::qt_metatype_id(), QtPrivate::QMetaTypeIdHelper<Arg2>::qt_metatype_id(),
+                                                            QtPrivate::QMetaTypeIdHelper<Arg3>::qt_metatype_id(), 0 }; return t; } };
+#else
+    template <typename ArgList> struct TypesAreDeclaredMetaType { enum { Value = false }; };
+    template <> struct TypesAreDeclaredMetaType<List<>> { enum { Value = true }; };
+    template <typename Arg, typename... Tail> struct TypesAreDeclaredMetaType<List<Arg, Tail...> >
+    { enum { Value = QMetaTypeId2<Arg>::Defined && TypesAreDeclaredMetaType<List<Tail...>>::Value }; };
+
+    template <typename ArgList, bool Declared = TypesAreDeclaredMetaType<ArgList>::Value > struct ConnectionTypes
+    { static const int *types() { return 0; } };
+    template <typename... Args> struct ConnectionTypes<List<Args...>, true>
+    { static const int *types() { static const int t[sizeof...(Args) + 1] = { (QtPrivate::QMetaTypeIdHelper<Args>::qt_metatype_id())..., 0 }; return t; } };
+#endif
 }
 
 
index 72cf5a8..5519a69 100644 (file)
@@ -264,6 +264,9 @@ class Q_CORE_EXPORT QMetaCallEvent : public QEvent
 public:
     QMetaCallEvent(ushort method_offset, ushort method_relative, QObjectPrivate::StaticMetaCallFunction callFunction , const QObject *sender, int signalId,
                    int nargs = 0, int *types = 0, void **args = 0, QSemaphore *semaphore = 0);
+    QMetaCallEvent(QObject::QSlotObjectBase *slotObj, const QObject *sender, int signalId,
+                   int nargs = 0, int *types = 0, void **args = 0, QSemaphore *semaphore = 0);
+
     ~QMetaCallEvent();
 
     inline int id() const { return method_offset_ + method_relative_; }
@@ -274,6 +277,7 @@ public:
     virtual void placeMetaCall(QObject *object);
 
 private:
+    QObject::QSlotObjectBase *slotObj_;
     const QObject *sender_;
     int signalId_;
     int nargs_;
index a001715..91ad392 100644 (file)
@@ -2812,6 +2812,10 @@ void tst_QObject::blockingQueuedConnection()
         QVERIFY(QMetaObject::invokeMethod(&receiver, "slot1", Qt::BlockingQueuedConnection));
         QVERIFY(receiver.called(1));
 
+        connect(&sender, &SenderObject::signal2, &receiver, &ReceiverObject::slot2, Qt::BlockingQueuedConnection);
+        sender.emitSignal2();
+        QVERIFY(receiver.called(2));
+
         thread.quit();
         QVERIFY(thread.wait());
     }
@@ -4173,6 +4177,29 @@ void tst_QObject::customTypesPointer()
         QCOMPARE(checker.received.value(), 0);
         checker.doEmit(t1);
         QCOMPARE(checker.received.value(), t1.value());
+        checker.received = t0;
+
+
+        checker.disconnect();
+
+        int idx = qRegisterMetaType<CustomType>("CustomType");
+        QCOMPARE(QMetaType::type("CustomType"), idx);
+
+        connect(&checker, &QCustomTypeChecker::signal1, &checker, &QCustomTypeChecker::slot1,
+                Qt::QueuedConnection);
+        QCOMPARE(instanceCount, 4);
+        checker.doEmit(t2);
+        QCOMPARE(instanceCount, 5);
+        QCOMPARE(checker.received.value(), t0.value());
+
+        QCoreApplication::processEvents();
+        QCOMPARE(checker.received.value(), t2.value());
+        QCOMPARE(instanceCount, 4);
+
+        QVERIFY(QMetaType::isRegistered(idx));
+        QCOMPARE(qRegisterMetaType<CustomType>("CustomType"), idx);
+        QCOMPARE(QMetaType::type("CustomType"), idx);
+        QVERIFY(QMetaType::isRegistered(idx));
     }
     QCOMPARE(instanceCount, 3);
 }