Async++ unknown
Async (co_await/co_return) code for C++
Loading...
Searching...
No Matches
async_generator.h
1#pragma once
2#include <asyncpp/detail/concepts.h>
3#include <asyncpp/detail/promise_allocator_base.h>
5
6#include <exception>
7#include <iterator>
8#include <memory>
9
10namespace asyncpp {
11 template<typename T, ByteAllocator Allocator = default_allocator_type>
12 class async_generator;
13
14 namespace detail {
15 template<typename T>
16 class async_generator_iterator;
17
18 class async_generator_yield_operation final {
19 public:
20 explicit async_generator_yield_operation(coroutine_handle<> consumer) noexcept : m_consumer(consumer) {}
21 [[nodiscard]] bool await_ready() const noexcept { return false; }
22 [[nodiscard]] coroutine_handle<> await_suspend([[maybe_unused]] coroutine_handle<> producer) noexcept {
23 return m_consumer;
24 }
25 void await_resume() noexcept {}
26
27 private:
28 coroutine_handle<> m_consumer;
29 };
30
31 template<typename T>
32 class async_generator_promise_base {
33 public:
34 async_generator_promise_base() noexcept = default;
35 async_generator_promise_base(const async_generator_promise_base& other) = delete;
36 async_generator_promise_base& operator=(const async_generator_promise_base& other) = delete;
37 [[nodiscard]] suspend_always initial_suspend() const noexcept { return {}; }
38 [[nodiscard]] async_generator_yield_operation final_suspend() noexcept {
39 m_value = nullptr;
40 return internal_yield_value();
41 }
42 void unhandled_exception() noexcept { m_exception = std::current_exception(); }
43 void return_void() noexcept {}
44 [[nodiscard]] bool finished() const noexcept { return m_value == nullptr; }
45 void rethrow_if_unhandled_exception() {
46 if (m_exception) { std::rethrow_exception(std::move(m_exception)); }
47 }
48 [[nodiscard]] T* value() const noexcept { return m_value; }
49
50 protected:
51 [[nodiscard]] async_generator_yield_operation internal_yield_value() noexcept {
52 return async_generator_yield_operation{m_consumer};
53 }
54
55 private:
56 friend class async_generator_yield_operation;
57 template<typename U, ByteAllocator Allocator>
58 friend class asyncpp::async_generator;
59 friend class async_generator_iterator<T>;
60
61 std::exception_ptr m_exception{nullptr};
62 coroutine_handle<> m_consumer;
63
64 protected:
65 T* m_value;
66 };
67
68 template<typename T, ByteAllocator Allocator>
69 class async_generator_promise final : public async_generator_promise_base<T>,
70 public promise_allocator_base<Allocator> {
71 using value_type = std::remove_reference_t<T>;
72
73 public:
74 async_generator_promise() noexcept = default;
75 [[nodiscard]] coroutine_handle<async_generator_promise> get_return_object() noexcept {
76 return coroutine_handle<async_generator_promise>::from_promise(*this);
77 }
78 [[nodiscard]] async_generator_yield_operation yield_value(value_type& value) noexcept {
79 this->m_value = std::addressof(value);
80 return this->internal_yield_value();
81 }
82 [[nodiscard]] async_generator_yield_operation yield_value(value_type&& value) noexcept {
83 return yield_value(value);
84 }
85 };
86
87 template<typename T, ByteAllocator Allocator>
88 class async_generator_promise<T&&, Allocator> final : public async_generator_promise_base<T>,
89 public promise_allocator_base<Allocator> {
90 public:
91 async_generator_promise() noexcept = default;
92 [[nodiscard]] coroutine_handle<async_generator_promise> get_return_object() noexcept {
93 return coroutine_handle<async_generator_promise>::from_promise(*this);
94 }
95 [[nodiscard]] async_generator_yield_operation yield_value(T&& value) noexcept {
96 this->m_value = std::addressof(value);
97 return this->internal_yield_value();
98 }
99 };
100
101 template<typename T>
102 class async_generator_iterator final {
103 public:
104 using iterator_category = std::input_iterator_tag;
105 // Not sure what type should be used for difference_type as we don't
106 // allow calculating difference between two iterators.
107 using difference_type = std::ptrdiff_t;
108 using value_type = std::remove_reference_t<T>;
109 using reference = std::add_lvalue_reference_t<T>;
110 using pointer = std::add_pointer_t<value_type>;
111
112 explicit async_generator_iterator(std::nullptr_t) noexcept {}
113 [[nodiscard]] auto operator++() noexcept {
114 class increment_op final {
115 public:
116 explicit increment_op(async_generator_iterator<T>& iterator) noexcept : m_iterator(iterator) {}
117 [[nodiscard]] bool await_ready() const noexcept { return false; }
118 [[nodiscard]] coroutine_handle<> await_suspend(coroutine_handle<> consumer) noexcept {
119 m_iterator.m_promise->m_consumer = consumer;
120 return m_iterator.m_coro;
121 }
122 async_generator_iterator<T>& await_resume() {
123 if (m_iterator.m_promise->finished()) {
124 // Update iterator to end()
125 auto promise = m_iterator.m_promise;
126 m_iterator = async_generator_iterator<T>{nullptr};
127 promise->rethrow_if_unhandled_exception();
128 }
129
130 return m_iterator;
131 }
132
133 private:
134 async_generator_iterator<T>& m_iterator;
135 };
136 return increment_op{*this};
137 }
138 [[nodiscard]] reference operator*() const noexcept { return *static_cast<T*>(m_promise->value()); }
139 [[nodiscard]] bool operator==(const async_generator_iterator& other) const noexcept {
140 return m_promise == other.m_promise;
141 }
142 [[nodiscard]] bool operator!=(const async_generator_iterator& other) const noexcept {
143 return !(*this == other);
144 }
145
146 private:
147 template<typename U, ByteAllocator Allocator>
148 friend class asyncpp::async_generator;
149
150 explicit async_generator_iterator(async_generator_promise_base<T>& promise,
151 coroutine_handle<> coro) noexcept
152 : m_promise(std::addressof(promise)), m_coro{coro} {}
153
154 async_generator_promise_base<T>* m_promise{};
155 coroutine_handle<> m_coro{};
156 };
157
158 } // namespace detail
159
163 template<typename T, ByteAllocator Allocator>
164 class [[nodiscard]] async_generator {
165 public:
166 using promise_type = detail::async_generator_promise<T, Allocator>;
167 using iterator = detail::async_generator_iterator<T>;
168
169 async_generator() noexcept : m_coroutine(nullptr) {}
170 //NOLINTNEXTLINE(google-explicit-constructor)
171 async_generator(coroutine_handle<promise_type> coro) noexcept : m_coroutine{coro} {}
172 async_generator(async_generator&& other) noexcept : m_coroutine(other.m_coroutine) {
173 other.m_coroutine = nullptr;
174 }
176 if (m_coroutine) { m_coroutine.destroy(); }
177 }
178 async_generator& operator=(async_generator&& other) noexcept {
179 async_generator temp(std::move(other));
180 swap(temp);
181 return *this;
182 }
183 async_generator(const async_generator&) = delete;
184 async_generator& operator=(const async_generator&) = delete;
185
186 [[nodiscard]] auto begin() noexcept {
187 class begin_operation final {
188 detail::async_generator_promise_base<T>* m_promise{};
189 coroutine_handle<> m_producer{};
190
191 public:
192 explicit begin_operation(std::nullptr_t) noexcept {}
193 explicit begin_operation(
194 coroutine_handle<detail::async_generator_promise<T, Allocator>> producer) noexcept
195 : m_promise(std::addressof(producer.promise())), m_producer(producer) {}
196 [[nodiscard]] bool await_ready() const noexcept { return this->m_promise == nullptr; }
197 [[nodiscard]] coroutine_handle<> await_suspend(coroutine_handle<> consumer) noexcept {
198 m_promise->m_consumer = consumer;
199 return m_producer;
200 }
201 [[nodiscard]] detail::async_generator_iterator<T> await_resume() {
202 if (this->m_promise == nullptr) return detail::async_generator_iterator<T>{nullptr};
203 if (this->m_promise->finished()) {
204 // Completed without yielding any values.
205 this->m_promise->rethrow_if_unhandled_exception();
206 return detail::async_generator_iterator<T>{nullptr};
207 }
208
209 return detail::async_generator_iterator<T>{*this->m_promise, this->m_producer};
210 }
211 };
212 if (!m_coroutine) return begin_operation{nullptr};
213 return begin_operation{m_coroutine};
214 }
215 [[nodiscard]] iterator end() noexcept { return iterator{nullptr}; }
216 void swap(async_generator& other) noexcept {
217 using std::swap;
218 swap(m_coroutine, other.m_coroutine);
219 }
220
221 private:
222 coroutine_handle<promise_type> m_coroutine;
223 };
224
225 template<typename T, ByteAllocator Allocator>
226 void swap(async_generator<T, Allocator>& lhs, async_generator<T, Allocator>& rhs) noexcept {
227 lhs.swap(rhs);
228 }
229} // namespace asyncpp
Generator coroutine class supporting co_await.
Definition async_generator.h:164
Provides a consistent import interface for coroutine, experimental/coroutine or a best effort fallbac...