Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Symmetric coroutines using the coroutines TS
// 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