LeviLamina
Loading...
Searching...
No Matches
Generator.h
1#pragma once
2
3#include <concepts>
4#include <coroutine>
5#include <cstddef>
6#include <cstdint>
7#include <exception>
8#include <functional>
9#include <iterator>
10#include <type_traits>
11#include <utility>
12
13#include "ll/api/base/Concepts.h"
14
15namespace ll::coro {
16
17template <class Range>
18 requires concepts::RangeLoopable<Range>
19struct ElementsOf {
20 static constexpr bool useRef = std::is_lvalue_reference_v<Range&&>;
21 using Stored =
22 std::conditional_t<useRef, std::reference_wrapper<std::remove_reference_t<Range>>, std::remove_cvref_t<Range>>;
23
24 Stored range;
25
26 constexpr explicit ElementsOf(
27 Range&& range_
28 ) noexcept(std::is_nothrow_constructible_v<Stored, decltype(std::forward<Range>(range_))>)
29 : range(makeStored(std::forward<Range>(range_))) {}
30
31 [[nodiscard]] constexpr decltype(auto) get() & noexcept {
32 if constexpr (useRef) {
33 return range.get();
34 } else {
35 return (range);
36 }
37 }
38
39 [[nodiscard]] constexpr decltype(auto) get() const& noexcept {
40 if constexpr (useRef) {
41 return range.get();
42 } else {
43 return (range);
44 }
45 }
46
47 [[nodiscard]] constexpr decltype(auto) get() && noexcept {
48 if constexpr (useRef) {
49 return range.get();
50 } else {
51 return std::move(range);
52 }
53 }
54
55private:
56 [[nodiscard]] static constexpr Stored makeStored(Range&& range_) {
57 if constexpr (useRef) {
58 return std::ref(range_);
59 } else {
60 return std::forward<Range>(range_);
61 }
62 }
63};
64
65template <class Range>
67
68template <class T>
69struct Generator {
70 using value = std::remove_cvref_t<T>;
71 using reference = T&&;
72 using yielded = reference;
73
74
75 struct promise_type {
76 struct NestInfo {
77 std::exception_ptr exception;
78 std::coroutine_handle<promise_type> parent;
79 std::coroutine_handle<promise_type> root;
80 };
81
82 struct YieldNested {
83 NestInfo nestedInfo{};
84 Generator nested;
85
86 explicit YieldNested(Generator&& nested_) noexcept : nested(std::move(nested_)) {}
87
88 [[nodiscard]] bool await_ready() const noexcept { return !nested.handle; }
89
90 std::coroutine_handle<promise_type> await_suspend(std::coroutine_handle<promise_type> h) noexcept {
91 auto nestedHandle = nested.handle;
92 nestedInfo.parent = h;
93 if (auto* info = h.promise().tryGetNestInfo()) {
94 nestedInfo.root = info->root;
95 } else {
96 nestedInfo.root = h;
97 }
98
99 nestedInfo.root.promise().setTop(nestedHandle);
100 nestedHandle.promise().setNestInfo(std::addressof(nestedInfo));
101 return nestedHandle;
102 }
103
104 void await_resume() {
105 if (nestedInfo.exception) {
106 std::rethrow_exception(nestedInfo.exception);
107 }
108 }
109 };
110
111 std::add_pointer_t<yielded> ptr = nullptr;
112 uintptr_t data =
113 reinterpret_cast<uintptr_t>(std::coroutine_handle<promise_type>::from_promise(*this).address());
114
116 constexpr bool await_ready() const noexcept { return false; }
117
118 constexpr std::coroutine_handle<> await_suspend(std::coroutine_handle<promise_type> h) noexcept {
119 if (auto* info = h.promise().tryGetNestInfo()) {
120 info->root.promise().setTop(info->parent);
121 return info->parent;
122 }
123
124 return std::noop_coroutine();
125 }
126
127 constexpr void await_resume() const noexcept {}
128 };
129
130 Generator get_return_object() noexcept { return Generator{*this}; }
131
132 std::suspend_always initial_suspend() noexcept { return {}; }
133
134 FinalAwaiter final_suspend() noexcept { return {}; }
135
136 void unhandled_exception() {
137 if (auto* info = tryGetNestInfo()) {
138 info->exception = std::current_exception();
139 } else {
140 throw;
141 }
142 }
143
144 std::suspend_always yield_value(yielded val) noexcept {
145 ptr = std::addressof(val);
146 return {};
147 }
148 auto yield_value(std::remove_reference_t<yielded> const& lval)
149 requires std::is_rvalue_reference_v<yielded>
150 && std::constructible_from<std::remove_cvref_t<yielded>, std::remove_reference_t<yielded> const&>
151 {
152 struct YieldCopied {
153 std::remove_cvref_t<yielded> storage;
154 constexpr YieldCopied(std::remove_reference_t<yielded> const& v) : storage(v) {}
155 constexpr bool await_ready() const noexcept { return false; }
156 constexpr void await_suspend(std::coroutine_handle<promise_type> h) noexcept {
157 h.promise().ptr = std::addressof(storage);
158 }
159 constexpr void await_resume() const noexcept {}
160 };
161 return YieldCopied{lval};
162 }
163
164 template <class U>
165 auto yield_value(ElementsOf<Generator<U>&&> elements)
166 requires std::same_as<typename Generator<U>::yielded, yielded>
167 {
168 return YieldNested{std::move(elements).get()};
169 }
170
171 template <class U>
172 auto yield_value(ElementsOf<Generator<U>&> elements)
173 requires std::same_as<typename Generator<U>::yielded, yielded>
174 {
175 return YieldNested{std::move(elements.get())};
176 }
177
178 template <class Range>
179 auto yield_value(ElementsOf<Range>&& elements) {
180 return YieldNested{Generator::makeElementGenerator(std::move(elements))};
181 }
182 void return_void() noexcept {}
183
184 template <class U>
185 U&& await_transform(U&&) = delete;
186
187 private:
188 [[nodiscard]] NestInfo* tryGetNestInfo() const noexcept {
189 if ((data & 1U) != 0) {
190 return reinterpret_cast<NestInfo*>(data ^ 1U);
191 }
192
193 return nullptr;
194 }
195
196 [[nodiscard]] std::coroutine_handle<promise_type> getTop() const noexcept {
197 return std::coroutine_handle<promise_type>::from_address(reinterpret_cast<void*>(data));
198 }
199
200 void setNestInfo(NestInfo* info) noexcept { data = reinterpret_cast<uintptr_t>(info) | 1U; }
201
202 void setTop(std::coroutine_handle<promise_type> top) noexcept {
203 data = reinterpret_cast<uintptr_t>(top.address());
204 }
205
206 friend struct iterator;
207 friend struct FinalAwaiter;
208 };
209
210 struct iterator {
211 using iterator_category = std::input_iterator_tag;
212 using difference_type = ptrdiff_t;
213 using value_type = Generator::value;
214 using reference = Generator::reference;
215 using pointer = std::add_pointer_t<yielded>;
216
217 std::coroutine_handle<promise_type> handle = nullptr;
218
219 iterator() = default;
220 explicit iterator(std::coroutine_handle<promise_type> handle) noexcept : handle(handle) {}
221
222 iterator& operator++() {
223 handle.promise().getTop().resume();
224 if (handle.done()) {
225 handle = nullptr;
226 }
227
228 return *this;
229 }
230
231 void operator++(int) { ++*this; }
232
233 [[nodiscard]] bool operator==(iterator const& other) const noexcept { return handle == other.handle; }
234
235 [[nodiscard]] reference operator*() const noexcept {
236 return static_cast<reference>(*handle.promise().getTop().promise().ptr);
237 }
238
239 [[nodiscard]] pointer operator->() const noexcept { return handle.promise().getTop().promise().ptr; }
240 };
241 [[nodiscard]] iterator begin() {
242 if (handle) {
243 handle.resume();
244 if (handle.done()) {
245 return {};
246 }
247 }
248 return iterator{handle};
249 }
250
251 [[nodiscard]] iterator end() noexcept { return {}; }
252
253 constexpr Generator(Generator&& other) noexcept : handle(std::exchange(other.handle, nullptr)) {}
254 Generator(Generator const&) = delete;
255
256 constexpr ~Generator() {
257 if (handle) {
258 handle.destroy();
259 }
260 }
261 constexpr Generator() = default;
262
263 Generator& operator=(Generator&& other) noexcept {
264 std::swap(other.handle, handle);
265 return *this;
266 }
267 Generator& operator=(Generator const&) = delete;
268
269private:
270 template <class>
271 friend struct Generator;
272
273 constexpr explicit Generator(promise_type& promise) noexcept
274 : handle(std::coroutine_handle<promise_type>::from_promise(promise)) {}
275
276 template <class Range>
277 requires concepts::RangeLoopable<decltype(std::declval<ElementsOf<Range>&>().get())>
278 static Generator makeElementGenerator(ElementsOf<Range> elements) {
279 for (auto&& element : elements.get()) {
280 co_yield std::forward<decltype(element)>(element);
281 }
282 }
283
284 std::coroutine_handle<promise_type> handle = nullptr;
285};
286} // namespace ll::coro
Definition Generator.h:19
Definition Generator.h:210
Definition Generator.h:75