Created
May 30, 2017 19:43
-
-
Save CaseyCarter/82b57ba186b66a605f8301431dbfd0fc to your computer and use it in GitHub Desktop.
generator type and coroutine-wrapper function for range-v3
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include <exception> | |
| #include <utility> | |
| #include <experimental/coroutine> | |
| #include <meta/meta.hpp> | |
| #include <range/v3/range_fwd.hpp> | |
| #include <range/v3/range_traits.hpp> | |
| #include <range/v3/view_facade.hpp> | |
| #include <range/v3/detail/variant.hpp> | |
| #include <range/v3/utility/concepts.hpp> | |
| #include <range/v3/utility/swap.hpp> | |
| #include <range/v3/view/all.hpp> | |
| namespace ranges | |
| { | |
| inline namespace v3 | |
| { | |
| template<class ReferenceType, class ValueType = detail::decay_t<ReferenceType>> | |
| struct generator : view_facade<generator<ReferenceType, ValueType>> | |
| { | |
| CONCEPT_ASSERT(std::is_reference<ReferenceType>::value || | |
| CopyConstructible<ReferenceType>()); | |
| using stored_ref_t = meta::if_c<std::is_reference<ReferenceType>::value, | |
| reference_wrapper< | |
| meta::_t<std::remove_reference<ReferenceType>>, | |
| std::is_rvalue_reference<ReferenceType>::value>, | |
| ReferenceType>; | |
| struct promise_type | |
| { | |
| variant<std::exception_ptr, stored_ref_t> data_; | |
| generator get_return_object() | |
| { | |
| return generator{*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 | |
| { | |
| ranges::emplace<0>(data_, std::current_exception()); | |
| } | |
| template<class U, CONCEPT_REQUIRES_(Constructible<stored_ref_t, U>())> | |
| std::experimental::suspend_always yield_value(U &&u) noexcept | |
| { | |
| ranges::emplace<1>(data_, static_cast<U &&>(u)); | |
| return {}; | |
| } | |
| }; | |
| using handle = std::experimental::coroutine_handle<promise_type>; | |
| static void advance(handle coro) | |
| { | |
| RANGES_EXPECT(coro); | |
| RANGES_EXPECT(!coro.done()); | |
| coro.resume(); | |
| } | |
| struct cursor | |
| { | |
| using value_type = ValueType; | |
| handle coro_ = nullptr; | |
| cursor() = default; | |
| constexpr explicit cursor(handle coro) noexcept | |
| : coro_{coro} | |
| {} | |
| bool equal(default_sentinel) const | |
| { | |
| RANGES_EXPECT(coro_); | |
| if (coro_.done()) | |
| { | |
| auto& data = coro_.promise().data_; | |
| if (data.index() == 0) | |
| { | |
| std::exception_ptr ptr = std::move(ranges::get<0>(data)); | |
| if (ptr) std::rethrow_exception(std::move(ptr)); | |
| } | |
| return true; | |
| } | |
| return false; | |
| } | |
| void next() | |
| { | |
| generator::advance(coro_); | |
| } | |
| ReferenceType read() const | |
| { | |
| RANGES_EXPECT(coro_); | |
| return ranges::get<1>(coro_.promise().data_); | |
| } | |
| }; | |
| ~generator() | |
| { | |
| if (coro_) coro_.destroy(); | |
| } | |
| constexpr generator() noexcept = default; | |
| explicit generator(promise_type &p) | |
| : coro_{handle::from_promise(p)} | |
| { | |
| RANGES_ASSUME(coro_); | |
| } | |
| constexpr generator(generator &&that) noexcept | |
| : coro_{ranges::exchange(that.coro_, nullptr)} | |
| {} | |
| generator &operator=(generator &&that) noexcept | |
| { | |
| ranges::exchange(*this, ranges::exchange(that, {})); | |
| return *this; | |
| } | |
| cursor begin_cursor() | |
| { | |
| generator::advance(coro_); | |
| return cursor{coro_}; | |
| } | |
| handle coro_ = nullptr; | |
| }; | |
| //#define BROKEN | |
| struct coro_fn | |
| { | |
| #ifdef BROKEN | |
| private: | |
| template<class V, CONCEPT_REQUIRES_(InputView<V>())> | |
| static generator<range_reference_t<V>, range_value_t<V>> | |
| impl(V v) | |
| { | |
| auto first = begin(v); | |
| auto const last = end(v); | |
| for (; first != last; ++first) | |
| co_yield *first; | |
| } | |
| public: | |
| template<class Rng, | |
| CONCEPT_REQUIRES_(meta::and_< | |
| meta::not_<meta::is<detail::decay_t<Rng>, generator>>, | |
| InputRange<Rng>>::value)> | |
| generator< | |
| range_reference_t<view::all_t<Rng>>, | |
| range_value_t<view::all_t<Rng>>> | |
| operator()(Rng &&rng) const | |
| { | |
| return impl(view::all(static_cast<Rng &&>(rng))); | |
| } | |
| #else | |
| template<class V, CONCEPT_REQUIRES_(InputView<V>())> | |
| generator<range_reference_t<V>, range_value_t<V>> | |
| operator()(V v) const | |
| { | |
| auto first = begin(v); | |
| auto const last = end(v); | |
| for (; first != last; ++first) | |
| co_yield *first; | |
| } | |
| #endif | |
| template<class R, class V> | |
| generator<R, V> operator()(generator<R, V> g) const noexcept | |
| { | |
| return g; | |
| } | |
| }; | |
| RANGES_INLINE_VARIABLE(coro_fn, coro) | |
| } | |
| } | |
| #include <iostream> | |
| #include <range/v3/begin_end.hpp> | |
| #include <range/v3/algorithm/copy.hpp> | |
| #include <range/v3/algorithm/count.hpp> | |
| #include <range/v3/algorithm/equal.hpp> | |
| #include <range/v3/view/filter.hpp> | |
| #include <range/v3/view/iota.hpp> | |
| #include <range/v3/view/move.hpp> | |
| #include <range/v3/view/take_exactly.hpp> | |
| auto f(int const n) | |
| RANGES_DECLTYPE_AUTO_RETURN | |
| ( | |
| ranges::coro(ranges::view::iota(0, n)) | |
| ) | |
| struct MoveInt | |
| { | |
| int i_; | |
| MoveInt(int i = 42) : i_{i} | |
| {} | |
| MoveInt(MoveInt&& that) : i_{ranges::exchange(that.i_, 0)} | |
| {} | |
| MoveInt& operator=(MoveInt&& that) | |
| { | |
| i_ = ranges::exchange(that.i_, 0); | |
| return *this; | |
| } | |
| friend bool operator==(MoveInt const& x, MoveInt const& y) | |
| { | |
| return x.i_ == y.i_; | |
| } | |
| friend bool operator!=(MoveInt const& x, MoveInt const& y) | |
| { | |
| return !(x == y); | |
| } | |
| friend std::ostream& operator<<(std::ostream& os, MoveInt const& mi) | |
| { | |
| return os << mi.i_; | |
| } | |
| }; | |
| int main() | |
| { | |
| using namespace ranges; | |
| RANGES_ASSERT(ranges::equal(f(10), {0,1,2,3,4,5,6,7,8,9})); | |
| CONCEPT_ASSERT(std::is_same<decltype(f(10)), decltype(coro(coro(coro(f(10)))))>::value); | |
| RANGES_ASSERT(ranges::equal(coro(coro(coro(f(10)))), {0,1,2,3,4,5,6,7,8,9})); | |
| auto even = [](int i){ return i % 2 == 0; }; | |
| RANGES_ASSERT(ranges::equal( | |
| coro(view::ints | view::filter(even) | view::take_exactly(10)), | |
| {0,2,4,6,8,10,12,14,16,18})); | |
| { | |
| MoveInt const control[] = {{1}, {2}, {3}}; | |
| MoveInt a[] = {{1}, {2}, {3}}; | |
| MoveInt b[3]; | |
| RANGES_ASSERT(ranges::equal(a, control)); | |
| RANGES_ASSERT(ranges::count(b, MoveInt{42}) == 3); | |
| ranges::copy(coro(view::move(a)), b); | |
| RANGES_ASSERT(ranges::count(a, MoveInt{0}) == 3); | |
| RANGES_ASSERT(ranges::equal(b, control)); | |
| } | |
| { | |
| int some_ints[] = {0,1,2}; | |
| #ifdef BROKEN | |
| auto rng = coro(some_ints); | |
| #else | |
| auto rng = coro(view::all(some_ints)); | |
| #endif | |
| auto i = ranges::begin(rng); | |
| auto e = ranges::end(rng); | |
| RANGES_ASSERT(i != e); | |
| RANGES_ASSERT(&*i == &some_ints[0]); | |
| ++i; | |
| RANGES_ASSERT(i != e); | |
| RANGES_ASSERT(&*i == &some_ints[1]); | |
| ++i; | |
| RANGES_ASSERT(i != e); | |
| RANGES_ASSERT(&*i == &some_ints[2]); | |
| ++i; | |
| RANGES_ASSERT(i == e); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment