Fix QFuture continuations/handlers to work with move-only callables
std::function, which is used to store the type-erased continuation lambdas, requires the passed callable to be copy-constructible. This makes impossible to use move-only callables with continuations/handlers. In particular, it makes impossible passing lambdas that are capturing move-only objects. The workaround is to store the continuation lambda inside a wrapper for the callable, which stores the move-only lambda in a QSharedPtr and can be stored in std::function, since it's copyable. Pick-to: 6.2 Fixes: QTBUG-98493 Change-Id: I8b7a22fcf68dc132b3c533216a7a1665e9f9fb0a Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io> Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
This commit is contained in:
parent
1a61b85a2c
commit
3be72253a6
@ -498,6 +498,17 @@ bool Continuation<Function, ResultType, ParentResultType>::execute()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Workaround for keeping move-only lambdas inside std::function
|
||||||
|
template<class Function>
|
||||||
|
struct ContinuationWrapper
|
||||||
|
{
|
||||||
|
ContinuationWrapper(Function &&f) : function(QSharedPointer<Function>::create(std::move(f))) { }
|
||||||
|
void operator()(const QFutureInterfaceBase &parentData) { (*function)(parentData); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
QSharedPointer<Function> function;
|
||||||
|
};
|
||||||
|
|
||||||
template<typename Function, typename ResultType, typename ParentResultType>
|
template<typename Function, typename ResultType, typename ParentResultType>
|
||||||
template<typename F>
|
template<typename F>
|
||||||
void Continuation<Function, ResultType, ParentResultType>::create(F &&func,
|
void Continuation<Function, ResultType, ParentResultType>::create(F &&func,
|
||||||
@ -543,7 +554,9 @@ void Continuation<Function, ResultType, ParentResultType>::create(F &&func,
|
|||||||
continuationJob = nullptr;
|
continuationJob = nullptr;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
if constexpr (!std::is_copy_constructible_v<Function>)
|
||||||
|
f->d.setContinuation(ContinuationWrapper(std::move(continuation)), p.d);
|
||||||
|
else
|
||||||
f->d.setContinuation(std::move(continuation), p.d);
|
f->d.setContinuation(std::move(continuation), p.d);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -573,6 +586,9 @@ void Continuation<Function, ResultType, ParentResultType>::create(F &&func,
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if constexpr (!std::is_copy_constructible_v<Function>)
|
||||||
|
f->d.setContinuation(ContinuationWrapper(std::move(continuation)), p.d);
|
||||||
|
else
|
||||||
f->d.setContinuation(std::move(continuation), p.d);
|
f->d.setContinuation(std::move(continuation), p.d);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -596,6 +612,9 @@ void Continuation<Function, ResultType, ParentResultType>::create(F &&func,
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if constexpr (!std::is_copy_constructible_v<Function>)
|
||||||
|
f->d.setContinuation(ContinuationWrapper(std::move(continuation)), p.d);
|
||||||
|
else
|
||||||
f->d.setContinuation(std::move(continuation), p.d);
|
f->d.setContinuation(std::move(continuation), p.d);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -675,6 +694,9 @@ void FailureHandler<Function, ResultType>::create(F &&function, QFuture<ResultTy
|
|||||||
failureHandler.run();
|
failureHandler.run();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if constexpr (!std::is_copy_constructible_v<Function>)
|
||||||
|
future->d.setContinuation(ContinuationWrapper(std::move(failureContinuation)));
|
||||||
|
else
|
||||||
future->d.setContinuation(std::move(failureContinuation));
|
future->d.setContinuation(std::move(failureContinuation));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -699,6 +721,9 @@ void FailureHandler<Function, ResultType>::create(F &&function, QFuture<ResultTy
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if constexpr (!std::is_copy_constructible_v<Function>)
|
||||||
|
future->d.setContinuation(ContinuationWrapper(std::move(failureContinuation)));
|
||||||
|
else
|
||||||
future->d.setContinuation(std::move(failureContinuation));
|
future->d.setContinuation(std::move(failureContinuation));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -782,7 +807,12 @@ public:
|
|||||||
auto parentFuture = QFutureInterface<ResultType>(parentData).future();
|
auto parentFuture = QFutureInterface<ResultType>(parentData).future();
|
||||||
run(std::forward<F>(handler), parentFuture, promise);
|
run(std::forward<F>(handler), parentFuture, promise);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if constexpr (!std::is_copy_constructible_v<Function>)
|
||||||
|
future->d.setContinuation(ContinuationWrapper(std::move(canceledContinuation)));
|
||||||
|
else
|
||||||
future->d.setContinuation(std::move(canceledContinuation));
|
future->d.setContinuation(std::move(canceledContinuation));
|
||||||
|
|
||||||
return promise.future();
|
return promise.future();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -802,7 +832,12 @@ public:
|
|||||||
run(std::forward<F>(handler), parentFuture, promise);
|
run(std::forward<F>(handler), parentFuture, promise);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if constexpr (!std::is_copy_constructible_v<Function>)
|
||||||
|
future->d.setContinuation(ContinuationWrapper(std::move(canceledContinuation)));
|
||||||
|
else
|
||||||
future->d.setContinuation(std::move(canceledContinuation));
|
future->d.setContinuation(std::move(canceledContinuation));
|
||||||
|
|
||||||
return promise.future();
|
return promise.future();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,3 +12,8 @@ qt_internal_add_test(tst_qfuture
|
|||||||
PUBLIC_LIBRARIES
|
PUBLIC_LIBRARIES
|
||||||
Qt::CorePrivate
|
Qt::CorePrivate
|
||||||
)
|
)
|
||||||
|
|
||||||
|
qt_internal_extend_target(tst_qmetatype CONDITION MSVC
|
||||||
|
COMPILE_OPTIONS
|
||||||
|
/bigobj
|
||||||
|
)
|
||||||
|
@ -145,6 +145,7 @@ private slots:
|
|||||||
void onCanceled();
|
void onCanceled();
|
||||||
void cancelContinuations();
|
void cancelContinuations();
|
||||||
void continuationsWithContext();
|
void continuationsWithContext();
|
||||||
|
void continuationsWithMoveOnlyLambda();
|
||||||
#if 0
|
#if 0
|
||||||
// TODO: enable when QFuture::takeResults() is enabled
|
// TODO: enable when QFuture::takeResults() is enabled
|
||||||
void takeResults();
|
void takeResults();
|
||||||
@ -3111,6 +3112,71 @@ void tst_QFuture::continuationsWithContext()
|
|||||||
thread.wait();
|
thread.wait();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void tst_QFuture::continuationsWithMoveOnlyLambda()
|
||||||
|
{
|
||||||
|
// .then()
|
||||||
|
{
|
||||||
|
std::unique_ptr<int> uniquePtr(new int(42));
|
||||||
|
auto future = QtFuture::makeReadyFuture().then([p = std::move(uniquePtr)] { return *p; });
|
||||||
|
QCOMPARE(future.result(), 42);
|
||||||
|
}
|
||||||
|
// .then() with thread pool
|
||||||
|
{
|
||||||
|
QThreadPool pool;
|
||||||
|
|
||||||
|
std::unique_ptr<int> uniquePtr(new int(42));
|
||||||
|
auto future =
|
||||||
|
QtFuture::makeReadyFuture().then(&pool, [p = std::move(uniquePtr)] { return *p; });
|
||||||
|
QCOMPARE(future.result(), 42);
|
||||||
|
}
|
||||||
|
// .then() with context
|
||||||
|
{
|
||||||
|
QObject object;
|
||||||
|
|
||||||
|
std::unique_ptr<int> uniquePtr(new int(42));
|
||||||
|
auto future = QtFuture::makeReadyFuture().then(&object,
|
||||||
|
[p = std::move(uniquePtr)] { return *p; });
|
||||||
|
QCOMPARE(future.result(), 42);
|
||||||
|
}
|
||||||
|
|
||||||
|
// .onCanceled()
|
||||||
|
{
|
||||||
|
std::unique_ptr<int> uniquePtr(new int(42));
|
||||||
|
auto future =
|
||||||
|
createCanceledFuture<int>().onCanceled([p = std::move(uniquePtr)] { return *p; });
|
||||||
|
QCOMPARE(future.result(), 42);
|
||||||
|
}
|
||||||
|
|
||||||
|
// .onCanceled() with context
|
||||||
|
{
|
||||||
|
QObject object;
|
||||||
|
|
||||||
|
std::unique_ptr<int> uniquePtr(new int(42));
|
||||||
|
auto future = createCanceledFuture<int>().onCanceled(
|
||||||
|
&object, [p = std::move(uniquePtr)] { return *p; });
|
||||||
|
QCOMPARE(future.result(), 42);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef QT_NO_EXCEPTIONS
|
||||||
|
// .onFailed()
|
||||||
|
{
|
||||||
|
std::unique_ptr<int> uniquePtr(new int(42));
|
||||||
|
auto future = QtFuture::makeExceptionalFuture<int>(QException())
|
||||||
|
.onFailed([p = std::move(uniquePtr)] { return *p; });
|
||||||
|
QCOMPARE(future.result(), 42);
|
||||||
|
}
|
||||||
|
// .onFailed() with context
|
||||||
|
{
|
||||||
|
QObject object;
|
||||||
|
|
||||||
|
std::unique_ptr<int> uniquePtr(new int(42));
|
||||||
|
auto future = QtFuture::makeExceptionalFuture<int>(QException())
|
||||||
|
.onFailed(&object, [p = std::move(uniquePtr)] { return *p; });
|
||||||
|
QCOMPARE(future.result(), 42);
|
||||||
|
}
|
||||||
|
#endif // QT_NO_EXCEPTIONS
|
||||||
|
}
|
||||||
|
|
||||||
void tst_QFuture::testSingleResult(const UniquePtr &p)
|
void tst_QFuture::testSingleResult(const UniquePtr &p)
|
||||||
{
|
{
|
||||||
QVERIFY(p.get() != nullptr);
|
QVERIFY(p.get() != nullptr);
|
||||||
|
Loading…
Reference in New Issue
Block a user