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 "../utility/utility.hpp"
14#include "../macros.hpp"
15
16
17
18namespace hi::inline v1 {
19
30template<typename T>
31class generator {
32public:
33 using value_type = T;
34
35 static_assert(not std::is_reference_v<value_type>);
36 static_assert(not std::is_const_v<value_type>);
37
39 public:
40 generator get_return_object()
41 {
42 return generator{handle_type::from_promise(*this)};
43 }
44
45 value_type const& value() const noexcept
46 {
47 hi_axiom(_value);
48 return *_value;
49 }
50
51 static std::suspend_never initial_suspend() noexcept
52 {
53 return {};
54 }
55
56 static std::suspend_always final_suspend() noexcept
57 {
58 return {};
59 }
60
61 std::suspend_always yield_value(value_type const& value) noexcept
62 {
63 _value = value;
64 return {};
65 }
66
67 std::suspend_always yield_value(value_type&& value) noexcept
68 {
69 _value = std::move(value);
70 return {};
71 }
72
73 void return_void() noexcept {}
74
75 // Disallow co_await in generator coroutines.
76 void await_transform() = delete;
77
78 void unhandled_exception() noexcept
79 {
80 _exception = std::current_exception();
81 }
82
83 void rethrow()
84 {
85 if (auto ptr = std::exchange(_exception, nullptr)) {
87 }
88 }
89
90 private:
91 std::optional<value_type> _value = {};
92 std::exception_ptr _exception = nullptr;
93 };
94
95 using handle_type = std::coroutine_handle<promise_type>;
96
98 public:
99 value_proxy(value_type const& value) noexcept : _value(value) {}
100
101 value_type const& operator*() const noexcept
102 {
103 return _value;
104 }
105
106 private:
107 value_type _value;
108 };
109
113 public:
114 using difference_type = ptrdiff_t;
115 using value_type = std::decay_t<value_type>;
116 using pointer = value_type const *;
117 using reference = value_type const&;
119
120 explicit const_iterator(handle_type coroutine) : _coroutine{coroutine} {}
121
125 {
126 hi_axiom(not at_end());
127 _coroutine.resume();
128 _coroutine.promise().rethrow();
129 return *this;
130 }
131
132 value_proxy operator++(int)
133 {
134 auto tmp = value_proxy(_coroutine.promise().value());
135 hi_axiom(not at_end());
136 _coroutine.resume();
137 _coroutine.promise().rethrow();
138 return tmp;
139 }
140
143 decltype(auto) operator*() const
144 {
145 hi_axiom(not at_end());
146 return _coroutine.promise().value();
147 }
148
149 pointer *operator->() const noexcept
150 {
151 hi_axiom(not at_end());
152 return std::addressof(_coroutine.promise().value());
153 }
154
155 [[nodiscard]] bool at_end() const noexcept
156 {
157 return not _coroutine or _coroutine.done();
158 }
159
162 [[nodiscard]] bool operator==(std::default_sentinel_t) const noexcept
163 {
164 return at_end();
165 }
166
167 private:
168 handle_type _coroutine;
169 };
170
171 explicit generator(handle_type coroutine) : _coroutine(coroutine) {}
172
173 generator() = default;
174 ~generator()
175 {
176 if (_coroutine) {
177 _coroutine.destroy();
178 }
179 }
180
181 generator(const generator&) = delete;
182 generator& operator=(const generator&) = delete;
183
184 generator(generator&& other) noexcept : _coroutine{std::exchange(other._coroutine, {})} {}
185
186 generator& operator=(generator&& other) noexcept
187 {
188 hi_return_on_self_assignment(other);
189 if (_coroutine) {
190 _coroutine.destroy();
191 }
192 _coroutine = std::exchange(other._coroutine, {});
193 return *this;
194 }
195
199 {
200 return const_iterator{_coroutine};
201 }
202
206 {
207 return const_iterator{_coroutine};
208 }
209
212 std::default_sentinel_t end() const
213 {
214 return {};
215 }
216
219 std::default_sentinel_t cend() const
220 {
221 return {};
222 }
223
224private:
225 handle_type _coroutine;
226};
227
228template<typename T>
229class generator<T&> {
230public:
231 using value_type = T&;
232
233 class promise_type {
234 public:
235 generator get_return_object()
236 {
237 return generator{handle_type::from_promise(*this)};
238 }
239
240 value_type value() const noexcept
241 {
242 hi_axiom(_value != nullptr);
243 // Convert stored pointer back to reference.
244 return *_value;
245 }
246
247 static std::suspend_never initial_suspend() noexcept
248 {
249 return {};
250 }
251
252 static std::suspend_always final_suspend() noexcept
253 {
254 return {};
255 }
256
257 std::suspend_always yield_value(value_type value) noexcept
258 {
259 _value = std::addressof(value);
260 return {};
261 }
262
263 void return_void() noexcept {}
264
265 // Disallow co_await in generator coroutines.
266 void await_transform() = delete;
267
268 void unhandled_exception() noexcept
269 {
270 _exception = std::current_exception();
271 }
272
273 void rethrow()
274 {
275 if (auto ptr = std::exchange(_exception, nullptr)) {
277 }
278 }
279
280 private:
281 std::remove_reference_t<value_type> *_value = nullptr;
282 std::exception_ptr _exception = nullptr;
283 };
284
285 using handle_type = std::coroutine_handle<promise_type>;
286
287 class value_proxy {
288 public:
289 value_proxy(value_type value) noexcept : _value(std::addressof(value)) {}
290
291 value_type operator*() const noexcept
292 {
293 return *_value;
294 }
295
296 private:
297 std::remove_reference_t<value_type> *_value;
298 };
299
302 class const_iterator {
303 public:
304 using difference_type = ptrdiff_t;
305 using value_type = std::decay_t<value_type>;
306 using pointer = value_type const *;
307 using reference = value_type const&;
309
310 explicit const_iterator(handle_type coroutine) : _coroutine{coroutine} {}
311
314 const_iterator& operator++()
315 {
316 hi_axiom(not at_end());
317 _coroutine.resume();
318 _coroutine.promise().rethrow();
319 return *this;
320 }
321
322 value_proxy operator++(int)
323 {
324 auto tmp = value_proxy(_coroutine.promise().value());
325 hi_axiom(not at_end());
326 _coroutine.resume();
327 _coroutine.promise().rethrow();
328 return tmp;
329 }
330
333 decltype(auto) operator*() const
334 {
335 hi_axiom(not at_end());
336 return _coroutine.promise().value();
337 }
338
339 pointer *operator->() const noexcept
340 {
341 hi_axiom(not at_end());
342 return std::addressof(_coroutine.promise().value());
343 }
344
345 [[nodiscard]] bool at_end() const noexcept
346 {
347 return not _coroutine or _coroutine.done();
348 }
349
352 [[nodiscard]] bool operator==(std::default_sentinel_t) const noexcept
353 {
354 return at_end();
355 }
356
357 private:
358 handle_type _coroutine;
359 };
360
361 explicit generator(handle_type coroutine) : _coroutine(coroutine) {}
362
363 generator() = default;
364 ~generator()
365 {
366 if (_coroutine) {
367 _coroutine.destroy();
368 }
369 }
370
371 generator(const generator&) = delete;
372 generator& operator=(const generator&) = delete;
373
374 generator(generator&& other) noexcept : _coroutine{std::exchange(other._coroutine, {})} {}
375
376 generator& operator=(generator&& other) noexcept
377 {
378 hi_return_on_self_assignment(other);
379 if (_coroutine) {
380 _coroutine.destroy();
381 }
382 _coroutine = std::exchange(other._coroutine, {});
383 return *this;
384 }
385
388 const_iterator begin() const
389 {
390 return const_iterator{_coroutine};
391 }
392
395 const_iterator cbegin() const
396 {
397 return const_iterator{_coroutine};
398 }
399
402 std::default_sentinel_t end() const
403 {
404 return {};
405 }
406
409 std::default_sentinel_t cend() const
410 {
411 return {};
412 }
413
414private:
415 handle_type _coroutine;
416};
417
418} // namespace hi::inline v1
@ other
The gui_event does not have associated data.
DOXYGEN BUG.
Definition algorithm.hpp:16
constexpr Out narrow_cast(In const &rhs) noexcept
Cast numeric values without loss of precision.
Definition cast.hpp:377
A return value for a generator-function.
Definition generator.hpp:31
std::default_sentinel_t end() const
Return a sentinel for the iterator.
Definition generator.hpp:212
const_iterator cbegin() const
Start the generator-function and return an iterator.
Definition generator.hpp:205
std::default_sentinel_t cend() const
Return a sentinel for the iterator.
Definition generator.hpp:219
const_iterator begin() const
Start the generator-function and return an iterator.
Definition generator.hpp:198
Definition generator.hpp:38
Definition generator.hpp:97
A forward iterator which iterates through values co_yieled by the generator-function.
Definition generator.hpp:112
const_iterator & operator++()
Resume the generator-function.
Definition generator.hpp:124
bool operator==(std::default_sentinel_t) const noexcept
Check if the generator-function has finished.
Definition generator.hpp:162
std::default_sentinel_t cend() const
Return a sentinel for the iterator.
Definition generator.hpp:409
std::default_sentinel_t end() const
Return a sentinel for the iterator.
Definition generator.hpp:402
const_iterator begin() const
Start the generator-function and return an iterator.
Definition generator.hpp:388
const_iterator cbegin() const
Start the generator-function and return an iterator.
Definition generator.hpp:395
bool operator==(std::default_sentinel_t) const noexcept
Check if the generator-function has finished.
Definition generator.hpp:352
const_iterator & operator++()
Resume the generator-function.
Definition generator.hpp:314
T addressof(T... args)
T current_exception(T... args)
T move(T... args)
T rethrow_exception(T... args)