sandbox
Loading...
Searching...
No Matches
task_graph.hpp
1#ifndef LIBSBX_CONTAINERS_TASK_GRAPH_HPP_
2#define LIBSBX_CONTAINERS_TASK_GRAPH_HPP_
3
4#include <cstdint>
5
6#include <string>
7#include <variant>
8#include <vector>
9#include <memory>
10#include <functional>
11#include <algorithm>
12
13namespace sbx::containers {
14
15namespace detail {
16
17enum class node_state : std::uint32_t {
18 none = 0x00000000,
19 conditioned = 0x10000000,
20 preempted = 0x20000000,
21 retain_subflow = 0x40000000,
22 joined_subflow = 0x80000000
23}; // enum class node_state
24
25enum class error_state : std::uint32_t {
26 none = 0x00000000,
27 exception = 0x10000000,
28 cancelled = 0x20000000,
29 anchored = 0x40000000
30}; // enum class node_state
31
32template<typename T, typename>
33struct get_index;
34
35template<std::size_t I, typename... Ts>
37
38template<std::size_t I, typename T, typename... Ts>
39struct get_index_impl<I, T, T, Ts...> : std::integral_constant<std::size_t, I>{};
40
41template<std::size_t I, typename T, typename U, typename... Ts>
42struct get_index_impl<I, T, U, Ts...> : get_index_impl<I+1u, T, Ts...>{};
43
44template<typename T, typename... Ts>
45struct get_index<T, std::variant<Ts...>> : get_index_impl<0u, T, Ts...>{};
46
47template<typename T, typename... Ts>
48constexpr auto get_index_v = get_index<T, Ts...>::value;
49
50class graph_node;
51class graph_base;
52class graph_builder;
53class task;
54class sub_graph;
55
56class graph_base : std::vector<std::unique_ptr<graph_node>> {
57
58 friend class graph_node;
59 friend class graph_builder;
60
61 using base = std::vector<std::unique_ptr<graph_node>>;
62
63public:
64
65 graph_base() = default;
66
67 graph_base(const graph_base& other) = delete;
68
69 graph_base(graph_base&& other) = default;
70
71 auto operator=(const graph_base& other) -> graph_base& = delete;
72
73 auto operator=(graph_base&& other) -> graph_base& = default;
74
75private:
76
77 auto _reserve(const std::size_t capacity) -> void;
78
79 auto _erase(graph_node* node) -> void;
80
81 template<typename... Args>
82 auto _emplace_back(Args&&... args) -> graph_node*;
83
84}; // class graph_base
85
87 std::string name;
88 void*data ;
89}; // struct task_parameters
90
92
93template<typename Type>
94constexpr bool is_task_params_v = std::is_same_v<std::decay_t<Type>, task_parameters> || std::is_same_v<std::decay_t<Type>, default_task_parameters> || std::is_constructible_v<std::string, Type>;
95
96template<typename Callable, typename = void>
97struct is_static_task : std::false_type{ };
98
99template<typename Callable>
100struct is_static_task<Callable, std::enable_if_t<std::is_invocable_v<Callable>>> : std::is_same<std::invoke_result_t<Callable>, void> { };
101
102template <typename Callable>
103constexpr bool is_static_task_v = is_static_task<Callable>::value;
104
106
107 friend class graph_builder;
108 friend class task;
109
110 using placeholder_task = std::monostate;
111
112 struct static_task {
113
114 template<typename Callable>
115 static_task(Callable&& callable);
116
117 std::function<void()> work;
118 }; // struct static_task
119
120 struct sub_graph {
121
122 template<typename Callable>
123 sub_graph(Callable&& callable);
124
125 std::function<void(sub_graph&)> work;
126 graph_base graph;
127 }; // struct sub_graph
128
129
130 using task_handle = std::variant<placeholder_task, static_task>;
131
132public:
133
134 inline static constexpr auto placeholder = get_index_v<placeholder_task, task_handle>;
135 inline static constexpr auto static_work = get_index_v<static_task, task_handle>;
136
137 graph_node();
138
139 template<typename... Args>
140 graph_node(node_state node_state, const task_parameters& parameters, graph_node* parent, Args&&...);
141
142 template<typename... Args>
143 graph_node(node_state node_state,const default_task_parameters& parameters, graph_node* parent, Args&&...);
144
145 auto num_successors() const -> std::size_t;
146 auto num_predecessors() const -> std::size_t;
147
148 auto name() const -> const std::string&;
149
150
151private:
152
153 auto _precede(graph_node* node) -> void;
154 auto _remove_successors(graph_node* node) -> void;
155 auto _remove_predecessors(graph_node* node) -> void;
156
157 node_state _state;
158 std::string _name;
159 void* _data;
160 graph_node* _parent;
161 std::size_t _num_successors;
162 std::vector<graph_node*> _edges;
163 task_handle _handle;
164
165}; // class graph_node
166
167class task {
168
169 friend class graph_builder;
170
171public:
172
173 auto name() const -> const std::string&;
174
175 auto num_predecessors() const -> std::size_t;
176 auto num_successors() const -> std::size_t;
177
178 template<typename... Tasks>
179 auto precede(Tasks&&... tasks) -> task&;
180
181 template<typename... Tasks>
182 auto succeed(Tasks&&... tasks) -> task&;
183
184private:
185
187
188 graph_node* _node;
189
190}; // class task
191
193
194public:
195
197
198 template <typename Callable>
199 requires (is_static_task_v<Callable>)
200 auto emplace(Callable&& callable) -> task;
201
202 template<typename... Callables>
203 requires (sizeof...(Callables) > 1u)
204 auto emplace(Callables&&... callables) -> decltype(auto);
205
206protected:
207
208 graph_base& _graph;
209
210private:
211
212}; // class graph_builder
213
214class sub_graph : public graph_builder {
215
216public:
217
218private:
219
220 sub_graph(graph_node* parent, graph_base& graph)
221 : graph_builder{graph},
222 _parent{parent} { }
223
224 graph_node* _parent;
225
226}; // class subgraph
227
228auto graph_base::_reserve(const std::size_t capacity) -> void {
229 base::reserve(capacity);
230}
231
232auto graph_base::_erase(graph_node* node) -> void {
233 base::erase(std::remove_if(base::begin(), base::end(), [&](auto& entry){ return entry.get() == node; }), base::end() );
234}
235
236template<typename... Args>
237auto graph_base::_emplace_back(Args&&... args) -> graph_node* {
238 base::push_back(std::make_unique<graph_node>(std::forward<Args>(args)...));
239 return back().get();
240}
241
242template<typename Callable>
243graph_node::static_task::static_task(Callable&& callable)
244: work{std::forward<Callable>(callable)} { }
245
246template<typename Callable>
247graph_node::sub_graph::sub_graph(Callable&& callable)
248: work{std::forward<Callable>(callable)} { }
249
250graph_node::graph_node()
251: _state{node_state::none},
252 _data{nullptr},
253 _parent{nullptr},
254 _num_successors{0u} { }
255
256template<typename... Args>
257graph_node::graph_node(node_state node_state, const task_parameters& parameters, graph_node* parent, Args&&... args)
258: _state{node_state},
259 _name{parameters.name},
260 _data{parameters.data},
261 _parent{parent},
262 _num_successors{0u},
263 _handle{std::forward<Args>(args)...} { }
264
265template<typename... Args>
266graph_node::graph_node(node_state node_state, const default_task_parameters& parameters, graph_node* parent, Args&&...args)
267: _state{node_state},
268 _data{nullptr},
269 _parent{parent},
270 _num_successors{0u},
271 _handle{std::forward<Args>(args)...} { }
272
273auto graph_node::num_successors() const -> std::size_t {
274 return _num_successors;
275}
276
277auto graph_node::num_predecessors() const -> std::size_t {
278 return _edges.size() - _num_successors;
279}
280
281auto graph_node::name() const -> const std::string& {
282 return _name;
283}
284
285auto graph_node::_precede(graph_node* node) -> void {
286 _edges.push_back(node);
287 std::swap(_edges[_num_successors++], _edges[_edges.size() - 1]);
288 node->_edges.push_back(this);
289}
290
291auto graph_node::_remove_successors(graph_node* node) -> void {
292 auto sit = std::remove(_edges.begin(), _edges.begin() + _num_successors, node);
293 size_t new_num_successors = std::distance(_edges.begin(), sit);
294 std::move(_edges.begin() + _num_successors, _edges.end(), sit);
295 _edges.resize(_edges.size() - (_num_successors - new_num_successors));
296 _num_successors = new_num_successors;
297}
298
299auto graph_node::_remove_predecessors(graph_node* node) -> void {
300 _edges.erase(std::remove(_edges.begin() + _num_successors, _edges.end(), node), _edges.end());
301}
302
303auto task::name() const -> const std::string& {
304 return _node->name();
305}
306
307auto task::num_predecessors() const -> std::size_t {
308 return _node->num_predecessors();
309}
310
311auto task::num_successors() const -> std::size_t {
312 return _node->num_predecessors();
313}
314
315template<typename... Tasks>
316auto task::precede(Tasks&&... tasks) -> task& {
317 (_node->_precede(tasks._node), ...);
318 return *this;
319}
320
321template<typename... Tasks>
322auto task::succeed(Tasks&&... tasks) -> task& {
323 (tasks._node->_precede(_node), ...);
324 return *this;
325}
326
327task::task(graph_node* node)
328: _node{node} { }
329
330graph_builder::graph_builder(graph_base& graph)
331: _graph{graph} { }
332
333template <typename Callable>
334requires (is_static_task_v<Callable>)
335auto graph_builder::emplace(Callable&& callable) -> task {
336 return task{_graph._emplace_back(node_state::none, default_task_parameters{}, nullptr, std::in_place_type_t<graph_node::static_task>{}, std::forward<Callable>(callable) )};
337}
338
339template<typename... Callables>
340requires (sizeof...(Callables) > 1u)
341auto graph_builder::emplace(Callables&&... callables) -> decltype(auto) {
342 _graph.reserve(sizeof...(Callables));
343 return std::make_tuple(emplace(std::forward<Callables>(callables))...);
344}
345
346} // namespace detail
347
349
351
352public:
353
354 task_graph(const std::string& name)
355 : base{_graph},
356 _name{name} { }
357
358private:
359
360 detail::graph_base _graph;
361 std::string _name;
362
363}; // class graph
364
365
366} // namespace sbx::containers
367
368#endif // LIBSBX_CONTAINERS_TASK_GRAPH_HPP_
Definition: tests.cpp:5
Definition: task_graph.hpp:56
Definition: task_graph.hpp:192
Definition: task_graph.hpp:105
Definition: task_graph.hpp:214
Definition: task_graph.hpp:167
Definition: task_graph.hpp:348
Definition: task_graph.hpp:36
Definition: task_graph.hpp:33
Definition: task_graph.hpp:97
Definition: task_graph.hpp:86