opynsim
Unofficial C++ Documentation
Loading...
Searching...
No Matches
generator.h
Go to the documentation of this file.
1#pragma once
2
3#include <version> // __cpp_lib_generator
4
5#ifdef __cpp_lib_generator
6 #include <generator>
7#else
9
10 #include <coroutine>
11 #include <cstddef>
12 #include <exception>
13 #include <iterator>
14 #include <memory>
15 #include <ranges>
16 #include <stack>
17 #include <type_traits>
18 #include <utility>
19#endif
20
21namespace osc::cpp23
22{
23#ifdef __cpp_lib_generator
24 template<class Ref, class V = void, class Allocator = void>
25 using generator = std::generator<Ref, V, Allocator>;
26#else
27 // WORK IN PROGRESS
28 //
29 // liboscar shim for `std::generator<Ref, V, Allocator>` (and also a bit of fun,
30 // because what kind of C++ programmer isn't intrigued by coroutines? ;)).
31 template<class Ref, class V = void>
32 class generator : public std::ranges::view_interface<generator<Ref, V>> {
33 private:
34 using value = std::conditional_t<std::is_void_v<V>, std::remove_cvref_t<Ref>, V>;
35 using reference = std::conditional_t<std::is_void_v<V>, Ref&&, Ref>;
36 public:
37 using yielded = std::conditional_t<std::is_reference_v<reference>, reference, const reference&>;
38
39 // The promise type of `generator`
41 public:
42 std::add_pointer_t<yielded> value_ = nullptr;
43 std::exception_ptr except_;
44 std::stack<std::coroutine_handle<>>* active_ = nullptr;
45
47 {
48 generator generator{std::coroutine_handle<promise_type>::from_promise(*this)};
49 generator.coroutine_.promise().active_ = generator.active_.get();
50 return generator;
51 }
52
53 std::suspend_always initial_suspend() const noexcept { return std::suspend_always{}; }
54
55 // Specialized awaiter: transfers control back to the parent of a nested coroutine.
57
58 // `final_suspend` always suspends the coroutine.
59 bool await_ready() noexcept { return false; }
60
61 // This is always called (`final_suspend` always suspends).
62 std::coroutine_handle<> await_suspend(std::coroutine_handle<promise_type> handle) noexcept
63 {
64 // Pops the coroutine handle from the top of *active_.
65 // If *x.active_ is not empty, resumes execution of the coroutine referred to by x.active_->top(). If it is empty, control flow returns to the current coroutine caller or resumer.
66 auto* active = handle.promise().active_;
67 OSC_ASSERT(active != nullptr and active->top() == handle);
68
69 active->pop();
70 if (not active->empty()) {
71 return active->top();
72 }
73 else {
74 return std::noop_coroutine();
75 }
76 }
77
78 // never called (final_suspend always suspends)
80 };
81
83
84 std::suspend_always yield_value(yielded val) { value_ = std::addressof(val); return {}; }
85
87
89 {
90 except_ = std::current_exception();
91 }
92 private:
93 };
94
95 // The return type of `generator::begin`. Models `indirectly_readable` and `input_iterator`.
97 public:
98 using value_type = value;
99 using difference_type = std::ptrdiff_t;
100
101 explicit iterator(std::coroutine_handle<promise_type> coroutine) :
102 coroutine_{coroutine}
103 {}
104
105 iterator(iterator&& other) noexcept : coroutine_{std::exchange(other.coroutine_), {}} {}
106 iterator& operator=(iterator&& other) noexcept { coroutine_ = std::exchange(other.coroutine_, {}); return *this; }
107
109 {
110 if (coroutine_.promise().except_) {
111 std::rethrow_exception(coroutine_.promise().except_);
112 }
113 // - Let `reference` be the `std::generator`'s underlying type.
114 // - Let for some `generator` object `x` its coroutine_ be in the stack `*x.active_`.
115 // - Let `x.active_->top()` refer to a suspended coroutine with promise object `p`
116 //
117 // Equivalent to `return static_cast<reference>(*p.value_);`.
118 return static_cast<reference>(*coroutine_.promise().value_);
119 }
120
121 constexpr iterator& operator++()
122 {
123 coroutine_.promise().except_ = nullptr;
124 coroutine_.resume();
125 return *this;
126 }
127
128 constexpr iterator& operator++(int)
129 {
130 return ++*this;
131 }
132
133 friend bool operator==(const iterator& lhs, std::default_sentinel_t)
134 {
135 return lhs.coroutine_.done();
136 }
137 private:
138 std::coroutine_handle<promise_type> coroutine_;
139 };
140
141 generator(const generator&) = delete;
143 active_{std::move(tmp.active_)},
144 coroutine_{std::exchange(tmp.coroutine_, {})}
145 {}
147 {
148 coroutine_.destroy();
149 }
150
152 {
153 std::swap(active_, other.active_);
154 std::swap(coroutine_, other.coroutine_);
155 }
156
158 {
159 OSC_ASSERT(coroutine_ and not coroutine_.done());
160 active_->push(coroutine_);
161 coroutine_.resume();
162 return iterator{coroutine_};
163 }
164
165 std::default_sentinel_t end() const noexcept
166 {
167 return std::default_sentinel;
168 }
169 private:
170 explicit generator(
171 std::coroutine_handle<promise_type> coroutine) :
172 coroutine_{std::move(coroutine)}
173 {}
174
175 std::unique_ptr<std::stack<std::coroutine_handle<>>> active_ = std::make_unique<std::stack<std::coroutine_handle<>>>();
176 std::coroutine_handle<promise_type> coroutine_;
177 };
178#endif
179}
#define OSC_ASSERT(expr)
Definition assertions.h:26
Definition generator.h:96
constexpr iterator & operator++()
Definition generator.h:121
iterator & operator=(iterator &&other) noexcept
Definition generator.h:106
iterator(iterator &&other) noexcept
Definition generator.h:105
constexpr iterator & operator++(int)
Definition generator.h:128
reference operator*() const noexcept(std::is_nothrow_copy_constructible_v< reference >)
Definition generator.h:108
iterator(std::coroutine_handle< promise_type > coroutine)
Definition generator.h:101
std::ptrdiff_t difference_type
Definition generator.h:99
friend bool operator==(const iterator &lhs, std::default_sentinel_t)
Definition generator.h:133
value value_type
Definition generator.h:98
Definition generator.h:40
std::add_pointer_t< yielded > value_
Definition generator.h:42
std::exception_ptr except_
Definition generator.h:43
std::suspend_always initial_suspend() const noexcept
Definition generator.h:53
std::stack< std::coroutine_handle<> > * active_
Definition generator.h:44
std::suspend_always yield_value(yielded val)
Definition generator.h:84
void unhandled_exception()
Definition generator.h:88
generator get_return_object() noexcept
Definition generator.h:46
void return_void() const noexcept
Definition generator.h:86
final_awaiter final_suspend() noexcept
Definition generator.h:82
Definition generator.h:32
generator(const generator &)=delete
std::default_sentinel_t end() const noexcept
Definition generator.h:165
std::conditional_t< std::is_reference_v< reference >, reference, const reference & > yielded
Definition generator.h:37
generator & operator=(generator other) noexcept
Definition generator.h:151
iterator begin()
Definition generator.h:157
generator(generator &&tmp) noexcept
Definition generator.h:142
~generator() noexcept
Definition generator.h:146
Definition algorithm.h:4
constexpr U to(T &&value)
Definition conversion.h:56
and
Definition algorithms.h:158
std::coroutine_handle await_suspend(std::coroutine_handle< promise_type > handle) noexcept
Definition generator.h:62
bool await_ready() noexcept
Definition generator.h:59
void await_resume() noexcept
Definition generator.h:79