chainreaction/src/core/generator.hpp

67 lines
1.6 KiB
C++

// https://www.scs.stanford.edu/~dm/blog/c++-coroutines.html#generic-generator-example
#include <exception>
#include <utility>
#ifdef __clang__
#include <experimental/coroutine>
namespace stdco = std::experimental;
#else
#include <coroutine>
namespace stdco = std;
#endif
template<typename T>
struct Generator
{
struct promise_type;
using handle_type = stdco::coroutine_handle<promise_type>;
struct promise_type
{
T value_;
std::exception_ptr exception_;
Generator get_return_object()
{
return Generator(handle_type::from_promise(*this));
}
stdco::suspend_always initial_suspend() { return {}; }
stdco::suspend_always final_suspend() noexcept { return {}; }
void unhandled_exception() { exception_ = std::current_exception(); }
template<std::convertible_to<T> From> // C++20 concept
stdco::suspend_always yield_value(From&& from)
{
value_ = std::forward<From>(from);
return {};
}
void return_void() {}
};
handle_type h_;
Generator(handle_type h) : h_(h) {}
~Generator() { h_.destroy(); }
explicit operator bool()
{
fill();
return !h_.done();
}
T operator()()
{
fill();
full_ = false;
return std::move(h_.promise().value_);
}
private:
bool full_ = false;
void fill()
{
if (!full_) {
h_();
if (h_.promise().exception_) std::rethrow_exception(h_.promise().exception_);
full_ = true;
}
}
};