TaskTree: Make it possible to pass DoneResult as a done handler

This patch addresses the 38th point in the bugreport below.

Add tests for it.

Adapt docs and warning messages accordingly.

Task-number: QTCREATORBUG-28741
Change-Id: I276d2d4c3a514147f67252dc5073d79fed94b9ff
Reviewed-by: Leena Miettinen <riitta-leena.miettinen@qt.io>
Reviewed-by: Eike Ziller <eike.ziller@qt.io>
This commit is contained in:
Jarek Kobus
2024-06-18 00:34:16 +02:00
parent ab78847af8
commit 33eb5f509c
3 changed files with 61 additions and 8 deletions

View File

@@ -677,7 +677,7 @@ private:
/*! /*!
\typealias CustomTask::TaskDoneHandler \typealias CustomTask::TaskDoneHandler
Type alias for \c std::function<DoneResult(const Task &, DoneWith)>. Type alias for \c std::function<DoneResult(const Task &, DoneWith)> or DoneResult.
The \c TaskDoneHandler is an optional argument of a custom task element's constructor. The \c TaskDoneHandler is an optional argument of a custom task element's constructor.
Any function with the above signature, when passed as a task done handler, Any function with the above signature, when passed as a task done handler,
@@ -702,6 +702,9 @@ private:
the DoneWith argument. When the handler returns the DoneResult value, the DoneWith argument. When the handler returns the DoneResult value,
the task's final result may be tweaked inside the done handler's body by the returned value. the task's final result may be tweaked inside the done handler's body by the returned value.
For a \c TaskDoneHandler of the DoneResult type, no additional handling is executed,
and the task finishes unconditionally with the passed value of DoneResult.
\sa CustomTask(), TaskSetupHandler, GroupDoneHandler \sa CustomTask(), TaskSetupHandler, GroupDoneHandler
*/ */
@@ -1056,7 +1059,7 @@ private:
/*! /*!
\typealias GroupItem::GroupDoneHandler \typealias GroupItem::GroupDoneHandler
Type alias for \c std::function<DoneResult(DoneWith)>. Type alias for \c std::function<DoneResult(DoneWith)> or DoneResult.
The \c GroupDoneHandler is an argument of the onGroupDone() element. The \c GroupDoneHandler is an argument of the onGroupDone() element.
Any function with the above signature, when passed as a group done handler, Any function with the above signature, when passed as a group done handler,
@@ -1071,6 +1074,10 @@ private:
the DoneWith argument. When the handler returns the DoneResult value, the DoneWith argument. When the handler returns the DoneResult value,
the group's final result may be tweaked inside the done handler's body by the returned value. the group's final result may be tweaked inside the done handler's body by the returned value.
For a \c GroupDoneHandler of the DoneResult type, no additional handling is executed,
and the group finishes unconditionally with the passed value of DoneResult,
ignoring the group's workflow policy.
\sa onGroupDone(), GroupSetupHandler, CustomTask::TaskDoneHandler \sa onGroupDone(), GroupSetupHandler, CustomTask::TaskDoneHandler
*/ */
@@ -2436,7 +2443,7 @@ bool TaskTreePrivate::invokeDoneHandler(RuntimeTask *node, DoneWith doneWith)
\section2 Task's Done Handler \section2 Task's Done Handler
When a running task finishes, the task tree invokes an optionally provided done handler. When a running task finishes, the task tree invokes an optionally provided done handler.
The handler should always take a \c const \e reference to the associated task class object: The handler should take a \c const \e reference to the associated task class object:
\code \code
const auto onSetup = [](QProcess &process) { const auto onSetup = [](QProcess &process) {

View File

@@ -350,15 +350,19 @@ private:
template <typename Handler> template <typename Handler>
static GroupDoneHandler wrapGroupDone(Handler &&handler) static GroupDoneHandler wrapGroupDone(Handler &&handler)
{ {
static constexpr bool isDoneResultType = std::is_same_v<Handler, DoneResult>;
// R, V, D stands for: Done[R]esult, [V]oid, [D]oneWith // R, V, D stands for: Done[R]esult, [V]oid, [D]oneWith
static constexpr bool isRD = isInvocable<DoneResult, Handler, DoneWith>(); static constexpr bool isRD = isInvocable<DoneResult, Handler, DoneWith>();
static constexpr bool isR = isInvocable<DoneResult, Handler>(); static constexpr bool isR = isInvocable<DoneResult, Handler>();
static constexpr bool isVD = isInvocable<void, Handler, DoneWith>(); static constexpr bool isVD = isInvocable<void, Handler, DoneWith>();
static constexpr bool isV = isInvocable<void, Handler>(); static constexpr bool isV = isInvocable<void, Handler>();
static_assert(isRD || isR || isVD || isV, static_assert(isDoneResultType || isRD || isR || isVD || isV,
"Group done handler needs to take (DoneWith) or (void) as an argument and has to " "Group done handler needs to take (DoneWith) or (void) as an argument and has to "
"return void or DoneResult. The passed handler doesn't fulfill these requirements."); "return void or DoneResult. Alternatively, it may be of DoneResult type. "
"The passed handler doesn't fulfill these requirements.");
return [handler](DoneWith result) { return [handler](DoneWith result) {
if constexpr (isDoneResultType)
return handler;
if constexpr (isRD) if constexpr (isRD)
return std::invoke(handler, result); return std::invoke(handler, result);
if constexpr (isR) if constexpr (isR)
@@ -496,7 +500,8 @@ private:
template <typename Handler> template <typename Handler>
static InterfaceDoneHandler wrapDone(Handler &&handler) { static InterfaceDoneHandler wrapDone(Handler &&handler) {
if constexpr (std::is_same_v<Handler, TaskDoneHandler>) if constexpr (std::is_same_v<Handler, TaskDoneHandler>)
return {}; // When user passed {} for the done handler. return {}; // User passed {} for the done handler.
static constexpr bool isDoneResultType = std::is_same_v<Handler, DoneResult>;
// R, V, T, D stands for: Done[R]esult, [V]oid, [T]ask, [D]oneWith // R, V, T, D stands for: Done[R]esult, [V]oid, [T]ask, [D]oneWith
static constexpr bool isRTD = isInvocable<DoneResult, Handler, const Task &, DoneWith>(); static constexpr bool isRTD = isInvocable<DoneResult, Handler, const Task &, DoneWith>();
static constexpr bool isRT = isInvocable<DoneResult, Handler, const Task &>(); static constexpr bool isRT = isInvocable<DoneResult, Handler, const Task &>();
@@ -506,11 +511,14 @@ private:
static constexpr bool isVT = isInvocable<void, Handler, const Task &>(); static constexpr bool isVT = isInvocable<void, Handler, const Task &>();
static constexpr bool isVD = isInvocable<void, Handler, DoneWith>(); static constexpr bool isVD = isInvocable<void, Handler, DoneWith>();
static constexpr bool isV = isInvocable<void, Handler>(); static constexpr bool isV = isInvocable<void, Handler>();
static_assert(isRTD || isRT || isRD || isR || isVTD || isVT || isVD || isV, static_assert(isDoneResultType || isRTD || isRT || isRD || isR || isVTD || isVT || isVD || isV,
"Task done handler needs to take (const Task &, DoneWith), (const Task &), " "Task done handler needs to take (const Task &, DoneWith), (const Task &), "
"(DoneWith) or (void) as arguments and has to return void or DoneResult. " "(DoneWith) or (void) as arguments and has to return void or DoneResult. "
"Alternatively, it may be of DoneResult type. "
"The passed handler doesn't fulfill these requirements."); "The passed handler doesn't fulfill these requirements.");
return [handler](const TaskInterface &taskInterface, DoneWith result) { return [handler](const TaskInterface &taskInterface, DoneWith result) {
if constexpr (isDoneResultType)
return handler;
const Adapter &adapter = static_cast<const Adapter &>(taskInterface); const Adapter &adapter = static_cast<const Adapter &>(taskInterface);
if constexpr (isRTD) if constexpr (isRTD)
return std::invoke(handler, *adapter.task(), result); return std::invoke(handler, *adapter.task(), result);

View File

@@ -3174,11 +3174,49 @@ void tst_Tasking::testTree_data()
QTest::newRow("ParallelDisorder") << TestData{storage, root, log, 2, DoneWith::Error, 1}; QTest::newRow("ParallelDisorder") << TestData{storage, root, log, 2, DoneWith::Error, 1};
} }
{
// This tests ensures the task done handler or onGroupDone accepts the DoneResult as an
// argument.
const Group groupSuccess {
storage,
Group {
onGroupDone(DoneResult::Success)
},
groupDone(0)
};
const Group groupError {
storage,
Group {
onGroupDone(DoneResult::Error)
},
groupDone(0)
};
const Group taskSuccess {
storage,
TestTask({}, DoneResult::Success),
groupDone(0)
};
const Group taskError {
storage,
TestTask({}, DoneResult::Error),
groupDone(0)
};
QTest::newRow("DoneResultGroupSuccess")
<< TestData{storage, groupSuccess, {{0, Handler::GroupSuccess}}, 0, DoneWith::Success, 0};
QTest::newRow("DoneResultGroupError")
<< TestData{storage, groupError, {{0, Handler::GroupError}}, 0, DoneWith::Error, 0};
QTest::newRow("DoneResultTaskSuccess")
<< TestData{storage, taskSuccess, {{0, Handler::GroupSuccess}}, 1, DoneWith::Success, 1};
QTest::newRow("DoneResultTaskError")
<< TestData{storage, taskError, {{0, Handler::GroupError}}, 1, DoneWith::Error, 1};
}
// This test checks if storage shadowing works OK. // This test checks if storage shadowing works OK.
QTest::newRow("StorageShadowing") << storageShadowingData(); QTest::newRow("StorageShadowing") << storageShadowingData();
} }
static QtMessageHandler s_oldMessageHandler = nullptr; static QtMessageHandler s_oldMessageHandler = nullptr;
static QStringList s_messages; static QStringList s_messages;