diff --git a/src/libs/solutions/tasking/tasktree.cpp b/src/libs/solutions/tasking/tasktree.cpp index b7ebb45a8c7..55ae3cfb56a 100644 --- a/src/libs/solutions/tasking/tasktree.cpp +++ b/src/libs/solutions/tasking/tasktree.cpp @@ -677,7 +677,7 @@ private: /*! \typealias CustomTask::TaskDoneHandler - Type alias for \c std::function. + Type alias for \c std::function 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. + Type alias for \c std::function 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) { diff --git a/src/libs/solutions/tasking/tasktree.h b/src/libs/solutions/tasking/tasktree.h index caedb78c2c8..308069edccf 100644 --- a/src/libs/solutions/tasking/tasktree.h +++ b/src/libs/solutions/tasking/tasktree.h @@ -350,15 +350,19 @@ private: template static GroupDoneHandler wrapGroupDone(Handler &&handler) { + static constexpr bool isDoneResultType = std::is_same_v; // R, V, D stands for: Done[R]esult, [V]oid, [D]oneWith static constexpr bool isRD = isInvocable(); static constexpr bool isR = isInvocable(); static constexpr bool isVD = isInvocable(); static constexpr bool isV = isInvocable(); - 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 static InterfaceDoneHandler wrapDone(Handler &&handler) { if constexpr (std::is_same_v) - return {}; // When user passed {} for the done handler. + return {}; // User passed {} for the done handler. + static constexpr bool isDoneResultType = std::is_same_v; // R, V, T, D stands for: Done[R]esult, [V]oid, [T]ask, [D]oneWith static constexpr bool isRTD = isInvocable(); static constexpr bool isRT = isInvocable(); @@ -506,11 +511,14 @@ private: static constexpr bool isVT = isInvocable(); static constexpr bool isVD = isInvocable(); static constexpr bool isV = isInvocable(); - 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(taskInterface); if constexpr (isRTD) return std::invoke(handler, *adapter.task(), result); diff --git a/tests/auto/solutions/tasking/tst_tasking.cpp b/tests/auto/solutions/tasking/tst_tasking.cpp index 15f5d12004d..261f0cc5c73 100644 --- a/tests/auto/solutions/tasking/tst_tasking.cpp +++ b/tests/auto/solutions/tasking/tst_tasking.cpp @@ -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;