Horizon
generator.hpp
Go to the documentation of this file.
1 // Range v3 library
3 //
4 // Copyright Casey Carter 2017
5 //
6 // Use, modification and distribution is subject to the
7 // Boost Software License, Version 1.0. (See accompanying
8 // file LICENSE_1_0.txt or copy at
9 // http://www.boost.org/LICENSE_1_0.txt)
10 //
11 // Project home: https://github.com/ericniebler/range-v3
12 //
13 #ifndef RANGES_V3_EXPERIMENTAL_UTILITY_GENERATOR_HPP
14 #define RANGES_V3_EXPERIMENTAL_UTILITY_GENERATOR_HPP
15 
16 #include <range/v3/detail/config.hpp>
17 #if RANGES_CXX_COROUTINES >= RANGES_CXX_COROUTINES_TS1
18 #include <atomic>
19 #include <cstddef>
20 #include <exception>
21 #include RANGES_COROUTINES_HEADER
22 #include <utility>
23 
24 #include <meta/meta.hpp>
25 
26 #include <concepts/concepts.hpp>
27 
28 #include <range/v3/range_fwd.hpp>
29 
32 #include <range/v3/utility/box.hpp>
35 #include <range/v3/view/all.hpp>
36 #include <range/v3/view/facade.hpp>
37 
38 #if defined(_MSC_VER) && !defined(RANGES_SILENCE_COROUTINE_WARNING)
39 #ifdef __clang__
40 #pragma message( \
41  "DANGER: clang doesn't (yet?) grok the MSVC coroutine ABI. " \
42  "Use at your own risk. " \
43  "(RANGES_SILENCE_COROUTINE_WARNING will silence this message.)")
44 #elif defined RANGES_WORKAROUND_MSVC_835948
45 #pragma message( \
46  "DANGER: ranges::experimental::generator is fine, but this " \
47  "version of MSVC likely miscompiles ranges::experimental::sized_generator. " \
48  "Use the latter at your own risk. " \
49  "(RANGES_SILENCE_COROUTINE_WARNING will silence this message.)")
50 #endif
51 #endif // RANGES_SILENCE_COROUTINE_WARNINGS
52 
53 #include <range/v3/detail/prologue.hpp>
54 
55 namespace ranges
56 {
59  namespace experimental
60  {
61  // The type of size() for a sized_generator
62  using generator_size_t = std::size_t;
63 
64  // Type upon which to co_await to set the size of a sized_generator
65  enum struct generator_size : generator_size_t
66  {
67  invalid = ~generator_size_t(0)
68  };
69 
70  template<typename Promise = void>
71  struct RANGES_EMPTY_BASES coroutine_owner;
72 
74  {
75  template<class>
76  friend struct coroutine_owner;
77  std::atomic<unsigned int> refcount_{1};
78  };
79  } // namespace experimental
80 
82  namespace detail
83  {
84  inline void resume(RANGES_COROUTINES_NS::coroutine_handle<> coro)
85  {
86  // Pre: coro refers to a suspended coroutine.
87  RANGES_EXPECT(coro);
88  RANGES_EXPECT(!coro.done());
89  coro.resume();
90  }
91 
92  namespace coroutine_owner_
93  {
94  struct adl_hook
95  {};
96 
97  template<typename Promise>
98  void swap(experimental::coroutine_owner<Promise> & x,
99  experimental::coroutine_owner<Promise> & y) noexcept
100  {
101  x.swap(y);
102  }
103  } // namespace coroutine_owner_
104  } // namespace detail
106 
107  namespace experimental
108  {
109  // An owning coroutine_handle
110  template<typename Promise>
111  struct RANGES_EMPTY_BASES coroutine_owner
112  : private RANGES_COROUTINES_NS::coroutine_handle<Promise>
113  , private detail::coroutine_owner_::adl_hook
114  {
115  CPP_assert(derived_from<Promise, enable_coroutine_owner>);
116  using base_t = RANGES_COROUTINES_NS::coroutine_handle<Promise>;
117 
118  using base_t::operator bool;
119  using base_t::done;
120  using base_t::promise;
121 
122  coroutine_owner() = default;
123  constexpr explicit coroutine_owner(base_t coro) noexcept
124  : base_t(coro)
125  {}
126  coroutine_owner(coroutine_owner && that) noexcept
127  : base_t(ranges::exchange(that.base(), {}))
128  , copied_(that.copied_.load(std::memory_order_relaxed))
129  {}
130  coroutine_owner(coroutine_owner const & that) noexcept
131  : base_t(that.handle())
132  , copied_(that.handle() != nullptr)
133  {
134  if(*this)
135  {
136  that.copied_.store(true, std::memory_order_relaxed);
137  base().promise().refcount_.fetch_add(1, std::memory_order_relaxed);
138  }
139  }
140  ~coroutine_owner()
141  {
142  if(base() && (!copied_.load(std::memory_order_relaxed) ||
143  1 == base().promise().refcount_.fetch_sub(
144  1, std::memory_order_acq_rel)))
145  base().destroy();
146  }
147  coroutine_owner & operator=(coroutine_owner that) noexcept
148  {
149  swap(that);
150  return *this;
151  }
152  void resume()
153  {
154  detail::resume(handle());
155  }
156  void operator()()
157  {
158  detail::resume(handle());
159  }
160  void swap(coroutine_owner & that) noexcept
161  {
162  bool tmp = copied_.load(std::memory_order_relaxed);
163  copied_.store(that.copied_.load(std::memory_order_relaxed),
164  std::memory_order_relaxed);
165  that.copied_.store(tmp, std::memory_order_relaxed);
166  std::swap(base(), that.base());
167  }
168  base_t handle() const noexcept
169  {
170  return *this;
171  }
172 
173  private:
174  mutable std::atomic<bool> copied_{false};
175 
176  base_t & base() noexcept
177  {
178  return *this;
179  }
180  };
181  } // namespace experimental
182 
184  namespace detail
185  {
186  template<typename Reference>
187  struct generator_promise : experimental::enable_coroutine_owner
188  {
189  std::exception_ptr except_ = nullptr;
190 
191  CPP_assert(std::is_reference<Reference>::value ||
192  copy_constructible<Reference>);
193 
194  generator_promise * get_return_object() noexcept
195  {
196  return this;
197  }
198  RANGES_COROUTINES_NS::suspend_always initial_suspend() const noexcept
199  {
200  return {};
201  }
202  RANGES_COROUTINES_NS::suspend_always final_suspend() const noexcept
203  {
204  return {};
205  }
206  void return_void() const noexcept
207  {}
208  void unhandled_exception() noexcept
209  {
210  except_ = std::current_exception();
211  RANGES_EXPECT(except_);
212  }
213  template(typename Arg)(
214  requires convertible_to<Arg, Reference> AND
215  std::is_assignable<semiregular_box_t<Reference> &, Arg>::value) //
216  RANGES_COROUTINES_NS::suspend_always yield_value(Arg && arg) noexcept(
217  std::is_nothrow_assignable<semiregular_box_t<Reference> &, Arg>::value)
218  {
219  ref_ = std::forward<Arg>(arg);
220  return {};
221  }
222  RANGES_COROUTINES_NS::suspend_never await_transform(
223  experimental::generator_size) const noexcept
224  {
225  RANGES_ENSURE_MSG(false,
226  "Invalid size request for a non-sized generator");
227  return {};
228  }
229  meta::if_<std::is_reference<Reference>, Reference, Reference const &> read()
230  const noexcept
231  {
232  return ref_;
233  }
234 
235  private:
236  semiregular_box_t<Reference> ref_;
237  };
238 
239  template<typename Reference>
240  struct sized_generator_promise : generator_promise<Reference>
241  {
242  sized_generator_promise * get_return_object() noexcept
243  {
244  return this;
245  }
246  RANGES_COROUTINES_NS::suspend_never initial_suspend() const noexcept
247  {
248  // sized_generator doesn't suspend at its initial suspend point because...
249  return {};
250  }
251  RANGES_COROUTINES_NS::suspend_always await_transform(
252  experimental::generator_size size) noexcept
253  {
254  // ...we need the coroutine set the size of the range first by
255  // co_awaiting on a generator_size.
256  size_ = size;
257  return {};
258  }
259  experimental::generator_size_t size() const noexcept
260  {
261  RANGES_EXPECT(size_ != experimental::generator_size::invalid);
262  return static_cast<experimental::generator_size_t>(size_);
263  }
264 
265  private:
266  experimental::generator_size size_ = experimental::generator_size::invalid;
267  };
268  } // namespace detail
270 
271  namespace experimental
272  {
273  template<typename Reference, typename Value = uncvref_t<Reference>>
274  struct sized_generator;
275 
276  template<typename Reference, typename Value = uncvref_t<Reference>>
277  struct generator : view_facade<generator<Reference, Value>>
278  {
279  using promise_type = detail::generator_promise<Reference>;
280 
281  constexpr generator() noexcept = default;
282  generator(promise_type * p)
283  : coro_{handle::from_promise(*p)}
284  {
285  RANGES_EXPECT(coro_);
286  }
287 
288  private:
289  friend range_access;
290  friend struct sized_generator<Reference, Value>;
291  using handle = RANGES_COROUTINES_NS::coroutine_handle<promise_type>;
293 
294  struct cursor
295  {
296  using value_type = Value;
297 
298  cursor() = default;
299  constexpr explicit cursor(handle coro) noexcept
300  : coro_{coro}
301  {}
302  bool equal(default_sentinel_t) const
303  {
304  RANGES_EXPECT(coro_);
305  if(coro_.done())
306  {
307  auto & e = coro_.promise().except_;
308  if(e)
309  std::rethrow_exception(std::move(e));
310  return true;
311  }
312  return false;
313  }
314  void next()
315  {
316  detail::resume(coro_);
317  }
318  Reference read() const
319  {
320  RANGES_EXPECT(coro_);
321  return coro_.promise().read();
322  }
323 
324  private:
325  handle coro_ = nullptr;
326  };
327 
328  cursor begin_cursor()
329  {
330  detail::resume(coro_.handle());
331  return cursor{coro_.handle()};
332  }
333  };
334 
335  template<typename Reference, typename Value /* = uncvref_t<Reference>*/>
336  struct sized_generator : generator<Reference, Value>
337  {
338  using promise_type = detail::sized_generator_promise<Reference>;
339  using handle = RANGES_COROUTINES_NS::coroutine_handle<promise_type>;
340 
341  constexpr sized_generator() noexcept = default;
342  sized_generator(promise_type * p)
344  {}
345  generator_size_t size() const noexcept
346  {
347  return promise().size();
348  }
349 
350  private:
352 
353  promise_type const & promise() const noexcept
354  {
355  RANGES_EXPECT(coro_);
356  return static_cast<promise_type const &>(coro_.promise());
357  }
358  };
359  } // namespace experimental
360 
362 } // namespace ranges
363 
364 #include <range/v3/detail/epilogue.hpp>
365 
366 #endif // RANGES_CXX_COROUTINES >= RANGES_CXX_COROUTINES_TS1
367 
368 #endif // RANGES_V3_EXPERIMENTAL_UTILITY_GENERATOR_HPP
std::integral_constant< std::size_t, N > size_t
An integral constant wrapper for std::size_t.
Definition: meta.hpp:163
meta::size_t< L::size()> size
An integral constant wrapper that is the size of the meta::list L.
Definition: meta.hpp:1696
_t< detail::_if_< list< Args... > >> if_
Select one type or another depending on a compile-time Boolean.
Definition: meta.hpp:1247
Tiny meta-programming library.
Definition: default_sentinel.hpp:26
Definition: generator.hpp:114
Definition: generator.hpp:278
Definition: generator.hpp:337
A utility for constructing a view from a (derived) type that implements begin and end cursors.
Definition: facade.hpp:66