Loading...
Searching...
No Matches
graph.hpp
1#pragma once
2
3#include "../utility/macros.hpp"
4#include "../utility/traits.hpp"
5#include "../utility/iterator.hpp"
6
7#ifdef TF_ENABLE_TASK_POOL
8#include "../utility/object_pool.hpp"
9#endif
10
11#include "../utility/os.hpp"
12#include "../utility/math.hpp"
13#include "../utility/small_vector.hpp"
14#include "../utility/serializer.hpp"
15#include "../utility/lazy_string.hpp"
16#include "error.hpp"
17#include "declarations.hpp"
18#include "semaphore.hpp"
19#include "environment.hpp"
20#include "topology.hpp"
21#include "wsq.hpp"
22
23
28
29namespace tf {
30
31// ----------------------------------------------------------------------------
32// Class: Graph
33// ----------------------------------------------------------------------------
34
47class Graph {
48
49 friend class Node;
50 friend class FlowBuilder;
51 friend class Subflow;
52 friend class Taskflow;
53 friend class Executor;
54
55 public:
56
60 Graph() = default;
61
65 ~Graph();
66
70 Graph(const Graph&) = delete;
71
75 Graph(Graph&&);
76
80 Graph& operator = (const Graph&) = delete;
81
86
90 void clear();
91
95 size_t size() const;
96
100 bool empty() const;
101
105 auto begin();
106
110 auto end();
111
115 auto begin() const;
116
120 auto end() const;
121
122 private:
123
124 std::vector<Node*> _nodes;
125
126 void _erase(Node*);
127
131 template <typename ...ArgsT>
132 Node* _emplace_back(ArgsT&&...);
133};
134
135// ----------------------------------------------------------------------------
136// TaskParams
137// ----------------------------------------------------------------------------
138
147
148 public:
149
153 std::string name;
154
158 void* data {nullptr};
159};
160
167
176template <typename P>
178 std::same_as<std::decay_t<P>, TaskParams> ||
179 std::same_as<std::decay_t<P>, DefaultTaskParams> ||
180 std::constructible_from<std::string, P>;
181
189template <typename P>
191
192// ----------------------------------------------------------------------------
193// NodeBase
194// ----------------------------------------------------------------------------
195
199class NodeBase {
200
201 friend class Node;
202 friend class Graph;
203 friend class Task;
204 friend class AsyncTask;
205 friend class TaskView;
206 friend class Taskflow;
207 friend class Executor;
208 friend class FlowBuilder;
209 friend class Subflow;
210 friend class Runtime;
211 friend class NonpreemptiveRuntime;
212 friend class ExplicitAnchorGuard;
213 friend class TaskGroup;
214 friend class Algorithm;
215
216 protected:
217
218 nstate_t _nstate {NSTATE::NONE};
219 std::atomic<estate_t> _estate {ESTATE::NONE};
220
221 NodeBase* _parent {nullptr};
222 std::atomic<size_t> _join_counter {0};
223
224 std::exception_ptr _exception_ptr {nullptr};
225
226 NodeBase() = default;
227
228 NodeBase(nstate_t nstate, estate_t estate, NodeBase* parent, size_t join_counter) :
229 _nstate {nstate},
230 _estate {estate},
231 _parent {parent},
232 _join_counter {join_counter} {
233 }
234
235 void _rethrow_exception() {
236 if(_exception_ptr) {
237 auto e = _exception_ptr;
238 _exception_ptr = nullptr;
239 _estate.fetch_and(~(ESTATE::EXCEPTION | ESTATE::CAUGHT), std::memory_order_relaxed);
240 std::rethrow_exception(e);
241 }
242 }
243};
244
245// ----------------------------------------------------------------------------
246// Topology
247// ----------------------------------------------------------------------------
248
252class Topology : public NodeBase {
253
254 friend class Executor;
255 friend class Subflow;
256 friend class Runtime;
257 friend class NonpreemptiveRuntime;
258 friend class Node;
259
260 template <typename T>
261 friend class Future;
262
263 public:
264
265 template <typename Predicate, typename OnFinish>
266 Topology(Taskflow&, Predicate&&, OnFinish&&);
267
268 bool cancelled() const;
269
270 private:
271
272 Taskflow& _taskflow;
273
274 std::promise<void> _promise;
275
276 std::function<bool()> _predicate;
277 std::function<void()> _on_finish;
278
279 void _carry_out_promise();
280};
281
282// Constructor
283template <typename Predicate, typename OnFinish>
284Topology::Topology(Taskflow& tf, Predicate&& predicate, OnFinish&& on_finish):
285 NodeBase(NSTATE::NONE, ESTATE::EXPLICITLY_ANCHORED, nullptr, 0),
286 _taskflow(tf),
287 _predicate(std::forward<Predicate>(predicate)),
288 _on_finish(std::forward<OnFinish> (on_finish)) {
289}
290
291// Procedure
292inline void Topology::_carry_out_promise() {
293 if(_exception_ptr) {
294 auto e = _exception_ptr;
295 _exception_ptr = nullptr;
296 _promise.set_exception(e);
297 }
298 else {
299 _promise.set_value();
300 }
301}
302
303// Function: cancelled
304inline bool Topology::cancelled() const {
305 return _estate.load(std::memory_order_relaxed) & (ESTATE::CANCELLED | ESTATE::EXCEPTION);
306}
307
308
309// ----------------------------------------------------------------------------
310// Node
311// ----------------------------------------------------------------------------
312
316class Node : public NodeBase {
317
318 friend class Graph;
319 friend class Task;
320 friend class AsyncTask;
321 friend class TaskView;
322 friend class Taskflow;
323 friend class Executor;
324 friend class FlowBuilder;
325 friend class Subflow;
326 friend class Runtime;
327 friend class NonpreemptiveRuntime;
328 friend class ExplicitAnchorGuard;
329 friend class TaskGroup;
330 friend class Algorithm;
331
332#ifdef TF_ENABLE_TASK_POOL
333 TF_ENABLE_POOLABLE_ON_THIS;
334#endif
335
336 using Placeholder = std::monostate;
337
338 // static work handle
339 struct Static {
340
341 template <typename C>
342 Static(C&&);
343
344 std::function<void()> work;
345 };
346
347 // runtime work handle
348 struct Runtime {
349
350 template <typename C>
351 Runtime(C&&);
352
353 std::function<void(tf::Runtime&)> work;
354 };
355
356 struct NonpreemptiveRuntime {
357
358 template <typename C>
359 NonpreemptiveRuntime(C&&);
360
361 std::function<void(tf::NonpreemptiveRuntime&)> work;
362 };
363
364 // subflow work handle
365 struct Subflow {
366
367 template <typename C>
368 Subflow(C&&);
369
370 std::function<void(tf::Subflow&)> work;
371 Graph subgraph;
372 };
373
374 // condition work handle
375 struct Condition {
376
377 template <typename C>
378 Condition(C&&);
379
380 std::function<int()> work;
381 };
382
383 // multi-condition work handle
384 struct MultiCondition {
385
386 template <typename C>
387 MultiCondition(C&&);
388
389 std::function<SmallVector<int>()> work;
390 };
391
392 // module work handle
393 struct Module {
394
395 Module(Graph&);
396
397 Graph& graph;
398 };
399
400 // adopted module work handle
401 struct AdoptedModule {
402
403 AdoptedModule(Graph&&);
404
405 Graph graph;
406 };
407
408 // Async work
409 struct Async {
410
411 template <typename T>
412 Async(T&&);
413
414 std::variant<
415 std::function<void()>,
416 std::function<void(tf::Runtime&)>, // silent async
417 std::function<void(tf::Runtime&, bool)> // async
418 > work;
419 };
420
421 // silent dependent async
422 struct DependentAsync {
423
424 template <typename C>
425 DependentAsync(C&&);
426
427 std::variant<
428 std::function<void()>,
429 std::function<void(tf::Runtime&)>, // silent async
430 std::function<void(tf::Runtime&, bool)> // async
431 > work;
432
433 std::atomic<size_t> use_count {1};
434 };
435
436 using handle_t = std::variant<
437 Placeholder, // placeholder
438 Static, // static tasking
439 Runtime, // runtime tasking
440 NonpreemptiveRuntime, // runtime (non-preemptive) tasking
441 Subflow, // subflow tasking
442 Condition, // conditional tasking
443 MultiCondition, // multi-conditional tasking
444 Module, // composable tasking
445 AdoptedModule, // composable tasking with move semantics
446 Async, // async tasking
447 DependentAsync // dependent async tasking
448 >;
449
450 struct Semaphores {
451 SmallVector<Semaphore*> to_acquire;
452 SmallVector<Semaphore*> to_release;
453 };
454
455 public:
456
457 // variant index
458 constexpr static auto PLACEHOLDER = get_index_v<Placeholder, handle_t>;
459 constexpr static auto STATIC = get_index_v<Static, handle_t>;
460 constexpr static auto RUNTIME = get_index_v<Runtime, handle_t>;
461 constexpr static auto NONPREEMPTIVE_RUNTIME = get_index_v<NonpreemptiveRuntime, handle_t>;
462 constexpr static auto SUBFLOW = get_index_v<Subflow, handle_t>;
463 constexpr static auto CONDITION = get_index_v<Condition, handle_t>;
464 constexpr static auto MULTI_CONDITION = get_index_v<MultiCondition, handle_t>;
465 constexpr static auto MODULE = get_index_v<Module, handle_t>;
466 constexpr static auto ADOPTED_MODULE = get_index_v<AdoptedModule, handle_t>;
467 constexpr static auto ASYNC = get_index_v<Async, handle_t>;
468 constexpr static auto DEPENDENT_ASYNC = get_index_v<DependentAsync, handle_t>;
469
470 Node() = default;
471
472 template <typename... Args>
473 Node(nstate_t, estate_t, const TaskParams&, Topology*, NodeBase*, size_t, Args&&...);
474
475 template <typename... Args>
476 Node(nstate_t, estate_t, const DefaultTaskParams&, Topology*, NodeBase*, size_t, Args&&...);
477
478 size_t num_successors() const;
479 size_t num_predecessors() const;
480 size_t num_strong_dependencies() const;
481 size_t num_weak_dependencies() const;
482
483 const std::string& name() const;
484
485 private:
486
487 std::string _name;
488
489 void* _data {nullptr};
490
491 Topology* _topology {nullptr};
492
493 size_t _num_successors {0};
494 SmallVector<Node*, 4> _edges;
495
496 handle_t _handle;
497
498 std::unique_ptr<Semaphores> _semaphores;
499
500 bool _is_parent_cancelled() const;
501 bool _is_conditioner() const;
502 bool _acquire_all(SmallVector<Node*>&);
503 void _release_all(SmallVector<Node*>&);
504 void _precede(Node*);
505 void _set_up_join_counter();
506
507 void _remove_successors(Node*);
508 void _remove_predecessors(Node*);
509};
510
511
512// ----------------------------------------------------------------------------
513// Definition for Node::Static
514// ----------------------------------------------------------------------------
515
516// Constructor
517template <typename C>
518Node::Static::Static(C&& c) : work {std::forward<C>(c)} {
519}
520
521// ----------------------------------------------------------------------------
522// Definition for Node::Runtime
523// ----------------------------------------------------------------------------
524
525// Constructor
526template <typename C>
527Node::Runtime::Runtime(C&& c) : work {std::forward<C>(c)} {
528}
529
530// Constructor
531template <typename C>
532Node::NonpreemptiveRuntime::NonpreemptiveRuntime(C&& c) : work {std::forward<C>(c)} {
533}
534
535// ----------------------------------------------------------------------------
536// Definition for Node::Subflow
537// ----------------------------------------------------------------------------
538
539// Constructor
540template <typename C>
541Node::Subflow::Subflow(C&& c) : work {std::forward<C>(c)} {
542}
543
544// ----------------------------------------------------------------------------
545// Definition for Node::Condition
546// ----------------------------------------------------------------------------
547
548// Constructor
549template <typename C>
550Node::Condition::Condition(C&& c) : work {std::forward<C>(c)} {
551}
552
553// ----------------------------------------------------------------------------
554// Definition for Node::MultiCondition
555// ----------------------------------------------------------------------------
556
557// Constructor
558template <typename C>
559Node::MultiCondition::MultiCondition(C&& c) : work {std::forward<C>(c)} {
560}
561
562// ----------------------------------------------------------------------------
563// Definition for Node::Module
564// ----------------------------------------------------------------------------
565
566// Constructor
567inline Node::Module::Module(Graph& g) : graph(g){
568}
569
570// Constructor
571inline Node::AdoptedModule::AdoptedModule(Graph&& g) : graph(std::move(g)){
572}
573
574// ----------------------------------------------------------------------------
575// Definition for Node::Async
576// ----------------------------------------------------------------------------
577
578// Constructor
579template <typename C>
580Node::Async::Async(C&& c) : work {std::forward<C>(c)} {
581}
582
583// ----------------------------------------------------------------------------
584// Definition for Node::DependentAsync
585// ----------------------------------------------------------------------------
586
587// Constructor
588template <typename C>
589Node::DependentAsync::DependentAsync(C&& c) : work {std::forward<C>(c)} {
590}
591
592// ----------------------------------------------------------------------------
593// Definition for Node
594// ----------------------------------------------------------------------------
595
596// Constructor
597template <typename... Args>
598Node::Node(
599 nstate_t nstate,
600 estate_t estate,
601 const TaskParams& params,
602 Topology* topology,
603 NodeBase* parent,
604 size_t join_counter,
605 Args&&... args
606) :
607 NodeBase(nstate, estate, parent, join_counter),
608 _name {params.name},
609 _data {params.data},
610 _topology {topology},
611 _handle {std::forward<Args>(args)...} {
612}
613
614// Constructor
615template <typename... Args>
616Node::Node(
617 nstate_t nstate,
618 estate_t estate,
619 const DefaultTaskParams&,
620 Topology* topology,
621 NodeBase* parent,
622 size_t join_counter,
623 Args&&... args
624) :
625 NodeBase(nstate, estate, parent, join_counter),
626 _topology {topology},
627 _handle {std::forward<Args>(args)...} {
628}
629
631//template <typename T, typename... Args>
632//void Node::reset(
633// nstate_t nstate,
634// estate_t estate,
635// const TaskParams& params,
636// Topology* topology,
637// NodeBase* parent,
638// size_t join_counter,
639// std::in_place_type_t<T>,
640// Args&&... args
641//) {
642// _nstate = nstate;
643// _estate = estate;
644// _parent = parent;
645// _join_counter.store(join_counter, std::memory_order_relaxed);
646// _exception_ptr = nullptr;
647// _name = params.name;
648// _data = params.data;
649// _topology = topology;
650// _handle.emplace<T>(std::forward<Args>(args)...);
651// _num_successors = 0;
652// _edges.clear();
653// _semaphores.reset();
654//}
655//
657//template <typename T, typename... Args>
658//void Node::reset(
659// nstate_t nstate,
660// estate_t estate,
661// const DefaultTaskParams&,
662// Topology* topology,
663// NodeBase* parent,
664// size_t join_counter,
665// std::in_place_type_t<T>,
666// Args&&... args
667//) {
668// _nstate = nstate;
669// _estate = estate;
670// _parent = parent;
671// _join_counter.store(join_counter, std::memory_order_relaxed);
672// _exception_ptr = nullptr;
673// _name.clear();
674// _data = nullptr;
675// _topology = topology;
676// _handle.emplace<T>(std::forward<Args>(args)...);
677// _num_successors = 0;
678// _edges.clear();
679// _semaphores.reset();
680//}
681
682// Procedure: _precede
683/*
684u edges layout: s1, s2, s3, p1, p2 (num_successors = 3)
685v edges layout: s1, p1, p2
686
687add a new successor: u->v
688u successor layout:
689 s1, s2, s3, p1, p2, v (push_back v)
690 s1, s2, s3, v, p2, p1 (swap edges[num_successors] with edges[n-1])
691v predecessor layout:
692 s1, p1, p2, u (push_back u)
693*/
694inline void Node::_precede(Node* v) {
695 _edges.push_back(v);
696 std::swap(_edges[_num_successors++], _edges[_edges.size() - 1]);
697 v->_edges.push_back(this);
698}
699
700// Function: _remove_successors
701inline void Node::_remove_successors(Node* node) {
702 auto sit = std::remove(_edges.begin(), _edges.begin() + _num_successors, node);
703 size_t new_num_successors = std::distance(_edges.begin(), sit);
704 std::move(_edges.begin() + _num_successors, _edges.end(), sit);
705 _edges.resize(_edges.size() - (_num_successors - new_num_successors));
706 _num_successors = new_num_successors;
707}
708
709// Function: _remove_predecessors
710inline void Node::_remove_predecessors(Node* node) {
711 _edges.erase(
712 std::remove(_edges.begin() + _num_successors, _edges.end(), node), _edges.end()
713 );
714}
715
716// Function: num_successors
717inline size_t Node::num_successors() const {
718 return _num_successors;
719}
720
721// Function: predecessors
722inline size_t Node::num_predecessors() const {
723 return _edges.size() - _num_successors;
724}
725
726// Function: num_weak_dependencies
727inline size_t Node::num_weak_dependencies() const {
728 size_t n = 0;
729 for(size_t i=_num_successors; i<_edges.size(); i++) {
730 n += _edges[i]->_is_conditioner();
731 }
732 return n;
733}
734
735// Function: num_strong_dependencies
736inline size_t Node::num_strong_dependencies() const {
737 size_t n = 0;
738 for(size_t i=_num_successors; i<_edges.size(); i++) {
739 n += !_edges[i]->_is_conditioner();
740 }
741 return n;
742}
743
744// Function: name
745inline const std::string& Node::name() const {
746 return _name;
747}
748
749// Function: _is_conditioner
750inline bool Node::_is_conditioner() const {
751 return _handle.index() == Node::CONDITION ||
752 _handle.index() == Node::MULTI_CONDITION;
753}
754
755// Function: _is_parent_cancelled
756inline bool Node::_is_parent_cancelled() const {
757 return (_topology && (_topology->_estate.load(std::memory_order_relaxed) & (ESTATE::CANCELLED | ESTATE::EXCEPTION)))
758 ||
759 (_parent && (_parent->_estate.load(std::memory_order_relaxed) & (ESTATE::CANCELLED | ESTATE::EXCEPTION)));
760}
761
762// Procedure: _set_up_join_counter
763inline void Node::_set_up_join_counter() {
764 //assert(_nstate == NSTATE::NONE);
765 for(size_t i=_num_successors; i<_edges.size(); i++) {
766 _nstate += !_edges[i]->_is_conditioner();
767 }
768 _join_counter.store(_nstate & NSTATE::STRONG_DEPENDENCIES_MASK, std::memory_order_relaxed);
769}
770
771
772// Function: _acquire_all
773inline bool Node::_acquire_all(SmallVector<Node*>& nodes) {
774 // assert(_semaphores != nullptr);
775 auto& to_acquire = _semaphores->to_acquire;
776 for(size_t i = 0; i < to_acquire.size(); ++i) {
777 if(!to_acquire[i]->_try_acquire_or_wait(this)) {
778 for(size_t j = 1; j <= i; ++j) {
779 to_acquire[i-j]->_release(nodes);
780 }
781 return false;
782 }
783 }
784 return true;
785}
786
787// Function: _release_all
788inline void Node::_release_all(SmallVector<Node*>& nodes) {
789 // assert(_semaphores != nullptr);
790 auto& to_release = _semaphores->to_release;
791 for(const auto& sem : to_release) {
792 sem->_release(nodes);
793 }
794}
795
796
797
798// ----------------------------------------------------------------------------
799// ExplicitAnchorGuard
800// ----------------------------------------------------------------------------
801
805class ExplicitAnchorGuard {
806
807 public:
808
809 // Explicit anchor must sit in estate as it may be accessed by multiple threads
810 // (e.g., corun's parent with tear_down_async's parent).
811 ExplicitAnchorGuard(NodeBase* node_base) : _node_base{node_base} {
812 _node_base->_estate.fetch_or(ESTATE::EXPLICITLY_ANCHORED, std::memory_order_relaxed);
813 }
814
815 ~ExplicitAnchorGuard() {
816 _node_base->_estate.fetch_and(~ESTATE::EXPLICITLY_ANCHORED, std::memory_order_relaxed);
817 }
818
819 private:
820
821 NodeBase* _node_base;
822};
823
824// ----------------------------------------------------------------------------
825// Node Object Pool
826// ----------------------------------------------------------------------------
827
831#ifdef TF_ENABLE_TASK_POOL
832inline ObjectPool<Node> _node_pool;
833#endif
834
838template <typename... ArgsT>
839TF_FORCE_INLINE Node* animate(ArgsT&&... args) {
840#ifdef TF_ENABLE_TASK_POOL
841 return _node_pool.animate(std::forward<ArgsT>(args)...);
842#else
843 return new Node(std::forward<ArgsT>(args)...);
844#endif
845}
846
850TF_FORCE_INLINE void recycle(Node* ptr) {
851#ifdef TF_ENABLE_TASK_POOL
852 _node_pool.recycle(ptr);
853#else
854 delete ptr;
855#endif
856}
857
858
859// ----------------------------------------------------------------------------
860// Graph definition
861// ----------------------------------------------------------------------------
862
863// Destructor
865 clear();
866}
867
868// Move constructor
869inline Graph::Graph(Graph&& other) :
870 _nodes {std::move(other._nodes)} {
871}
872
873// Move assignment
875 clear();
876 _nodes = std::move(other._nodes);
877 return *this;
878}
879
880// Procedure: clear
881inline void Graph::clear() {
882 for(auto node : _nodes) {
883 recycle(node);
884 }
885 _nodes.clear();
886}
887
888// Function: size
889inline size_t Graph::size() const {
890 return _nodes.size();
891}
892
893// Function: empty
894inline bool Graph::empty() const {
895 return _nodes.empty();
896}
897
898// Function: begin
899inline auto Graph::begin() {
900 return _nodes.begin();
901}
902
903// Function: end
904inline auto Graph::end() {
905 return _nodes.end();
906}
907
908// Function: begin
909inline auto Graph::begin() const {
910 return _nodes.begin();
911}
912
913// Function: end
914inline auto Graph::end() const {
915 return _nodes.end();
916}
917
918// Function: erase
919inline void Graph::_erase(Node* node) {
920 //erase(
921 // std::remove_if(begin(), end(), [&](auto& p){ return p.get() == node; }),
922 // end()
923 //);
924 _nodes.erase(
925 std::remove_if(_nodes.begin(), _nodes.end(), [&](auto& p){
926 if(p == node) {
927 recycle(p);
928 return true;
929 }
930 return false;
931 }),
932 _nodes.end()
933 );
934}
935
939template <typename ...ArgsT>
940Node* Graph::_emplace_back(ArgsT&&... args) {
941 _nodes.push_back(animate(std::forward<ArgsT>(args)...));
942 return _nodes.back();
943}
944
945// ----------------------------------------------------------------------------
946// Graph checker
947// ----------------------------------------------------------------------------
948
956template <typename T>
957concept HasGraph = std::derived_from<T, Graph> ||
958 requires(T& t) {
959 { t.graph() } -> std::convertible_to<Graph&>;
960 };
961
974template <HasGraph T>
976 if constexpr (requires { t.graph(); }) {
977 return t.graph();
978 } else {
979 return static_cast<Graph&>(t);
980 }
981}
982
983} // end of namespace tf. ----------------------------------------------------
class to hold a dependent asynchronous task with shared ownership
Definition async_task.hpp:45
class to create an empty task parameter for compile-time optimization
Definition graph.hpp:166
class to create an executor
Definition executor.hpp:62
class to build a task dependency graph
Definition flow_builder.hpp:22
class to create a graph object
Definition graph.hpp:47
Graph & operator=(const Graph &)=delete
disabled copy assignment operator
Graph()=default
constructs the graph object
bool empty() const
queries the emptiness of the graph
Definition graph.hpp:894
~Graph()
destroys the graph object
Definition graph.hpp:864
auto end()
returns an iterator past the last element of this graph
Definition graph.hpp:904
size_t size() const
returns the number of nodes in the graph
Definition graph.hpp:889
void clear()
clears the graph
Definition graph.hpp:881
auto begin()
returns an iterator to the first node of this graph
Definition graph.hpp:899
Graph(const Graph &)=delete
disabled copy constructor
class to create a runtime task
Definition runtime.hpp:47
class to construct a subflow graph from the execution of a dynamic task
Definition flow_builder.hpp:1516
class to create a task group from a task
Definition task_group.hpp:61
class to create a task parameter object
Definition graph.hpp:146
std::string name
name of the task
Definition graph.hpp:153
void * data
C-styled pointer to user data.
Definition graph.hpp:158
class to access task information from the observer interface
Definition task.hpp:1235
class to create a task handle over a taskflow node
Definition task.hpp:263
class to create a taskflow object
Definition taskflow.hpp:64
determines if a type owns or provides a graph
Definition graph.hpp:957
determines if a type is a task parameter type
Definition graph.hpp:177
taskflow namespace
Definition small_vector.hpp:20
@ MODULE
module task type
Definition task.hpp:33
@ SUBFLOW
dynamic (subflow) task type
Definition task.hpp:29
@ CONDITION
condition task type
Definition task.hpp:31
@ ASYNC
asynchronous task type
Definition task.hpp:35
@ PLACEHOLDER
placeholder task type
Definition task.hpp:23
@ RUNTIME
runtime task type
Definition task.hpp:27
@ STATIC
static task type
Definition task.hpp:25
Graph & retrieve_graph(T &t)
retrieves a reference to the underlying tf::Graph from an object
Definition graph.hpp:975
constexpr bool is_task_params_v
determines if a type is a task parameter type (variable template)
Definition graph.hpp:190