HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
generator.hpp
1// Copyright Take Vos 2020-2021.
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 "arguments.hpp"
13
14namespace hi::inline v1 {
15
26template<typename T, typename Allocator = std::allocator<std::byte>>
27class generator {
28public:
29 static_assert(
30 std::is_same_v<std::allocator_traits<Allocator>::value_type, std::byte>,
31 "Allocator's value_type must be std::byte");
32 using allocator_type = Allocator;
33 using value_type = T;
34
36 public:
38 allocator_type *allocator;
39 std::size_t size;
40 };
41
42 void operator delete(void *ptr)
43 {
44 if (ptr) {
45 auto *ptr_ = reinterpret_cast<std::byte *>(ptr) - sizeof(allocator_info);
46 auto *info_ptr = std::launder(reinterpret_cast<allocator_info *>(ptr_));
47 auto info = *info_ptr;
48 std::destroy_at(info_ptr);
49
51 auto allocator = allocator_type{};
52 std::allocator_traits<allocator_type>::deallocate(allocator, ptr_, info.size);
53 } else {
54 std::allocator_traits<allocator_type>::deallocate(*(info.allocator), ptr_, info.size);
55 }
56 }
57 }
58
59 [[nodiscard]] static void *simple_new(std::size_t size)
60 {
61 auto size_plus_info = size + sizeof(allocator_info);
62
63 // Prefix the allocated memory with the allocated size.
64 auto allocator = allocator_type{};
65 auto *ptr = std::allocator_traits<allocator_type>::allocate(allocator, size_plus_info);
66 [[maybe_unused]] auto *info = new (ptr) allocator_info(nullptr, size_plus_info);
67 return ptr + sizeof(allocator_info);
68 }
69
70 [[nodiscard]] static void *allocator_new(std::size_t size, allocator_type &allocator)
71 {
72 auto size_plus_info = size + sizeof(allocator_info);
73
74 // Allocator is in the trailing argument, because coroutines can be methods
75 // and the first argument would be `this`.
76 // Prefix the allocated memory with a pointer to the allocator and the allocated size.
77 auto *ptr = std::allocator_traits<allocator_type>::allocate(allocator, size_plus_info);
78 [[maybe_unused]] auto *info = new (ptr) allocator_info(&allocator, size_plus_info);
79 return ptr + sizeof(allocator_info);
80 }
81
82 template<typename... Args>
83 void *operator new(std::size_t size, Args &&...args)
84 {
86 // This is just a coroutine with arguments.
87 return promise_type::simple_new(size);
88 } else {
89 return promise_type::allocator_new(size, get_last_argument(args...));
90 }
91 }
92
93 generator get_return_object()
94 {
95 return generator{handle_type::from_promise(*this)};
96 }
97
98 value_type const &value()
99 {
100 return *_value;
101 }
102
103 static std::suspend_always initial_suspend() noexcept
104 {
105 return {};
106 }
107
108 static std::suspend_always final_suspend() noexcept
109 {
110 return {};
111 }
112
113 std::suspend_always yield_value(value_type const &value) noexcept
114 {
115 _value = value;
116 return {};
117 }
118
119 std::suspend_always yield_value(value_type &&value) noexcept
120 {
121 _value = std::move(value);
122 return {};
123 }
124
125 void return_void() noexcept {}
126
127 // Disallow co_await in generator coroutines.
128 void await_transform() = delete;
129
130 [[noreturn]] static void unhandled_exception()
131 {
132 throw;
133 }
134
135 private:
136 std::optional<value_type> _value;
137 };
138
139 using handle_type = std::coroutine_handle<promise_type>;
140
142 public:
143 value_proxy(value_type const &value) noexcept : _value(value) {}
144
145 value_type const &operator*() const noexcept
146 {
147 return _value;
148 }
149
150 private:
151 value_type _value;
152 };
153
156 class iterator {
157 public:
158 using difference_type = ptrdiff_t;
159 using value_type = std::remove_cv_t<value_type>;
160 using pointer = value_type *;
161 using reference = value_type &;
163
164 explicit iterator(handle_type coroutine) : _coroutine{coroutine} {}
165
169 {
170 _coroutine.resume();
171 return *this;
172 }
173
174 value_proxy operator++(int)
175 {
176 auto tmp = value_proxy(**this);
177 _coroutine.resume();
178 return tmp;
179 }
180
183 value_type const &operator*() const
184 {
185 return _coroutine.promise().value();
186 }
187
188 value_type const *operator->() const
189 {
190 return std::addressof(_coroutine.promise().value());
191 }
192
195 [[nodiscard]] bool operator==(std::default_sentinel_t) const
196 {
197 return (not _coroutine) or _coroutine.done();
198 }
199
200 private:
201 handle_type _coroutine;
202 };
203
204 explicit generator(handle_type coroutine) : _coroutine(coroutine) {}
205
206 generator() = default;
207 ~generator()
208 {
209 if (_coroutine) {
210 _coroutine.destroy();
211 }
212 }
213
214 generator(const generator &) = delete;
215 generator &operator=(const generator &) = delete;
216
217 generator(generator &&other) noexcept : _coroutine{other._coroutine}
218 {
219 hi_axiom(&other != this);
220 other._coroutine = {};
221 }
222
223 generator &operator=(generator &&other) noexcept
224 {
225 hi_return_on_self_assignment(other);
226 if (_coroutine) {
227 _coroutine.destroy();
228 }
229 _coroutine = other._coroutine;
230 other._coroutine = {};
231 return *this;
232 }
233
237 {
238 if (_coroutine) {
239 _coroutine.resume();
240 }
241 return iterator{_coroutine};
242 }
243
246 std::default_sentinel_t end()
247 {
248 return {};
249 }
250
251private:
252 handle_type _coroutine;
253};
254
255namespace pmr {
256
257template<typename T>
258using generator = hi::generator<T, std::pmr::polymorphic_allocator<>>;
259
260}
261
262} // namespace hi::inline v1
This file includes functions for manipulating parameter-packs.
A return value for a generator-function.
Definition generator.hpp:27
iterator begin()
Start the generator-function and return an iterator.
Definition generator.hpp:236
std::default_sentinel_t end()
Return a sentinal for the iterator.
Definition generator.hpp:246
Definition generator.hpp:35
Definition generator.hpp:141
A forward iterator which iterates through values co_yieled by the generator-function.
Definition generator.hpp:156
value_type const & operator*() const
Retrieve the value co_yielded by the generator-function.
Definition generator.hpp:183
bool operator==(std::default_sentinel_t) const
Check if the generator-function has finished.
Definition generator.hpp:195
iterator & operator++()
Resume the generator-function.
Definition generator.hpp:168
Definition concepts.hpp:35
Definition concepts.hpp:38
T addressof(T... args)
T allocate(T... args)
T deallocate(T... args)
T move(T... args)