QtConcurrent::run: support non default-constructible return types

The QtConcurrent::RunFunctionTask class keeps a variable to store the
result of QtConcurrent::run when it becomes available, so that it can be
reported afterwards. This requires the result type to be
default-constructible. However there's no need in storing the result, it
can be reported immediately after it becomes available.

Pick-to: 6.1 6.2
Fixes: QTBUG-95214
Change-Id: I95f3dbff0ab41eaa81b104a8834b37d10a0d193a
Reviewed-by: Jarek Kobus <jaroslaw.kobus@qt.io>
Reviewed-by: Andrei Golubev <andrei.golubev@qt.io>
This commit is contained in:
Sona Kurazyan 2021-08-17 16:00:26 +02:00
parent c4ac9e74c7
commit 642b9fce81
4 changed files with 27 additions and 30 deletions

View File

@ -117,37 +117,15 @@ public:
promise.reportException(QUnhandledException(std::current_exception()));
}
#endif
reportResult();
promise.reportFinished();
}
protected:
virtual void runFunctor() = 0;
virtual void reportResult() {}
QFutureInterface<T> promise;
};
template <typename T>
class RunFunctionTask : public RunFunctionTaskBase<T>
{
protected:
void reportResult() override
{
if constexpr (std::is_move_constructible_v<T>)
this->promise.reportAndMoveResult(std::move(result));
else if constexpr (std::is_copy_constructible_v<T>)
this->promise.reportResult(result);
}
T result;
};
template <>
class RunFunctionTask<void> : public RunFunctionTaskBase<void> {};
} //namespace QtConcurrent
#endif //Q_QDOC

View File

@ -138,7 +138,7 @@ template <class ...Types>
using DecayedTuple = std::tuple<std::decay_t<Types>...>;
template <class Function, class ...Args>
struct StoredFunctionCall : public RunFunctionTask<InvokeResultType<Function, Args...>>
struct StoredFunctionCall : public RunFunctionTaskBase<InvokeResultType<Function, Args...>>
{
StoredFunctionCall(DecayedTuple<Function, Args...> &&_data)
: data(std::move(_data))
@ -152,10 +152,17 @@ protected:
return std::invoke(function, args...);
};
if constexpr (std::is_void_v<InvokeResultType<Function, Args...>>)
if constexpr (std::is_void_v<InvokeResultType<Function, Args...>>) {
std::apply(invoke, std::move(data));
else
this->result = std::apply(invoke, std::move(data));
} else {
auto result = std::apply(invoke, std::move(data));
using T = InvokeResultType<Function, Args...>;
if constexpr (std::is_move_constructible_v<T>)
this->promise.reportAndMoveResult(std::move(result));
else if constexpr (std::is_copy_constructible_v<T>)
this->promise.reportResult(result);
}
}
private:

View File

@ -64,6 +64,7 @@ private slots:
void moveOnlyType();
void crefFunction();
void customPromise();
void nonDefaultConstructibleValue();
};
void light()
@ -1564,6 +1565,17 @@ void tst_QtConcurrentRun::customPromise()
QCOMPARE(p.future().progressMaximum(), 10);
}
void tst_QtConcurrentRun::nonDefaultConstructibleValue()
{
struct NonDefaultConstructible
{
explicit NonDefaultConstructible(int v) : value(v) { }
int value = 0;
};
auto future = QtConcurrent::run([] { return NonDefaultConstructible(42); });
QCOMPARE(future.result().value, 42);
}
QTEST_MAIN(tst_QtConcurrentRun)
#include "tst_qtconcurrentrun.moc"

View File

@ -229,12 +229,12 @@ void tst_QFutureWatcher::canceled()
future.waitForFinished();
}
class IntTask : public RunFunctionTask<int>
class IntTask : public RunFunctionTaskBase<int>
{
public:
void runFunctor() override
{
result = 10;
promise.reportResult(10);
}
};
@ -463,7 +463,7 @@ void tst_QFutureWatcher::disconnectRunningFuture()
}
const int maxProgress = 100000;
class ProgressEmitterTask : public RunFunctionTask<void>
class ProgressEmitterTask : public RunFunctionTaskBase<void>
{
public:
void runFunctor() override
@ -493,7 +493,7 @@ void tst_QFutureWatcher::tooMuchProgress()
}
template <typename T>
class ProgressTextTask : public RunFunctionTask<T>
class ProgressTextTask : public RunFunctionTaskBase<T>
{
public:
void runFunctor() override