Horizon
Loading...
Searching...
No Matches
generator.hpp
Go to the documentation of this file.
1
2// 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
29
35#include <range/v3/view/all.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
55namespace 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>;
292 coroutine_owner<promise_type> coro_;
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)
343 : generator<Reference, Value>{p}
344 {}
345 generator_size_t size() const noexcept
346 {
347 return promise().size();
348 }
349
350 private:
351 using generator<Reference, Value>::coro_;
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
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.