Last active
December 7, 2019 00:08
-
-
Save andres-erbsen/ff24a6b0a78ed1d3f675457f5f8445fc to your computer and use it in GitHub Desktop.
generic C++ coroutine template
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
// 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