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 <thiago.macieira@intel.com>
This commit is contained in:
Volker Hilsheimer 2023-04-27 16:26:32 +02:00
parent bd69821074
commit 9958edba41
3 changed files with 20 additions and 23 deletions

View File

@ -417,11 +417,11 @@ struct Q_CORE_EXPORT QMetaObject
&& !std::is_convertible<Func, const char*>::value
&& QtPrivate::FunctionPointer<Func>::ArgumentCount == 0, bool>::type
invokeMethod(typename QtPrivate::FunctionPointer<Func>::Object *object,
Func function,
Func &&function,
Qt::ConnectionType type = Qt::AutoConnection,
typename QtPrivate::FunctionPointer<Func>::ReturnType *ret = nullptr)
{
return invokeMethodImpl(object, new QtPrivate::QSlotObjectWithNoArgs<Func>(function), type, ret);
return invokeMethodImpl(object, new QtPrivate::QSlotObjectWithNoArgs<Func>(std::forward<Func>(function)), type, ret);
}
template <typename Func>
@ -429,10 +429,10 @@ struct Q_CORE_EXPORT QMetaObject
&& !std::is_convertible<Func, const char*>::value
&& QtPrivate::FunctionPointer<Func>::ArgumentCount == 0, bool>::type
invokeMethod(typename QtPrivate::FunctionPointer<Func>::Object *object,
Func function,
Func &&function,
typename QtPrivate::FunctionPointer<Func>::ReturnType *ret)
{
return invokeMethodImpl(object, new QtPrivate::QSlotObjectWithNoArgs<Func>(function), Qt::AutoConnection, ret);
return invokeMethodImpl(object, new QtPrivate::QSlotObjectWithNoArgs<Func>(std::forward<Func>(function)), Qt::AutoConnection, ret);
}
// invokeMethod() for function pointer (not member)
@ -440,21 +440,21 @@ struct Q_CORE_EXPORT QMetaObject
static typename std::enable_if<!QtPrivate::FunctionPointer<Func>::IsPointerToMemberFunction
&& !std::is_convertible<Func, const char*>::value
&& QtPrivate::FunctionPointer<Func>::ArgumentCount == 0, bool>::type
invokeMethod(QObject *context, Func function,
invokeMethod(QObject *context, Func &&function,
Qt::ConnectionType type = Qt::AutoConnection,
typename QtPrivate::FunctionPointer<Func>::ReturnType *ret = nullptr)
{
return invokeMethodImpl(context, new QtPrivate::QFunctorSlotObjectWithNoArgsImplicitReturn<Func>(function), type, ret);
return invokeMethodImpl(context, new QtPrivate::QFunctorSlotObjectWithNoArgsImplicitReturn<Func>(std::forward<Func>(function)), type, ret);
}
template <typename Func>
static typename std::enable_if<!QtPrivate::FunctionPointer<Func>::IsPointerToMemberFunction
&& !std::is_convertible<Func, const char*>::value
&& QtPrivate::FunctionPointer<Func>::ArgumentCount == 0, bool>::type
invokeMethod(QObject *context, Func function,
invokeMethod(QObject *context, Func &&function,
typename QtPrivate::FunctionPointer<Func>::ReturnType *ret)
{
return invokeMethodImpl(context, new QtPrivate::QFunctorSlotObjectWithNoArgsImplicitReturn<Func>(function), Qt::AutoConnection, ret);
return invokeMethodImpl(context, new QtPrivate::QFunctorSlotObjectWithNoArgsImplicitReturn<Func>(std::forward<Func>(function)), Qt::AutoConnection, ret);
}
// invokeMethod() for Functor
@ -462,11 +462,11 @@ struct Q_CORE_EXPORT QMetaObject
static typename std::enable_if<!QtPrivate::FunctionPointer<Func>::IsPointerToMemberFunction
&& QtPrivate::FunctionPointer<Func>::ArgumentCount == -1
&& !std::is_convertible<Func, const char*>::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<Func, decltype(function())>(std::move(function)),
new QtPrivate::QFunctorSlotObjectWithNoArgs<Func, decltype(function())>(std::forward<Func>(function)),
type,
ret);
}
@ -475,10 +475,10 @@ struct Q_CORE_EXPORT QMetaObject
static typename std::enable_if<!QtPrivate::FunctionPointer<Func>::IsPointerToMemberFunction
&& QtPrivate::FunctionPointer<Func>::ArgumentCount == -1
&& !std::is_convertible<Func, const char*>::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<Func, decltype(function())>(std::move(function)),
new QtPrivate::QFunctorSlotObjectWithNoArgs<Func, decltype(function())>(std::forward<Func>(function)),
Qt::AutoConnection,
ret);
}

