Generator.h
1/*
2 * Copyright (C) 2026 by Marc Boris Duerner
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * As a special exception, you may use this file as part of a free
10 * software library without restriction. Specifically, if other files
11 * instantiate templates or use macros or inline functions from this
12 * file, or you compile this file and link it with other files to
13 * produce an executable, this file does not by itself cause the
14 * resulting executable to be covered by the GNU General Public
15 * License. This exception does not however invalidate any other
16 * reasons why the executable file might be covered by the GNU Library
17 * General Public License.
18 *
19 * This library is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * Lesser General Public License for more details.
23 *
24 * You should have received a copy of the GNU Lesser General Public
25 * License along with this library; if not, write to the Free Software
26 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
27 * MA 02110-1301 USA
28 */
29
30#ifndef PT_GENERATOR_H
31#define PT_GENERATOR_H
32
33#include <Pt/Api.h>
34#include <Pt/Coroutine.h>
35
36#if __cplusplus >= 202002L
37
38namespace Pt {
39
42template<typename T>
43class GeneratorResult
44{
45 public:
46 void set(const T& v)
47 { _result = v; }
48
49 void set(T&& v)
50 { _result = std::move(v); }
51
52 T get()
53 { return std::move(_result); }
54
55 protected:
56 GeneratorResult() = default;
57 ~GeneratorResult() = default;
58
59 GeneratorResult(const GeneratorResult&) = delete;
60 GeneratorResult& operator=(const GeneratorResult&) = delete;
61
62 private:
63 T _result{};
64};
65
66
69template<typename T>
70class GeneratorResult<T&>
71{
72 public:
73 void set(T& v)
74 { _result = &v; }
75
76 T& get()
77 { return *_result; }
78
79 protected:
80 GeneratorResult() = default;
81 ~GeneratorResult() = default;
82
83 GeneratorResult(const GeneratorResult&) = delete;
84 GeneratorResult& operator=(const GeneratorResult&) = delete;
85
86 private:
87 T* _result = nullptr;
88};
89
90
93class YieldAwaiter
94{
95 public:
96 bool await_ready() const noexcept
97 { return false; }
98
99 template<typename P>
100 std::coroutine_handle<> await_suspend(std::coroutine_handle<P> h) noexcept
101 {
102 PromiseBase* outer = h.promise()._outer;
103
104 if( outer && outer != &h.promise() )
105 outer->setFinished();
106
107 h.promise()._outer = nullptr;
108
109 if( h.promise()._continuation )
110 return h.promise()._continuation;
111
112 return std::noop_coroutine();
113 }
114
115 void await_resume() noexcept
116 {}
117};
118
161template<typename T>
162class Generator : public AwaiterBase
163{
164 public:
167 class Promise : public GeneratorResult<T>
168 , public Pt::PromiseBase
169 {
170 public:
171 std::exception_ptr _exception;
172
173 Generator get_return_object()
174 {
175 return Generator(std::coroutine_handle<Promise>::from_promise(*this));
176 }
177
178 std::suspend_always initial_suspend() noexcept
179 { return {}; }
180
181 FinalAwaiter final_suspend() noexcept
182 { return {}; }
183
184 void unhandled_exception()
185 { _exception = std::current_exception(); }
186
187 void return_void() noexcept
188 {}
189
190 template<typename A>
191 AwaiterProxy<A> await_transform(A&& a)
192 {
193 _pending = &a;
194 return AwaiterProxy<A>{ std::forward<A>(a), this };
195 }
196
197 YieldAwaiter yield_value(T value) noexcept
198 {
199 this->set(std::forward<T>(value));
200 return {};
201 }
202 };
203
204 using promise_type = Promise;
205 using handle_type = std::coroutine_handle<promise_type>;
206
217 {
218 public:
221 explicit NextAwaiter(Generator& generator)
222 : _generator(&generator)
223 , _handle(generator._handle)
224 , _isPending(false)
225 {}
226
230 {
231 if(_generator && _isPending)
232 _generator->detachAwaiter(*this);
233 }
234
237 void cancel() override
238 {
239 if( _handle && ! _handle.done() )
240 _handle.promise().cancel();
241 }
242
245 bool await_ready() const noexcept
246 { return ! _handle || _handle.done(); }
247
252 template<typename FormP>
253 std::coroutine_handle<> await_suspend(std::coroutine_handle<FormP> outer)
254 {
255 _generator->attachAwaiter(*this);
256 _isPending = true;
257
258 if( _handle.promise()._outer )
259 throw std::logic_error("generator pending");
260
261 _handle.promise()._continuation = outer;
262 _handle.promise()._outer = &outer.promise();
263 return _handle;
264 }
265
271 {
272 if(_generator && _isPending)
273 {
274 _generator->detachAwaiter(*this);
275 _isPending = false;
276 }
277
278 if( ! _handle )
279 return false;
280
281 if (_handle.promise()._exception)
282 std::rethrow_exception(_handle.promise()._exception);
283
284 return ! _handle.done();
285 }
286
287 private:
288 friend class Generator;
289
290 void onDetach()
291 {
292 _generator = nullptr;
293 _handle = nullptr;
294 _isPending = false;
295 }
296
297 Generator* _generator;
298 handle_type _handle;
299 bool _isPending;
300 };
301
302 public:
305 explicit Generator(handle_type h)
306 : _handle(h)
307 {}
308
311 Generator(Generator&& other) noexcept
312 : _handle(other._handle)
313 {
314 other._handle = nullptr;
315 }
316
320 {
321 cancel();
322 }
323
329 void cancel() override
330 {
331 if(_awaiter)
332 {
333 _awaiter->onDetach();
334 _awaiter = nullptr;
335 }
336
337 if (_handle)
338 {
339 _handle.promise().cancel();
340 _handle.destroy();
341 _handle = nullptr;
342 }
343 }
344
351 {
352 return NextAwaiter(*this);
353 }
354
360 {
361 return _handle.promise().get();
362 }
363
364 private:
365 void attachAwaiter(NextAwaiter& awaiter)
366 {
367 if(_awaiter && _awaiter != &awaiter)
368 throw std::logic_error("generator pending");
369
370 _awaiter = &awaiter;
371 }
372
373 void detachAwaiter(NextAwaiter& awaiter)
374 {
375 if(_awaiter == &awaiter)
376 _awaiter = nullptr;
377 }
378
379 Generator(const Generator&) = delete;
380 Generator& operator=(const Generator&) = delete;
381
382 private:
383 handle_type _handle;
384 NextAwaiter* _awaiter = nullptr;
385};
386
387} // namespace Pt
388
389#endif // __cplusplus >= 202002L
390
391#endif // PT_GENERATOR_H
AwaiterBase()=default
Constructor.
Provides the awaitable returned by Generator::next().
Definition Generator.h:217
bool await_resume()
Returns true if a yielded value is available.
Definition Generator.h:270
bool await_ready() const noexcept
Returns true if the generator has already finished.
Definition Generator.h:245
void cancel() override
Cancels the generator's pending operation.
Definition Generator.h:237
~NextAwaiter()
Detaches from the generator if still pending.
Definition Generator.h:229
std::coroutine_handle await_suspend(std::coroutine_handle< FormP > outer)
Suspends the consumer and resumes the generator.
Definition Generator.h:253
NextAwaiter(Generator &generator)
Constructs a next-awaiter for generator.
Definition Generator.h:221
Represents a coroutine that yields a sequence of values and may itself co_await.
Definition Generator.h:163
Generator(handle_type h)
Constructs a generator that takes ownership of h.
Definition Generator.h:305
T value()
Returns the last yielded value.
Definition Generator.h:359
NextAwaiter next()
Returns an awaitable that produces the next value.
Definition Generator.h:350
void cancel() override
Cancels the generator.
Definition Generator.h:329
~Generator()
Cancels the generator if it still owns a coroutine frame.
Definition Generator.h:319
Generator(Generator &&other) noexcept
Moves the coroutine frame from other.
Definition Generator.h:311
Core module.
Definition Allocator.h:33