Oxygen Engine
Modern C++ 3D Engine using OpenGL
Loading...
Searching...
No Matches
pipeline.h
1#ifndef OE_UTIL_PIPELINE_H
2#define OE_UTIL_PIPELINE_H
3
4#include <cassert>
5#include <memory>
6#include <optional>
7#include <vector>
8#include <map>
9
10namespace oe::util
11{
12 template <typename Request, typename Response, bool TopToBottom>
13 class Pipeline;
14
15 namespace pipeline
16 {
17 template <typename Request, typename Response>
18 class Handler
19 {
21
22 public:
23 virtual void init() {}
24 virtual void clean() {}
25
26 virtual ~Handler() {}
27
28 virtual Response handle(const Request&& input) = 0;
29
30 Response next(const Request&& input)
31 {
32 if (_next_handler != nullptr)
33 return _next_handler->handle(std::move(input));
34
35 // No next handler, return default Response
36 return _pipeline->getDefaultResponse();
37 }
38
39 Response next(const Request& input)
40 {
41 if (_next_handler != nullptr)
42 return _next_handler->handle(std::move(input));
43
44 // No next handler, return default Response
45 return _pipeline->getDefaultResponse();
46 }
47
48 protected:
49 ConcretePipeline* _pipeline = nullptr;
50
51 private:
52 Handler* _next_handler = nullptr;
53
56 };
57
63 template <typename Request, typename Response>
64 class LambdaHandler : public Handler<Request, Response>
65 {
66 using handler_function = Response (*)(const Request&&, Handler<Request, Response>*);
67
68 public:
69 LambdaHandler(const handler_function& handler) : _handler(handler)
70 {}
71
72 Response handle(const Request&& input) override
73 {
74 return _handler(input, this);
75 }
76
77 protected:
78 handler_function _handler;
79 };
80 }
81
110 template <typename Request, typename Response, bool TopToBottom = true>
112 {
113 public:
114 using RequestType = Request;
115 using ResponseType = Response;
116
118
124 template<class... Args>
125 Pipeline(Args&&... args) : _default_response(std::forward<Args>(args)...)
126 {
127
128 }
129
136 template <typename T, typename ...Args>
137 Pipeline& addPipe(const std::string& name, Args&& ...args)
138 {
139 std::unique_ptr<T> handler = std::make_unique<T>(std::forward<Args>(args)...);
140
141 // Reintepret cast because the pipe order is not used in the handler, thus defaulted to true
142 handler->_pipeline = reinterpret_cast<Pipeline<Request, Response, true>*>(this);
143
144 _pipes.push_back(std::move(handler));
145
146 if (name != "")
147 _pipes_by_name[name] = _pipes.back().get();
148
149 _rebuildPipeOrder();
150
151 return *this;
152 }
153
158 void init()
159 {
160 for (auto& it : _pipes)
161 it->init();
162 }
163
168 void clean()
169 {
170 // Clean in reverse
171 for (auto it = _pipes.rbegin(); it != _pipes.rend(); ++it)
172 (*it)->clean();
173 }
174
178 Response run(const Request&& request)
179 {
180 // Cannot use 'run' on an step-by-step pipe
181 assert(!_last_step_pipe);
182
183 if (_pipes.size() > 0)
184 {
185 if constexpr (TopToBottom)
186 {
187 return _pipes.front()->handle(std::move(request));
188 }
189 else
190 {
191 return _pipes.back()->handle(std::move(request));
192 }
193 }
194
195 // No pipes, return default Response value
196 return _default_response;
197 }
198
199 Response getDefaultResponse() { return _default_response; }
200
206 template<class... Types>
207 void generateDefaultResponse(Types... args)
208 {
209 _default_response = {args...};
210 }
211
212 template <typename T>
213 T* getPipeByName(const std::string& name)
214 {
215 return static_cast<T*>(_pipes_by_name[name]);
216 }
217
218 void enableStepByStepMode()
219 {
220 _last_step_pipe.emplace(0);
221
222 for (auto it = _pipes.begin(); it != _pipes.end(); ++it)
223 {
224 (*it)->_next_handler = nullptr;
225 }
226 }
227
228 void disableStepByStepMode()
229 {
230 _last_step_pipe.reset();
231 _rebuildPipeOrder();
232 }
233
234 bool isStepByStepMode()
235 {
236 return (bool)_last_step_pipe;
237 }
238
239 std::pair<Response, bool> step(const Request&& request)
240 {
241 // Cannot use 'step' on an linear pipe
242 assert(_last_step_pipe);
243
244 PipeHandler* pipe = _pipes[*_last_step_pipe].get();
245
246 ++(*_last_step_pipe);
247
248 bool has_more_pipes = _last_step_pipe < _pipes.size();
249
250 if (!has_more_pipes)
251 *_last_step_pipe = 0;
252
253 return {pipe->handle(std::move(request)), has_more_pipes};
254 }
255
256 protected:
257 std::vector<std::unique_ptr<PipeHandler>> _pipes;
258 Response _default_response;
259
260 std::map<std::string, PipeHandler*> _pipes_by_name;
261
262 std::optional<int32_t> _last_step_pipe = std::nullopt;
263
264 void _rebuildPipeOrder()
265 {
266 if constexpr (TopToBottom)
267 {
268 PipeHandler* last_handler = _pipes.back().get();
269 last_handler->_next_handler = nullptr;
270
271 for (auto it = _pipes.rbegin()+1; it != _pipes.rend(); ++it)
272 {
273 (*it)->_next_handler = last_handler;
274 last_handler = it->get();
275 }
276 }
277 else
278 {
279 PipeHandler* last_handler = _pipes.front().get();
280
281 last_handler->_next_handler = _pipes.size() > 1
282 ? (_pipes.begin() + 1)->get()
283 : nullptr
284 ;
285
286 for (auto it = _pipes.begin()+1; it != _pipes.end(); ++it)
287 {
288 (*it)->_next_handler = last_handler;
289 last_handler = it->get();
290 }
291 }
292 }
293 };
294}
295
296#endif
Definition pipeline.h:112
void clean()
Definition pipeline.h:168
Pipeline & addPipe(const std::string &name, Args &&...args)
Definition pipeline.h:137
void generateDefaultResponse(Types... args)
Definition pipeline.h:207
void init()
Definition pipeline.h:158
Response run(const Request &&request)
Definition pipeline.h:178
Pipeline(Args &&... args)
Definition pipeline.h:125
Definition pipeline.h:19
Definition pipeline.h:65
Various utilities.
Definition node.h:15