examples

Toy examples in single C files.
git clone git://henryandlizzy.uk/examples
Log | Files | Refs

commit 8d22581197d69a18816496fb3693e16b8f04b350
parent f6636a139c4bc1c525fe9db4a1cc2eb07d4d4f50
Author: Henry Wilson <henry@henryandlizzy.uk>
Date:   Wed,  5 Jul 2023 08:27:12 +0000

coro-generator-consumer: add coroutine example for generating and consuming

Diffstat:
Asrc/coro-generator-consumer.cpp | 117+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 117 insertions(+), 0 deletions(-)

diff --git a/src/coro-generator-consumer.cpp b/src/coro-generator-consumer.cpp @@ -0,0 +1,117 @@ +#include <coroutine> // coroutine_handle noop_coroutine suspend_always suspend_never +#include <exception> // terminate +#include <iostream> // cout +#include <optional> // optional +#include <cassert> // assert +#include <utility> // exchange + +struct sentry +{ + char const* msg; + sentry(char const* m) : msg(m) { std::cout << msg << " {\n"; } + ~sentry() { std::cout << "} " << msg << '\n'; } +}; + + +struct symmetric_transfer : std::suspend_always +{ + std::coroutine_handle<> await_suspend(std::coroutine_handle<>) { return h; } + + std::coroutine_handle<> h; +}; + +struct generator +{ + struct promise_type; + ~generator() + { + h.destroy(); + } + promise_type& promise() { return h.promise(); } + + std::coroutine_handle<promise_type> h; +}; + +struct generator::promise_type +{ + promise_type() { std::cout << __PRETTY_FUNCTION__ << '\n'; } + ~promise_type() { std::cout << __PRETTY_FUNCTION__ << '\n'; } + generator get_return_object() { return {std::coroutine_handle<promise_type>::from_promise(*this)}; } + std::suspend_always initial_suspend() { return {}; } + symmetric_transfer yield_value(unsigned v) + { + assert(not yielded_value); + yielded_value = v; + if (yield_to) + return {{}, std::exchange(yield_to, {})}; + else + return {{}, std::noop_coroutine()}; + } + std::suspend_always final_suspend() noexcept { return {}; } + + bool await_ready() { return yielded_value.has_value(); } + std::coroutine_handle<> await_suspend(std::coroutine_handle<> h) + { + assert(not yield_to); + yield_to = h; + return std::coroutine_handle<promise_type>::from_promise(*this); + } + unsigned await_resume() { return *std::exchange(yielded_value, {}); } + void unhandled_exception() { std::terminate(); } + +private: + std::optional<unsigned> yielded_value; + std::coroutine_handle<> yield_to; +}; + + +generator number_generator(unsigned d) +{ + sentry s(__func__); + unsigned i = 0; + for (;;) + co_yield i += d; +} + +struct consumer +{ + struct promise_type { + promise_type() { std::cout << __PRETTY_FUNCTION__ << '\n'; } + ~promise_type() { std::cout << __PRETTY_FUNCTION__ << '\n'; } + consumer get_return_object() { return {std::coroutine_handle<promise_type>::from_promise(*this)}; } + std::suspend_never initial_suspend() { return {}; } + void return_value(unsigned x) { result = x; } + std::suspend_always final_suspend() noexcept { return {}; } + void unhandled_exception() { std::terminate(); } + unsigned result; + }; + + ~consumer() { + h.destroy(); + } + unsigned result() const { + return h.promise().result; + } + + std::coroutine_handle<promise_type> h; +}; + +consumer number_consumer(generator::promise_type& g, unsigned i) +{ + sentry s(__func__); + unsigned total = 0; + while (i--) + { + unsigned x = co_await g; + std::cout << __func__ << ": " << x << '\n'; + total += x; + } + co_return total; +} + +int main() +{ + auto g = number_generator(2); + auto c = number_consumer(g.promise(), 5); + std::cout << __PRETTY_FUNCTION__ << ": " << c.result() << '\n'; +}