HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
generator.hpp
1// Copyright Take Vos 2020-2022.
2// Distributed under the Boost Software License, Version 1.0.
3// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
4
5#pragma once
6
7#include <concepts>
8#include <coroutine>
9#include <optional>
10#include <memory>
11#include <memory_resource>
12#include <type_traits>
13#include "arguments.hpp"
14#include "utility/module.hpp"
15
16namespace hi::inline v1 {
17
28template<typename T>
29class generator {
30public:
31 using value_type = T;
32
33 static_assert(not std::is_reference_v<value_type>);
34 static_assert(not std::is_const_v<value_type>);
35
37 public:
38 generator get_return_object()
39 {
40 return generator{handle_type::from_promise(*this)};
41 }
42
43 value_type const& value() const noexcept
44 {
45 hi_axiom(_value);
46 return *_value;
47 }
48
49 static std::suspend_never initial_suspend() noexcept
50 {
51 return {};
52 }
53
54 static std::suspend_always final_suspend() noexcept
55 {
56 return {};
57 }
58
59 std::suspend_always yield_value(value_type const& value) noexcept
60 {
61 _value = value;
62 return {};
63 }
64
65 std::suspend_always yield_value(value_type&& value) noexcept
66 {
67 _value = std::move(value);
68 return {};
69 }
70
71 void return_void() noexcept {}
72
73 // Disallow co_await in generator coroutines.
74 void await_transform() = delete;
75
76 void unhandled_exception() noexcept
77 {
78 _exception = std::current_exception();
79 }
80
81 void rethrow()
82 {
83 if (auto ptr = std::exchange(_exception, nullptr)) {
85 }
86 }
87
88 private:
89 std::optional<value_type> _value = {};
90 std::exception_ptr _exception = nullptr;
91 };
92
93 using handle_type = std::coroutine_handle<promise_type>;
94
96 public:
97 value_proxy(value_type const& value) noexcept : _value(value) {}
98
99 value_type const& operator*() const noexcept
100 {
101 return _value;
102 }
103
104 private:
105 value_type _value;
106 };
107
111 public:
112 using difference_type = ptrdiff_t;
113 using value_type = std::decay_t<value_type>;
114 using pointer = value_type const *;
115 using reference = value_type const&;
117
118 explicit const_iterator(handle_type coroutine) : _coroutine{coroutine} {}
119
123 {
124 hi_axiom(not at_end());
125 _coroutine.resume();
126 _coroutine.promise().rethrow();
127 return *this;
128 }
129
130 value_proxy operator++(int)
131 {
132 auto tmp = value_proxy(_coroutine.promise().value());
133 hi_axiom(not at_end());
134 _coroutine.resume();
135 _coroutine.promise().rethrow();
136 return tmp;
137 }
138
141 decltype(auto) operator*() const
142 {
143 hi_axiom(not at_end());
144 return _coroutine.promise().value();
145 }
146
147 pointer *operator->() const noexcept
148 {
149 hi_axiom(not at_end());
150 return std::addressof(_coroutine.promise().value());
151 }
152
153 [[nodiscard]] bool at_end() const noexcept
154 {
155 return not _coroutine or _coroutine.done();
156 }
157
160 [[nodiscard]] bool operator==(std::default_sentinel_t) const noexcept
161 {
162 return at_end();
163 }
164
165 private:
166 handle_type _coroutine;
167 };
168
169 explicit generator(handle_type coroutine) : _coroutine(coroutine) {}
170
171 generator() = default;
172 ~generator()
173 {
174 if (_coroutine) {
175 _coroutine.destroy();
176 }
177 }
178
179 generator(const generator&) = delete;
180 generator& operator=(const generator&) = delete;
181
182 generator(generator&& other) noexcept : _coroutine{std::exchange(other._coroutine, {})} {}
183
184 generator& operator=(generator&& other) noexcept
185 {
186 hi_return_on_self_assignment(other);
187 if (_coroutine) {
188 _coroutine.destroy();
189 }
190 _coroutine = std::exchange(other._coroutine, {});
191 return *this;
192 }
193
197 {
198 return const_iterator{_coroutine};
199 }
200
204 {
205 return const_iterator{_coroutine};
206 }
207
210 std::default_sentinel_t end() const
211 {
212 return {};
213 }
214
217 std::default_sentinel_t cend() const
218 {
219 return {};
220 }
221
222private:
223 handle_type _coroutine;
224};
225
226template<typename T>
227class generator<T&> {
228public:
229 using value_type = T&;
230
231 class promise_type {
232 public:
233 generator get_return_object()
234 {
235 return generator{handle_type::from_promise(*this)};
236 }
237
238 value_type value() const noexcept
239 {
240 hi_axiom(_value != nullptr);
241 // Convert stored pointer back to reference.
242 return *_value;
243 }
244
245 static std::suspend_never initial_suspend() noexcept
246 {
247 return {};
248 }
249
250 static std::suspend_always final_suspend() noexcept
251 {
252 return {};
253 }
254
255 std::suspend_always yield_value(value_type value) noexcept
256 {
257 _value = std::addressof(value);
258 return {};
259 }
260
261 void return_void() noexcept {}
262
263 // Disallow co_await in generator coroutines.
264 void await_transform() = delete;
265
266 void unhandled_exception() noexcept
267 {
268 _exception = std::current_exception();
269 }
270
271 void rethrow()
272 {
273 if (auto ptr = std::exchange(_exception, nullptr)) {
275 }
276 }
277
278 private:
279 std::remove_reference_t<value_type> *_value = nullptr;
280 std::exception_ptr _exception = nullptr;
281 };
282
283 using handle_type = std::coroutine_handle<promise_type>;
284
285 class value_proxy {
286 public:
287 value_proxy(value_type value) noexcept : _value(std::addressof(value)) {}
288
289 value_type operator*() const noexcept
290 {
291 return *_value;
292 }
293
294 private:
295 std::remove_reference_t<value_type> *_value;
296 };
297
300 class const_iterator {
301 public:
302 using difference_type = ptrdiff_t;
303 using value_type = std::decay_t<value_type>;
304 using pointer = value_type const *;
305 using reference = value_type const&;
307
308 explicit const_iterator(handle_type coroutine) : _coroutine{coroutine} {}
309
312 const_iterator& operator++()
313 {
314 hi_axiom(not at_end());
315 _coroutine.resume();
316 _coroutine.promise().rethrow();
317 return *this;
318 }
319
320 value_proxy operator++(int)
321 {
322 auto tmp = value_proxy(_coroutine.promise().value());
323 hi_axiom(not at_end());
324 _coroutine.resume();
325 _coroutine.promise().rethrow();
326 return tmp;
327 }
328
331 decltype(auto) operator*() const
332 {
333 hi_axiom(not at_end());
334 return _coroutine.promise().value();
335 }
336
337 pointer *operator->() const noexcept
338 {
339 hi_axiom(not at_end());
340 return std::addressof(_coroutine.promise().value());
341 }
342
343 [[nodiscard]] bool at_end() const noexcept
344 {
345 return not _coroutine or _coroutine.done();
346 }
347
350 [[nodiscard]] bool operator==(std::default_sentinel_t) const noexcept
351 {
352 return at_end();
353 }
354
355 private:
356 handle_type _coroutine;
357 };
358
359 explicit generator(handle_type coroutine) : _coroutine(coroutine) {}
360
361 generator() = default;
362 ~generator()
363 {
364 if (_coroutine) {
365 _coroutine.destroy();
366 }
367 }
368
369 generator(const generator&) = delete;
370 generator& operator=(const generator&) = delete;
371
372 generator(generator&& other) noexcept : _coroutine{std::exchange(other._coroutine, {})} {}
373
374 generator& operator=(generator&& other) noexcept
375 {
376 hi_return_on_self_assignment(other);
377 if (_coroutine) {
378 _coroutine.destroy();
379 }
380 _coroutine = std::exchange(other._coroutine, {});
381 return *this;
382 }
383
386 const_iterator begin() const
387 {
388 return const_iterator{_coroutine};
389 }
390
393 const_iterator cbegin() const
394 {
395 return const_iterator{_coroutine};
396 }
397
400 std::default_sentinel_t end() const
401 {
402 return {};
403 }
404
407 std::default_sentinel_t cend() const
408 {
409 return {};
410 }
411
412private:
413 handle_type _coroutine;
414};
415
416} // namespace hi::inline v1
This file includes functions for manipulating parameter-packs.
#define hi_axiom(expression,...)
Specify an axiom; an expression that is true.
Definition assert.hpp:253
@ other
The gui_event does not have associated data.
DOXYGEN BUG.
Definition algorithm.hpp:13
A return value for a generator-function.
Definition generator.hpp:29
std::default_sentinel_t end() const
Return a sentinel for the iterator.
Definition generator.hpp:210
const_iterator cbegin() const
Start the generator-function and return an iterator.
Definition generator.hpp:203
std::default_sentinel_t cend() const
Return a sentinel for the iterator.
Definition generator.hpp:217
const_iterator begin() const
Start the generator-function and return an iterator.
Definition generator.hpp:196
Definition generator.hpp:36
Definition generator.hpp:95
A forward iterator which iterates through values co_yieled by the generator-function.
Definition generator.hpp:110
const_iterator & operator++()
Resume the generator-function.
Definition generator.hpp:122
bool operator==(std::default_sentinel_t) const noexcept
Check if the generator-function has finished.
Definition generator.hpp:160
std::default_sentinel_t cend() const
Return a sentinel for the iterator.
Definition generator.hpp:407
std::default_sentinel_t end() const
Return a sentinel for the iterator.
Definition generator.hpp:400
const_iterator begin() const
Start the generator-function and return an iterator.
Definition generator.hpp:386
const_iterator cbegin() const
Start the generator-function and return an iterator.
Definition generator.hpp:393
bool operator==(std::default_sentinel_t) const noexcept
Check if the generator-function has finished.
Definition generator.hpp:350
const_iterator & operator++()
Resume the generator-function.
Definition generator.hpp:312
Definition concepts.hpp:36
Definition concepts.hpp:39
T addressof(T... args)
T current_exception(T... args)
T move(T... args)
T rethrow_exception(T... args)