|
- /// \file
- // Range v3 library
- //
- // Copyright Casey Carter 2017
- //
- // Use, modification and distribution is subject to the
- // Boost Software License, Version 1.0. (See accompanying
- // file LICENSE_1_0.txt or copy at
- // http://www.boost.org/LICENSE_1_0.txt)
- //
- // Project home: https://github.com/ericniebler/range-v3
- //
- #ifndef RANGES_V3_EXPERIMENTAL_UTILITY_GENERATOR_HPP
- #define RANGES_V3_EXPERIMENTAL_UTILITY_GENERATOR_HPP
-
- #include <range/v3/detail/config.hpp>
- #if RANGES_CXX_COROUTINES >= RANGES_CXX_COROUTINES_TS1
- #include <atomic>
- #include <cstddef>
- #include <exception>
- #include <experimental/coroutine>
- #include <utility>
-
- #include <meta/meta.hpp>
-
- #include <concepts/concepts.hpp>
-
- #include <range/v3/range_fwd.hpp>
-
- #include <range/v3/iterator/default_sentinel.hpp>
- #include <range/v3/range/traits.hpp>
- #include <range/v3/utility/box.hpp>
- #include <range/v3/utility/semiregular_box.hpp>
- #include <range/v3/utility/swap.hpp>
- #include <range/v3/view/all.hpp>
- #include <range/v3/view/facade.hpp>
-
- #if defined(_MSC_VER) && !defined(RANGES_SILENCE_COROUTINE_WARNING)
- #ifdef __clang__
- #pragma message( \
- "DANGER: clang doesn't (yet?) grok the MSVC coroutine ABI. " \
- "Use at your own risk. " \
- "(RANGES_SILENCE_COROUTINE_WARNING will silence this message.)")
- #elif defined RANGES_WORKAROUND_MSVC_835948
- #pragma message( \
- "DANGER: ranges::experimental::generator is fine, but this " \
- "version of MSVC likely miscompiles ranges::experimental::sized_generator. " \
- "Use the latter at your own risk. " \
- "(RANGES_SILENCE_COROUTINE_WARNING will silence this message.)")
- #endif
- #endif // RANGES_SILENCE_COROUTINE_WARNINGS
-
- namespace ranges
- {
- /// \addtogroup group-view
- /// @{
- namespace experimental
- {
- // The type of size() for a sized_generator
- using generator_size_t = std::size_t;
-
- // Type upon which to co_await to set the size of a sized_generator
- enum struct generator_size : generator_size_t
- {
- invalid = ~generator_size_t(0)
- };
-
- template<typename Promise = void>
- struct RANGES_EMPTY_BASES coroutine_owner;
-
- class enable_coroutine_owner
- {
- template<class>
- friend struct coroutine_owner;
- std::atomic<unsigned int> refcount_{1};
- };
- } // namespace experimental
-
- /// \cond
- namespace detail
- {
- inline void resume(std::experimental::coroutine_handle<> coro)
- {
- // Pre: coro refers to a suspended coroutine.
- RANGES_EXPECT(coro);
- RANGES_EXPECT(!coro.done());
- coro.resume();
- }
-
- namespace coroutine_owner_
- {
- struct adl_hook
- {};
-
- template<typename Promise>
- void swap(experimental::coroutine_owner<Promise> & x,
- experimental::coroutine_owner<Promise> & y) noexcept
- {
- x.swap(y);
- }
- } // namespace coroutine_owner_
- } // namespace detail
- /// \endcond
-
- namespace experimental
- {
- // An owning coroutine_handle
- template<typename Promise>
- struct RANGES_EMPTY_BASES coroutine_owner
- : private std::experimental::coroutine_handle<Promise>
- , private detail::coroutine_owner_::adl_hook
- {
- CPP_assert(derived_from<Promise, enable_coroutine_owner>);
- using base_t = std::experimental::coroutine_handle<Promise>;
-
- using base_t::operator bool;
- using base_t::done;
- using base_t::promise;
-
- coroutine_owner() = default;
- explicit constexpr coroutine_owner(base_t coro) noexcept
- : base_t(coro)
- {}
- coroutine_owner(coroutine_owner && that) noexcept
- : base_t(ranges::exchange(that.base(), {}))
- , copied_(that.copied_.load(std::memory_order_relaxed))
- {}
- coroutine_owner(coroutine_owner const & that) noexcept
- : base_t(that.handle())
- , copied_(that.handle() != nullptr)
- {
- if(*this)
- {
- that.copied_.store(true, std::memory_order_relaxed);
- base().promise().refcount_.fetch_add(1, std::memory_order_relaxed);
- }
- }
- ~coroutine_owner()
- {
- if(base() && (!copied_.load(std::memory_order_relaxed) ||
- 1 == base().promise().refcount_.fetch_sub(
- 1, std::memory_order_acq_rel)))
- base().destroy();
- }
- coroutine_owner & operator=(coroutine_owner that) noexcept
- {
- swap(that);
- return *this;
- }
- void resume()
- {
- detail::resume(handle());
- }
- void operator()()
- {
- detail::resume(handle());
- }
- void swap(coroutine_owner & that) noexcept
- {
- bool tmp = copied_.load(std::memory_order_relaxed);
- copied_.store(that.copied_.load(std::memory_order_relaxed),
- std::memory_order_relaxed);
- that.copied_.store(tmp, std::memory_order_relaxed);
- std::swap(base(), that.base());
- }
- base_t handle() const noexcept
- {
- return *this;
- }
-
- private:
- std::atomic<bool> copied_{false};
-
- base_t & base() noexcept
- {
- return *this;
- }
- };
- } // namespace experimental
-
- /// \cond
- namespace detail
- {
- template<typename Reference>
- struct generator_promise : experimental::enable_coroutine_owner
- {
- std::exception_ptr except_ = nullptr;
-
- CPP_assert(std::is_reference<Reference>::value ||
- copy_constructible<Reference>);
-
- generator_promise * get_return_object() noexcept
- {
- return this;
- }
- std::experimental::suspend_always initial_suspend() const noexcept
- {
- return {};
- }
- std::experimental::suspend_always final_suspend() const noexcept
- {
- return {};
- }
- void return_void() const noexcept
- {}
- void unhandled_exception() noexcept
- {
- except_ = std::current_exception();
- RANGES_EXPECT(except_);
- }
- template<typename Arg>
- auto yield_value(Arg && arg) noexcept(
- std::is_nothrow_assignable<semiregular_box_t<Reference> &, Arg>::value)
- -> CPP_ret(std::experimental::suspend_always)( //
- requires convertible_to<Arg, Reference> &&
- std::is_assignable<semiregular_box_t<Reference> &, Arg>::value)
- {
- ref_ = std::forward<Arg>(arg);
- return {};
- }
- std::experimental::suspend_never await_transform(
- experimental::generator_size) const noexcept
- {
- RANGES_ENSURE_MSG(false,
- "Invalid size request for a non-sized generator");
- return {};
- }
- meta::if_<std::is_reference<Reference>, Reference, Reference const &> read()
- const noexcept
- {
- return ref_;
- }
-
- private:
- semiregular_box_t<Reference> ref_;
- };
-
- template<typename Reference>
- struct sized_generator_promise : generator_promise<Reference>
- {
- sized_generator_promise * get_return_object() noexcept
- {
- return this;
- }
- std::experimental::suspend_never initial_suspend() const noexcept
- {
- // sized_generator doesn't suspend at its initial suspend point because...
- return {};
- }
- std::experimental::suspend_always await_transform(
- experimental::generator_size size) noexcept
- {
- // ...we need the coroutine set the size of the range first by
- // co_awaiting on a generator_size.
- size_ = size;
- return {};
- }
- experimental::generator_size_t size() const noexcept
- {
- RANGES_EXPECT(size_ != experimental::generator_size::invalid);
- return static_cast<experimental::generator_size_t>(size_);
- }
-
- private:
- experimental::generator_size size_ = experimental::generator_size::invalid;
- };
- } // namespace detail
- /// \endcond
-
- namespace experimental
- {
- template<typename Reference, typename Value = uncvref_t<Reference>>
- struct sized_generator;
-
- template<typename Reference, typename Value = uncvref_t<Reference>>
- struct generator : view_facade<generator<Reference, Value>>
- {
- using promise_type = detail::generator_promise<Reference>;
-
- constexpr generator() noexcept = default;
- generator(promise_type * p)
- : coro_{handle::from_promise(*p)}
- {
- RANGES_EXPECT(coro_);
- }
-
- private:
- friend range_access;
- friend struct sized_generator<Reference, Value>;
- using handle = std::experimental::coroutine_handle<promise_type>;
- coroutine_owner<promise_type> coro_;
-
- struct cursor
- {
- using value_type = Value;
-
- cursor() = default;
- constexpr explicit cursor(handle coro) noexcept
- : coro_{coro}
- {}
- bool equal(default_sentinel_t) const
- {
- RANGES_EXPECT(coro_);
- if(coro_.done())
- {
- auto & e = coro_.promise().except_;
- if(e)
- std::rethrow_exception(std::move(e));
- return true;
- }
- return false;
- }
- void next()
- {
- detail::resume(coro_);
- }
- Reference read() const
- {
- RANGES_EXPECT(coro_);
- return coro_.promise().read();
- }
-
- private:
- handle coro_ = nullptr;
- };
-
- cursor begin_cursor()
- {
- detail::resume(coro_.handle());
- return cursor{coro_.handle()};
- }
- };
-
- template<typename Reference, typename Value /* = uncvref_t<Reference>*/>
- struct sized_generator : generator<Reference, Value>
- {
- using promise_type = detail::sized_generator_promise<Reference>;
- using handle = std::experimental::coroutine_handle<promise_type>;
-
- constexpr sized_generator() noexcept = default;
- sized_generator(promise_type * p)
- : generator<Reference, Value>{p}
- {}
- generator_size_t size() const noexcept
- {
- return promise().size();
- }
-
- private:
- using generator<Reference, Value>::coro_;
-
- promise_type const & promise() const noexcept
- {
- RANGES_EXPECT(coro_);
- return static_cast<promise_type const &>(coro_.promise());
- }
- };
- } // namespace experimental
-
- /// @}
- } // namespace ranges
- #endif // RANGES_CXX_COROUTINES >= RANGES_CXX_COROUTINES_TS1
-
- #endif // RANGES_V3_EXPERIMENTAL_UTILITY_GENERATOR_HPP
|