ThreadSchedule 1.0.0
Modern C++ thread management library
Loading...
Searching...
No Matches
generator.hpp
Go to the documentation of this file.
1#pragma once
2
16
17#if defined(__cpp_impl_coroutine) && __cpp_impl_coroutine >= 201902L
18
19#include <coroutine>
20#include <exception>
21#include <iterator>
22#include <optional>
23#include <type_traits>
24#include <utility>
25
26#if defined(__has_include)
27# if __has_include(<version>)
28# include <version>
29# endif
30#endif
31
32#if defined(__cpp_lib_generator) && __cpp_lib_generator >= 202207L
33# include <generator>
34# define THREADSCHEDULE_HAS_STD_GENERATOR 1
35#else
36# define THREADSCHEDULE_HAS_STD_GENERATOR 0
37#endif
38
39namespace threadschedule
40{
41
42#if THREADSCHEDULE_HAS_STD_GENERATOR
43
44template <typename T>
45using generator = std::generator<T>;
46
47#else
48
93template <typename T>
94class generator
95{
96 public:
97 struct promise_type
98 {
99 auto get_return_object() noexcept -> generator
100 {
101 return generator{std::coroutine_handle<promise_type>::from_promise(*this)};
102 }
103
104 auto initial_suspend() noexcept -> std::suspend_always
105 {
106 return {};
107 }
108
109 auto final_suspend() noexcept -> std::suspend_always
110 {
111 return {};
112 }
113
114 auto yield_value(T const& value) -> std::suspend_always
115 {
116 value_.emplace(value);
117 return {};
118 }
119
120 auto yield_value(T&& value) noexcept(std::is_nothrow_move_constructible_v<T>) -> std::suspend_always
121 {
122 value_.emplace(std::move(value));
123 return {};
124 }
125
126 void return_void() noexcept
127 {
128 }
129
130 void unhandled_exception()
131 {
132 exception_ = std::current_exception();
133 }
134
135 void rethrow_if_exception()
136 {
137 if (exception_)
138 std::rethrow_exception(exception_);
139 }
140
141 std::optional<T> value_{};
142
143 private:
144 std::exception_ptr exception_{};
145 };
146
157 class iterator
158 {
159 public:
160 using iterator_category = std::input_iterator_tag;
161 using difference_type = std::ptrdiff_t;
162 using value_type = T;
163 using reference = T&;
164 using pointer = T*;
165
166 iterator() noexcept = default;
167
168 explicit iterator(std::coroutine_handle<promise_type> h) noexcept : handle_(h)
169 {
170 }
171
172 auto operator++() -> iterator&
173 {
174 handle_.resume();
175 if (handle_.done())
176 {
177 handle_.promise().rethrow_if_exception();
178 handle_ = nullptr;
179 }
180 return *this;
181 }
182
183 void operator++(int)
184 {
185 ++(*this);
186 }
187
188 [[nodiscard]] auto operator*() const -> T&
189 {
190 return *handle_.promise().value_;
191 }
192
193 [[nodiscard]] auto operator->() const -> T*
194 {
195 return std::addressof(*handle_.promise().value_);
196 }
197
198 [[nodiscard]] friend auto operator==(iterator const& it, std::default_sentinel_t) noexcept -> bool
199 {
200 return !it.handle_ || it.handle_.done();
201 }
202
203 [[nodiscard]] friend auto operator==(std::default_sentinel_t s, iterator const& it) noexcept -> bool
204 {
205 return it == s;
206 }
207
208 [[nodiscard]] friend auto operator!=(iterator const& it, std::default_sentinel_t s) noexcept -> bool
209 {
210 return !(it == s);
211 }
212
213 [[nodiscard]] friend auto operator!=(std::default_sentinel_t s, iterator const& it) noexcept -> bool
214 {
215 return !(it == s);
216 }
217
218 private:
219 std::coroutine_handle<promise_type> handle_{};
220 };
221
222 generator() noexcept = default;
223
224 generator(generator&& other) noexcept : handle_(std::exchange(other.handle_, nullptr))
225 {
226 }
227
228 auto operator=(generator&& other) noexcept -> generator&
229 {
230 if (this != &other)
231 {
232 destroy();
233 handle_ = std::exchange(other.handle_, nullptr);
234 }
235 return *this;
236 }
237
238 ~generator()
239 {
240 destroy();
241 }
242
243 generator(generator const&) = delete;
244 auto operator=(generator const&) -> generator& = delete;
245
246 [[nodiscard]] auto begin() -> iterator
247 {
248 if (handle_)
249 {
250 handle_.resume();
251 if (handle_.done())
252 {
253 handle_.promise().rethrow_if_exception();
254 return iterator{};
255 }
256 }
257 return iterator{handle_};
258 }
259
260 [[nodiscard]] auto end() const noexcept -> std::default_sentinel_t
261 {
262 return {};
263 }
264
265 private:
266 explicit generator(std::coroutine_handle<promise_type> h) noexcept : handle_(h)
267 {
268 }
269
270 void destroy()
271 {
272 if (handle_)
273 {
274 handle_.destroy();
275 handle_ = nullptr;
276 }
277 }
278
279 std::coroutine_handle<promise_type> handle_{};
280};
281
282#endif // THREADSCHEDULE_HAS_STD_GENERATOR
283
284} // namespace threadschedule
285
286#endif // __cpp_impl_coroutine