Fleet  0.0.9
Inference in the LOT
generator.hpp
Go to the documentation of this file.
1 // Copyright (c) Lewis Baker
3 // Licenced under MIT license. See LICENSE.txt for details.
5 
6 #pragma once
7 
8 #ifdef __GNUC__
9  #include <coroutine>
10 #endif
11 
12 #include <type_traits>
13 #include <utility>
14 #include <exception>
15 #include <iterator>
16 #include <functional>
17 
18 namespace cppcoro
19 {
20  template<typename T>
21  class generator;
22 
23  namespace detail
24  {
25  template<typename T>
27  {
28  public:
29 
30  using value_type = std::remove_reference_t<T>;
31  using reference_type = std::conditional_t<std::is_reference_v<T>, T, T&>;
33 
34  generator_promise() = default;
35 
37 
38  constexpr std::suspend_always initial_suspend() const noexcept { return {}; }
39  constexpr std::suspend_always final_suspend() const noexcept { return {}; }
40 
41  template<
42  typename U = T,
43  std::enable_if_t<!std::is_rvalue_reference<U>::value, int> = 0>
44  std::suspend_always yield_value(std::remove_reference_t<T>& value) noexcept
45  {
46  m_value = std::addressof(value);
47  return {};
48  }
49 
50  std::suspend_always yield_value(std::remove_reference_t<T>&& value) noexcept
51  {
52  m_value = std::addressof(value);
53  return {};
54  }
55 
57  {
58  m_exception = std::current_exception();
59  }
60 
61  void return_void()
62  {
63  }
64 
65  reference_type value() const noexcept
66  {
67  return static_cast<reference_type>(*m_value);
68  }
69 
70  // Don't allow any use of 'co_await' inside the generator coroutine.
71  template<typename U>
72  std::suspend_never await_transform(U&& value) = delete;
73 
75  {
76  if (m_exception)
77  {
78  std::rethrow_exception(m_exception);
79  }
80  }
81 
82  private:
83 
84  pointer_type m_value;
85  std::exception_ptr m_exception;
86 
87  };
88 
89  struct generator_sentinel {};
90 
91  template<typename T>
93  {
94  using coroutine_handle = std::coroutine_handle<generator_promise<T>>;
95 
96  public:
97 
98  using iterator_category = std::input_iterator_tag;
99  // What type should we use for counting elements of a potentially infinite sequence?
100  using difference_type = std::ptrdiff_t;
104 
105  // Iterator needs to be default-constructible to satisfy the Range concept.
107  : m_coroutine(nullptr)
108  {}
109 
110  explicit generator_iterator(coroutine_handle coroutine) noexcept
111  : m_coroutine(coroutine)
112  {}
113 
114  friend bool operator==(const generator_iterator& it, generator_sentinel) noexcept
115  {
116  return !it.m_coroutine || it.m_coroutine.done();
117  }
118 
119  friend bool operator!=(const generator_iterator& it, generator_sentinel s) noexcept
120  {
121  return !(it == s);
122  }
123 
124  friend bool operator==(generator_sentinel s, const generator_iterator& it) noexcept
125  {
126  return (it == s);
127  }
128 
129  friend bool operator!=(generator_sentinel s, const generator_iterator& it) noexcept
130  {
131  return it != s;
132  }
133 
135  {
136  m_coroutine.resume();
137  if (m_coroutine.done())
138  {
139  m_coroutine.promise().rethrow_if_exception();
140  }
141 
142  return *this;
143  }
144 
145  // Need to provide post-increment operator to implement the 'Range' concept.
146  void operator++(int)
147  {
148  (void)operator++();
149  }
150 
151  reference operator*() const noexcept
152  {
153  return m_coroutine.promise().value();
154  }
155 
156  pointer operator->() const noexcept
157  {
158  return std::addressof(operator*());
159  }
160 
161  private:
162 
163  coroutine_handle m_coroutine;
164  };
165  }
166 
167  template<typename T>
168  class [[nodiscard]] generator
169  {
170  public:
171 
174 
175  generator() noexcept
176  : m_coroutine(nullptr)
177  {}
178 
179  generator(generator&& other) noexcept
180  : m_coroutine(other.m_coroutine)
181  {
182  other.m_coroutine = nullptr;
183  }
184 
185  generator(const generator& other) = delete;
186 
188  {
189  if (m_coroutine)
190  {
191  m_coroutine.destroy();
192  }
193  }
194 
195  generator& operator=(generator other) noexcept
196  {
197  swap(other);
198  return *this;
199  }
200 
202  {
203  if (m_coroutine)
204  {
205  m_coroutine.resume();
206  if (m_coroutine.done())
207  {
208  m_coroutine.promise().rethrow_if_exception();
209  }
210  }
211 
212  return iterator{ m_coroutine };
213  }
214 
216  {
218  }
219 
220  void swap(generator& other) noexcept
221  {
222  std::swap(m_coroutine, other.m_coroutine);
223  }
224 
225  private:
226 
227  friend class detail::generator_promise<T>;
228 
229  explicit generator(std::coroutine_handle<promise_type> coroutine) noexcept
230  : m_coroutine(coroutine)
231  {}
232 
233  std::coroutine_handle<promise_type> m_coroutine;
234 
235  };
236 
237  template<typename T>
239  {
240  a.swap(b);
241  }
242 
243  namespace detail
244  {
245  template<typename T>
247  {
248  using coroutine_handle = std::coroutine_handle<generator_promise<T>>;
249  return generator<T>{ coroutine_handle::from_promise(*this) };
250  }
251  }
252 
253  template<typename FUNC, typename T>
255  {
256  for (auto&& value : source)
257  {
258  co_yield std::invoke(func, static_cast<decltype(value)>(value));
259  }
260  }
261 }
std::conditional_t< std::is_reference_v< T >, T, T & > reference_type
Definition: generator.hpp:31
constexpr std::suspend_always final_suspend() const noexcept
Definition: generator.hpp:39
void unhandled_exception()
Definition: generator.hpp:56
generator_iterator(coroutine_handle coroutine) noexcept
Definition: generator.hpp:110
reference operator*() const noexcept
Definition: generator.hpp:151
friend bool operator==(generator_sentinel s, const generator_iterator &it) noexcept
Definition: generator.hpp:124
std::suspend_never await_transform(U &&value)=delete
generator< T > get_return_object() noexcept
Definition: generator.hpp:246
constexpr std::suspend_always initial_suspend() const noexcept
Definition: generator.hpp:38
value_type * pointer_type
Definition: generator.hpp:32
std::suspend_always yield_value(std::remove_reference_t< T > &value) noexcept
Definition: generator.hpp:44
typename generator_promise< T >::pointer_type pointer
Definition: generator.hpp:103
generator_iterator & operator++()
Definition: generator.hpp:134
void rethrow_if_exception()
Definition: generator.hpp:74
std::remove_reference_t< T > value_type
Definition: generator.hpp:30
void operator++(int)
Definition: generator.hpp:146
Definition: generator.hpp:89
void swap(generator &other) noexcept
Definition: generator.hpp:220
Definition: generator.hpp:26
friend bool operator!=(generator_sentinel s, const generator_iterator &it) noexcept
Definition: generator.hpp:129
generator & operator=(generator other) noexcept
Definition: generator.hpp:195
generator_iterator() noexcept
Definition: generator.hpp:106
std::suspend_always yield_value(std::remove_reference_t< T > &&value) noexcept
Definition: generator.hpp:50
void return_void()
Definition: generator.hpp:61
generator< std::invoke_result_t< FUNC &, typename generator< T >::iterator::reference > > fmap(FUNC func, generator< T > source)
Definition: generator.hpp:254
reference_type value() const noexcept
Definition: generator.hpp:65
Definition: generator.hpp:21
cppcoro::generator< T > generator
Definition: SampleStreams.h:10
std::ptrdiff_t difference_type
Definition: generator.hpp:100
const word U
Definition: Main.cpp:12
detail::generator_sentinel end() noexcept
Definition: generator.hpp:215
Definition: generator.hpp:18
typename generator_promise< T >::value_type value_type
Definition: generator.hpp:101
generator() noexcept
Definition: generator.hpp:175
void swap(generator< T > &a, generator< T > &b)
Definition: generator.hpp:238
typename generator_promise< T >::reference_type reference
Definition: generator.hpp:102
Definition: generator.hpp:92
generator(generator &&other) noexcept
Definition: generator.hpp:179
iterator begin()
Definition: generator.hpp:201
pointer operator->() const noexcept
Definition: generator.hpp:156
friend bool operator==(const generator_iterator &it, generator_sentinel) noexcept
Definition: generator.hpp:114
friend bool operator!=(const generator_iterator &it, generator_sentinel s) noexcept
Definition: generator.hpp:119
std::input_iterator_tag iterator_category
Definition: generator.hpp:98
~generator()
Definition: generator.hpp:187