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 "../macros.hpp"
14#include "debugger.hpp"
15
16hi_export_module(hikogui.utility.generator);
17
18hi_export namespace 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 {
122 _coroutine.promise().rethrow();
123 }
124
128 {
129 hi_axiom(not at_end());
130 _coroutine.resume();
131 _coroutine.promise().rethrow();
132 return *this;
133 }
134
135 value_proxy operator++(int)
136 {
137 auto tmp = value_proxy(_coroutine.promise().value());
138 hi_axiom(not at_end());
139 _coroutine.resume();
140 _coroutine.promise().rethrow();
141 return tmp;
142 }
143
146 decltype(auto) operator*() const
147 {
148 hi_axiom(not at_end());
149 return _coroutine.promise().value();
150 }
151
152 pointer *operator->() const noexcept
153 {
154 hi_axiom(not at_end());
155 return std::addressof(_coroutine.promise().value());
156 }
157
158 [[nodiscard]] bool at_end() const noexcept
159 {
160 return not _coroutine or _coroutine.done();
161 }
162
165 [[nodiscard]] bool operator==(std::default_sentinel_t) const noexcept
166 {
167 return at_end();
168 }
169
170 private:
171 handle_type _coroutine;
172 };
173
174 explicit generator(handle_type coroutine) : _coroutine(coroutine) {}
175
176 generator() = default;
177 ~generator()
178 {
179 if (_coroutine) {
180 _coroutine.destroy();
181 }
182 }
183
184 generator(const generator&) = delete;
185 generator& operator=(const generator&) = delete;
186
187 generator(generator&& other) noexcept : _coroutine{std::exchange(other._coroutine, {})} {}
188
189 generator& operator=(generator&& other) noexcept
190 {
191 hi_return_on_self_assignment(other);
192 if (_coroutine) {
193 _coroutine.destroy();
194 }
195 _coroutine = std::exchange(other._coroutine, {});
196 return *this;
197 }
198
202 {
203 return const_iterator{_coroutine};
204 }
205
209 {
210 return const_iterator{_coroutine};
211 }
212
215 std::default_sentinel_t end() const
216 {
217 return {};
218 }
219
222 std::default_sentinel_t cend() const
223 {
224 return {};
225 }
226
227private:
228 handle_type _coroutine;
229};
230
231template<typename T>
232class generator<T&> {
233public:
234 using value_type = T&;
235
236 class promise_type {
237 public:
238 generator get_return_object()
239 {
240 return generator{handle_type::from_promise(*this)};
241 }
242
243 value_type value() const noexcept
244 {
245 hi_axiom(_value != nullptr);
246 // Convert stored pointer back to reference.
247 return *_value;
248 }
249
250 static std::suspend_never initial_suspend() noexcept
251 {
252 return {};
253 }
254
255 static std::suspend_always final_suspend() noexcept
256 {
257 return {};
258 }
259
260 std::suspend_always yield_value(value_type value) noexcept
261 {
262 _value = std::addressof(value);
263 return {};
264 }
265
266 void return_void() noexcept {}
267
268 // Disallow co_await in generator coroutines.
269 void await_transform() = delete;
270
271 void unhandled_exception() noexcept
272 {
273 _exception = std::current_exception();
274 }
275
276 void rethrow()
277 {
278 if (auto ptr = std::exchange(_exception, nullptr)) {
280 }
281 }
282
283 private:
284 std::remove_reference_t<value_type> *_value = nullptr;
285 std::exception_ptr _exception = nullptr;
286 };
287
288 using handle_type = std::coroutine_handle<promise_type>;
289
290 class value_proxy {
291 public:
292 value_proxy(value_type value) noexcept : _value(std::addressof(value)) {}
293
294 value_type operator*() const noexcept
295 {
296 return *_value;
297 }
298
299 private:
300 std::remove_reference_t<value_type> *_value;
301 };
302
305 class const_iterator {
306 public:
307 using difference_type = ptrdiff_t;
308 using value_type = std::decay_t<value_type>;
309 using pointer = value_type const *;
310 using reference = value_type const&;
312
313 explicit const_iterator(handle_type coroutine) : _coroutine{coroutine} {}
314
317 const_iterator& operator++()
318 {
319 hi_axiom(not at_end());
320 _coroutine.resume();
321 _coroutine.promise().rethrow();
322 return *this;
323 }
324
325 value_proxy operator++(int)
326 {
327 auto tmp = value_proxy(_coroutine.promise().value());
328 hi_axiom(not at_end());
329 _coroutine.resume();
330 _coroutine.promise().rethrow();
331 return tmp;
332 }
333
336 decltype(auto) operator*() const
337 {
338 hi_axiom(not at_end());
339 return _coroutine.promise().value();
340 }
341
342 pointer *operator->() const noexcept
343 {
344 hi_axiom(not at_end());
345 return std::addressof(_coroutine.promise().value());
346 }
347
348 [[nodiscard]] bool at_end() const noexcept
349 {
350 return not _coroutine or _coroutine.done();
351 }
352
355 [[nodiscard]] bool operator==(std::default_sentinel_t) const noexcept
356 {
357 return at_end();
358 }
359
360 private:
361 handle_type _coroutine;
362 };
363
364 explicit generator(handle_type coroutine) : _coroutine(coroutine) {}
365
366 generator() = default;
367 ~generator()
368 {
369 if (_coroutine) {
370 _coroutine.destroy();
371 }
372 }
373
374 generator(const generator&) = delete;
375 generator& operator=(const generator&) = delete;
376
377 generator(generator&& other) noexcept : _coroutine{std::exchange(other._coroutine, {})} {}
378
379 generator& operator=(generator&& other) noexcept
380 {
381 hi_return_on_self_assignment(other);
382 if (_coroutine) {
383 _coroutine.destroy();
384 }
385 _coroutine = std::exchange(other._coroutine, {});
386 return *this;
387 }
388
391 const_iterator begin() const
392 {
393 return const_iterator{_coroutine};
394 }
395
398 const_iterator cbegin() const
399 {
400 return const_iterator{_coroutine};
401 }
402
405 std::default_sentinel_t end() const
406 {
407 return {};
408 }
409
412 std::default_sentinel_t cend() const
413 {
414 return {};
415 }
416
417private:
418 handle_type _coroutine;
419};
420
421} // namespace hi::inline v1
@ other
The gui_event does not have associated data.
DOXYGEN BUG.
Definition algorithm_misc.hpp:20
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:215
const_iterator cbegin() const
Start the generator-function and return an iterator.
Definition generator.hpp:208
std::default_sentinel_t cend() const
Return a sentinel for the iterator.
Definition generator.hpp:222
const_iterator begin() const
Start the generator-function and return an iterator.
Definition generator.hpp:201
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:127
bool operator==(std::default_sentinel_t) const noexcept
Check if the generator-function has finished.
Definition generator.hpp:165
std::default_sentinel_t cend() const
Return a sentinel for the iterator.
Definition generator.hpp:412
std::default_sentinel_t end() const
Return a sentinel for the iterator.
Definition generator.hpp:405
const_iterator begin() const
Start the generator-function and return an iterator.
Definition generator.hpp:391
const_iterator cbegin() const
Start the generator-function and return an iterator.
Definition generator.hpp:398
bool operator==(std::default_sentinel_t) const noexcept
Check if the generator-function has finished.
Definition generator.hpp:355
const_iterator & operator++()
Resume the generator-function.
Definition generator.hpp:317
T addressof(T... args)
T current_exception(T... args)
T move(T... args)
T rethrow_exception(T... args)