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
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.
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 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
*/
@@ -1056,7 +1059,7 @@ private:
/*!
\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.
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 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
*/
@@ -2436,7 +2443,7 @@ bool TaskTreePrivate::invokeDoneHandler(RuntimeTask *node, DoneWith doneWith)
\section2 Task's 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
const auto onSetup = [](QProcess &process) {

View File

@@ -350,15 +350,19 @@ private:
template <typename 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
static constexpr bool isRD = isInvocable<DoneResult, Handler, DoneWith>();
static constexpr bool isR = isInvocable<DoneResult, Handler>();
static constexpr bool isVD = isInvocable<void, Handler, DoneWith>();
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 "
"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) {
if constexpr (isDoneResultType)
return handler;
if constexpr (isRD)
return std::invoke(handler, result);
if constexpr (isR)
@@ -496,7 +500,8 @@ private:
template <typename Handler>
static InterfaceDoneHandler wrapDone(Handler &&handler) {
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
static constexpr bool isRTD = isInvocable<DoneResult, Handler, const Task &, DoneWith>();
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 isVD = isInvocable<void, Handler, DoneWith>();
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 &), "
"(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.");
return [handler](const TaskInterface &taskInterface, DoneWith result) {
if constexpr (isDoneResultType)
return handler;
const Adapter &adapter = static_cast<const Adapter &>(taskInterface);
if constexpr (isRTD)
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};
}
{
// 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.
QTest::newRow("StorageShadowing") << storageShadowingData();
}
static QtMessageHandler s_oldMessageHandler = nullptr;
static QStringList s_messages;