Last active
June 13, 2025 09:02
-
-
Save yohhoy/cd567aaf1aeb6ca6ff555d528b348b58 to your computer and use it in GitHub Desktop.
C++26 Sender/Receiver and awaitable Coroutine
This file contains hidden or 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
#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 : ex::with_awaitable_senders<promise_type> { | |
value_type value_; | |
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_; | |
} | |
private: | |
handle_type coro_; | |
}; | |
Lazy<int> coro(int n) | |
{ | |
std::println("coro start"); | |
ex::sender auto sndr = | |
ex::just(n) | |
| ex::then([](int m){ return m * 3; }); | |
int val = co_await sndr; | |
std::println("coro end"); | |
co_return val * 7; | |
} | |
int main() | |
{ | |
try { | |
auto task = coro(2); | |
std::println("get"); | |
auto value = task.get(); | |
std::println("value={}", value); | |
} catch (...) { | |
std::println("<exception>"); | |
} | |
} |
This file contains hidden or 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
#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_; | |
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); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
awaitable-coroutine
coroutine-to-sender