5#ifndef TASKING_TASKTREE_H
6#define TASKING_TASKTREE_H
21#include <QtCore/QList>
22#include <QtCore/QObject>
62Q_ENUM_NS(WorkflowPolicy)
109 template <
typename Task,
typename Deleter>
friend class TaskAdapter;
111 TaskInterface() =
default;
128 Loop(
int count,
const ValueGetter &valueGetter = {});
129 Loop(
const Condition &condition);
158class LoopList
final :
public Loop
161 LoopList(
const QList<T> &list) :
Loop(list.size(), [list](
int i) {
return &list.at(i); }) {}
173 StorageBase(
const StorageConstructor &ctor,
const StorageDestructor &dtor);
175 void *activeStorageVoid()
const;
178 {
return first.m_storageData == second.m_storageData; }
181 {
return first.m_storageData != second.m_storageData; }
184 {
return size_t(storage.m_storageData.get()) ^ seed; }
188 template <
typename StorageStruct>
friend class Storage;
196template <
typename StorageStruct>
201#if __cplusplus
>= 201803L
202 template <
typename ...
Args>
206 template <
typename ...Args>
209 return std::apply([](
const Args &...arguments) {
return new StorageStruct(arguments...); }, argsTuple);
210 }, Storage::dtor()) {}
215 return static_cast<StorageStruct *>(activeStorageVoid());
219 static StorageConstructor ctor() {
return [] {
return new StorageStruct(); }; }
220 static StorageDestructor dtor() {
221 return [](
void *storage) {
delete static_cast<StorageStruct *>(storage); };
233 template <
typename StorageStruct>
292 template <
typename Result,
typename Function,
typename ...Args,
297 if constexpr (std::is_invocable_r_v<Result, DecayedFunction, Args...>)
298 return std::is_same_v<Result, std::invoke_result_t<DecayedFunction, Args...>>;
307 friend class ParallelLimitFunctor;
308 friend class WorkflowPolicyFunctor;
310 GroupItems m_children;
312 QList<StorageBase> m_storageList;
320 const std::function<
void()> &handler = {})
const;
322 template <
typename SenderSignalPairGetter>
323 Group withCancel(SenderSignalPairGetter &&getter, std::initializer_list<GroupItem> postCancelRecipe = {})
const;
324 template <
typename SenderSignalPairGetter>
340 Group withCancelImpl(
341 const std::function<
void(QObject *,
const std::function<
void()> &)> &connectWrapper,
342 const GroupItems &postCancelRecipe)
const;
343 Group withAcceptImpl(
344 const std::function<
void(QObject *,
const std::function<
void()> &)> &connectWrapper)
const;
350 Group(
const GroupItems &children) { addChildren(children); }
351 Group(std::initializer_list<GroupItem> children) { addChildren(children); }
354 template <
typename Handler>
358 template <
typename Handler>
360 return groupHandler({{}, wrapGroupDone(
std::forward<Handler>(handler)), callDoneIf}
);
364 template <
typename Handler>
365 static GroupSetupHandler wrapGroupSetup(Handler &&handler)
368 static constexpr bool isR = isInvocable<
SetupResult, Handler>();
369 static constexpr bool isV = isInvocable<
void, Handler>();
370 static_assert(isR || isV,
371 "Group setup handler needs to take no arguments and has to return void or SetupResult. "
372 "The passed handler doesn't fulfill these requirements.");
373 return [handler =
std::move(handler)] {
375 return std::invoke(handler);
376 std::invoke(handler);
380 template <
typename Handler>
381 static GroupDoneHandler wrapGroupDone(Handler &&handler)
383 static constexpr bool isDoneResultType = std::is_same_v<std::decay_t<Handler>, DoneResult>;
386 static constexpr bool isR = isInvocable<
DoneResult, Handler>();
387 static constexpr bool isBD = isInvocable<
bool, Handler,
DoneWith>();
388 static constexpr bool isB = isInvocable<
bool, Handler>();
389 static constexpr bool isVD = isInvocable<
void, Handler,
DoneWith>();
390 static constexpr bool isV = isInvocable<
void, Handler>();
391 static_assert(isDoneResultType || isRD || isR || isBD || isB || isVD || isV,
392 "Group done handler needs to take (DoneWith) or (void) as an argument and has to "
393 "return void, bool or DoneResult. Alternatively, it may be of DoneResult type. "
394 "The passed handler doesn't fulfill these requirements.");
395 return [handler =
std::move(handler)](
DoneWith result) {
396 if constexpr (isDoneResultType)
399 return std::invoke(handler, result);
401 return std::invoke(handler);
403 return toDoneResult(std::invoke(handler, result));
405 return toDoneResult(std::invoke(handler));
407 std::invoke(handler, result);
408 else if constexpr (isV)
409 std::invoke(handler);
415template <
typename SenderSignalPairGetter>
417 std::initializer_list<GroupItem> postCancelRecipe)
const
419 const auto connectWrapper = [getter](QObject *guard,
const std::function<
void()> &trigger) {
420 const auto senderSignalPair = getter();
421 QObject::connect(senderSignalPair.first, senderSignalPair.second, guard, [trigger] {
423 },
static_cast<Qt::ConnectionType>(Qt::QueuedConnection | Qt::SingleShotConnection));
425 return withCancelImpl(connectWrapper, postCancelRecipe);
428template <
typename SenderSignalPairGetter>
431 const auto connectWrapper = [getter](QObject *guard,
const std::function<
void()> &trigger) {
432 const auto senderSignalPair = getter();
433 QObject::connect(senderSignalPair.first, senderSignalPair.second, guard, [trigger] {
435 },
static_cast<Qt::ConnectionType>(Qt::QueuedConnection | Qt::SingleShotConnection));
437 return withAcceptImpl(connectWrapper);
440template <
typename Handler>
443 return Group::onGroupSetup(
std::forward<Handler>(handler));
446template <
typename Handler>
449 return Group::onGroupDone(
std::forward<Handler>(handler), callDoneIf);
504 { addChildren({ For (LoopForever()) >> Do { children } } ); }
505 explicit Forever(std::initializer_list<GroupItem> children)
506 { addChildren({ For (LoopForever()) >> Do { children } } ); }
513 template <
typename Handler>
515 addChildren({ onGroupDone(wrapHandler(std::forward<Handler>(handler))) });
519 template <
typename Handler>
520 static auto wrapHandler(Handler &&handler) {
522 static constexpr bool isR = isInvocable<
DoneResult, Handler>();
523 static constexpr bool isB = isInvocable<
bool, Handler>();
524 static constexpr bool isV = isInvocable<
void, Handler>();
525 static_assert(isR || isB || isV,
526 "Sync handler needs to take no arguments and has to return void, bool or DoneResult. "
527 "The passed handler doesn't fulfill these requirements.");
532template <
typename Task,
typename Deleter =
std::default_delete<Task>>
537 Task *
task() {
return m_task.get(); }
538 const Task *
task()
const {
return m_task.get(); }
541 using TaskType = Task;
542 using DeleterType = Deleter;
544 std::unique_ptr<Task, Deleter> m_task;
547template <
typename Adapter>
554 "The Adapter type for the CustomTask<Adapter> needs to be derived from "
555 "TaskAdapter<Task>.");
560 CustomTask(SetupHandler &&setup = TaskSetupHandler(), DoneHandler &&done = TaskDoneHandler(),
563 wrapDone(
std::forward<DoneHandler>(done)), callDoneIf})
567 static Adapter *createAdapter() {
return new Adapter; }
569 template <
typename Handler>
570 static InterfaceSetupHandler wrapSetup(Handler &&handler) {
571 if constexpr (std::is_same_v<Handler, TaskSetupHandler>)
574 static constexpr bool isR = isInvocable<
SetupResult, Handler,
Task &>();
575 static constexpr bool isV = isInvocable<
void, Handler,
Task &>();
576 static_assert(isR || isV,
577 "Task setup handler needs to take (Task &) as an argument and has to return void or "
578 "SetupResult. The passed handler doesn't fulfill these requirements.");
580 Adapter &adapter =
static_cast<Adapter &>(taskInterface);
582 return std::invoke(handler, *adapter.task());
583 std::invoke(handler, *adapter.task());
588 template <
typename Handler>
589 static InterfaceDoneHandler wrapDone(Handler &&handler) {
590 if constexpr (std::is_same_v<Handler, TaskDoneHandler>)
592 static constexpr bool isDoneResultType = std::is_same_v<std::decay_t<Handler>, DoneResult>;
595 static constexpr bool isRT = isInvocable<
DoneResult, Handler,
const Task &>();
597 static constexpr bool isR = isInvocable<
DoneResult, Handler>();
598 static constexpr bool isBTD = isInvocable<
bool, Handler,
const Task &,
DoneWith>();
599 static constexpr bool isBT = isInvocable<
bool, Handler,
const Task &>();
600 static constexpr bool isBD = isInvocable<
bool, Handler,
DoneWith>();
601 static constexpr bool isB = isInvocable<
bool, Handler>();
602 static constexpr bool isVTD = isInvocable<
void, Handler,
const Task &,
DoneWith>();
603 static constexpr bool isVT = isInvocable<
void, Handler,
const Task &>();
604 static constexpr bool isVD = isInvocable<
void, Handler,
DoneWith>();
605 static constexpr bool isV = isInvocable<
void, Handler>();
606 static_assert(isDoneResultType || isRTD || isRT || isRD || isR
607 || isBTD || isBT || isBD || isB
608 || isVTD || isVT || isVD || isV,
609 "Task done handler needs to take (const Task &, DoneWith), (const Task &), "
610 "(DoneWith) or (void) as arguments and has to return void, bool or DoneResult. "
611 "Alternatively, it may be of DoneResult type. "
612 "The passed handler doesn't fulfill these requirements.");
614 if constexpr (isDoneResultType)
616 const Adapter &adapter =
static_cast<
const Adapter &>(taskInterface);
618 return std::invoke(handler, *adapter.task(), result);
620 return std::invoke(handler, *adapter.task());
622 return std::invoke(handler, result);
624 return std::invoke(handler);
626 return toDoneResult(std::invoke(handler, *adapter.task(), result));
628 return toDoneResult(std::invoke(handler, *adapter.task()));
630 return toDoneResult(std::invoke(handler, result));
632 return toDoneResult(std::invoke(handler));
634 std::invoke(handler, *adapter.task(), result);
635 else if constexpr (isVT)
636 std::invoke(handler, *adapter.task());
637 else if constexpr (isVD)
638 std::invoke(handler, result);
639 else if constexpr (isV)
640 std::invoke(handler);
646template <
typename Task>
647class SimpleTaskAdapter
final :
public TaskAdapter<Task>
658template <
typename Task>
689 template <
typename StorageStruct,
typename Handler>
691 static_assert(std::is_invocable_v<std::decay_t<Handler>, StorageStruct &>,
692 "Storage setup handler needs to take (Storage &) as an argument. "
693 "The passed handler doesn't fulfill this requirement.");
694 setupStorageHandler(storage,
695 wrapHandler<StorageStruct>(std::forward<Handler>(handler)), {});
697 template <
typename StorageStruct,
typename Handler>
699 static_assert(std::is_invocable_v<std::decay_t<Handler>,
const StorageStruct &>,
700 "Storage done handler needs to take (const Storage &) as an argument. "
701 "The passed handler doesn't fulfill this requirement.");
702 setupStorageHandler(storage, {},
703 wrapHandler<
const StorageStruct>(std::forward<Handler>(handler)));
713 void setupStorageHandler(
const StorageBase &storage,
714 const StorageBase::StorageHandler &setupHandler,
715 const StorageBase::StorageHandler &doneHandler);
716 template <
typename StorageStruct,
typename Handler>
717 StorageBase::StorageHandler wrapHandler(Handler &&handler) {
718 return [handler](
void *voidStruct) {
719 auto *storageStruct =
static_cast<StorageStruct *>(voidStruct);
720 std::invoke(handler, *storageStruct);
744 std::optional<
int> m_timerId;
void setupDownload(NetworkQuery *query, const QString &progressText)
QDir m_preferredLocalDownloadDir
void setLocalDownloadDir(const QDir &dir)
void setProgress(int progressValue, int progressMaximum, const QString &progressText)
QUrl m_offlineAssetsFilePath
AssetDownloaderPrivate(AssetDownloader *q)
void clearProgress(const QString &progressText)
QStringList m_networkErrors
TaskTreeRunner m_taskTreeRunner
QString m_lastProgressText
void updateProgress(int progressValue, int progressMaximum)
std::unique_ptr< QNetworkAccessManager > m_manager
std::unique_ptr< QTemporaryDir > m_temporaryDir
void progressChanged(int progressValue, int progressMaximum, const QString &progressText)
void localDownloadDirChanged(const QUrl &url)
void setOfflineAssetsFilePath(const QUrl &offlineAssetsFilePath)
QUrl preferredLocalDownloadDir() const
QUrl downloadBase() const
virtual QUrl resolvedUrl(const QUrl &url) const
void setJsonFileName(const QString &jsonFileName)
void jsonFileNameChanged(const QString &)
void downloadBaseChanged(const QUrl &)
void setPreferredLocalDownloadDir(const QUrl &localDir)
void offlineAssetsFilePathChanged(const QUrl &)
QString jsonFileName() const
QUrl offlineAssetsFilePath() const
void setDownloadBase(const QUrl &downloadBase)
QString zipFileName() const
void setZipFileName(const QString &zipFileName)
void finished(bool success)
QUrl localDownloadDir() const
void zipFileNameChanged(const QString &)
void preferredLocalDownloadDirChanged(const QUrl &url)
void start() final
This method is called by the running TaskTree for starting the Task instance.
~ConcurrentCallTaskAdapter()
void setThreadPool(QThreadPool *pool)
QFuture< ResultType > future() const
QList< ResultType > results() const
ResultType result() const
void setConcurrentCallData(Function &&function, Args &&...args)
typename Adapter::TaskType Task
typename Adapter::DeleterType Deleter
std::function< SetupResult(Task &)> TaskSetupHandler
CustomTask(SetupHandler &&setup=TaskSetupHandler(), DoneHandler &&done=TaskDoneHandler(), CallDoneIf callDoneIf=CallDoneIf::SuccessOrError)
\typealias Tasking::CustomTask::Task
Do(std::initializer_list< GroupItem > children)
Do(const GroupItems &children)
\inheaderfile solutions/tasking/tasktree.h \inmodule TaskingSolution
Group withCancel(SenderSignalPairGetter &&getter, std::initializer_list< GroupItem > postCancelRecipe={}) const
ExecutableItem(const TaskHandler &handler)
Group withLog(const QString &logName) const
Attaches a custom debug printout to a copy of this ExecutableItem, issued on task startup and after t...
Group withTimeout(std::chrono::milliseconds timeout, const std::function< void()> &handler={}) const
Attaches TimeoutTask to a copy of this ExecutableItem, elapsing after timeout in milliseconds,...
Group withAccept(SenderSignalPairGetter &&getter) const
Forever(const GroupItems &children)
Forever(std::initializer_list< GroupItem > children)
\typealias Tasking::GroupItems
GroupItem(const Loop &loop)
GroupItem(std::initializer_list< GroupItem > children)
GroupItem(const TaskHandler &handler)
GroupItem(const GroupData &data)
GroupItem(const GroupItems &children)
Constructs a GroupItem element with a given list of items.
GroupItem(const Storage< StorageStruct > &storage)
Constructs a GroupItem element holding the storage object.
static GroupItem groupHandler(const GroupHandler &handler)
void addChildren(const GroupItems &children)
static constexpr bool isInvocable()
\inheaderfile solutions/tasking/tasktree.h \inmodule TaskingSolution
static GroupItem onGroupSetup(Handler &&handler)
static GroupItem onGroupDone(Handler &&handler, CallDoneIf callDoneIf=CallDoneIf::SuccessOrError)
Group(const GroupItems &children)
Constructs a group with a given list of children.
Group(std::initializer_list< GroupItem > children)
Constructs a group from std::initializer_list given by children.
const T & operator*() const
LoopList(const QList< T > &list)
const T * operator->() const
LoopUntil(const Condition &condition)
Loop(int count, const ValueGetter &valueGetter={})
Loop(const Condition &condition)
const void * valuePtr() const
void start() final
This method is called by the running TaskTree for starting the Task instance.
friend bool operator==(const StorageBase &first, const StorageBase &second)
friend size_t qHash(const StorageBase &storage, uint seed=0)
friend bool operator!=(const StorageBase &first, const StorageBase &second)
StorageStruct * operator->() const noexcept
Returns a pointer to the active StorageStruct object, created by the running task tree.
StorageStruct & operator*() const noexcept
Returns a reference to the active StorageStruct object, created by the running task tree.
Storage()
Creates a storage for the given StorageStruct type.
StorageStruct * activeStorage() const
Returns a pointer to the active StorageStruct object, created by the running task tree.
Storage(const Args &...args)
Sync(Handler &&handler)
Constructs an element that executes a passed handler synchronously.
\inheaderfile solutions/tasking/tasktree.h \inmodule TaskingSolution
const Task * task() const
\inheaderfile solutions/tasking/tasktree.h \inmodule TaskingSolution
virtual void start()=0
This method is called by the running TaskTree for starting the Task instance.
void start() final
This method is called by the running TaskTree for starting the Task instance.
void done(DoneWith result)
This signal is emitted when the task tree finished, passing the final result of the execution.
DoneWith runBlocking(const QFuture< void > &future)
int progressMaximum() const
Returns the maximum progressValue().
TaskTree()
Constructs an empty task tree.
int taskCount() const
Returns the number of asynchronous tasks contained in the stored recipe.
void cancel()
Cancels the execution of the running task tree.
void start()
Starts the task tree.
void onStorageSetup(const Storage< StorageStruct > &storage, Handler &&handler)
Installs a storage setup handler for the storage to pass the initial data dynamically to the running ...
~TaskTree()
Destroys the task tree.
bool isRunning() const
Returns true if the task tree is currently running; otherwise returns false.
void onStorageDone(const Storage< StorageStruct > &storage, Handler &&handler)
Installs a storage done handler for the storage to retrieve the final data dynamically from the runni...
void setRecipe(const Group &recipe)
Sets a given recipe for the task tree.
void asyncCountChanged(int count)
This signal is emitted when the running task tree is about to return control to the caller's event lo...
TaskTree(const Group &recipe)
This is an overloaded member function, provided for convenience. It differs from the above function o...
int progressValue() const
Returns the current progress value, which is between the 0 and progressMaximum().
DoneWith runBlocking()
Executes a local event loop with QEventLoop::ExcludeUserInputEvents and starts the task tree.
static DoneWith runBlocking(const Group &recipe)
Constructs a temporary task tree using the passed recipe and runs it blocking.
void progressValueChanged(int value)
This signal is emitted when the running task tree finished, canceled, or skipped some tasks.
int asyncCount() const
Returns the current real count of asynchronous chains of invocations.
static DoneWith runBlocking(const Group &recipe, const QFuture< void > &future)
void start() final
This method is called by the running TaskTree for starting the Task instance.
TASKING_EXPORT friend Group operator>>(const When &whenItem, const Do &doItem)
static void unzip(QPromise< void > &promise, const QByteArray &content, const QDir &directory, const QString &fileName)
static bool sameFileContent(const QFileInfo &first, const QFileInfo &second)
static void writeAsset(QPromise< void > &promise, const QByteArray &content, const QString &filePath)
static void precheckLocalFile(const QUrl &url)
static QDir baseLocalDir(const QDir &preferredLocalDir)
static QString pathFromUrl(const QUrl &url)
static bool allAssetsPresent(const QList< QUrl > &assetFiles, const QDir &expectedDir)
static void copyAndCheck(QPromise< void > &promise, const QString &sourcePath, const QString &destPath)
static bool canBeALocalBaseDir(const QDir &dir)
static QList< QUrl > filterDownloadableAssets(const QList< QUrl > &assetFiles, const QDir &expectedDir)
static void readAssetsFileContent(QPromise< DownloadableAssets > &promise, const QByteArray &content)
static bool createDirectory(const QDir &dir)
static bool isWritableDir(const QDir &dir)
\inmodule TaskingSolution
const ExecutableItem successItem
const GroupItem sequential
GroupItem parallelLimit(int limit)
Constructs a group's element describing the \l{Execution Mode}{execution mode}.
Group operator||(const ExecutableItem &first, const ExecutableItem &second)
CustomTask< ConcurrentCallTaskAdapter< T > > ConcurrentCallTask
const GroupItem continueOnSuccess
const GroupItem stopOnError
const GroupItem finishAllAndError
const ExecutableItem errorItem
CustomTask< SimpleTaskAdapter< Task > > SimpleCustomTask
CustomTask< TimeoutTaskAdapter > TimeoutTask
const GroupItem finishAllAndSuccess
Group operator||(const ExecutableItem &item, DoneResult result)
DoneResult toDoneResult(bool success)
const GroupItem parallelIdealThreadCountLimit
ExecutableItem timeoutTask(const std::chrono::milliseconds &timeout, DoneResult result)
CustomTask< TaskTreeTaskAdapter > TaskTreeTask
const GroupItem stopOnSuccessOrError
Group operator&&(const ExecutableItem &item, DoneResult result)
Group operator&&(const ExecutableItem &first, const ExecutableItem &second)
static GroupItem onGroupSetup(Handler &&handler)
\typealias Tasking::GroupItem::GroupSetupHandler
Group operator>>(const For &forItem, const Do &doItem)
Group operator!(const ExecutableItem &item)
const GroupItem continueOnError
GroupItem workflowPolicy(WorkflowPolicy policy)
Constructs a group's \l {Workflow Policy} {workflow policy} element for a given policy.
const GroupItem stopOnSuccess
static GroupItem onGroupDone(Handler &&handler, CallDoneIf callDoneIf=CallDoneIf::SuccessOrError)
Constructs a group's element holding the group done handler.
GroupHandler m_groupHandler
std::optional< WorkflowPolicy > m_workflowPolicy
std::optional< int > m_parallelLimit
std::optional< Loop > m_loop
GroupDoneHandler m_doneHandler
GroupSetupHandler m_setupHandler
InterfaceSetupHandler m_setupHandler
InterfaceCreateHandler m_createHandler
InterfaceDoneHandler m_doneHandler