Allow connect to functors with less parameters than the signal
authorOlivier Goffart <ogoffart@woboq.com>
Sat, 3 Nov 2012 17:17:59 +0000 (18:17 +0100)
committerThe Qt Project <gerrit-noreply@qt-project.org>
Tue, 6 Nov 2012 17:43:14 +0000 (18:43 +0100)
Before, the functor slot (or lambda expression) had to have the same amount
of arguments as the signal.
This shown to be a big problem to be able to connect to signals that had
a QPrivateSlot.

This implementation use the type of the operator() of the functor to
know how many arguments we have.
As a bonus, we also can check the arguments in a static assert.

The test comes from https://codereview.qt-project.org/#change,38703
But this patch also works without variadic template

If the compiler does not support decltype, we workaround the lack of it
by using another level of indirection.

Change-Id: I9850b43e8caf77356a2ec3f4c0b0ed532d96029e
Reviewed-by: Stephen Kelly <stephen.kelly@kdab.com>
src/corelib/kernel/qobject.h
tests/auto/corelib/kernel/qobject/tst_qobject.cpp

index 689946b..e9316c9 100644 (file)
@@ -279,10 +279,28 @@ public:
     static inline typename QtPrivate::QEnableIf<QtPrivate::FunctionPointer<Func2>::ArgumentCount == -1, QMetaObject::Connection>::Type
             connect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal, Func2 slot)
     {
+#ifndef Q_COMPILER_DECLTYPE  //Workaround the lack of decltype using another function as indirection
+        return connect_functor(sender, signal, slot, &Func2::operator()); }
+    template <typename Func1, typename Func2, typename Func2Operator>
+    static inline QMetaObject::Connection connect_functor(const QObject *sender, Func1 signal, Func2 slot, Func2Operator) {
+        typedef QtPrivate::FunctionPointer<Func2Operator> SlotType ;
+#else
+
+        typedef QtPrivate::FunctionPointer<decltype(&Func2::operator())> SlotType ;
+#endif
         typedef QtPrivate::FunctionPointer<Func1> SignalType;
 
+        Q_STATIC_ASSERT_X(int(SignalType::ArgumentCount) >= int(SlotType::ArgumentCount),
+                          "The slot requires more arguments than the signal provides.");
+        Q_STATIC_ASSERT_X((QtPrivate::CheckCompatibleArguments<typename SignalType::Arguments, typename SlotType::Arguments>::value),
+                          "Signal and slot arguments are not compatible.");
+        Q_STATIC_ASSERT_X((QtPrivate::AreArgumentsCompatible<typename SlotType::ReturnType, typename SignalType::ReturnType>::value),
+                          "Return type of the slot is not compatible with the return type of the signal.");
+
         return connectImpl(sender, reinterpret_cast<void **>(&signal), sender, 0,
-                           new QtPrivate::QFunctorSlotObject<Func2, SignalType::ArgumentCount, typename SignalType::Arguments, typename SignalType::ReturnType>(slot),
+                           new QtPrivate::QFunctorSlotObject<Func2, SlotType::ArgumentCount,
+                                typename QtPrivate::List_Left<typename SignalType::Arguments, SlotType::ArgumentCount>::Value,
+                                typename SignalType::ReturnType>(slot),
                            Qt::DirectConnection, 0, &SignalType::Object::staticMetaObject);
     }
 #endif //Q_QDOC
index f48f86c..c340d00 100644 (file)
@@ -140,6 +140,7 @@ private slots:
     void returnValue2_data();
     void returnValue2();
     void connectVirtualSlots();
+    void connectFunctorArgDifference();
 };
 
 class SenderObject : public QObject
@@ -5484,5 +5485,40 @@ void tst_QObject::connectVirtualSlots()
     */
 }
 
+struct SlotFunctor
+{
+    void operator()() {}
+};
+
+struct SlotFunctorString
+{
+    void operator()(const QString &) {}
+};
+
+void tst_QObject::connectFunctorArgDifference()
+{
+    QTimer timer;
+    // Compile-time tests that the connection is successful.
+    connect(&timer, &QTimer::timeout, SlotFunctor());
+    connect(&timer, &QTimer::objectNameChanged, SlotFunctorString());
+    connect(qApp, &QCoreApplication::aboutToQuit, SlotFunctor());
+
+    connect(&timer, &QTimer::objectNameChanged, SlotFunctor());
+    QStringListModel model;
+    connect(&model, &QStringListModel::rowsInserted, SlotFunctor());
+
+#if defined(Q_COMPILER_LAMBDA)
+    connect(&timer, &QTimer::timeout, [=](){});
+    connect(&timer, &QTimer::objectNameChanged, [=](const QString &){});
+    connect(qApp, &QCoreApplication::aboutToQuit, [=](){});
+
+    connect(&timer, &QTimer::objectNameChanged, [=](){});
+    connect(&model, &QStringListModel::rowsInserted, [=](){});
+    connect(&model, &QStringListModel::rowsInserted, [=](const QModelIndex &){});
+#endif
+
+    QVERIFY(true);
+}
+
 QTEST_MAIN(tst_QObject)
 #include "tst_qobject.moc"