61
61
62
62
namespace itlib {
63
63
64
- namespace genimpl {
65
- // tempting to include expected here so we could have optional of ref and
66
- // ditch the T& specialization
67
- // ... but we promised to make standalone libs
64
+ // why std::optional still doesn't have T& specialization is beyond me
65
+ // it's tempting to include expected here so we could have optional of ref and
66
+ // ditch our T& specialization, but we promised to make standalone libs
68
67
69
68
template <typename T>
70
- class val_holder : public std ::optional<T> {};
69
+ class generator_value : public std ::optional<T> {};
71
70
72
71
template <typename T>
73
- class val_holder <T&> {
74
- T* val = nullptr ;
72
+ class generator_value <T&> {
73
+ T* m_val = nullptr ;
75
74
public:
76
- void emplace (T& v) noexcept { val = &v; }
77
- void reset () noexcept { val = nullptr ; }
78
- T& operator *() noexcept { return *val ; }
79
- bool has_value () const noexcept { return val != nullptr ; }
75
+ void emplace (T& v) noexcept { m_val = &v; }
76
+ void reset () noexcept { m_val = nullptr ; }
77
+ T& operator *() noexcept { return *m_val ; }
78
+ bool has_value () const noexcept { return m_val != nullptr ; }
80
79
explicit operator bool () const noexcept { return has_value (); }
81
80
};
82
81
83
- } // namespace genimpl
84
-
85
82
template <typename T>
86
83
class generator {
87
84
public:
85
+ // return const ref in case we're generating values, otherwise keep the ref type
88
86
using value_ret_t = std::conditional_t <std::is_reference_v<T>, T, const T&>;
89
87
90
- class promise_type {
91
- genimpl::val_holder <T> m_val;
92
- public:
88
+ struct promise_type {
89
+ generator_value <T> m_val;
90
+
93
91
promise_type () noexcept = default ;
94
92
95
93
~promise_type () = default ;
@@ -99,25 +97,89 @@ class generator {
99
97
std::suspend_always initial_suspend () noexcept { return {}; }
100
98
std::suspend_always final_suspend () noexcept { return {}; }
101
99
std::suspend_always yield_value (T value) noexcept { // assume T is noexcept move constructible
102
- m_val = std::move (value);
100
+ if constexpr (std::is_reference_v<T>) {
101
+ m_val.emplace (value);
102
+ }
103
+ else {
104
+ m_val.emplace (std::move (value));
105
+ }
103
106
return {};
104
107
}
105
108
void return_void () noexcept {}
106
109
void unhandled_exception () { throw ; }
107
110
108
- value_ret_t val () const noexcept {
111
+ value_ret_t val () & noexcept {
109
112
return *m_val;
110
113
}
114
+ T&& val() && noexcept {
115
+ return std::move (*m_val);
116
+ }
117
+ void clear_value () noexcept {
118
+ m_val.reset ();
119
+ }
111
120
};
112
121
122
+ using handle_t = std::coroutine_handle<promise_type>;
123
+
113
124
~generator () {
114
125
if (m_handle) m_handle.destroy ();
115
126
}
116
127
117
- // std::optional interface
118
- genimpl::val_holder<T> next () {}
128
+ // next (optional-based) interface
129
+
130
+ // NOTE: this won't return true until next() has returned an empty optional at least once
131
+ bool done () const noexcept {
132
+ return m_handle.done ();
133
+ }
134
+
135
+ generator_value<T> next () {
136
+ if (done ()) return {};
137
+ m_handle.promise ().clear_value ();
138
+ m_handle.resume ();
139
+ return std::move (m_handle.promise ().m_val );
140
+ }
141
+
142
+ // iterator-like/range-for interface
143
+
144
+ // emphasize that this is not a real iterator
145
+ class pseudo_iterator {
146
+ handle_t m_handle;
147
+ public:
148
+ using value_type = std::decay_t <T>;
149
+ using reference = value_ret_t ;
150
+
151
+ pseudo_iterator () noexcept = default ;
152
+ explicit pseudo_iterator (handle_t handle) noexcept : m_handle(handle) {}
153
+
154
+ reference operator *() const noexcept {
155
+ return m_handle.promise ().val ();
156
+ }
157
+
158
+ pseudo_iterator& operator ++() {
159
+ m_handle.promise ().clear_value ();
160
+ m_handle.resume ();
161
+ return *this ;
162
+ }
163
+
164
+ struct end_t {};
165
+
166
+ // we're not really an iterator, but we can pretend to be one
167
+ friend bool operator ==(const pseudo_iterator& i, end_t ) noexcept { return i.m_handle .done (); }
168
+ friend bool operator ==(end_t , const pseudo_iterator& i) noexcept { return i.m_handle .done (); }
169
+ friend bool operator !=(const pseudo_iterator& i, end_t ) noexcept { return !i.m_handle .done (); }
170
+ friend bool operator !=(end_t , const pseudo_iterator& i) noexcept { return !i.m_handle .done (); }
171
+ };
172
+
173
+ pseudo_iterator begin () {
174
+ m_handle.resume ();
175
+ return pseudo_iterator{m_handle};
176
+ }
177
+
178
+ pseudo_iterator::end_t end () {
179
+ return {};
180
+ }
181
+
119
182
private:
120
- using handle_t = std::coroutine_handle<promise_type>;
121
183
handle_t m_handle;
122
184
explicit generator (handle_t handle) noexcept : m_handle(handle) {}
123
185
};
0 commit comments