wwa-coro 0.0.1
Yet Another C++20 Coroutine Library
async_generator.h
Go to the documentation of this file.
1#ifndef C16AFA99_7780_4436_8BC0_60F24EF771A4
2#define C16AFA99_7780_4436_8BC0_60F24EF771A4
3
20
21#include <coroutine>
22#include <exception>
23#include <iterator>
24#include <memory>
25#include <type_traits>
26#include <utility>
27
28#include "detail.h"
29#include "exceptions.h"
30
31namespace wwa::coro {
32
46template<typename Result>
47class [[nodiscard]] async_generator {
48public:
58 public:
60 using value_type = std::remove_reference_t<Result>;
62 using pointer = std::add_pointer_t<value_type>;
63
69 promise_type() noexcept = default;
70
74 ~promise_type() = default;
75
77 promise_type(const promise_type&) = delete;
78 promise_type(promise_type&&) noexcept = delete;
79 promise_type& operator=(const promise_type&) = delete;
82
90 auto get_return_object() noexcept { return async_generator<Result>{*this}; }
91
101 [[nodiscard]] constexpr auto initial_suspend() const noexcept { return std::suspend_always{}; }
102
112 auto final_suspend() noexcept
113 {
114 this->m_current_value = nullptr;
115 return yield_op{this->m_consumer};
116 }
117
126 void unhandled_exception() noexcept { this->m_exception = std::current_exception(); }
127
133 constexpr void return_void() const noexcept {}
134
146 auto yield_value(value_type& value) noexcept
147 {
148 this->m_current_value = std::addressof(value);
149 return yield_op{this->m_consumer};
150 }
151
165 auto yield_value(std::add_rvalue_reference_t<std::remove_cv_t<value_type>> value) noexcept
166 {
167 this->m_current_value = std::addressof(value);
168 return yield_op{this->m_consumer};
169 }
170
178 [[nodiscard]] constexpr std::add_lvalue_reference_t<value_type> value() const noexcept
179 {
180 return *this->m_current_value;
181 }
182
188 [[nodiscard]] constexpr bool finished() const noexcept { return this->m_current_value == nullptr; }
189
199 void rethrow_if_unhandled_exception()
200 {
201 if (this->m_exception) {
202 std::rethrow_exception(std::move(this->m_exception));
203 }
204 }
205
214 void set_consumer(std::coroutine_handle<> consumer) noexcept { this->m_consumer = consumer; }
215
216 private:
218 pointer m_current_value = nullptr;
220 std::coroutine_handle<> m_consumer;
222 std::exception_ptr m_exception = nullptr;
223
224 // NOLINTBEGIN(readability-convert-member-functions-to-static)
231 class yield_op {
232 public:
237 constexpr explicit yield_op(std::coroutine_handle<> consumer) noexcept : m_consumer(consumer) {}
238
246 [[nodiscard]] constexpr bool await_ready() const noexcept { return false; }
247
256 [[nodiscard]] constexpr auto await_suspend([[maybe_unused]] std::coroutine_handle<> h) const noexcept
257 {
258 return this->m_consumer;
259 }
260
266 constexpr void await_resume() const noexcept {}
267
268 private:
270 std::coroutine_handle<> m_consumer;
271 };
272 // NOLINTEND(readability-convert-member-functions-to-static)
273
275 };
276
286 class iterator {
287 public:
289
298 class advance_op {
299 public:
308 constexpr advance_op() noexcept : m_promise(nullptr), m_producer(nullptr) {}
309
317 explicit advance_op(std::coroutine_handle<promise_type> producer) noexcept
318 : m_promise(std::addressof(producer.promise())), m_producer(producer)
319 {}
320
333 [[nodiscard]] constexpr bool await_ready() const noexcept { return this->m_promise == nullptr; }
334
343 constexpr std::coroutine_handle<> await_suspend(std::coroutine_handle<> consumer) noexcept
344 {
345 this->m_promise->set_consumer(consumer);
346 return this->m_producer;
347 }
348
365 iterator await_resume()
366 {
367 if (this->m_promise == nullptr) {
368 return iterator{nullptr};
369 }
370
371 if (this->m_promise->finished()) {
372 this->m_promise->rethrow_if_unhandled_exception();
373 return iterator{nullptr};
374 }
375
376 return iterator{std::coroutine_handle<promise_type>::from_promise(*this->m_promise)};
377 }
378
379 private:
381 promise_type* m_promise;
383 std::coroutine_handle<> m_producer;
384 };
385
387
389 using iterator_concept = std::input_iterator_tag;
390
395 using difference_type = std::ptrdiff_t;
396
398
399 using value_type = std::remove_reference_t<Result>;
401 using reference = std::add_lvalue_reference_t<value_type>;
403 using pointer = std::add_pointer_t<value_type>;
404
412 explicit iterator(std::coroutine_handle<promise_type> coroutine) noexcept : m_coroutine(coroutine) {}
414
424 [[nodiscard]] advance_op operator++()
425 {
426 if (detail::is_good_handle(this->m_coroutine)) {
427 return advance_op{this->m_coroutine};
428 }
429
431 throw bad_result_access{"Incrementing past the end of the generator"};
432 }
433
442 reference operator*() const
443 {
444 if (detail::is_good_handle(this->m_coroutine)) [[likely]] {
445 return this->m_coroutine.promise().value();
446 }
447
449 throw bad_result_access{"Dereferencing the end of the generator"};
450 }
451
467 friend bool operator==(const iterator& it, [[maybe_unused]] std::default_sentinel_t sentinel) noexcept
468 {
469 return !detail::is_good_handle(it.m_coroutine);
470 }
471
483 bool operator==(const iterator& other) const noexcept
484 {
485 return this->m_coroutine == other.m_coroutine ||
486 (!detail::is_good_handle(this->m_coroutine) && !detail::is_good_handle(other.m_coroutine));
487 }
488
489 private:
490 std::coroutine_handle<promise_type> m_coroutine;
491 };
492
501 constexpr async_generator() noexcept = default;
502
512 constexpr async_generator(async_generator&& other) noexcept : m_coroutine(std::exchange(other.m_coroutine, nullptr))
513 {}
514
525 {
526 if (this->m_coroutine) {
527 this->m_coroutine.destroy();
528 }
529 }
530
544 {
545 if (this != std::addressof(other)) {
546 if (this->m_coroutine) {
547 this->m_coroutine.destroy();
548 }
549
550 this->m_coroutine = std::exchange(other.m_coroutine, nullptr);
551 }
552
553 return *this;
554 }
555
557 async_generator(const async_generator&) = delete;
558 async_generator& operator=(const async_generator&) = delete;
560
576 [[nodiscard]] auto begin() noexcept
577 {
578 if (!detail::is_good_handle(this->m_coroutine)) {
579 return typename iterator::advance_op();
580 }
581
582 return typename iterator::advance_op{this->m_coroutine};
583 }
584
592 [[nodiscard]] constexpr auto end() const noexcept { return std::default_sentinel; }
593
594private:
595 std::coroutine_handle<promise_type> m_coroutine;
596
602 explicit async_generator(promise_type& promise) noexcept
603 : m_coroutine(std::coroutine_handle<promise_type>::from_promise(promise))
604 {}
605};
606
611
612} // namespace wwa::coro
613
614#endif /* C16AFA99_7780_4436_8BC0_60F24EF771A4 */
An input iterator that asynchronously produces values of type Result.
friend bool operator==(const iterator &it, std::default_sentinel_t sentinel) noexcept
Compares the iterator it with the sentinel sentinel.
std::input_iterator_tag iterator_concept
The strongest iterator concept supported by the iterator.
advance_op operator++()
Advances the iterator to the next value.
std::ptrdiff_t difference_type
Difference between the addresses of any two elements in the controlled sequence.
bool operator==(const iterator &other) const noexcept
Compares two iterators for equality.
reference operator*() const
Dereferences the iterator to obtain the current value.
The promise type of the generator.
An asynchronous generator that produces values of type Result.
constexpr async_generator() noexcept=default
Default constructor.
async_generator & operator=(async_generator &&other) noexcept
Move assignment operator.
constexpr auto end() const noexcept
Returns a sentinel iterator.
auto begin() noexcept
Returns an awaitable iterator to the current item of the generator.
Exception thrown when accessing a result that is not available.
Definition exceptions.h:23
Internal utility functions for coroutine handling.
Custom exception classes for coroutine handling.
Library namespace.