View File

@ -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<Func>(f)) {}
};
// typedefs for readability for when there are no parameters
@ -492,7 +492,7 @@ namespace QtPrivate {
template <typename Prototype, typename Functor>
static constexpr std::enable_if_t<QtPrivate::countMatchingArguments<Prototype, Functor>() >= 0,
QtPrivate::QSlotObjectBase *>
makeSlotObject(Functor func)
makeSlotObject(Functor &&func)
{
using ExpectedSignature = QtPrivate::FunctionPointer<Prototype>;
using ExpectedArguments = typename ExpectedSignature::Arguments;
@ -503,13 +503,13 @@ namespace QtPrivate {
if constexpr (QtPrivate::FunctionPointer<Functor>::IsPointerToMemberFunction) {
using ActualArguments = typename ActualSignature::Arguments;
return new QtPrivate::QSlotObject<Functor, ActualArguments, void>(func);
return new QtPrivate::QSlotObject<Functor, ActualArguments, void>(std::forward<Functor>(func));
} else {
constexpr int MatchingArgumentCount = QtPrivate::countMatchingArguments<Prototype, Functor>();
using ActualArguments = typename QtPrivate::List_Left<ExpectedArguments, MatchingArgumentCount>::Value;
return new QtPrivate::QFunctorSlotObject<Functor, MatchingArgumentCount,
ActualArguments, void>(std::move(func));
ActualArguments, void>(std::forward<Functor>(func));
}
}
}

View File

@ -8434,6 +8434,7 @@ void tst_QObject::asyncCallbackHelper()
static_assert(compiles<AsyncCaller::Prototype0>(&AsyncCaller::callback0));
static_assert(compiles<AsyncCaller::Prototype0>(&AsyncCaller::staticCallback0));
static_assert(compiles<AsyncCaller::Prototype0>(lambda0));
static_assert(compiles<AsyncCaller::Prototype0>(moveOnlyLambda));
static_assert(compiles<AsyncCaller::Prototype0>(freeFunction0));
static_assert(compiles<AsyncCaller::Prototype0>(functor0));
@ -8448,6 +8449,7 @@ void tst_QObject::asyncCallbackHelper()
static_assert(compiles<AsyncCaller::Prototype1>(&AsyncCaller::callback1));
static_assert(compiles<AsyncCaller::Prototype1>(&AsyncCaller::staticCallback1));
static_assert(compiles<AsyncCaller::Prototype1>(lambda1));
static_assert(compiles<AsyncCaller::Prototype1>(moveOnlyLambda));
static_assert(compiles<AsyncCaller::Prototype1>(constLambda));
static_assert(compiles<AsyncCaller::Prototype1>(freeFunction1));
static_assert(compiles<AsyncCaller::Prototype1>(functor1));
@ -8461,11 +8463,6 @@ void tst_QObject::asyncCallbackHelper()
static_assert(!compiles<AsyncCaller::Prototype0>(freeFunction1));
static_assert(!compiles<AsyncCaller::Prototype0>(functor1));
// move-only functor - should work, but doesn't because QFunctorSlotObject requires
// the functor to be of a copyable type!
static_assert(!compiles<AsyncCaller::Prototype0>(moveOnlyLambda));
static_assert(!compiles<AsyncCaller::Prototype1>(moveOnlyLambda));
// wrong parameter type
static_assert(!compiles<AsyncCaller::Prototype1>(&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));