Skip to content

Instantly share code, notes, and snippets.

@yohhoy
Last active April 20, 2025 08:42
Show Gist options
  • Save yohhoy/cd567aaf1aeb6ca6ff555d528b348b58 to your computer and use it in GitHub Desktop.
Save yohhoy/cd567aaf1aeb6ca6ff555d528b348b58 to your computer and use it in GitHub Desktop.
C++26 Sender/Receiver and awaitable Coroutine
#include <cassert>
#include <concepts>
#include <coroutine>
#include <print>
// https://github.com/NVIDIA/stdexec/
#include <stdexec/execution.hpp>
namespace ex = stdexec;
template<typename T>
class Lazy {
public:
struct promise_type;
using value_type = T;
using handle_type = std::coroutine_handle<promise_type>;
struct promise_type {
value_type value_;
static auto get_return_object_on_allocation_failure()
{ return Lazy{nullptr}; }
auto get_return_object()
{ return Lazy{handle_type::from_promise(*this)}; }
auto initial_suspend() noexcept { return std::suspend_always{}; }
auto final_suspend() noexcept { return std::suspend_always{}; }
void unhandled_exception() { throw; }
void return_value(value_type v) noexcept { value_ = v; }
};
private:
Lazy(handle_type h) : coro_{h} {}
public:
Lazy(Lazy&& rhs)
: coro_{std::exchange(rhs.coro_, nullptr)} {}
Lazy& operator=(Lazy&& rhs) {
if (coro_) { std::exchange(coro_, nullptr).destroy(); }
std::swap(rhs.coro_, coro_);
return *this;
}
~Lazy()
{ if (coro_) { coro_.destroy(); } }
value_type get() {
assert(coro_);
if (!coro_.done()) {
coro_.resume();
}
return coro_.promise().value_;
}
auto operator co_await() noexcept {
struct awaiter {
handle_type h;
bool await_ready() const { return h.done(); }
bool await_suspend(std::coroutine_handle<>)
{ h.resume(); return false; }
value_type await_resume()
{ return h.promise().value_; }
};
return awaiter{coro_};
}
private:
handle_type coro_;
};
Lazy<int> subtask(int n) // child coro
{
std::println("subtask {}", n);
co_return n * 3;
}
Lazy<int> task(int n) // parent coro
{
std::println("task.1 {}", n);
int v = co_await subtask(7);
std::println("task.2 {}", v);
co_return v * n;
}
int main()
{
std::println("-- Coroutine");
{
try {
auto t = task(2);
std::println("get");
auto v = t.get();
std::println("value={}", v);
} catch (...) {
std::println("<exception>");
}
}
std::println("-- Sender/Receiver");
{
struct DumpReceiver {
using receiver_concept = ex::receiver_t;
void set_value(int v) && noexcept
{ std::println("value={}", v); }
void set_error(std::exception_ptr) && noexcept
{ std::println("<error>"); }
void set_stopped() && noexcept
{ std::println("<stopped>"); }
};
ex::sender auto sndr = task(2);
ex::receiver auto rcvr = DumpReceiver{};
std::println("connect");
ex::operation_state auto op = ex::connect(std::move(sndr), rcvr);
std::println("start");
ex::start(op);
}
}
@yohhoy
Copy link
Author

yohhoy commented Apr 19, 2025

-- Coroutine
get
task.1  2
subtask 7
task.2  21
value=42
-- Sender/Receiver
connect
start
task.1  2
subtask 7
task.2  21
value=42

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment