2017-03-04 17:25:09 +00:00
|
|
|
/* Coroutines for OctaSTD.
|
|
|
|
*
|
|
|
|
* This file is part of OctaSTD. See COPYING.md for further information.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef OSTD_COROUTINE_HH
|
|
|
|
#define OSTD_COROUTINE_HH
|
|
|
|
|
|
|
|
#include <stdexcept>
|
|
|
|
#include <utility>
|
|
|
|
#include <tuple>
|
2017-03-05 22:48:51 +00:00
|
|
|
#include <type_traits>
|
2017-03-05 23:47:15 +00:00
|
|
|
#include <optional>
|
2017-03-04 17:25:09 +00:00
|
|
|
|
|
|
|
#include "ostd/types.hh"
|
2017-03-05 23:47:15 +00:00
|
|
|
#include "ostd/range.hh"
|
2017-03-05 15:48:44 +00:00
|
|
|
|
2017-03-07 20:29:12 +00:00
|
|
|
#include "ostd/internal/context.hh"
|
|
|
|
|
2017-03-04 17:25:09 +00:00
|
|
|
namespace ostd {
|
|
|
|
|
|
|
|
struct coroutine_error: std::runtime_error {
|
|
|
|
using std::runtime_error::runtime_error;
|
|
|
|
};
|
|
|
|
|
2017-03-07 01:18:22 +00:00
|
|
|
template<typename T>
|
|
|
|
struct coroutine;
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
struct coroutine_range;
|
|
|
|
|
2017-03-05 15:48:44 +00:00
|
|
|
namespace detail {
|
2017-03-05 22:48:51 +00:00
|
|
|
/* like reference_wrapper but for any value */
|
|
|
|
template<typename T>
|
|
|
|
struct arg_wrapper {
|
|
|
|
arg_wrapper() = default;
|
|
|
|
arg_wrapper(T arg): p_arg(std::move(arg)) {}
|
|
|
|
|
|
|
|
void operator=(T arg) {
|
|
|
|
p_arg = std::move(arg);
|
|
|
|
}
|
|
|
|
operator T &&() {
|
|
|
|
return std::move(p_arg);
|
|
|
|
}
|
|
|
|
|
2017-03-06 17:37:33 +00:00
|
|
|
void swap(arg_wrapper &other) {
|
2017-03-07 16:33:56 +00:00
|
|
|
using std::swap;
|
|
|
|
swap(p_arg, other.p_arg);
|
2017-03-06 17:37:33 +00:00
|
|
|
}
|
|
|
|
|
2017-03-05 22:48:51 +00:00
|
|
|
private:
|
|
|
|
T p_arg = T{};
|
|
|
|
};
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
struct arg_wrapper<T &&> {
|
|
|
|
arg_wrapper() = default;
|
|
|
|
arg_wrapper(T &&arg): p_arg(&arg) {}
|
|
|
|
|
|
|
|
void operator=(T &&arg) {
|
|
|
|
p_arg = &arg;
|
|
|
|
}
|
|
|
|
operator T &&() {
|
|
|
|
return *p_arg;
|
|
|
|
}
|
|
|
|
|
2017-03-06 17:37:33 +00:00
|
|
|
void swap(arg_wrapper &other) {
|
2017-03-07 16:33:56 +00:00
|
|
|
using std::swap;
|
|
|
|
swap(p_arg, other.p_arg);
|
2017-03-06 17:37:33 +00:00
|
|
|
}
|
|
|
|
|
2017-03-05 22:48:51 +00:00
|
|
|
private:
|
|
|
|
T *p_arg = nullptr;
|
|
|
|
};
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
struct arg_wrapper<T &> {
|
|
|
|
arg_wrapper() = default;
|
|
|
|
arg_wrapper(T &arg): p_arg(&arg) {}
|
|
|
|
|
|
|
|
void operator=(T &arg) {
|
|
|
|
p_arg = &arg;
|
|
|
|
}
|
|
|
|
operator T &() {
|
|
|
|
return *p_arg;
|
|
|
|
}
|
|
|
|
|
2017-03-06 17:37:33 +00:00
|
|
|
void swap(arg_wrapper &other) {
|
2017-03-07 16:33:56 +00:00
|
|
|
using std::swap;
|
|
|
|
swap(p_arg, other.p_arg);
|
2017-03-06 17:37:33 +00:00
|
|
|
}
|
|
|
|
|
2017-03-05 22:48:51 +00:00
|
|
|
private:
|
|
|
|
T *p_arg = nullptr;
|
|
|
|
};
|
|
|
|
|
2017-03-06 17:37:33 +00:00
|
|
|
template<typename T>
|
|
|
|
inline void swap(arg_wrapper<T> &a, arg_wrapper<T> &b) {
|
|
|
|
a.swap(b);
|
|
|
|
}
|
|
|
|
|
2017-03-05 18:29:44 +00:00
|
|
|
template<typename ...A>
|
|
|
|
struct coro_types {
|
2017-03-05 22:48:51 +00:00
|
|
|
using yield_type = std::tuple<A...>;
|
2017-03-05 18:29:44 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
template<typename A>
|
|
|
|
struct coro_types<A> {
|
2017-03-05 22:48:51 +00:00
|
|
|
using yield_type = A;
|
2017-03-05 18:29:44 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
template<typename A, typename B>
|
|
|
|
struct coro_types<A, B> {
|
2017-03-05 22:48:51 +00:00
|
|
|
using yield_type = std::pair<A, B>;
|
2017-03-05 18:29:44 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
template<typename ...A>
|
|
|
|
using coro_args = typename coro_types<A...>::yield_type;
|
|
|
|
|
|
|
|
template<typename ...A, size_t ...I>
|
|
|
|
inline coro_args<A...> yield_ret(
|
2017-03-05 22:48:51 +00:00
|
|
|
std::tuple<arg_wrapper<A>...> &args, std::index_sequence<I...>
|
2017-03-05 18:29:44 +00:00
|
|
|
) {
|
|
|
|
if constexpr(sizeof...(A) == 1) {
|
2017-03-05 22:48:51 +00:00
|
|
|
return std::forward<A...>(std::get<0>(args));
|
2017-03-05 18:29:44 +00:00
|
|
|
} else if constexpr(sizeof...(A) == 2) {
|
|
|
|
return std::make_pair(std::forward<A>(std::get<I>(args))...);
|
|
|
|
} else {
|
|
|
|
return std::move(args);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-05 19:11:39 +00:00
|
|
|
/* default case, yield returns args and takes a value */
|
2017-03-04 17:25:09 +00:00
|
|
|
template<typename R, typename ...A>
|
2017-03-07 01:18:22 +00:00
|
|
|
struct coro_base: coroutine_context {
|
2017-03-06 17:37:33 +00:00
|
|
|
protected:
|
|
|
|
struct yielder {
|
|
|
|
yielder(coro_base<R, A...> &coro): p_coro(coro) {}
|
|
|
|
|
|
|
|
coro_args<A...> operator()(R &&ret) {
|
|
|
|
p_coro.p_result = std::forward<R>(ret);
|
2017-03-07 01:07:13 +00:00
|
|
|
p_coro.yield_jump();
|
2017-03-06 17:37:33 +00:00
|
|
|
return yield_ret(
|
|
|
|
p_coro.p_args, std::make_index_sequence<sizeof...(A)>{}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
private:
|
|
|
|
coro_base<R, A...> &p_coro;
|
|
|
|
};
|
|
|
|
|
|
|
|
template<typename F, size_t ...I>
|
|
|
|
void call_helper(F &func, std::index_sequence<I...>) {
|
2017-03-05 22:07:00 +00:00
|
|
|
p_result = std::forward<R>(
|
2017-03-06 17:37:33 +00:00
|
|
|
func(yielder{*this}, std::forward<A>(std::get<I>(p_args))...)
|
2017-03-05 22:07:00 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
R call(A ...args) {
|
2017-03-05 22:48:51 +00:00
|
|
|
p_args = std::make_tuple(arg_wrapper<A>(std::forward<A>(args))...);
|
2017-03-07 01:18:22 +00:00
|
|
|
coroutine_context::call();
|
2017-03-05 22:48:51 +00:00
|
|
|
return std::forward<R>(p_result);
|
2017-03-05 22:07:00 +00:00
|
|
|
}
|
|
|
|
|
2017-03-06 17:37:33 +00:00
|
|
|
void swap(coro_base &other) {
|
2017-03-07 16:33:56 +00:00
|
|
|
using std::swap;
|
|
|
|
swap(p_args, other.p_args);
|
|
|
|
swap(p_result, other.p_result);
|
2017-03-07 01:18:22 +00:00
|
|
|
coroutine_context::swap(other);
|
2017-03-06 17:37:33 +00:00
|
|
|
}
|
|
|
|
|
2017-03-05 22:48:51 +00:00
|
|
|
std::tuple<arg_wrapper<A>...> p_args;
|
|
|
|
arg_wrapper<R> p_result;
|
2017-03-04 17:25:09 +00:00
|
|
|
};
|
|
|
|
|
2017-03-05 19:11:39 +00:00
|
|
|
/* yield takes a value but doesn't return any args */
|
|
|
|
template<typename R>
|
2017-03-07 01:18:22 +00:00
|
|
|
struct coro_base<R>: coroutine_context {
|
2017-03-06 18:01:17 +00:00
|
|
|
coroutine_range<R> iter();
|
|
|
|
|
2017-03-06 17:37:33 +00:00
|
|
|
protected:
|
|
|
|
struct yielder {
|
|
|
|
yielder(coro_base<R> &coro): p_coro(coro) {}
|
|
|
|
|
|
|
|
void operator()(R &&ret) {
|
|
|
|
p_coro.p_result = std::forward<R>(ret);
|
2017-03-07 01:07:13 +00:00
|
|
|
p_coro.yield_jump();
|
2017-03-06 17:37:33 +00:00
|
|
|
}
|
|
|
|
private:
|
|
|
|
coro_base<R> &p_coro;
|
|
|
|
};
|
|
|
|
|
|
|
|
template<typename F, size_t ...I>
|
|
|
|
void call_helper(F &func, std::index_sequence<I...>) {
|
|
|
|
p_result = std::forward<R>(func(yielder{*this}));
|
2017-03-05 22:07:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
R call() {
|
2017-03-07 01:18:22 +00:00
|
|
|
coroutine_context::call();
|
2017-03-05 22:07:00 +00:00
|
|
|
return std::forward<R>(this->p_result);
|
|
|
|
}
|
|
|
|
|
2017-03-06 17:37:33 +00:00
|
|
|
void swap(coro_base &other) {
|
2017-03-07 16:33:56 +00:00
|
|
|
using std::swap;
|
|
|
|
swap(p_result, other.p_result);
|
2017-03-07 01:18:22 +00:00
|
|
|
coroutine_context::swap(other);
|
2017-03-06 17:37:33 +00:00
|
|
|
}
|
|
|
|
|
2017-03-05 22:48:51 +00:00
|
|
|
arg_wrapper<R> p_result;
|
2017-03-05 19:11:39 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/* yield doesn't take a value and returns args */
|
2017-03-04 17:25:09 +00:00
|
|
|
template<typename ...A>
|
2017-03-07 01:18:22 +00:00
|
|
|
struct coro_base<void, A...>: coroutine_context {
|
2017-03-06 17:37:33 +00:00
|
|
|
protected:
|
|
|
|
struct yielder {
|
|
|
|
yielder(coro_base<void, A...> &coro): p_coro(coro) {}
|
|
|
|
|
|
|
|
coro_args<A...> operator()() {
|
2017-03-07 01:07:13 +00:00
|
|
|
p_coro.yield_jump();
|
2017-03-06 17:37:33 +00:00
|
|
|
return yield_ret(
|
|
|
|
p_coro.p_args, std::make_index_sequence<sizeof...(A)>{}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
private:
|
|
|
|
coro_base<void, A...> &p_coro;
|
|
|
|
};
|
|
|
|
|
|
|
|
template<typename F, size_t ...I>
|
|
|
|
void call_helper(F &func, std::index_sequence<I...>) {
|
|
|
|
func(yielder{*this}, std::forward<A>(std::get<I>(p_args))...);
|
2017-03-05 22:07:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void call(A ...args) {
|
2017-03-05 22:48:51 +00:00
|
|
|
p_args = std::make_tuple(arg_wrapper<A>(std::forward<A>(args))...);
|
2017-03-07 01:18:22 +00:00
|
|
|
coroutine_context::call();
|
2017-03-05 22:07:00 +00:00
|
|
|
}
|
|
|
|
|
2017-03-06 17:37:33 +00:00
|
|
|
void swap(coro_base &other) {
|
2017-03-07 16:33:56 +00:00
|
|
|
using std::swap;
|
|
|
|
swap(p_args, other.p_args);
|
2017-03-07 01:18:22 +00:00
|
|
|
coroutine_context::swap(other);
|
2017-03-06 17:37:33 +00:00
|
|
|
}
|
|
|
|
|
2017-03-05 22:48:51 +00:00
|
|
|
std::tuple<arg_wrapper<A>...> p_args;
|
2017-03-04 17:25:09 +00:00
|
|
|
};
|
2017-03-05 19:11:39 +00:00
|
|
|
|
|
|
|
/* yield doesn't take a value or return any args */
|
|
|
|
template<>
|
2017-03-07 01:18:22 +00:00
|
|
|
struct coro_base<void>: coroutine_context {
|
2017-03-06 17:37:33 +00:00
|
|
|
protected:
|
|
|
|
struct yielder {
|
|
|
|
yielder(coro_base<void> &coro): p_coro(coro) {}
|
|
|
|
|
|
|
|
void operator()() {
|
2017-03-07 01:07:13 +00:00
|
|
|
p_coro.yield_jump();
|
2017-03-06 17:37:33 +00:00
|
|
|
}
|
|
|
|
private:
|
|
|
|
coro_base<void> &p_coro;
|
|
|
|
};
|
|
|
|
|
|
|
|
template<typename F, size_t ...I>
|
|
|
|
void call_helper(F &func, std::index_sequence<I...>) {
|
|
|
|
func(yielder{*this});
|
2017-03-05 22:07:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void call() {
|
2017-03-07 01:18:22 +00:00
|
|
|
coroutine_context::call();
|
2017-03-05 22:07:00 +00:00
|
|
|
}
|
|
|
|
|
2017-03-06 17:37:33 +00:00
|
|
|
void swap(coro_base &other) {
|
2017-03-07 01:18:22 +00:00
|
|
|
coroutine_context::swap(other);
|
2017-03-06 17:37:33 +00:00
|
|
|
}
|
2017-03-05 19:11:39 +00:00
|
|
|
};
|
2017-03-04 17:25:09 +00:00
|
|
|
} /* namespace detail */
|
|
|
|
|
|
|
|
template<typename R, typename ...A>
|
|
|
|
struct coroutine<R(A...)>: detail::coro_base<R, A...> {
|
2017-03-06 17:37:33 +00:00
|
|
|
private:
|
|
|
|
using base_t = detail::coro_base<R, A...>;
|
|
|
|
|
|
|
|
public:
|
|
|
|
using yield_type = typename detail::coro_base<R, A...>::yielder;
|
|
|
|
|
2017-03-07 16:33:56 +00:00
|
|
|
/* we have no way to assign a function anyway... */
|
|
|
|
coroutine() = delete;
|
|
|
|
|
2017-03-07 21:22:22 +00:00
|
|
|
/* 0 means default size decided by the stack allocator */
|
2017-03-05 18:50:25 +00:00
|
|
|
template<typename F>
|
2017-03-07 21:22:22 +00:00
|
|
|
coroutine(F func, size_t ss = 0): base_t(), p_func(std::move(func))
|
2017-03-07 01:18:22 +00:00
|
|
|
{
|
2017-03-07 16:33:56 +00:00
|
|
|
/* that way there is no context creation/stack allocation */
|
2017-03-07 01:18:22 +00:00
|
|
|
if (!p_func) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
this->make_context(ss, &context_call);
|
|
|
|
}
|
2017-03-04 17:25:09 +00:00
|
|
|
|
2017-03-06 17:37:33 +00:00
|
|
|
coroutine(coroutine const &) = delete;
|
2017-03-07 00:12:52 +00:00
|
|
|
coroutine(coroutine &&c):
|
|
|
|
detail::coro_base<R, A...>(std::move(c)), p_func(std::move(c.p_func))
|
|
|
|
{
|
|
|
|
c.p_func = nullptr;
|
|
|
|
}
|
2017-03-06 17:37:33 +00:00
|
|
|
|
|
|
|
coroutine &operator=(coroutine const &) = delete;
|
2017-03-07 00:12:52 +00:00
|
|
|
coroutine &operator=(coroutine &&c) {
|
|
|
|
base_t::operator=(std::move(c));
|
|
|
|
p_func = std::move(c.p_func);
|
|
|
|
c.p_func = nullptr;
|
|
|
|
}
|
2017-03-06 17:37:33 +00:00
|
|
|
|
2017-03-05 15:48:44 +00:00
|
|
|
~coroutine() {
|
2017-03-07 00:12:52 +00:00
|
|
|
if (!p_func) {
|
2017-03-07 16:33:56 +00:00
|
|
|
/* the stack has already unwound by a normal return */
|
2017-03-07 00:12:52 +00:00
|
|
|
return;
|
|
|
|
}
|
2017-03-07 01:07:13 +00:00
|
|
|
this->unwind();
|
2017-03-05 15:48:44 +00:00
|
|
|
}
|
|
|
|
|
2017-03-07 16:33:56 +00:00
|
|
|
explicit operator bool() const {
|
2017-03-07 00:12:52 +00:00
|
|
|
return bool(p_func);
|
2017-03-04 17:25:09 +00:00
|
|
|
}
|
|
|
|
|
2017-03-06 17:37:33 +00:00
|
|
|
R resume(A ...args) {
|
2017-03-07 00:12:52 +00:00
|
|
|
if (!p_func) {
|
|
|
|
throw coroutine_error{"dead coroutine"};
|
|
|
|
}
|
2017-03-06 17:37:33 +00:00
|
|
|
return this->call(std::forward<A>(args)...);
|
|
|
|
}
|
|
|
|
|
2017-03-04 17:25:09 +00:00
|
|
|
R operator()(A ...args) {
|
2017-03-07 00:12:52 +00:00
|
|
|
return resume(std::forward<A>(args)...);
|
2017-03-04 17:25:09 +00:00
|
|
|
}
|
2017-03-06 17:37:33 +00:00
|
|
|
|
|
|
|
void swap(coroutine &other) {
|
|
|
|
std::swap(p_func, other.p_func);
|
|
|
|
base_t::swap(other);
|
|
|
|
}
|
|
|
|
|
2017-03-04 17:25:09 +00:00
|
|
|
private:
|
2017-03-07 16:33:56 +00:00
|
|
|
/* the main entry point of the coroutine */
|
2017-03-07 01:18:22 +00:00
|
|
|
static void context_call(detail::transfer_t t) {
|
|
|
|
auto &self = *(static_cast<coroutine *>(t.data));
|
|
|
|
self.p_orig = t.ctx;
|
|
|
|
try {
|
|
|
|
self.call_helper(self.p_func, std::index_sequence_for<A...>{});
|
2017-03-07 16:33:56 +00:00
|
|
|
} catch (detail::coroutine_context::forced_unwind v) {
|
|
|
|
/* forced_unwind is unique */
|
2017-03-07 01:18:22 +00:00
|
|
|
self.p_orig = v.ctx;
|
|
|
|
} catch (...) {
|
2017-03-07 16:33:56 +00:00
|
|
|
/* some other exception, will be rethrown later */
|
2017-03-07 01:18:22 +00:00
|
|
|
self.p_except = std::current_exception();
|
|
|
|
}
|
2017-03-07 16:33:56 +00:00
|
|
|
/* the func has fully finished here, so mark dead, stack
|
|
|
|
* will be freed by the coroutine's destructor later
|
|
|
|
*/
|
2017-03-07 00:12:52 +00:00
|
|
|
self.p_func = nullptr;
|
2017-03-07 16:33:56 +00:00
|
|
|
/* perform a last switch back to original context */
|
2017-03-07 01:18:22 +00:00
|
|
|
self.yield_jump();
|
2017-03-04 17:25:09 +00:00
|
|
|
}
|
|
|
|
|
2017-03-06 17:37:33 +00:00
|
|
|
std::function<R(yield_type, A...)> p_func;
|
2017-03-04 17:25:09 +00:00
|
|
|
};
|
|
|
|
|
2017-03-06 17:37:33 +00:00
|
|
|
template<typename R, typename ...A>
|
|
|
|
inline void swap(coroutine<R(A...)> &a, coroutine<R(A...)> &b) {
|
|
|
|
a.swap(b);
|
|
|
|
}
|
|
|
|
|
2017-03-05 23:47:15 +00:00
|
|
|
template<typename T>
|
2017-03-06 18:01:17 +00:00
|
|
|
struct coroutine_range: input_range<coroutine_range<T>> {
|
2017-03-05 23:47:15 +00:00
|
|
|
using range_category = input_range_tag;
|
|
|
|
using value_type = T;
|
|
|
|
using reference = T &;
|
|
|
|
using size_type = size_t;
|
|
|
|
using difference_type = stream_off_t;
|
|
|
|
|
2017-03-06 18:01:17 +00:00
|
|
|
coroutine_range() = delete;
|
|
|
|
coroutine_range(coroutine<T()> &c): p_coro(&c) {
|
2017-03-06 01:09:52 +00:00
|
|
|
pop_front();
|
2017-03-05 23:47:15 +00:00
|
|
|
}
|
2017-03-06 18:01:17 +00:00
|
|
|
coroutine_range(coroutine_range const &r):
|
|
|
|
p_coro(r.p_coro), p_item(r.p_item) {}
|
2017-03-05 23:47:15 +00:00
|
|
|
|
|
|
|
bool empty() const {
|
|
|
|
return !p_item;
|
|
|
|
}
|
|
|
|
|
|
|
|
void pop_front() {
|
2017-03-06 18:01:17 +00:00
|
|
|
if (*p_coro) {
|
|
|
|
p_item = (*p_coro)();
|
2017-03-05 23:47:15 +00:00
|
|
|
} else {
|
|
|
|
p_item = std::nullopt;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
reference front() const {
|
|
|
|
return p_item.value();
|
|
|
|
}
|
|
|
|
|
2017-03-07 16:33:56 +00:00
|
|
|
bool equals_front(coroutine_range const &g) const {
|
2017-03-06 18:01:17 +00:00
|
|
|
return p_coro == g.p_coro;
|
2017-03-05 23:47:15 +00:00
|
|
|
}
|
|
|
|
private:
|
2017-03-06 18:01:17 +00:00
|
|
|
coroutine<T()> *p_coro;
|
2017-03-05 23:47:15 +00:00
|
|
|
mutable std::optional<T> p_item;
|
|
|
|
};
|
|
|
|
|
2017-03-06 18:01:17 +00:00
|
|
|
namespace detail {
|
|
|
|
template<typename R>
|
|
|
|
coroutine_range<R> coro_base<R>::iter() {
|
|
|
|
return coroutine_range<R>{static_cast<coroutine<R()> &>(*this)};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-04 17:25:09 +00:00
|
|
|
} /* namespace ostd */
|
|
|
|
|
|
|
|
#endif
|