diff --git a/src/corelib/kernel/qcoreapplication.h b/src/corelib/kernel/qcoreapplication.h index 5a5ddf4837..2514c7630b 100644 --- a/src/corelib/kernel/qcoreapplication.h +++ b/src/corelib/kernel/qcoreapplication.h @@ -123,20 +123,20 @@ public: template void requestPermission(const QPermission &permission, const typename QtPrivate::ContextTypeForFunctor::ContextType *receiver, - Functor func) + Functor &&func) { using Prototype = void(*)(QPermission); requestPermission(permission, - QtPrivate::makeSlotObject(std::move(func)), + QtPrivate::makeSlotObject(std::forward(func)), receiver); } # endif // Q_QDOC // requestPermission to a functor or function pointer (without context) template - void requestPermission(const QPermission &permission, Functor func) + void requestPermission(const QPermission &permission, Functor &&func) { - requestPermission(permission, nullptr, std::move(func)); + requestPermission(permission, nullptr, std::forward(func)); } private: diff --git a/src/corelib/kernel/qobjectdefs_impl.h b/src/corelib/kernel/qobjectdefs_impl.h index fea9e0ac52..3c3457f694 100644 --- a/src/corelib/kernel/qobjectdefs_impl.h +++ b/src/corelib/kernel/qobjectdefs_impl.h @@ -342,15 +342,19 @@ namespace QtPrivate { constexpr int inline countMatchingArguments() { using ExpectedArguments = typename QtPrivate::FunctionPointer::Arguments; + using Actual = std::decay_t; - if constexpr (QtPrivate::FunctionPointer::IsPointerToMemberFunction) { - using ActualArguments = typename QtPrivate::FunctionPointer::Arguments; + if constexpr (QtPrivate::FunctionPointer::IsPointerToMemberFunction + || QtPrivate::FunctionPointer::ArgumentCount >= 0) { + // PMF or free function + using ActualArguments = typename QtPrivate::FunctionPointer::Arguments; if constexpr (QtPrivate::CheckCompatibleArguments::value) - return QtPrivate::FunctionPointer::ArgumentCount; + return QtPrivate::FunctionPointer::ArgumentCount; else return -1; } else { - return QtPrivate::ComputeFunctorArgumentCount::Value; + // lambda or functor + return QtPrivate::ComputeFunctorArgumentCount::Value; } } @@ -488,7 +492,7 @@ namespace QtPrivate { template static constexpr std::enable_if_t() >= 0, QtPrivate::QSlotObjectBase *> - makeSlotObject(Functor &&func) + makeSlotObject(Functor func) { using ExpectedSignature = QtPrivate::FunctionPointer; using ExpectedArguments = typename ExpectedSignature::Arguments; @@ -496,15 +500,16 @@ namespace QtPrivate { static_assert(int(ActualSignature::ArgumentCount) <= int(ExpectedSignature::ArgumentCount), "Functor requires more arguments than what can be provided."); - constexpr int MatchingArgumentCount = QtPrivate::countMatchingArguments(); if constexpr (QtPrivate::FunctionPointer::IsPointerToMemberFunction) { using ActualArguments = typename ActualSignature::Arguments; - return new QtPrivate::QSlotObject(func); + return new QtPrivate::QSlotObject(func); } else { + constexpr int MatchingArgumentCount = QtPrivate::countMatchingArguments(); using ActualArguments = typename QtPrivate::List_Left::Value; - return new QtPrivate::QFunctorSlotObject(std::move(func)); + return new QtPrivate::QFunctorSlotObject(std::move(func)); } } } diff --git a/src/network/kernel/qhostinfo.h b/src/network/kernel/qhostinfo.h index d990af685a..9b420bd3ca 100644 --- a/src/network/kernel/qhostinfo.h +++ b/src/network/kernel/qhostinfo.h @@ -63,20 +63,20 @@ public: template static inline int lookupHost(const QString &name, const typename QtPrivate::ContextTypeForFunctor::ContextType *receiver, - Functor func) + Functor &&func) { using Prototype = void(*)(QHostInfo); return lookupHostImpl(name, receiver, - QtPrivate::makeSlotObject(std::move(func)), + QtPrivate::makeSlotObject(std::forward(func)), nullptr); } #endif // Q_QDOC // lookupHost to a callable (without context) template - static inline int lookupHost(const QString &name, Functor slot) + static inline int lookupHost(const QString &name, Functor &&slot) { - return lookupHost(name, nullptr, std::move(slot)); + return lookupHost(name, nullptr, std::forward(slot)); } private: 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"