QObject: assert connection type isn't UniqueConnection for lambdas

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.5
Task-number: QTBUG-115125
Change-Id: I2935d32ea5a2c288d7a836abbae66ac53cb5ab2f
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
(cherry picked from commit afddb327bd866ac693e475901d4d10acc6c83757)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
(cherry picked from commit fd664000625bca62db20f0954240e0dcbd32cad5)
This commit is contained in:
Ahmad Samir 2023-10-15 13:51:48 +03:00 committed by Qt Cherry-pick Bot
parent a47c5e3e43
commit a40ca88b63
2 changed files with 42 additions and 2 deletions

View File

@ -227,8 +227,13 @@ public:
types = QtPrivate::ConnectionTypes<typename SignalType::Arguments>::types(); types = QtPrivate::ConnectionTypes<typename SignalType::Arguments>::types();
void **pSlot = nullptr; void **pSlot = nullptr;
if constexpr (std::is_member_function_pointer_v<std::decay_t<Func2>>) if constexpr (std::is_member_function_pointer_v<std::decay_t<Func2>>) {
pSlot = const_cast<void **>(reinterpret_cast<void *const *>(&slot)); pSlot = const_cast<void **>(reinterpret_cast<void *const *>(&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<void **>(&signal), context, pSlot, return connectImpl(sender, reinterpret_cast<void **>(&signal), context, pSlot,
QtPrivate::makeCallableObject<Func1>(std::forward<Func2>(slot)), QtPrivate::makeCallableObject<Func1>(std::forward<Func2>(slot)),

View File

@ -6303,11 +6303,46 @@ void tst_QObject::connectFunctorWithContextUnique()
QVERIFY(QObject::connect(&sender, &SenderObject::signal1, &receiver, &ReceiverObject::slot1)); QVERIFY(QObject::connect(&sender, &SenderObject::signal1, &receiver, &ReceiverObject::slot1));
receiver.count_slot1 = 0; 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)); 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(); sender.emitSignal1();
QCOMPARE(receiver.count_slot1, 1); 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 class MyFunctor