From afddb327bd866ac693e475901d4d10acc6c83757 Mon Sep 17 00:00:00 2001 From: Ahmad Samir Date: Sun, 15 Oct 2023 13:51:48 +0300 Subject: [PATCH] QObject: assert connection type isn't UniqueConnection for lambdas MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit An assert is harder to miss than a warning, which makes it more likely to get fixed. Thanks to Mårten Nordheim for the idea. Add the assert in inline code so that users compiling their code in debug mode (or with -DQT_FORCE_ASSERTS) can hit the assert even when built agaist a release build of Qt (not everyone compile their code with a debug build of Qt). Thanks to Giuseppe D'Angelo for the idea. Pick-to: 6.7 6.6 6.5 Task-number: QTBUG-115125 Change-Id: I2935d32ea5a2c288d7a836abbae66ac53cb5ab2f Reviewed-by: Mårten Nordheim --- src/corelib/kernel/qobject.h | 7 +++- .../corelib/kernel/qobject/tst_qobject.cpp | 37 ++++++++++++++++++- 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/src/corelib/kernel/qobject.h b/src/corelib/kernel/qobject.h index 9916626a3c..713a62c249 100644 --- a/src/corelib/kernel/qobject.h +++ b/src/corelib/kernel/qobject.h @@ -241,8 +241,13 @@ public: types = QtPrivate::ConnectionTypes::types(); void **pSlot = nullptr; - if constexpr (std::is_member_function_pointer_v>) + if constexpr (std::is_member_function_pointer_v>) { pSlot = const_cast(reinterpret_cast(&slot)); + } else { + Q_ASSERT_X((type & Qt::UniqueConnection) == 0, "", + "QObject::connect: Unique connection requires the slot to be a pointer to " + "a member function of a QObject subclass."); + } return connectImpl(sender, reinterpret_cast(&signal), context, pSlot, QtPrivate::makeCallableObject(std::forward(slot)), diff --git a/tests/auto/corelib/kernel/qobject/tst_qobject.cpp b/tests/auto/corelib/kernel/qobject/tst_qobject.cpp index a06238a494..b626d0e3c4 100644 --- a/tests/auto/corelib/kernel/qobject/tst_qobject.cpp +++ b/tests/auto/corelib/kernel/qobject/tst_qobject.cpp @@ -6429,11 +6429,46 @@ void tst_QObject::connectFunctorWithContextUnique() QVERIFY(QObject::connect(&sender, &SenderObject::signal1, &receiver, &ReceiverObject::slot1)); receiver.count_slot1 = 0; - QTest::ignoreMessage(QtWarningMsg, "QObject::connect(SenderObject, ReceiverObject): unique connections require a pointer to member function of a QObject subclass"); + QVERIFY(QObject::connect(&sender, &SenderObject::signal2, &receiver, &ReceiverObject::slot2)); + receiver.count_slot2 = 0; + + const auto oredType = Qt::ConnectionType(Qt::DirectConnection | Qt::UniqueConnection); + + // Will assert in debug builds, so only test in release builds +#if defined(QT_NO_DEBUG) && !defined(QT_FORCE_ASSERTS) + auto ignoreMsg = [] { + QTest::ignoreMessage(QtWarningMsg, + "QObject::connect(SenderObject, ReceiverObject): unique connections " + "require a pointer to member function of a QObject subclass"); + }; + + ignoreMsg(); QVERIFY(!QObject::connect(&sender, &SenderObject::signal1, &receiver, [&](){ receiver.slot1(); }, Qt::UniqueConnection)); + ignoreMsg(); + QVERIFY(!QObject::connect( + &sender, &SenderObject::signal2, &receiver, [&]() { receiver.slot2(); }, oredType)); +#endif + sender.emitSignal1(); QCOMPARE(receiver.count_slot1, 1); + + sender.emitSignal2(); + QCOMPARE(receiver.count_slot2, 1); + + // Check connecting to PMF doesn't hit the assert + + QVERIFY(QObject::connect(&sender, &SenderObject::signal3, &receiver, &ReceiverObject::slot3, + Qt::UniqueConnection)); + receiver.count_slot3 = 0; + sender.emitSignal3(); + QCOMPARE(receiver.count_slot3, 1); + + QVERIFY(QObject::connect(&sender, &SenderObject::signal4, &receiver, &ReceiverObject::slot4, + oredType)); + receiver.count_slot4 = 0; + sender.emitSignal4(); + QCOMPARE(receiver.count_slot4, 1); } class MyFunctor