From 9958edba41ac49097a54e0872c3c4934d2dd81f9 Mon Sep 17 00:00:00 2001 From: Volker Hilsheimer Date: Thu, 27 Apr 2023 16:26:32 +0200 Subject: Support move-only functors in invokeMethod and async APIs Move-only functors must never be passed by value, so fix the QFunctorSlotObject constructor accordingly. This then requires adjustments to the various QMetaMethod::invokeMethod overloads, as those must also perfectly forwad the functor type. Enable the previously failing test case for move-only functors. Change-Id: I9c544fd3ddbc5e1da3ca193236291a9f83d86211 Reviewed-by: Thiago Macieira --- src/corelib/kernel/qobjectdefs.h | 24 +++++++++++------------ src/corelib/kernel/qobjectdefs_impl.h | 8 ++++---- tests/auto/corelib/kernel/qobject/tst_qobject.cpp | 11 ++++------- 3 files changed, 20 insertions(+), 23 deletions(-) diff --git a/src/corelib/kernel/qobjectdefs.h b/src/corelib/kernel/qobjectdefs.h index 28b4e368fe8..982af5a18a0 100644 --- a/src/corelib/kernel/qobjectdefs.h +++ b/src/corelib/kernel/qobjectdefs.h @@ -417,11 +417,11 @@ struct Q_CORE_EXPORT QMetaObject && !std::is_convertible::value && QtPrivate::FunctionPointer::ArgumentCount == 0, bool>::type invokeMethod(typename QtPrivate::FunctionPointer::Object *object, - Func function, + Func &&function, Qt::ConnectionType type = Qt::AutoConnection, typename QtPrivate::FunctionPointer::ReturnType *ret = nullptr) { - return invokeMethodImpl(object, new QtPrivate::QSlotObjectWithNoArgs(function), type, ret); + return invokeMethodImpl(object, new QtPrivate::QSlotObjectWithNoArgs(std::forward(function)), type, ret); } template @@ -429,10 +429,10 @@ struct Q_CORE_EXPORT QMetaObject && !std::is_convertible::value && QtPrivate::FunctionPointer::ArgumentCount == 0, bool>::type invokeMethod(typename QtPrivate::FunctionPointer::Object *object, - Func function, + Func &&function, typename QtPrivate::FunctionPointer::ReturnType *ret) { - return invokeMethodImpl(object, new QtPrivate::QSlotObjectWithNoArgs(function), Qt::AutoConnection, ret); + return invokeMethodImpl(object, new QtPrivate::QSlotObjectWithNoArgs(std::forward(function)), Qt::AutoConnection, ret); } // invokeMethod() for function pointer (not member) @@ -440,21 +440,21 @@ struct Q_CORE_EXPORT QMetaObject static typename std::enable_if::IsPointerToMemberFunction && !std::is_convertible::value && QtPrivate::FunctionPointer::ArgumentCount == 0, bool>::type - invokeMethod(QObject *context, Func function, + invokeMethod(QObject *context, Func &&function, Qt::ConnectionType type = Qt::AutoConnection, typename QtPrivate::FunctionPointer::ReturnType *ret = nullptr) { - return invokeMethodImpl(context, new QtPrivate::QFunctorSlotObjectWithNoArgsImplicitReturn(function), type, ret); + return invokeMethodImpl(context, new QtPrivate::QFunctorSlotObjectWithNoArgsImplicitReturn(std::forward(function)), type, ret); } template static typename std::enable_if::IsPointerToMemberFunction && !std::is_convertible::value && QtPrivate::FunctionPointer::ArgumentCount == 0, bool>::type - invokeMethod(QObject *context, Func function, + invokeMethod(QObject *context, Func &&function, typename QtPrivate::FunctionPointer::ReturnType *ret) { - return invokeMethodImpl(context, new QtPrivate::QFunctorSlotObjectWithNoArgsImplicitReturn(function), Qt::AutoConnection, ret); + return invokeMethodImpl(context, new QtPrivate::QFunctorSlotObjectWithNoArgsImplicitReturn(std::forward(function)), Qt::AutoConnection, ret); } // invokeMethod() for Functor @@ -462,11 +462,11 @@ struct Q_CORE_EXPORT QMetaObject static typename std::enable_if::IsPointerToMemberFunction && QtPrivate::FunctionPointer::ArgumentCount == -1 && !std::is_convertible::value, bool>::type - invokeMethod(QObject *context, Func function, + invokeMethod(QObject *context, Func &&function, Qt::ConnectionType type = Qt::AutoConnection, decltype(function()) *ret = nullptr) { return invokeMethodImpl(context, - new QtPrivate::QFunctorSlotObjectWithNoArgs(std::move(function)), + new QtPrivate::QFunctorSlotObjectWithNoArgs(std::forward(function)), type, ret); } @@ -475,10 +475,10 @@ struct Q_CORE_EXPORT QMetaObject static typename std::enable_if::IsPointerToMemberFunction && QtPrivate::FunctionPointer::ArgumentCount == -1 && !std::is_convertible::value, bool>::type - invokeMethod(QObject *context, Func function, decltype(function()) *ret) + invokeMethod(QObject *context, Func &&function, decltype(function()) *ret) { return invokeMethodImpl(context, - new QtPrivate::QFunctorSlotObjectWithNoArgs(std::move(function)), + new QtPrivate::QFunctorSlotObjectWithNoArgs(std::forward(function)), Qt::AutoConnection, ret); } diff --git a/src/corelib/kernel/qobjectdefs_impl.h b/src/corelib/kernel/qobjectdefs_impl.h index 3c3457f6941..e8b9b4a2091 100644 --- a/src/corelib/kernel/qobjectdefs_impl.h +++ b/src/corelib/kernel/qobjectdefs_impl.h @@ -439,7 +439,7 @@ namespace QtPrivate { } } public: - explicit QFunctorSlotObject(Func f) : QSlotObjectBase(&impl), function(std::move(f)) {} + explicit QFunctorSlotObject(Func &&f) : QSlotObjectBase(&impl), function(std::forward(f)) {} }; // typedefs for readability for when there are no parameters @@ -492,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; @@ -503,13 +503,13 @@ namespace QtPrivate { if constexpr (QtPrivate::FunctionPointer::IsPointerToMemberFunction) { using ActualArguments = typename ActualSignature::Arguments; - return new QtPrivate::QSlotObject(func); + return new QtPrivate::QSlotObject(std::forward(func)); } else { constexpr int MatchingArgumentCount = QtPrivate::countMatchingArguments(); using ActualArguments = typename QtPrivate::List_Left::Value; return new QtPrivate::QFunctorSlotObject(std::move(func)); + ActualArguments, void>(std::forward(func)); } } } diff --git a/tests/auto/corelib/kernel/qobject/tst_qobject.cpp b/tests/auto/corelib/kernel/qobject/tst_qobject.cpp index 9c18a2a0f60..649f075a02e 100644 --- a/tests/auto/corelib/kernel/qobject/tst_qobject.cpp +++ b/tests/auto/corelib/kernel/qobject/tst_qobject.cpp @@ -8434,6 +8434,7 @@ void tst_QObject::asyncCallbackHelper() static_assert(compiles(&AsyncCaller::callback0)); static_assert(compiles(&AsyncCaller::staticCallback0)); static_assert(compiles(lambda0)); + static_assert(compiles(moveOnlyLambda)); static_assert(compiles(freeFunction0)); static_assert(compiles(functor0)); @@ -8448,6 +8449,7 @@ void tst_QObject::asyncCallbackHelper() static_assert(compiles(&AsyncCaller::callback1)); static_assert(compiles(&AsyncCaller::staticCallback1)); static_assert(compiles(lambda1)); + static_assert(compiles(moveOnlyLambda)); static_assert(compiles(constLambda)); static_assert(compiles(freeFunction1)); static_assert(compiles(functor1)); @@ -8461,11 +8463,6 @@ void tst_QObject::asyncCallbackHelper() 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)); @@ -8483,7 +8480,7 @@ void tst_QObject::asyncCallbackHelper() QVERIFY(caller.callMe0(&caller, &AsyncCaller::staticCallback0)); QVERIFY(caller.callMe0(&caller, lambda0)); QVERIFY(caller.callMe0(&caller, freeFunction0)); -// QVERIFY(caller.callMe0(&caller, moveOnlyLambda)); + QVERIFY(caller.callMe0(&caller, moveOnlyLambda)); QVERIFY(caller.callMe1(&caller, &AsyncCaller::callback1)); QVERIFY(caller.callMe1(&caller, &AsyncCaller::staticCallback1)); @@ -8495,7 +8492,7 @@ void tst_QObject::asyncCallbackHelper() QVERIFY(caller.callMe0(&AsyncCaller::staticCallback0)); QVERIFY(caller.callMe0(lambda0)); QVERIFY(caller.callMe0(freeFunction0)); -// QVERIFY(caller.callMe0(moveOnlyLambda)); + QVERIFY(caller.callMe0(moveOnlyLambda)); QVERIFY(caller.callMe1(&AsyncCaller::staticCallback1)); QVERIFY(caller.callMe1(lambda1)); -- cgit v1.2.3