wwa-coro 0.0.1
Yet Another C++20 Coroutine Library
generator.h
Go to the documentation of this file.
1#ifndef DED82BB9_7596_4598_B34D_B60883A4775D
2#define DED82BB9_7596_4598_B34D_B60883A4775D
3
17
18#include <coroutine>
19#include <exception>
20#include <iterator>
21#include <memory>
22#include <ranges>
23#include <type_traits>
24#include <utility>
25
26#include "detail.h"
27#include "exceptions.h"
28
29namespace wwa::coro {
30
42template<typename Result>
43class [[nodiscard]] generator : public std::ranges::view_interface<generator<Result>> {
44public:
54 public:
56 using value_type = std::remove_reference_t<Result>;
58 using reference_type = std::add_lvalue_reference_t<value_type>;
60 using pointer_type = std::add_pointer_t<value_type>;
61
69 generator<Result> get_return_object() noexcept
70 {
71 using coroutine_handle = std::coroutine_handle<promise_type>;
72 return generator<Result>{coroutine_handle::from_promise(*this)};
73 }
74
84 [[nodiscard]] constexpr auto initial_suspend() const noexcept { return std::suspend_always{}; }
85
95 [[nodiscard]] constexpr auto final_suspend() const noexcept { return std::suspend_always{}; }
96
108 constexpr auto yield_value(std::add_const_t<reference_type> value) noexcept
109 {
110 this->m_value = std::addressof(value);
111 return std::suspend_always{};
112 }
113
126 constexpr auto yield_value(std::add_rvalue_reference_t<std::remove_cvref_t<value_type>> value) noexcept
127 {
128 /*
129 * This seems to be safe.
130 * - `yield` calls `generator_promise::yield_value(T&&)`;
131 * - `yield_value` stores the address of the temporary value;
132 * - the generator suspends and the control is transferred to the calling function;
133 * - after the generator is resumed, the temporary gets destroyed.
134 */
135 this->m_value = std::addressof(value);
136 return std::suspend_always{};
137 }
138
151 constexpr void unhandled_exception() noexcept { this->m_exception = std::current_exception(); }
152
158 constexpr void return_void() const noexcept {}
159
167 [[nodiscard]] reference_type value() const noexcept { return static_cast<reference_type>(*this->m_value); }
168
174 void await_transform() = delete;
175
185 void rethrow_if_exception()
186 {
187 if (this->m_exception) {
188 std::rethrow_exception(std::move(this->m_exception));
189 }
190 }
191
192 private:
194 pointer_type m_value = nullptr;
196 std::exception_ptr m_exception = nullptr;
198 };
199
211 class iterator {
212 public:
214 using iterator_concept = std::input_iterator_tag;
215
220 using difference_type = std::ptrdiff_t;
221
223
224 using value_type = promise_type::value_type;
225
233 explicit iterator(std::coroutine_handle<promise_type> coroutine) noexcept : m_coroutine(coroutine) {}
235
243 iterator() noexcept = default;
244
256 [[nodiscard]] constexpr bool operator==(const iterator& other) const noexcept
257 {
258 return this->m_coroutine == other.m_coroutine ||
259 (!detail::is_good_handle(this->m_coroutine) && !detail::is_good_handle(other.m_coroutine));
260 }
261
274 {
275 if (detail::is_good_handle(this->m_coroutine)) [[likely]] {
276 this->m_coroutine.resume();
277 if (this->m_coroutine.done()) {
278 this->m_coroutine.promise().rethrow_if_exception();
279 }
280
281 return *this;
282 }
283
284 throw bad_result_access{"Incrementing past the end of the generator"};
285 }
286
295 void operator++(int) { this->operator++(); }
296
308 promise_type::reference_type operator*() const
309 {
310 if (detail::is_good_handle(this->m_coroutine)) [[likely]] {
311 return this->m_coroutine.promise().value();
312 }
313
314 throw bad_result_access("Access past the end of the generator");
315 }
316
317 private:
319 std::coroutine_handle<promise_type> m_coroutine;
320 };
321
330 generator() noexcept = default;
331
339 generator(generator&& other) noexcept : m_coroutine(std::exchange(other.m_coroutine, {})) {}
340
350 {
351 if (this->m_coroutine) {
352 this->m_coroutine.destroy();
353 }
354 }
355
369 generator& operator=(generator&& other) noexcept
370 {
371 if (this != std::addressof(other)) {
372 if (this->m_coroutine) {
373 this->m_coroutine.destroy();
374 }
375
376 this->m_coroutine = std::exchange(other.m_coroutine, nullptr);
377 }
378
379 return *this;
380 }
381
383 generator(const generator&) = delete;
384 generator& operator=(const generator&) = delete;
386
401 [[nodiscard]] iterator begin()
402 {
403 this->advance();
404 return iterator(this->m_coroutine);
405 }
406
417 [[nodiscard]] iterator begin() const
418 {
419 this->advance();
420 return iterator(this->m_coroutine);
421 }
422
434 [[nodiscard]] constexpr iterator end() const noexcept { return iterator(nullptr); }
435
436private:
438 std::coroutine_handle<promise_type> m_coroutine;
439
445 explicit generator(std::coroutine_handle<promise_type> coroutine) noexcept : m_coroutine(coroutine) {}
446
457 void advance() const
458 {
459 if (detail::is_good_handle(this->m_coroutine)) {
460 this->m_coroutine.resume();
461 if (this->m_coroutine.done()) {
462 this->m_coroutine.promise().rethrow_if_exception();
463 }
464 }
465 }
466};
467
472
477
482
483} // namespace wwa::coro
484
485#endif /* DED82BB9_7596_4598_B34D_B60883A4775D */
Exception thrown when accessing a result that is not available.
Definition exceptions.h:23
An input iterator that produces values of type Result.
Definition generator.h:211
std::ptrdiff_t difference_type
Difference between the addresses of any two elements in the controlled sequence.
Definition generator.h:220
promise_type::reference_type operator*() const
Dereferences the iterator to obtain the current value.
Definition generator.h:308
iterator & operator++()
Advances the iterator to the next value.
Definition generator.h:273
iterator() noexcept=default
Default constructor.
std::input_iterator_tag iterator_concept
The strongest iterator concept supported by the iterator.
Definition generator.h:214
void operator++(int)
Advances the iterator to the next value.
Definition generator.h:295
The promise type of the generator.
Definition generator.h:52
An synchronous generator that produces values of type Result.
Definition generator.h:43
~generator()
Destructor.
Definition generator.h:349
generator() noexcept=default
Default constructor.
iterator begin() const
Returns a constant iterator to the current item of the generator.
Definition generator.h:417
constexpr iterator end() const noexcept
Returns a sentinel iterator that marks the end of the generator.
Definition generator.h:434
generator & operator=(generator &&other) noexcept
Move assignment operator.
Definition generator.h:369
iterator begin()
Returns an iterator to the current item of the generator.
Definition generator.h:401
Internal utility functions for coroutine handling.
Custom exception classes for coroutine handling.
Library namespace.