Created
February 5, 2019 18:07
-
-
Save RedBeard0531/1dd6171d7ff1c33dcc06b16705232ee8 to your computer and use it in GitHub Desktop.
Symmetric coroutines using the coroutines TS
This file contains 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
// A possible implementation of symmetic coroutines on top of the asymmetric coroutines ts. | |
// At the bottom of this file, I've rewritten the symmetric examples from Boot.Coroutine | |
// using this mechanism. You can see the originals at | |
// https://www.boost.org/doc/libs/1_69_0/libs/coroutine/doc/html/coroutine/coroutine/symmetric.html | |
#include <experimental/coroutine> | |
#include <cassert> | |
#include <list> | |
#include <optional> | |
#include <vector> | |
#include <deque> | |
#include <iostream> | |
using std::get; | |
using std::exception_ptr; | |
using std::monostate; | |
using std::experimental::coroutine_handle; | |
using std::experimental::suspend_never; | |
using std::experimental::suspend_always; | |
inline constexpr struct get_promise_t{} get_promise; | |
inline constexpr struct leave_coro_t{} leave_coro; | |
template <typename T> | |
struct sym_coro; | |
struct VOID{}; | |
template <typename T> | |
struct yield_to_t { | |
sym_coro<T>& coro; | |
T&& val; | |
}; | |
template <typename T> | |
yield_to_t<T> to(sym_coro<T>& coro, T val) { | |
return {coro, std::move(val)}; | |
} | |
yield_to_t<VOID> to(sym_coro<VOID>& coro, VOID val = {}) { | |
return {coro, std::move(val)}; | |
} | |
template <typename T> | |
struct sym_coro { | |
struct promise_type { | |
constexpr static struct yield_tag_t{} yield_tag; | |
auto get_return_object() { return sym_coro(this); } | |
auto initial_suspend() { return std::experimental::suspend_never(); } | |
auto yield_value(leave_coro_t) { | |
struct Awaitable { | |
bool await_ready() { return false; } | |
void await_suspend(coroutine_handle<> from) {} | |
T&& await_resume() { | |
assert(p->val); | |
return std::move(*p->val); | |
} | |
promise_type* p; | |
}; | |
val.reset(); | |
return Awaitable{this}; | |
} | |
template <typename U> | |
auto yield_value(yield_to_t<U>&& dest) { | |
struct Awaitable { | |
bool await_ready() { return false; } | |
auto await_suspend(coroutine_handle<> from){ | |
dest.coro.p->pump(std::move(dest.val)); | |
return dest.coro.handle(); | |
} | |
T&& await_resume() { | |
assert(p->val); | |
return std::move(*p->val); | |
} | |
yield_to_t<U>& dest; | |
promise_type* p; | |
}; | |
val.reset(); | |
return Awaitable{dest, this}; | |
} | |
template <typename U> | |
void await_transform(U&&) = delete; | |
void return_value(leave_coro_t) { | |
done = true; | |
} | |
auto final_suspend() { | |
assert(done); | |
return suspend_always{}; | |
} | |
void unhandled_exception() { std::terminate(); } | |
auto handle() { | |
return coroutine_handle<promise_type>::from_promise(*this); | |
} | |
void pump(T newVal) { | |
assert(!val); | |
val.emplace(std::move(newVal)); | |
} | |
bool done = false; | |
std::optional<T> val; | |
}; | |
sym_coro() : p(nullptr) {} | |
sym_coro(promise_type* p) : p(p) {} | |
sym_coro(sym_coro&& other) : p(std::exchange(other.p, nullptr)) {} | |
~sym_coro() { | |
if (p) p->handle().destroy(); | |
} | |
void operator()(T val) { | |
assert(!p->val); | |
p->val.emplace(std::move(val)); | |
handle().resume(); | |
} | |
auto handle() { return p->handle(); } | |
promise_type* p; | |
}; | |
std::vector<int> merge(const std::vector<int>& a,const std::vector<int>& b) | |
{ | |
std::vector<int> c; | |
std::size_t idx_a=0,idx_b=0; | |
sym_coro<VOID>* other_a=0,* other_b=0; | |
auto output = [&](int i) { | |
c.push_back(i); | |
}; | |
auto coro_a_launcher = [&]() -> sym_coro<VOID> { | |
co_yield leave_coro; // initial suspend | |
while(idx_a<a.size()) | |
{ | |
if(b[idx_b]<a[idx_a]) // test if element in array b is less than in array a | |
co_yield to(*other_b); // yield to coroutine coro_b | |
output(a[idx_a++]); // add element to final array | |
} | |
// add remaining elements of array b | |
while ( idx_b < b.size()) | |
output( b[idx_b++]); | |
co_return leave_coro; | |
}; | |
auto coro_b_launcher = [&]() -> sym_coro<VOID> { | |
co_yield leave_coro; // initial suspend | |
while(idx_b<b.size()) | |
{ | |
if (a[idx_a]<b[idx_b]) // test if element in array a is less than in array b | |
co_yield to(*other_a); // yield to coroutine coro_a | |
output(b[idx_b++]); // add element to final array | |
} | |
// add remaining elements of array a | |
while ( idx_a < a.size()) | |
output( a[idx_a++]); | |
co_return leave_coro; | |
}; | |
auto coro_a = coro_a_launcher(); | |
auto coro_b = coro_b_launcher(); | |
other_a = &coro_a; | |
other_b = &coro_b; | |
coro_a(VOID{}); // enter coroutine-fn of coro_a | |
return c; | |
} | |
void example1() { | |
std::vector<int> a = {1,5,6,10}; | |
std::vector<int> b = {2,4,7,8,9,13}; | |
for (auto i : merge(a,b)) { | |
std::cout << i<< std::endl; | |
} | |
} | |
void example2() { | |
auto coro = []() -> sym_coro<int>{ | |
for (;;) { | |
std::cout << (co_yield leave_coro) << " "; | |
} | |
}(); | |
coro(1); // transfer {1} to coroutine-function | |
coro(2); // transfer {2} to coroutine-function | |
coro(3); // transfer {3} to coroutine-function | |
coro(4); // transfer {4} to coroutine-function | |
coro(5); // transfer {5} to coroutine-function | |
std::cout << std::endl; | |
} | |
int main() { | |
example1(); | |
std::cout << "----" << std::endl; | |
example2(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment