From bd69821074d62a6e8b5eca56d7b9307e1b3e8645 Mon Sep 17 00:00:00 2001 From: Volker Hilsheimer Date: Thu, 27 Apr 2023 14:53:40 +0200 Subject: Support free functions and const functors as callbacks Amend 207aae5560aa2865ec55ddb9ecbb50048060c0c0, as code checker complained that we std::move'd a potential lvalue. This warning was valid if the public API did not accept the functor parameter by value. Fix this by consistently std::forward'ing the parameters through the call stack, and add a compile-time test. Writing that test revealed that the helper API didn't work with free functions, so fix that as well. It also revealed that QFunctorSlotObject couldn't work with a const functor, which is also fixed by this change. We cannot support move-only functors with that change, as it requires a change to QFunctorSlotObject that breaks the QMetaObject test. Change-Id: Iafd747baf4cb0213ecedb391ed46b4595388182b Reviewed-by: Thiago Macieira --- tests/auto/corelib/kernel/qobject/tst_qobject.cpp | 148 ++++++++++++++++++++++ 1 file changed, 148 insertions(+) (limited to 'tests/auto/corelib/kernel/qobject') diff --git a/tests/auto/corelib/kernel/qobject/tst_qobject.cpp b/tests/auto/corelib/kernel/qobject/tst_qobject.cpp index 0877637a50..9c18a2a0f6 100644 --- a/tests/auto/corelib/kernel/qobject/tst_qobject.cpp +++ b/tests/auto/corelib/kernel/qobject/tst_qobject.cpp @@ -149,6 +149,7 @@ private slots: void objectNameBinding(); void emitToDestroyedClass(); void declarativeData(); + void asyncCallbackHelper(); }; struct QObjectCreatedOnShutdown @@ -8355,5 +8356,152 @@ void tst_QObject::declarativeData() #endif } +/* + Compile-time test for the helpers in qobjectdefs_impl.h. +*/ +class AsyncCaller : public QObject +{ + Q_OBJECT +public: + void callback0() {} + void callback1(const QString &) {} + void callbackInt(int) {} + int returnInt() const { return 0; } + + static void staticCallback0() {} + static void staticCallback1(const QString &) {} + + using Prototype0 = void(*)(); + using Prototype1 = void(*)(QString); + + template + bool callMe0(const typename QtPrivate::ContextTypeForFunctor::ContextType *, Functor &&func) + { + auto *slotObject = QtPrivate::makeSlotObject(std::forward(func)); + slotObject->destroyIfLastRef(); + return true; + } + + template + bool callMe0(Functor &&func) + { + return callMe0(nullptr, std::forward(func)); + } + + template + bool callMe1(const typename QtPrivate::ContextTypeForFunctor::ContextType *, Functor &&func) + { + auto *slotObject = QtPrivate::makeSlotObject(std::forward(func)); + slotObject->destroyIfLastRef(); + return true; + } + + template + bool callMe1(Functor &&func) + { + return callMe1(nullptr, std::forward(func)); + } +}; + +static void freeFunction0() {} +static void freeFunction1(QString) {} + +template +struct AreFunctionsCompatible : std::false_type {}; +template +struct AreFunctionsCompatible(std::forward(std::declval()))), + QtPrivate::QSlotObjectBase *>> +> : std::true_type {}; + +template +inline constexpr bool compiles(Functor &&) { + return QtPrivate::AreFunctionsCompatible::value; +} + +void tst_QObject::asyncCallbackHelper() +{ + auto lambda0 = []{}; + auto lambda1 = [](const QString &) {}; + auto lambda2 = [](const QString &, int) {}; + const auto constLambda = [](const QString &) {}; + auto moveOnlyLambda = [u = std::unique_ptr()]{}; + + SlotFunctor functor0; + SlotFunctorString functor1; + + // no parameters provided or needed + static_assert(compiles(&AsyncCaller::callback0)); + static_assert(compiles(&AsyncCaller::staticCallback0)); + static_assert(compiles(lambda0)); + static_assert(compiles(freeFunction0)); + static_assert(compiles(functor0)); + + // more parameters than needed + static_assert(compiles(&AsyncCaller::callback0)); + static_assert(compiles(&AsyncCaller::staticCallback0)); + static_assert(compiles(lambda0)); + static_assert(compiles(freeFunction0)); + static_assert(compiles(functor0)); + + // matching parameter + static_assert(compiles(&AsyncCaller::callback1)); + static_assert(compiles(&AsyncCaller::staticCallback1)); + static_assert(compiles(lambda1)); + static_assert(compiles(constLambda)); + static_assert(compiles(freeFunction1)); + static_assert(compiles(functor1)); + + // not enough parameters + static_assert(!compiles(&AsyncCaller::callback1)); + static_assert(!compiles(&AsyncCaller::staticCallback1)); + static_assert(!compiles(lambda1)); + static_assert(!compiles(constLambda)); + static_assert(!compiles(lambda2)); + static_assert(!compiles(freeFunction1)); + static_assert(!compiles(functor1)); + + // move-only functor - should work, but doesn't because QFunctorSlotObject requires + // the functor to be of a copyable type! + static_assert(!compiles(moveOnlyLambda)); + static_assert(!compiles(moveOnlyLambda)); + + // wrong parameter type + static_assert(!compiles(&AsyncCaller::callbackInt)); + + // old-style slot name + static_assert(!compiles("callback1")); + + // slot with return value is ok, we just don't pass + // the return value through to anything. + static_assert(compiles(&AsyncCaller::returnInt)); + + AsyncCaller caller; + // with context + QVERIFY(caller.callMe0(&caller, &AsyncCaller::callback0)); + QVERIFY(caller.callMe0(&caller, &AsyncCaller::returnInt)); + QVERIFY(caller.callMe0(&caller, &AsyncCaller::staticCallback0)); + QVERIFY(caller.callMe0(&caller, lambda0)); + QVERIFY(caller.callMe0(&caller, freeFunction0)); +// QVERIFY(caller.callMe0(&caller, moveOnlyLambda)); + + QVERIFY(caller.callMe1(&caller, &AsyncCaller::callback1)); + QVERIFY(caller.callMe1(&caller, &AsyncCaller::staticCallback1)); + QVERIFY(caller.callMe1(&caller, lambda1)); + QVERIFY(caller.callMe1(&caller, freeFunction1)); + QVERIFY(caller.callMe1(&caller, constLambda)); + + // without context + QVERIFY(caller.callMe0(&AsyncCaller::staticCallback0)); + QVERIFY(caller.callMe0(lambda0)); + QVERIFY(caller.callMe0(freeFunction0)); +// QVERIFY(caller.callMe0(moveOnlyLambda)); + + QVERIFY(caller.callMe1(&AsyncCaller::staticCallback1)); + QVERIFY(caller.callMe1(lambda1)); + QVERIFY(caller.callMe1(constLambda)); + QVERIFY(caller.callMe1(freeFunction1)); +} + QTEST_MAIN(tst_QObject) #include "tst_qobject.moc" -- cgit v1.2.3