only pass yield to coroutines, make them movable and swappable, improve examples
parent
b9260c8918
commit
64d92743e4
|
@ -5,192 +5,286 @@ using namespace ostd;
|
|||
|
||||
struct foo {
|
||||
foo() {
|
||||
writeln("***foo ctor***");
|
||||
writeln("<constructing foo>");
|
||||
}
|
||||
~foo() {
|
||||
writeln("***foo dtor***");
|
||||
writeln("<destroying foo>");
|
||||
}
|
||||
};
|
||||
|
||||
int main() {
|
||||
writeln("MAIN START");
|
||||
writeln("starting main...");
|
||||
for (int steps: range(1, 10)) {
|
||||
if (steps != 1) {
|
||||
/* separate the results */
|
||||
writeln();
|
||||
}
|
||||
writefln("** SCOPE START %d **", steps);
|
||||
coroutine<int(int)> f = [](auto &coro, int x) {
|
||||
writefln(" main loop: step %s", steps);
|
||||
writeln(" coroutine creation");
|
||||
coroutine<int(int)> f = [](auto yield, int x) {
|
||||
writefln(" coroutine call, first arg: %s", x);
|
||||
foo test;
|
||||
writeln("got: ", x);
|
||||
for (int i: range(x)) {
|
||||
writeln("loop: ", i);
|
||||
writeln("yield: ", coro.yield(i * 10));
|
||||
writefln(" loop inside coroutine %s", i + 1);
|
||||
writefln(" yielding %s...", i * 10);
|
||||
auto yr = yield(i * 10);
|
||||
writefln(" yielded: %s", yr);
|
||||
}
|
||||
writeln("return");
|
||||
writeln(" return from coroutine (returning 1234)...");
|
||||
return 1234;
|
||||
};
|
||||
writeln("CALL");
|
||||
writeln(" coroutine call loop");
|
||||
int val = 5;
|
||||
for (int i: range(steps)) {
|
||||
writeln("COROUTINE RETURNED: ", f(val));
|
||||
writefln("OUT %d (dead: %s)", i, !f);
|
||||
writeln(" calling into coroutine...");
|
||||
auto v = f(val);
|
||||
writefln(" called into coroutine which yielded: %s", v);
|
||||
writefln(" call loop iteration %s done", i + 1);
|
||||
writefln(" coroutine dead: %s", !f);
|
||||
val += 5;
|
||||
}
|
||||
writefln("** SCOPE END %d **", steps);
|
||||
writefln(" main loop iteration %s done", steps);
|
||||
}
|
||||
writeln("MAIN END");
|
||||
writeln("... main has ended");
|
||||
}
|
||||
|
||||
/*
|
||||
MAIN START
|
||||
** SCOPE START 1 **
|
||||
CALL
|
||||
***foo ctor***
|
||||
got: 5
|
||||
loop: 0
|
||||
COROUTINE RETURNED: 0
|
||||
OUT 0 (dead: false)
|
||||
** SCOPE END 1 **
|
||||
***foo dtor***
|
||||
starting main...
|
||||
main loop: step 1
|
||||
coroutine creation
|
||||
coroutine call loop
|
||||
calling into coroutine...
|
||||
coroutine call, first arg: 5
|
||||
<constructing foo>
|
||||
loop inside coroutine 1
|
||||
yielding 0...
|
||||
called into coroutine which yielded: 0
|
||||
call loop iteration 1 done
|
||||
coroutine dead: false
|
||||
main loop iteration 1 done
|
||||
<destroying foo>
|
||||
|
||||
** SCOPE START 2 **
|
||||
CALL
|
||||
***foo ctor***
|
||||
got: 5
|
||||
loop: 0
|
||||
COROUTINE RETURNED: 0
|
||||
OUT 0 (dead: false)
|
||||
yield: 10
|
||||
loop: 1
|
||||
COROUTINE RETURNED: 10
|
||||
OUT 1 (dead: false)
|
||||
** SCOPE END 2 **
|
||||
***foo dtor***
|
||||
main loop: step 2
|
||||
coroutine creation
|
||||
coroutine call loop
|
||||
calling into coroutine...
|
||||
coroutine call, first arg: 5
|
||||
<constructing foo>
|
||||
loop inside coroutine 1
|
||||
yielding 0...
|
||||
called into coroutine which yielded: 0
|
||||
call loop iteration 1 done
|
||||
coroutine dead: false
|
||||
calling into coroutine...
|
||||
yielded: 10
|
||||
loop inside coroutine 2
|
||||
yielding 10...
|
||||
called into coroutine which yielded: 10
|
||||
call loop iteration 2 done
|
||||
coroutine dead: false
|
||||
main loop iteration 2 done
|
||||
<destroying foo>
|
||||
|
||||
** SCOPE START 3 **
|
||||
CALL
|
||||
***foo ctor***
|
||||
got: 5
|
||||
loop: 0
|
||||
COROUTINE RETURNED: 0
|
||||
OUT 0 (dead: false)
|
||||
yield: 10
|
||||
loop: 1
|
||||
COROUTINE RETURNED: 10
|
||||
OUT 1 (dead: false)
|
||||
yield: 15
|
||||
loop: 2
|
||||
COROUTINE RETURNED: 20
|
||||
OUT 2 (dead: false)
|
||||
** SCOPE END 3 **
|
||||
***foo dtor***
|
||||
main loop: step 3
|
||||
coroutine creation
|
||||
coroutine call loop
|
||||
calling into coroutine...
|
||||
coroutine call, first arg: 5
|
||||
<constructing foo>
|
||||
loop inside coroutine 1
|
||||
yielding 0...
|
||||
called into coroutine which yielded: 0
|
||||
call loop iteration 1 done
|
||||
coroutine dead: false
|
||||
calling into coroutine...
|
||||
yielded: 10
|
||||
loop inside coroutine 2
|
||||
yielding 10...
|
||||
called into coroutine which yielded: 10
|
||||
call loop iteration 2 done
|
||||
coroutine dead: false
|
||||
calling into coroutine...
|
||||
yielded: 15
|
||||
loop inside coroutine 3
|
||||
yielding 20...
|
||||
called into coroutine which yielded: 20
|
||||
call loop iteration 3 done
|
||||
coroutine dead: false
|
||||
main loop iteration 3 done
|
||||
<destroying foo>
|
||||
|
||||
** SCOPE START 4 **
|
||||
CALL
|
||||
***foo ctor***
|
||||
got: 5
|
||||
loop: 0
|
||||
COROUTINE RETURNED: 0
|
||||
OUT 0 (dead: false)
|
||||
yield: 10
|
||||
loop: 1
|
||||
COROUTINE RETURNED: 10
|
||||
OUT 1 (dead: false)
|
||||
yield: 15
|
||||
loop: 2
|
||||
COROUTINE RETURNED: 20
|
||||
OUT 2 (dead: false)
|
||||
yield: 20
|
||||
loop: 3
|
||||
COROUTINE RETURNED: 30
|
||||
OUT 3 (dead: false)
|
||||
** SCOPE END 4 **
|
||||
***foo dtor***
|
||||
main loop: step 4
|
||||
coroutine creation
|
||||
coroutine call loop
|
||||
calling into coroutine...
|
||||
coroutine call, first arg: 5
|
||||
<constructing foo>
|
||||
loop inside coroutine 1
|
||||
yielding 0...
|
||||
called into coroutine which yielded: 0
|
||||
call loop iteration 1 done
|
||||
coroutine dead: false
|
||||
calling into coroutine...
|
||||
yielded: 10
|
||||
loop inside coroutine 2
|
||||
yielding 10...
|
||||
called into coroutine which yielded: 10
|
||||
call loop iteration 2 done
|
||||
coroutine dead: false
|
||||
calling into coroutine...
|
||||
yielded: 15
|
||||
loop inside coroutine 3
|
||||
yielding 20...
|
||||
called into coroutine which yielded: 20
|
||||
call loop iteration 3 done
|
||||
coroutine dead: false
|
||||
calling into coroutine...
|
||||
yielded: 20
|
||||
loop inside coroutine 4
|
||||
yielding 30...
|
||||
called into coroutine which yielded: 30
|
||||
call loop iteration 4 done
|
||||
coroutine dead: false
|
||||
main loop iteration 4 done
|
||||
<destroying foo>
|
||||
|
||||
** SCOPE START 5 **
|
||||
CALL
|
||||
***foo ctor***
|
||||
got: 5
|
||||
loop: 0
|
||||
COROUTINE RETURNED: 0
|
||||
OUT 0 (dead: false)
|
||||
yield: 10
|
||||
loop: 1
|
||||
COROUTINE RETURNED: 10
|
||||
OUT 1 (dead: false)
|
||||
yield: 15
|
||||
loop: 2
|
||||
COROUTINE RETURNED: 20
|
||||
OUT 2 (dead: false)
|
||||
yield: 20
|
||||
loop: 3
|
||||
COROUTINE RETURNED: 30
|
||||
OUT 3 (dead: false)
|
||||
yield: 25
|
||||
loop: 4
|
||||
COROUTINE RETURNED: 40
|
||||
OUT 4 (dead: false)
|
||||
** SCOPE END 5 **
|
||||
***foo dtor***
|
||||
main loop: step 5
|
||||
coroutine creation
|
||||
coroutine call loop
|
||||
calling into coroutine...
|
||||
coroutine call, first arg: 5
|
||||
<constructing foo>
|
||||
loop inside coroutine 1
|
||||
yielding 0...
|
||||
called into coroutine which yielded: 0
|
||||
call loop iteration 1 done
|
||||
coroutine dead: false
|
||||
calling into coroutine...
|
||||
yielded: 10
|
||||
loop inside coroutine 2
|
||||
yielding 10...
|
||||
called into coroutine which yielded: 10
|
||||
call loop iteration 2 done
|
||||
coroutine dead: false
|
||||
calling into coroutine...
|
||||
yielded: 15
|
||||
loop inside coroutine 3
|
||||
yielding 20...
|
||||
called into coroutine which yielded: 20
|
||||
call loop iteration 3 done
|
||||
coroutine dead: false
|
||||
calling into coroutine...
|
||||
yielded: 20
|
||||
loop inside coroutine 4
|
||||
yielding 30...
|
||||
called into coroutine which yielded: 30
|
||||
call loop iteration 4 done
|
||||
coroutine dead: false
|
||||
calling into coroutine...
|
||||
yielded: 25
|
||||
loop inside coroutine 5
|
||||
yielding 40...
|
||||
called into coroutine which yielded: 40
|
||||
call loop iteration 5 done
|
||||
coroutine dead: false
|
||||
main loop iteration 5 done
|
||||
<destroying foo>
|
||||
|
||||
** SCOPE START 6 **
|
||||
CALL
|
||||
***foo ctor***
|
||||
got: 5
|
||||
loop: 0
|
||||
COROUTINE RETURNED: 0
|
||||
OUT 0 (dead: false)
|
||||
yield: 10
|
||||
loop: 1
|
||||
COROUTINE RETURNED: 10
|
||||
OUT 1 (dead: false)
|
||||
yield: 15
|
||||
loop: 2
|
||||
COROUTINE RETURNED: 20
|
||||
OUT 2 (dead: false)
|
||||
yield: 20
|
||||
loop: 3
|
||||
COROUTINE RETURNED: 30
|
||||
OUT 3 (dead: false)
|
||||
yield: 25
|
||||
loop: 4
|
||||
COROUTINE RETURNED: 40
|
||||
OUT 4 (dead: false)
|
||||
yield: 30
|
||||
return
|
||||
***foo dtor***
|
||||
COROUTINE RETURNED: 1234
|
||||
OUT 5 (dead: true)
|
||||
** SCOPE END 6 **
|
||||
main loop: step 6
|
||||
coroutine creation
|
||||
coroutine call loop
|
||||
calling into coroutine...
|
||||
coroutine call, first arg: 5
|
||||
<constructing foo>
|
||||
loop inside coroutine 1
|
||||
yielding 0...
|
||||
called into coroutine which yielded: 0
|
||||
call loop iteration 1 done
|
||||
coroutine dead: false
|
||||
calling into coroutine...
|
||||
yielded: 10
|
||||
loop inside coroutine 2
|
||||
yielding 10...
|
||||
called into coroutine which yielded: 10
|
||||
call loop iteration 2 done
|
||||
coroutine dead: false
|
||||
calling into coroutine...
|
||||
yielded: 15
|
||||
loop inside coroutine 3
|
||||
yielding 20...
|
||||
called into coroutine which yielded: 20
|
||||
call loop iteration 3 done
|
||||
coroutine dead: false
|
||||
calling into coroutine...
|
||||
yielded: 20
|
||||
loop inside coroutine 4
|
||||
yielding 30...
|
||||
called into coroutine which yielded: 30
|
||||
call loop iteration 4 done
|
||||
coroutine dead: false
|
||||
calling into coroutine...
|
||||
yielded: 25
|
||||
loop inside coroutine 5
|
||||
yielding 40...
|
||||
called into coroutine which yielded: 40
|
||||
call loop iteration 5 done
|
||||
coroutine dead: false
|
||||
calling into coroutine...
|
||||
yielded: 30
|
||||
return from coroutine (returning 1234)...
|
||||
<destroying foo>
|
||||
called into coroutine which yielded: 1234
|
||||
call loop iteration 6 done
|
||||
coroutine dead: true
|
||||
main loop iteration 6 done
|
||||
|
||||
** SCOPE START 7 **
|
||||
CALL
|
||||
***foo ctor***
|
||||
got: 5
|
||||
loop: 0
|
||||
COROUTINE RETURNED: 0
|
||||
OUT 0 (dead: false)
|
||||
yield: 10
|
||||
loop: 1
|
||||
COROUTINE RETURNED: 10
|
||||
OUT 1 (dead: false)
|
||||
yield: 15
|
||||
loop: 2
|
||||
COROUTINE RETURNED: 20
|
||||
OUT 2 (dead: false)
|
||||
yield: 20
|
||||
loop: 3
|
||||
COROUTINE RETURNED: 30
|
||||
OUT 3 (dead: false)
|
||||
yield: 25
|
||||
loop: 4
|
||||
COROUTINE RETURNED: 40
|
||||
OUT 4 (dead: false)
|
||||
yield: 30
|
||||
return
|
||||
***foo dtor***
|
||||
COROUTINE RETURNED: 1234
|
||||
OUT 5 (dead: true)
|
||||
main loop: step 7
|
||||
coroutine creation
|
||||
coroutine call loop
|
||||
calling into coroutine...
|
||||
coroutine call, first arg: 5
|
||||
<constructing foo>
|
||||
loop inside coroutine 1
|
||||
yielding 0...
|
||||
called into coroutine which yielded: 0
|
||||
call loop iteration 1 done
|
||||
coroutine dead: false
|
||||
calling into coroutine...
|
||||
yielded: 10
|
||||
loop inside coroutine 2
|
||||
yielding 10...
|
||||
called into coroutine which yielded: 10
|
||||
call loop iteration 2 done
|
||||
coroutine dead: false
|
||||
calling into coroutine...
|
||||
yielded: 15
|
||||
loop inside coroutine 3
|
||||
yielding 20...
|
||||
called into coroutine which yielded: 20
|
||||
call loop iteration 3 done
|
||||
coroutine dead: false
|
||||
calling into coroutine...
|
||||
yielded: 20
|
||||
loop inside coroutine 4
|
||||
yielding 30...
|
||||
called into coroutine which yielded: 30
|
||||
call loop iteration 4 done
|
||||
coroutine dead: false
|
||||
calling into coroutine...
|
||||
yielded: 25
|
||||
loop inside coroutine 5
|
||||
yielding 40...
|
||||
called into coroutine which yielded: 40
|
||||
call loop iteration 5 done
|
||||
coroutine dead: false
|
||||
calling into coroutine...
|
||||
yielded: 30
|
||||
return from coroutine (returning 1234)...
|
||||
<destroying foo>
|
||||
called into coroutine which yielded: 1234
|
||||
call loop iteration 6 done
|
||||
coroutine dead: true
|
||||
calling into coroutine...
|
||||
terminating with uncaught exception of type ostd::coroutine_error: dead coroutine
|
||||
zsh: abort ./coro
|
||||
*/
|
||||
|
|
|
@ -4,11 +4,11 @@
|
|||
using namespace ostd;
|
||||
|
||||
int main() {
|
||||
generator<int> g = [](auto &coro) {
|
||||
coro.yield(5);
|
||||
coro.yield(10);
|
||||
coro.yield(15);
|
||||
coro.yield(20);
|
||||
generator<int> g = [](auto yield) {
|
||||
yield(5);
|
||||
yield(10);
|
||||
yield(15);
|
||||
yield(20);
|
||||
return 25;
|
||||
};
|
||||
|
||||
|
|
|
@ -80,11 +80,32 @@ struct coroutine_context {
|
|||
p_coro = detail::ostd_make_fcontext(p_stack.get() + ss, ss, context_call);
|
||||
}
|
||||
|
||||
coroutine_context(coroutine_context const &) = delete;
|
||||
coroutine_context(coroutine_context &&c):
|
||||
p_stack(std::move(c.p_stack)), p_coro(c.p_coro), p_orig(c.p_orig),
|
||||
p_except(std::move(c.p_except)), p_callp(c.p_callp), p_data(c.p_data),
|
||||
p_finished(c.p_finished)
|
||||
{
|
||||
c.p_coro = c.p_orig = nullptr;
|
||||
c.p_data = nullptr;
|
||||
c.p_callp = nullptr;
|
||||
/* make sure it's not unwound */
|
||||
c.p_finished = true;
|
||||
}
|
||||
|
||||
coroutine_context &operator=(coroutine_context const &) = delete;
|
||||
coroutine_context &operator=(coroutine_context &&c) {
|
||||
swap(c);
|
||||
/* make sure it's not unwound */
|
||||
c.p_finished = true;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void call() {
|
||||
if (p_finished) {
|
||||
throw coroutine_error{"dead coroutine"};
|
||||
}
|
||||
p_coro = detail::ostd_jump_fcontext(p_coro, this).ctx;
|
||||
coro_jump();
|
||||
if (p_except) {
|
||||
std::rethrow_exception(std::move(p_except));
|
||||
}
|
||||
|
@ -102,6 +123,10 @@ struct coroutine_context {
|
|||
);
|
||||
}
|
||||
|
||||
void coro_jump() {
|
||||
p_coro = detail::ostd_jump_fcontext(p_coro, this).ctx;
|
||||
}
|
||||
|
||||
void yield_jump() {
|
||||
p_orig = detail::ostd_jump_fcontext(p_orig, nullptr).ctx;
|
||||
}
|
||||
|
@ -110,6 +135,20 @@ struct coroutine_context {
|
|||
return p_finished;
|
||||
}
|
||||
|
||||
void set_data(void *data) {
|
||||
p_data = data;
|
||||
}
|
||||
|
||||
void swap(coroutine_context &other) noexcept {
|
||||
std::swap(p_stack, other.p_stack);
|
||||
std::swap(p_coro, other.p_coro);
|
||||
std::swap(p_orig, other.p_orig);
|
||||
std::swap(p_except, other.p_except);
|
||||
std::swap(p_callp, other.p_callp);
|
||||
std::swap(p_data, other.p_data);
|
||||
std::swap(p_finished, other.p_finished);
|
||||
}
|
||||
|
||||
private:
|
||||
static void context_call(detail::transfer_t t) {
|
||||
auto &self = *(static_cast<coroutine_context *>(t.data));
|
||||
|
@ -135,6 +174,10 @@ private:
|
|||
bool p_finished = false;
|
||||
};
|
||||
|
||||
inline void swap(coroutine_context &a, coroutine_context &b) {
|
||||
a.swap(b);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
struct coroutine;
|
||||
|
||||
|
@ -152,6 +195,10 @@ namespace detail {
|
|||
return std::move(p_arg);
|
||||
}
|
||||
|
||||
void swap(arg_wrapper &other) {
|
||||
std::swap(p_arg, other.p_arg);
|
||||
}
|
||||
|
||||
private:
|
||||
T p_arg = T{};
|
||||
};
|
||||
|
@ -168,6 +215,10 @@ namespace detail {
|
|||
return *p_arg;
|
||||
}
|
||||
|
||||
void swap(arg_wrapper &other) {
|
||||
std::swap(p_arg, other.p_arg);
|
||||
}
|
||||
|
||||
private:
|
||||
T *p_arg = nullptr;
|
||||
};
|
||||
|
@ -184,10 +235,19 @@ namespace detail {
|
|||
return *p_arg;
|
||||
}
|
||||
|
||||
void swap(arg_wrapper &other) {
|
||||
std::swap(p_arg, other.p_arg);
|
||||
}
|
||||
|
||||
private:
|
||||
T *p_arg = nullptr;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
inline void swap(arg_wrapper<T> &a, arg_wrapper<T> &b) {
|
||||
a.swap(b);
|
||||
}
|
||||
|
||||
template<typename ...A>
|
||||
struct coro_types {
|
||||
using yield_type = std::tuple<A...>;
|
||||
|
@ -222,21 +282,46 @@ namespace detail {
|
|||
/* default case, yield returns args and takes a value */
|
||||
template<typename R, typename ...A>
|
||||
struct coro_base {
|
||||
protected:
|
||||
coro_base(void (*callp)(void *), size_t ss):
|
||||
p_ctx(ss, callp, this)
|
||||
{}
|
||||
|
||||
coro_args<A...> yield(R &&ret) {
|
||||
p_result = std::forward<R>(ret);
|
||||
p_ctx.yield_jump();
|
||||
return yield_ret(p_args, std::make_index_sequence<sizeof...(A)>{});
|
||||
coro_base(coro_base const &) = delete;
|
||||
coro_base(coro_base &&c):
|
||||
p_args(std::move(c.p_args)), p_result(std::move(c.p_result)),
|
||||
p_ctx(std::move(c.p_ctx))
|
||||
{
|
||||
p_ctx.set_data(this);
|
||||
}
|
||||
|
||||
protected:
|
||||
template<typename F, typename C, size_t ...I>
|
||||
void call_helper(F &func, C &coro, std::index_sequence<I...>) {
|
||||
coro_base &operator=(coro_base const &) = delete;
|
||||
coro_base &operator=(coro_base &&c) {
|
||||
std::swap(p_args, c.p_args);
|
||||
std::swap(p_result, c.p_result);
|
||||
std::swap(p_ctx, c.p_ctx);
|
||||
p_ctx.set_data(this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
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);
|
||||
p_coro.p_ctx.yield_jump();
|
||||
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...>) {
|
||||
p_result = std::forward<R>(
|
||||
func(coro, std::forward<A>(std::get<I>(p_args))...)
|
||||
func(yielder{*this}, std::forward<A>(std::get<I>(p_args))...)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -246,6 +331,12 @@ namespace detail {
|
|||
return std::forward<R>(p_result);
|
||||
}
|
||||
|
||||
void swap(coro_base &other) {
|
||||
std::swap(p_args, other.p_args);
|
||||
std::swap(p_result, other.p_result);
|
||||
std::swap(p_ctx, other.p_ctx);
|
||||
}
|
||||
|
||||
std::tuple<arg_wrapper<A>...> p_args;
|
||||
arg_wrapper<R> p_result;
|
||||
coroutine_context p_ctx;
|
||||
|
@ -254,19 +345,41 @@ namespace detail {
|
|||
/* yield takes a value but doesn't return any args */
|
||||
template<typename R>
|
||||
struct coro_base<R> {
|
||||
protected:
|
||||
coro_base(void (*callp)(void *), size_t ss):
|
||||
p_ctx(ss, callp, this)
|
||||
{}
|
||||
|
||||
void yield(R &&ret) {
|
||||
p_result = std::forward<R>(ret);
|
||||
p_ctx.yield_jump();
|
||||
coro_base(coro_base const &) = delete;
|
||||
coro_base(coro_base &&c):
|
||||
p_result(std::move(c.p_result)),
|
||||
p_ctx(std::move(c.p_ctx))
|
||||
{
|
||||
p_ctx.set_data(this);
|
||||
}
|
||||
|
||||
protected:
|
||||
template<typename F, typename C, size_t ...I>
|
||||
void call_helper(F &func, C &coro, std::index_sequence<I...>) {
|
||||
p_result = std::forward<R>(func(coro));
|
||||
coro_base &operator=(coro_base const &) = delete;
|
||||
coro_base &operator=(coro_base &&c) {
|
||||
std::swap(p_result, c.p_result);
|
||||
std::swap(p_ctx, c.p_ctx);
|
||||
p_ctx.set_data(this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
struct yielder {
|
||||
yielder(coro_base<R> &coro): p_coro(coro) {}
|
||||
|
||||
void operator()(R &&ret) {
|
||||
p_coro.p_result = std::forward<R>(ret);
|
||||
p_coro.p_ctx.yield_jump();
|
||||
}
|
||||
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}));
|
||||
}
|
||||
|
||||
R call() {
|
||||
|
@ -274,6 +387,11 @@ namespace detail {
|
|||
return std::forward<R>(this->p_result);
|
||||
}
|
||||
|
||||
void swap(coro_base &other) {
|
||||
std::swap(p_result, other.p_result);
|
||||
std::swap(p_ctx, other.p_ctx);
|
||||
}
|
||||
|
||||
arg_wrapper<R> p_result;
|
||||
coroutine_context p_ctx;
|
||||
};
|
||||
|
@ -281,19 +399,43 @@ namespace detail {
|
|||
/* yield doesn't take a value and returns args */
|
||||
template<typename ...A>
|
||||
struct coro_base<void, A...> {
|
||||
protected:
|
||||
coro_base(void (*callp)(void *), size_t ss):
|
||||
p_ctx(ss, callp, this)
|
||||
{}
|
||||
|
||||
coro_args<A...> yield() {
|
||||
p_ctx.yield_jump();
|
||||
return yield_ret(p_args, std::make_index_sequence<sizeof...(A)>{});
|
||||
coro_base(coro_base const &) = delete;
|
||||
coro_base(coro_base &&c):
|
||||
p_args(std::move(c.p_args)),
|
||||
p_ctx(std::move(c.p_ctx))
|
||||
{
|
||||
p_ctx.set_data(this);
|
||||
}
|
||||
|
||||
protected:
|
||||
template<typename F, typename C, size_t ...I>
|
||||
void call_helper(F &func, C &coro, std::index_sequence<I...>) {
|
||||
func(coro, std::forward<A>(std::get<I>(p_args))...);
|
||||
coro_base &operator=(coro_base const &) = delete;
|
||||
coro_base &operator=(coro_base &&c) {
|
||||
std::swap(p_args, c.p_args);
|
||||
std::swap(p_ctx, c.p_ctx);
|
||||
p_ctx.set_data(this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
struct yielder {
|
||||
yielder(coro_base<void, A...> &coro): p_coro(coro) {}
|
||||
|
||||
coro_args<A...> operator()() {
|
||||
p_coro.p_ctx.yield_jump();
|
||||
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))...);
|
||||
}
|
||||
|
||||
void call(A ...args) {
|
||||
|
@ -301,6 +443,11 @@ namespace detail {
|
|||
p_ctx.call();
|
||||
}
|
||||
|
||||
void swap(coro_base &other) {
|
||||
std::swap(p_args, other.p_args);
|
||||
std::swap(p_ctx, other.p_ctx);
|
||||
}
|
||||
|
||||
std::tuple<arg_wrapper<A>...> p_args;
|
||||
coroutine_context p_ctx;
|
||||
};
|
||||
|
@ -308,35 +455,69 @@ namespace detail {
|
|||
/* yield doesn't take a value or return any args */
|
||||
template<>
|
||||
struct coro_base<void> {
|
||||
protected:
|
||||
coro_base(void (*callp)(void *), size_t ss):
|
||||
p_ctx(ss, callp, this)
|
||||
{}
|
||||
|
||||
void yield() {
|
||||
p_ctx.yield_jump();
|
||||
coro_base(coro_base const &) = delete;
|
||||
coro_base(coro_base &&c): p_ctx(std::move(c.p_ctx)) {
|
||||
p_ctx.set_data(this);
|
||||
}
|
||||
|
||||
protected:
|
||||
template<typename F, typename C, size_t ...I>
|
||||
void call_helper(F &func, C &coro, std::index_sequence<I...>) {
|
||||
func(coro);
|
||||
coro_base &operator=(coro_base const &) = delete;
|
||||
coro_base &operator=(coro_base &&c) {
|
||||
std::swap(p_ctx, c.p_ctx);
|
||||
p_ctx.set_data(this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
struct yielder {
|
||||
yielder(coro_base<void> &coro): p_coro(coro) {}
|
||||
|
||||
void operator()() {
|
||||
p_coro.p_ctx.yield_jump();
|
||||
}
|
||||
private:
|
||||
coro_base<void> &p_coro;
|
||||
};
|
||||
|
||||
template<typename F, size_t ...I>
|
||||
void call_helper(F &func, std::index_sequence<I...>) {
|
||||
func(yielder{*this});
|
||||
}
|
||||
|
||||
void call() {
|
||||
p_ctx.call();
|
||||
}
|
||||
|
||||
void swap(coro_base &other) {
|
||||
std::swap(p_ctx, other.p_ctx);
|
||||
}
|
||||
|
||||
coroutine_context p_ctx;
|
||||
};
|
||||
} /* namespace detail */
|
||||
|
||||
template<typename R, typename ...A>
|
||||
struct coroutine<R(A...)>: detail::coro_base<R, A...> {
|
||||
private:
|
||||
using base_t = detail::coro_base<R, A...>;
|
||||
|
||||
public:
|
||||
using yield_type = typename detail::coro_base<R, A...>::yielder;
|
||||
|
||||
template<typename F>
|
||||
coroutine(F func, size_t ss = COROUTINE_DEFAULT_STACK_SIZE):
|
||||
detail::coro_base<R, A...>(&context_call, ss), p_func(std::move(func))
|
||||
{}
|
||||
|
||||
coroutine(coroutine const &) = delete;
|
||||
coroutine(coroutine &&) = default;
|
||||
|
||||
coroutine &operator=(coroutine const &) = delete;
|
||||
coroutine &operator=(coroutine &&) = default;
|
||||
|
||||
~coroutine() {
|
||||
this->p_ctx.unwind();
|
||||
}
|
||||
|
@ -345,18 +526,33 @@ struct coroutine<R(A...)>: detail::coro_base<R, A...> {
|
|||
return !this->p_ctx.is_done();
|
||||
}
|
||||
|
||||
R resume(A ...args) {
|
||||
return this->call(std::forward<A>(args)...);
|
||||
}
|
||||
|
||||
R operator()(A ...args) {
|
||||
return this->call(std::forward<A>(args)...);
|
||||
}
|
||||
|
||||
void swap(coroutine &other) {
|
||||
std::swap(p_func, other.p_func);
|
||||
base_t::swap(other);
|
||||
}
|
||||
|
||||
private:
|
||||
static void context_call(void *data) {
|
||||
coroutine &self = *(static_cast<coroutine *>(data));
|
||||
self.call_helper(self.p_func, self, std::index_sequence_for<A...>{});
|
||||
self.call_helper(self.p_func, std::index_sequence_for<A...>{});
|
||||
}
|
||||
|
||||
std::function<R(coroutine<R(A...)> &, A...)> p_func;
|
||||
std::function<R(yield_type, A...)> p_func;
|
||||
};
|
||||
|
||||
template<typename R, typename ...A>
|
||||
inline void swap(coroutine<R(A...)> &a, coroutine<R(A...)> &b) {
|
||||
a.swap(b);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
struct generator: input_range<generator<T>> {
|
||||
using range_category = input_range_tag;
|
||||
|
|
Loading…
Reference in New Issue