Skip to content

Instantly share code, notes, and snippets.

@CaseyCarter
Created May 30, 2017 19:43
Show Gist options
  • Select an option

  • Save CaseyCarter/82b57ba186b66a605f8301431dbfd0fc to your computer and use it in GitHub Desktop.

Select an option

Save CaseyCarter/82b57ba186b66a605f8301431dbfd0fc to your computer and use it in GitHub Desktop.
generator type and coroutine-wrapper function for range-v3
#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