Skip to content

Instantly share code, notes, and snippets.

@andres-erbsen
Last active December 7, 2019 00:08
Show Gist options
  • Save andres-erbsen/ff24a6b0a78ed1d3f675457f5f8445fc to your computer and use it in GitHub Desktop.
Save andres-erbsen/ff24a6b0a78ed1d3f675457f5f8445fc to your computer and use it in GitHub Desktop.
generic C++ coroutine template
// dependency: libc++experimental
// clang++ -std=c++2a -fcoroutines-ts -lc++ coro.cpp
#include <cstdio>
#include <cassert>
#include <variant>
#include <experimental/coroutine>
using unit = std::monostate;
template <typename input, typename output>
struct interactive {
struct promise_type;
using handle_type = std::experimental::coroutine_handle<promise_type>;
handle_type coro;
interactive(handle_type h) : coro(h) { }
interactive(interactive &&) = default;
interactive(const interactive &) = delete;
~interactive() { coro.destroy(); }
output& blocker() {
return coro.promise().outgoing;
}
void resume(input z) {
coro.promise().incoming = z;
coro.resume();
}
struct yield_awaiter {
struct promise_type* p;
input await_resume() const { return p->incoming; }
static bool await_ready() { return false; }
static void await_suspend(std::experimental::coroutine_handle<>) {}
};
struct promise_type {
input incoming;
output outgoing;
interactive get_return_object() { return handle_type::from_promise(*this); }
yield_awaiter yield_value(output value) {
outgoing = value;
return yield_awaiter{this};
}
static auto initial_suspend() { return std::experimental::suspend_never{}; }
static auto final_suspend() { return std::experimental::suspend_always{}; }
static auto return_void() { return std::experimental::suspend_always{}; }
static void unhandled_exception() { std::abort(); }
};
};
// generator example
interactive<unit,int> gen(int start, int max) {
for (int i = start; i < max; i++) {
printf("(%d)", i);
co_yield i;
}
}
interactive<int,int> integral(int state) {
while (1) {
state += co_yield state;
}
}
void gen_integral_test() {
auto g = gen(0,10);
auto s = integral(0);
while (!g.coro.done()) {
printf("%d\n", s.blocker());
g.resume(unit());
s.resume(g.blocker());
}
assert(g.coro.done());
}
// io interface example
using get_int_rq = unit;
using get_int_rs = int;
using print_int_rq = int;
using print_int_rs = unit;
using iorq = std::variant<get_int_rq, print_int_rq>;
using iors = std::variant<get_int_rs, print_int_rs>;
interactive<iorq, iors> proc() {
int x = std::get<int>(co_yield unit());
int y = std::get<int>(co_yield unit());
co_yield x+y;
}
int main(int argc, char **argv) {
printf("integral of generator:\n"); // trigger memory allocation in printf
gen_integral_test();
auto p = proc();
assert (std::holds_alternative<get_int_rq>(p.blocker()));
p.resume(4);
assert (std::holds_alternative<get_int_rq>(p.blocker()));
p.resume(3);
assert (std::holds_alternative<print_int_rq>(p.blocker()));
printf("proc: %d\n", std::get<print_int_rq>(p.blocker()));
p.resume(unit());
assert(p.coro.done());
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment