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
224 class iterator {
225 public:
227 using iterator_concept = std::input_iterator_tag;
228
233 using difference_type = std::ptrdiff_t;
234
236
237 using value_type = promise_type::value_type;
238
246 explicit iterator(std::coroutine_handle<promise_type> coroutine) noexcept : m_coroutine(coroutine) {}
248
256 iterator() noexcept = default;
257
269 [[nodiscard]] constexpr bool operator==(const iterator& other) const noexcept
270 {
271 return this->m_coroutine == other.m_coroutine ||
272 (!detail::is_good_handle(this->m_coroutine) && !detail::is_good_handle(other.m_coroutine));
273 }
274
287 {
288 if (detail::is_good_handle(this->m_coroutine)) [[likely]] {
289 this->m_coroutine.resume();
290 if (this->m_coroutine.done()) {
291 this->m_coroutine.promise().rethrow_if_exception();
292 }
293
294 return *this;
295 }
296
297 throw bad_result_access{"Incrementing past the end of the generator"};
298 }
299
308 void operator++(int) { this->operator++(); }
309
321 promise_type::reference_type operator*() const
322 {
323 if (detail::is_good_handle(this->m_coroutine)) [[likely]] {
324 return this->m_coroutine.promise().value();
325 }
326
327 throw bad_result_access("Access past the end of the generator");
328 }
329
330 private:
332 std::coroutine_handle<promise_type> m_coroutine;
333 };
334
343 generator() noexcept = default;
344
352 generator(generator&& other) noexcept : m_coroutine(std::exchange(other.m_coroutine, {})) {}
353
363 {
364 if (this->m_coroutine) {
365 this->m_coroutine.destroy();
366 }
367 }
368
382 generator& operator=(generator&& other) noexcept
383 {
384 if (this != std::addressof(other)) {
385 if (this->m_coroutine) {
386 this->m_coroutine.destroy();
387 }
388
389 this->m_coroutine = std::exchange(other.m_coroutine, nullptr);
390 }
391
392 return *this;
393 }
394
396 generator(const generator&) = delete;
397 generator& operator=(const generator&) = delete;
399
414 [[nodiscard]] iterator begin()
415 {
416 this->advance();
417 return iterator(this->m_coroutine);
418 }
419
430 [[nodiscard]] iterator begin() const
431 {
432 this->advance();
433 return iterator(this->m_coroutine);
434 }
435
447 [[nodiscard]] constexpr iterator end() const noexcept { return iterator(nullptr); }
448
457 [[nodiscard]] iterator cbegin() const { return this->begin(); }
458
468 [[nodiscard]] iterator cend() const noexcept { return this->end(); }
469
470private:
472 std::coroutine_handle<promise_type> m_coroutine;
473
479 explicit generator(std::coroutine_handle<promise_type> coroutine) noexcept : m_coroutine(coroutine) {}
480
491 void advance() const
492 {
493 if (detail::is_good_handle(this->m_coroutine)) {
494 this->m_coroutine.resume();
495 if (this->m_coroutine.done()) {
496 this->m_coroutine.promise().rethrow_if_exception();
497 }
498 }
499 }
500};
501
506
511
512} // namespace wwa::coro
513
514#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:224
std::ptrdiff_t difference_type
Difference between the addresses of any two elements in the controlled sequence.
Definition generator.h:233
promise_type::reference_type operator*() const
Dereferences the iterator to obtain the current value.
Definition generator.h:321
iterator & operator++()
Advances the iterator to the next value.
Definition generator.h:286
iterator() noexcept=default
Default constructor.
std::input_iterator_tag iterator_concept
The strongest iterator concept supported by the iterator.
Definition generator.h:227
void operator++(int)
Advances the iterator to the next value.
Definition generator.h:308
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:362
generator() noexcept=default
Default constructor.
iterator begin() const
Returns a constant iterator to the current item of the generator.
Definition generator.h:430
constexpr iterator end() const noexcept
Returns a sentinel iterator that marks the end of the generator.
Definition generator.h:447
iterator cbegin() const
Returns a constant iterator to the current item of the generator.
Definition generator.h:457
generator & operator=(generator &&other) noexcept
Move assignment operator.
Definition generator.h:382
iterator begin()
Returns an iterator to the current item of the generator.
Definition generator.h:414
iterator cend() const noexcept
Returns a cinstant sentinel iterator.
Definition generator.h:468
Internal utility functions for coroutine handling.
Custom exception classes for coroutine handling.
Library namespace.