From 7d4d47de7049ba52726071d4d2ba03fc014aa48d Mon Sep 17 00:00:00 2001 From: Giuseppe D'Angelo Date: Thu, 26 Aug 2021 17:18:40 +0200 Subject: [PATCH] QObject::connect(): fail to connect to a functor if UniqueConnection is passed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The connect() documentation makes it clear that UniqueConnection does not work with free functions / function objects and the like; only with actual PMFs. Rather than silently *ignoring* the flag, be vocal about its presence by warning, and make the connection fail (as the user has passed an illegal argument). [ChangeLog][QtCore][QObject] QObject::connect() now will refuse to connect a signal to a free function / function object if UniqueConnection is passed. Note that UniqueConnection has never worked for such connections -- the flag was simply ignored, and they were established multiple times. Now, the flag is not ignored and results in a connection failure (as well as a runtime warning by Qt). Change-Id: I6509667018c74f9bd24910cde0a1b16c5f84f064 Reviewed-by: Qt CI Bot Reviewed-by: Fabian Kosmale Reviewed-by: MÃ¥rten Nordheim Reviewed-by: Thiago Macieira --- src/corelib/kernel/qobject.cpp | 33 +++++++++++++++---- .../corelib/kernel/qobject/tst_qobject.cpp | 5 +-- 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/src/corelib/kernel/qobject.cpp b/src/corelib/kernel/qobject.cpp index d524fd4305..582073a3b2 100644 --- a/src/corelib/kernel/qobject.cpp +++ b/src/corelib/kernel/qobject.cpp @@ -4974,6 +4974,19 @@ QMetaObject::Connection QObject::connectImpl(const QObject *sender, void **signa return QObjectPrivate::connectImpl(sender, signal_index, receiver, slot, slotObj, type, types, senderMetaObject); } +static void connectWarning(const QObject *sender, + const QMetaObject *senderMetaObject, + const QObject *receiver, + const char *message) +{ + const char *senderString = sender ? sender->metaObject()->className() + : senderMetaObject ? senderMetaObject->className() + : "Unknown"; + const char *receiverString = receiver ? receiver->metaObject()->className() + : "Unknown"; + qCWarning(lcConnect, "QObject::connect(%s, %s): %s", senderString, receiverString, message); +} + /*! \internal @@ -4986,18 +4999,24 @@ QMetaObject::Connection QObjectPrivate::connectImpl(const QObject *sender, int s QtPrivate::QSlotObjectBase *slotObj, int type, const int *types, const QMetaObject *senderMetaObject) { - if (!sender || !receiver || !slotObj || !senderMetaObject) { - const char *senderString = sender ? sender->metaObject()->className() - : senderMetaObject ? senderMetaObject->className() - : "Unknown"; - const char *receiverString = receiver ? receiver->metaObject()->className() - : "Unknown"; - qCWarning(lcConnect, "QObject::connect(%s, %s): invalid nullptr parameter", senderString, receiverString); + auto connectFailureGuard = qScopeGuard([&]() + { if (slotObj) slotObj->destroyIfLastRef(); + }); + + if (!sender || !receiver || !slotObj || !senderMetaObject) { + connectWarning(sender, senderMetaObject, receiver, "invalid nullptr parameter"); return QMetaObject::Connection(); } + if (type & Qt::UniqueConnection && !slot) { + connectWarning(sender, senderMetaObject, receiver, "unique connections require a pointer to member function of a QObject subclass"); + return QMetaObject::Connection(); + } + + connectFailureGuard.dismiss(); + QObject *s = const_cast(sender); QObject *r = const_cast(receiver); diff --git a/tests/auto/corelib/kernel/qobject/tst_qobject.cpp b/tests/auto/corelib/kernel/qobject/tst_qobject.cpp index 599f690ab2..52b687cbfa 100644 --- a/tests/auto/corelib/kernel/qobject/tst_qobject.cpp +++ b/tests/auto/corelib/kernel/qobject/tst_qobject.cpp @@ -6241,10 +6241,11 @@ void tst_QObject::connectFunctorWithContextUnique() SenderObject sender; ReceiverObject receiver; - QObject::connect(&sender, &SenderObject::signal1, &receiver, &ReceiverObject::slot1); + QVERIFY(QObject::connect(&sender, &SenderObject::signal1, &receiver, &ReceiverObject::slot1)); receiver.count_slot1 = 0; - QObject::connect(&sender, &SenderObject::signal1, &receiver, SlotFunctor(), Qt::UniqueConnection); + QTest::ignoreMessage(QtWarningMsg, "QObject::connect(SenderObject, ReceiverObject): unique connections require a pointer to member function of a QObject subclass"); + QVERIFY(!QObject::connect(&sender, &SenderObject::signal1, &receiver, [&](){ receiver.slot1(); }, Qt::UniqueConnection)); sender.emitSignal1(); QCOMPARE(receiver.count_slot1, 1);