libcoro  1.0
Coroutine support library for C++20
generator.h
1 #pragma once
2 
3 #include "allocator.h"
4 #include "future.h"
5 
6 namespace coro {
7 
9 
13 template<typename Src>
15 public:
16 
17 
18  using storage_type = std::invoke_result_t<Src>;
19 
20  using iterator_category = std::input_iterator_tag;
21  using value_type = typename std::decay_t<Src>::value_type;
22  using reference = future<value_type> &;
23  using difference_type = std::ptrdiff_t;
24  using pointer = std::add_pointer_t<value_type>;
25 
28  return _stor;
29  }
30 
31 
34  _stor = _src();
35  _stor.start();
36  if (!_stor.is_pending() && !_stor.has_value()) {
37  _is_end = true;
38  }
39  return *this;
40  }
41 
43  bool operator==(const generator_iterator &other) const {
44  return _is_end == other._is_end;
45  }
46 
48  static generator_iterator begin(Src src) {
49  generator_iterator ret(src, false);
50  ++ret;
51  return ret;
52  }
53 
55  static generator_iterator end(Src src) {
56  return generator_iterator(src, true);
57  }
58 
59 protected:
60  Src _src;
61  bool _is_end;
62  mutable storage_type _stor;
63 
64  generator_iterator(Src src, bool is_end):_src(src), _is_end(is_end) {
65 
66  }
67 
68 };
69 
71 
93 template<typename T, coro_allocator Alloc = std_allocator>
94 class generator {
95 public:
96 
98  using value_type = T;
100  using reference = std::add_lvalue_reference_t<T>;
101 
103  class promise_type: public _details::coro_promise_base<T>,
104  public coro_allocator_helper<Alloc> {
105  public:
106 
107  promise_type() {
108  trace::set_class(std::coroutine_handle<promise_type>::from_promise(*this), typeid(generator).name());
109  }
110 
111  constexpr trace::suspend_always initial_suspend() const {return {};}
112 
113  struct switch_awaiter {
114  static constexpr bool await_ready() noexcept {return false;}
115  static constexpr void await_resume() noexcept {}
116 
117  std::coroutine_handle<> await_suspend(std::coroutine_handle<promise_type> h, std::source_location loc = std::source_location::current()) noexcept {
118  promise_type &self = h.promise();
119  return trace::on_switch(h, self.set_resolved_switch(), &loc);
120  }
121  };
122 
123  struct final_switch_awaiter {
124  static constexpr bool await_ready() noexcept {return false;}
125  static constexpr void await_resume() noexcept {}
126 
127  std::coroutine_handle<> await_suspend(std::coroutine_handle<promise_type> h) noexcept {
128  promise_type &self = h.promise();
129  return trace::on_switch(h, self.set_resolved_switch(), {});
130  }
131  };
132 
133  /* WORKAROUND: clang 16+ fails here resulting in code crash during test - this is workaround
134  *
135  * CORO_OPT_BARRIER separates optimization between suspended coroutine and
136  * normal code. For the clang it means to disable optimizations
137  *
138  * DETAILS: return value of set_resolved is prepared_coro which
139  * must be initialized in current stack frame. However, the clang's
140  * too aggresive optimization causes that this value is initialized
141  * in coroutine frame at the same address as next awaiter, which
142  * leads to overwritting its content on next (probably nested)
143  * suspend event. The code crashes when it returns from suspenssion.
144  *
145  */
146 
147  CORO_OPT_BARRIER inline std::coroutine_handle<> set_resolved_switch() {
148  return this->set_resolved().symmetric_transfer();
149  }
150 
151 
152  template<typename Arg>
153  requires future_constructible<T, Arg>
154  switch_awaiter yield_value(Arg && val) {
155  trace::on_yield(std::coroutine_handle<const promise_type>::from_promise(*this), val);
156  this->set_value(std::forward<Arg>(val));
157  return {};
158  }
159  switch_awaiter yield_value(std::exception_ptr e) {
160  trace::on_yield(std::coroutine_handle<const promise_type>::from_promise(*this), e);
161  this->set_exception(std::move(e));
162  return {};
163  }
164 
165  final_switch_awaiter final_suspend() noexcept {
166  return {};
167  }
168 
169  void return_void() {
170  }
171 
172  bool done() const {
173  return std::coroutine_handle<const promise_type>::from_promise(*this).done();
174  }
175 
176  generator get_return_object() {return this;}
177 
179  return [this](auto promise) -> std::coroutine_handle<> {
180  if (done()) return {};
181  this->fut = promise.release();
182  auto h = std::coroutine_handle<promise_type>::from_promise(*this);
183  trace::awaiting_ref(h, this->fut);
184  return h;
185  };
186  }
187  };
188 
190 
195  return _prom->resume();
196  }
197 
198 
200 
207  explicit operator bool() const {
208  return !_prom->done();
209  }
210 
212  generator() = default;
213 
215  template<typename A>
216  generator(generator<T, A> &&other): _prom(cast_promise(other._prom.release())) {
217 
218  }
219 
221 
226  auto begin() {
228  }
230 
235  auto end() {
237  }
238 
239  operator ident_t() const {return std::coroutine_handle<promise_type>::from_promise(*_prom);}
240 
241 protected:
242 
243  template<typename X>
244  static promise_type *cast_promise(X *other) {
245  return static_cast<promise_type *>(static_cast<_details::coro_promise_base<T> *>(other));
246  }
247 
248  generator(promise_type *p):_prom(p) {}
249 
250  struct deleter {
251  void operator()(promise_type *x) {
252  auto h = std::coroutine_handle<promise_type>::from_promise(*x);
253  h.destroy();
254  }
255  };
256 
257  template<typename A, coro_allocator B> friend class generator;
258 
259  std::unique_ptr<promise_type,deleter> _prom;
260 
261 };
262 
264 template<typename R, coro_allocator Alloc>
265 class generator<R(), Alloc>: public generator<R, Alloc> {
266 public:
268 };
269 
270 class fetch_args_tag {};
271 
272 inline constexpr fetch_args_tag fetch_args = {};
273 
275 
305 template<typename R, coro_allocator Alloc, typename ... Args>
306 class generator<R(Args...), Alloc> {
307 public:
308 
309  using return_type = R;
310 
311  using arg_type = std::tuple<Args...>;
312 
314  class promise_type: public _details::coro_promise_base<R>,
315  public coro_allocator_helper<Alloc> {
316  public:
317 
318  promise_type() {
319  trace::set_class(std::coroutine_handle<promise_type>::from_promise(*this), typeid(generator).name());
320  }
321 
322 
323  constexpr trace::suspend_always initial_suspend() const {return {};}
324 
325  struct fetch_arg_awaiter {
326  arg_type *arg;
327  static constexpr bool await_ready() noexcept {return true;}
328  static constexpr void await_suspend(std::coroutine_handle<>) noexcept {}
329  arg_type &await_resume() noexcept {return *arg;};
330  };
331 
332  struct switch_awaiter {
333  promise_type *self = nullptr;
334  static constexpr bool await_ready() noexcept {return false;}
335  arg_type &await_resume() noexcept {return *self->arg;}
336 
337  CORO_OPT_BARRIER std::coroutine_handle<> await_suspend(std::coroutine_handle<promise_type> h, std::source_location loc = std::source_location::current()) noexcept {
338  self = &h.promise();
339  return trace::on_switch(h,self->set_resolved().symmetric_transfer(),&loc);
340  }
341  };
342 
343  struct switch_final_awaiter: public switch_awaiter {
344  CORO_OPT_BARRIER std::coroutine_handle<> await_suspend(std::coroutine_handle<promise_type> h) noexcept {
345  this->self = &h.promise();
346  return trace::on_switch(h,this->self->set_resolved().symmetric_transfer(),{});
347  }
348  };
349 
350 
351  template<typename Arg>
352  requires future_constructible<R, Arg>
353  switch_awaiter yield_value(Arg && val) {
354  trace::on_yield(std::coroutine_handle<const promise_type>::from_promise(*this), val);
355  this->set_value(std::forward<Arg>(val));
356  return {};
357  }
358 
359  switch_awaiter yield_value(std::exception_ptr e) {
360  LIBCORO_TRACE_YIELD(std::coroutine_handle<const promise_type>::from_promise(*this),e);
361  this->set_exception(std::move(e));
362  return {};
363  }
364 
365 
366  fetch_arg_awaiter yield_value(std::nullptr_t) {
367  return {&(*arg)};
368  }
369 
370  fetch_arg_awaiter yield_value(fetch_args_tag) {
371  return {&(*arg)};
372  }
373 
374  switch_final_awaiter final_suspend() noexcept {
375  return {};
376  }
377 
378  void return_void() {
379  }
380 
381  bool done() const {
382  return std::coroutine_handle<const promise_type>::from_promise(*this).done();
383  }
384 
385  generator get_return_object() {return this;}
386 
387  future<R> resume() {
388  return [this](auto promise) {
389  if (done()) return;
390  this->fut = promise.release();
391  auto h = std::coroutine_handle<promise_type>::from_promise(*this);
392  trace::awaiting_ref(h, this->fut);
393  trace::resume(h);
394  };
395  }
396 
397  std::optional<arg_type> arg;
398 
399  template<typename ... XArgs>
400  void set_arg(XArgs && ... args) {
401  arg.emplace(std::forward<XArgs>(args)...);
402  }
403  };
405 
412  explicit operator bool() const {
413  return !_prom->done();
414  }
415 
417  generator() = default;
418 
420  template<typename A>
421  generator(generator<R(Args...), A> &&other): _prom(cast_promise(other._prom.release())) {
422 
423  }
424 
426 
431  template<typename ... XArgs>
432  future<R> operator()(XArgs &&... args) {
433  static_assert(std::is_constructible_v<arg_type, XArgs...>);
434  _prom->set_arg(std::forward<XArgs>(args)...);
435  return _prom->resume();
436  }
437 
438 
439 
440 protected:
441 
442  template<typename X>
443  static promise_type *cast_promise(X *other) {
444  return static_cast<promise_type *>(static_cast<_details::coro_promise_base<R> *>(other));
445  }
446 
447  generator(promise_type *p):_prom(p) {}
448 
449  struct deleter {
450  void operator()(promise_type *x) {
451  auto h = std::coroutine_handle<promise_type>::from_promise(*x);
452  h.destroy();
453  }
454  };
455 
456  template<typename A, coro_allocator B> friend class generator;
457 
458  std::unique_ptr<promise_type,deleter> _prom;
459 
460 
461 
462 };
463 
464 
465 
466 }
inherit this class to include coro_allocator into your promise_type
Definition: allocator.h:77
Contains future value of T, where evaluation is deferred until the value is needed.
Definition: future.h:1245
Contains future value of T, can be co_awaited in coroutine.
Definition: future.h:417
generator(generator< R(Args...), A > &&other)
convert from different allocator
Definition: generator.h:421
future< R > operator()(XArgs &&... args)
call the generator
Definition: generator.h:432
generator()=default
object can be default constructed
reference operator*()
retrieve current value
Definition: generator.h:27
static generator_iterator begin(Src src)
retrieve iterator to generator
Definition: generator.h:48
static generator_iterator end(Src src)
retrieve end iterator
Definition: generator.h:55
generator_iterator & operator++()
advance next value
Definition: generator.h:33
bool operator==(const generator_iterator &other) const
you can only compare with end()
Definition: generator.h:43
Iterator to access generators.
Definition: generator.h:14
generator(generator< T, A > &&other)
convert from different allocator
Definition: generator.h:216
auto end()
retrieve end iterator
Definition: generator.h:235
generator()=default
object can be default constructed
std::add_lvalue_reference_t< T > reference
reference type
Definition: generator.h:100
auto begin()
retrieve begin iterator
Definition: generator.h:226
deferred_future< T > operator()()
call the generator
Definition: generator.h:194
T value_type
value type
Definition: generator.h:98
COROUTINE: Generator.
Definition: generator.h:94
FutureType * release()
Release the future pointer from the promise object.
Definition: future.h:273
Carries reference to future<T>, callable, sets value of an associated future<T>
Definition: future.h:73
#define CORO_OPT_BARRIER
marks function which servers as barrier between suspended coroutine and normal code
Definition: common.h:125
void resume(std::coroutine_handle<> h) noexcept
Record resumption of an coroutine.
Definition: trace.h:382
std::coroutine_handle on_switch(std::coroutine_handle<>, std::coroutine_handle<> to, const void *)
Record switch (symmetric transfer) from one coroutine to other.
Definition: trace.h:393
main namespace
Definition: aggregator.h:8