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 struct iterator;
75
76 struct promise_type {
77 struct NestInfo {
78 std::exception_ptr exception;
79 std::coroutine_handle<promise_type> parent;
80 std::coroutine_handle<promise_type> root;
81 };
82
83 struct YieldNested {
84 NestInfo nestedInfo{};
85 Generator nested;
86
87 explicit YieldNested(Generator&& nested_) noexcept : nested(std::move(nested_)) {}
88
89 [[nodiscard]] bool await_ready() const noexcept { return !nested.handle; }
90
91 std::coroutine_handle<promise_type> await_suspend(std::coroutine_handle<promise_type> h) noexcept {
92 auto nestedHandle = nested.handle;
93 nestedInfo.parent = h;
94 if (auto* info = h.promise().tryGetNestInfo()) {
95 nestedInfo.root = info->root;
96 } else {
97 nestedInfo.root = h;
98 }
99
100 nestedInfo.root.promise().setTop(nestedHandle);
101 nestedHandle.promise().setNestInfo(std::addressof(nestedInfo));
102 return nestedHandle;
103 }
104
105 void await_resume() {
106 if (nestedInfo.exception) {
107 std::rethrow_exception(nestedInfo.exception);
108 }
109 }
110 };
111
112 std::add_pointer_t<yielded> ptr = nullptr;
113 uintptr_t data =
114 reinterpret_cast<uintptr_t>(std::coroutine_handle<promise_type>::from_promise(*this).address());
115
117 constexpr bool await_ready() const noexcept { return false; }
118
119 constexpr std::coroutine_handle<> await_suspend(std::coroutine_handle<promise_type> h) noexcept {
120 if (auto* info = h.promise().tryGetNestInfo()) {
121 info->root.promise().setTop(info->parent);
122 return info->parent;
123 }
124
125 return std::noop_coroutine();
126 }
127
128 constexpr void await_resume() const noexcept {}
129 };
130
131 Generator get_return_object() noexcept { return Generator{*this}; }
132
133 std::suspend_always initial_suspend() noexcept { return {}; }
134
135 FinalAwaiter final_suspend() noexcept { return {}; }
136
137 void unhandled_exception() {
138 if (auto* info = tryGetNestInfo()) {
139 info->exception = std::current_exception();
140 } else {
141 throw;
142 }
143 }
144
145 std::suspend_always yield_value(yielded val) noexcept {
146 ptr = std::addressof(val);
147 return {};
148 }
149 auto yield_value(std::remove_reference_t<yielded> const& lval)
150 requires std::is_rvalue_reference_v<yielded>
151 && std::constructible_from<std::remove_cvref_t<yielded>, std::remove_reference_t<yielded> const&>
152 {
153 struct YieldCopied {
154 std::remove_cvref_t<yielded> storage;
155 constexpr YieldCopied(std::remove_reference_t<yielded> const& v) : storage(v) {}
156 constexpr bool await_ready() const noexcept { return false; }
157 constexpr void await_suspend(std::coroutine_handle<promise_type> h) noexcept {
158 h.promise().ptr = std::addressof(storage);
159 }
160 constexpr void await_resume() const noexcept {}
161 };
162 return YieldCopied{lval};
163 }
164
165 template <class U>
166 auto yield_value(ElementsOf<Generator<U>&&> elements)
167 requires std::same_as<typename Generator<U>::yielded, yielded>
168 {
169 return YieldNested{std::move(elements).get()};
170 }
171
172 template <class U>
173 auto yield_value(ElementsOf<Generator<U>&> elements)
174 requires std::same_as<typename Generator<U>::yielded, yielded>
175 {
176 return YieldNested{std::move(elements.get())};
177 }
178
179 template <class Range>
180 auto yield_value(ElementsOf<Range>&& elements) {
181 return YieldNested{Generator::makeElementGenerator(std::move(elements))};
182 }
183 void return_void() noexcept {}
184
185 template <class U>
186 U&& await_transform(U&&) = delete;
187
188 private:
189 [[nodiscard]] NestInfo* tryGetNestInfo() const noexcept {
190 if ((data & 1U) != 0) {
191 return reinterpret_cast<NestInfo*>(data ^ 1U);
192 }
193
194 return nullptr;
195 }
196
197 [[nodiscard]] std::coroutine_handle<promise_type> getTop() const noexcept {
198 return std::coroutine_handle<promise_type>::from_address(reinterpret_cast<void*>(data));
199 }
200
201 void setNestInfo(NestInfo* info) noexcept { data = reinterpret_cast<uintptr_t>(info) | 1U; }
202
203 void setTop(std::coroutine_handle<promise_type> top) noexcept {
204 data = reinterpret_cast<uintptr_t>(top.address());
205 }
206
207 friend struct iterator;
208 friend struct FinalAwaiter;
209 };
210
211 struct iterator {
212 using iterator_category = std::input_iterator_tag;
213 using difference_type = ptrdiff_t;
214 using value_type = Generator::value;
215 using reference = Generator::reference;
216 using pointer = std::add_pointer_t<yielded>;
217
218 std::coroutine_handle<promise_type> handle = nullptr;
219
220 iterator() = default;
221 explicit iterator(std::coroutine_handle<promise_type> handle) noexcept : handle(handle) {}
222
223 iterator& operator++() {
224 handle.promise().getTop().resume();
225 if (handle.done()) {
226 handle = nullptr;
227 }
228
229 return *this;
230 }
231
232 void operator++(int) { ++*this; }
233
234 [[nodiscard]] bool operator==(iterator const& other) const noexcept { return handle == other.handle; }
235
236 [[nodiscard]] reference operator*() const noexcept {
237 return static_cast<reference>(*handle.promise().getTop().promise().ptr);
238 }
239
240 [[nodiscard]] pointer operator->() const noexcept { return handle.promise().getTop().promise().ptr; }
241 };
242 [[nodiscard]] iterator begin() {
243 if (handle) {
244 handle.resume();
245 if (handle.done()) {
246 return {};
247 }
248 }
249 return iterator{handle};
250 }
251
252 [[nodiscard]] iterator end() noexcept { return {}; }
253
254 constexpr Generator(Generator&& other) noexcept : handle(std::exchange(other.handle, nullptr)) {}
255 Generator(Generator const&) = delete;
256
257 constexpr ~Generator() {
258 if (handle) {
259 handle.destroy();
260 }
261 }
262 constexpr Generator() = default;
263
264 Generator& operator=(Generator&& other) noexcept {
265 std::swap(other.handle, handle);
266 return *this;
267 }
268 Generator& operator=(Generator const&) = delete;
269
270private:
271 template <class>
272 friend struct Generator;
273
274 constexpr explicit Generator(promise_type& promise) noexcept
275 : handle(std::coroutine_handle<promise_type>::from_promise(promise)) {}
276
277 template <class Range>
278 requires concepts::RangeLoopable<decltype(std::declval<ElementsOf<Range>&>().get())>
279 static Generator makeElementGenerator(ElementsOf<Range> elements) {
280 for (auto&& element : elements.get()) {
281 co_yield std::forward<decltype(element)>(element);
282 }
283 }
284
285 std::coroutine_handle<promise_type> handle = nullptr;
286};
287} // namespace ll::coro
Definition Generator.h:19
Definition Generator.h:211
Definition Generator.h:76