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 
38 namespace Pt {
39 
42 template<typename T>
43 class 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 
69 template<typename T>
70 class 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 
93 class 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 
161 template<typename T>
162 class 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 
216  class NextAwaiter : public AwaiterBase
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 
359  T value()
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
Core module.
Definition: Allocator.h:33
bool await_ready() const noexcept
Returns true if the generator has already finished.
Definition: Generator.h:245
Generator(Generator &&other) noexcept
Moves the coroutine frame from other.
Definition: Generator.h:311
Provides the awaitable returned by Generator::next().
Definition: Generator.h:217
Represents a coroutine that yields a sequence of values and may itself co_await.
Definition: Generator.h:163
T value()
Returns the last yielded value.
Definition: Generator.h:359
Defines the cancellation interface for a pending awaitable.
Definition: Coroutine.h:54
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
Generator(handle_type h)
Constructs a generator that takes ownership of h.
Definition: Generator.h:305
NextAwaiter next()
Returns an awaitable that produces the next value.
Definition: Generator.h:350
bool await_resume()
Returns true if a yielded value is available.
Definition: Generator.h:270
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
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