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:
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';
+}