C++:如何正确使用#include<coroutine> 中可用的 generator<>、task<> 和 lazy<> 类?
C++: How to properly use generator<>, task<>, and lazy<> classes available in #include<coroutine>?
我(从here)收集到协程类型大致class化为 3:
generator<> , task<>, and lazy<>
我的问题是:如果我想决定 return 类型,这三者之间有什么区别?
例如:对于延迟加载一组文件处理程序的协程,return class 是什么?我的实现将使用 task<FileHandler>
和 generator<Filehandler>
来实现相同的目的。
我查看了 'execution' 部分下的 this 关于 promise object
与协程交互时的限制。但我只能找到实现差异而不是 方法 差异。
我做了一些研究,我认为答案如下:
首先,C++中没有lazy<>
class。 https://en.cppreference.com/w/cpp/language/coroutines 错了。 (参考草稿确认)
因此,这是generator<T>
和task<T>
的return类型之间的区别。
TLDR;
最容易记住的方法是:
generators
are associated with co_yield
; whereas, tasks
are associated with co_await
发电机
与生成器 class 关联的 co_yield 机制与我们在 python 中遇到的完全相同(请参阅文档)并且与 thread_suspend
操作系统机制中的概念。
您可以选择同步或异步实现。 (参考 cppcoro library 示例。)
生成器类型 (kind-of) 如下所示:
struct generator {
struct promise_type;
using handle = std::coroutine_handle<promise_type>;
struct promise_type {
int current_value;
static auto get_return_object_on_allocation_failure() { return generator{nullptr}; }
auto get_return_object() { return generator{handle::from_promise(*this)}; }
auto initial_suspend() { return std::suspend_always{}; }
auto final_suspend() noexcept { return std::suspend_always{}; }
void unhandled_exception() { std::terminate(); }
void return_void() {}
auto yield_value(int value) {
current_value = value;
return std::suspend_always{};
}
};
bool move_next() { return coro ? (coro.resume(), !coro.done()) : false; }
int current_value() { return coro.promise().current_value; }
generator(generator const&) = delete;
generator(generator && rhs) : coro(rhs.coro) { rhs.coro = nullptr; }
~generator() { if (coro) coro.destroy(); }
private:
generator(handle h) : coro(h) {}
handle coro;
};
您将使用如下生成器类型:
generator f() { co_yield 1; co_yield 2; }
任务
另一方面,任务与 co_await
表达式相关联。它需要 Awaitable<T>
和 Awaiter<T>
概念,因此请确保正确使用与这两个概念关联的约束。 Awaiter 概念包括约束:await_ready
、await_suspend
和await_resume
。 Awaitable 概念具有约束条件:(1) co_await
specialization/overload 和 (2) 没有 await_transform
.[ ref ]
的重载
任务类型如下所示:
class task
{
public:
using promise_type = <unspecified>;
using value_type = T;
task() noexcept;
task(task&& other) noexcept;
task& operator=(task&& other);
task(const task& other) = delete;
task& operator=(const task& other) = delete;
bool is_ready() const noexcept;
Awaiter<T&> operator co_await() const & noexcept;
Awaiter<T&&> operator co_await() const && noexcept;
Awaitable<void> when_ready() const noexcept;
};
你可以选择使用这些概念(最好是我的方法),或者如果你还不熟悉这些概念的存在,那么你可能需要自己实现相关的约束来实现。
使用任务就像[ref]:
一样简单
task<> tcp_echo_server() {
char data[1024];
for (;;) {
size_t n = co_await socket.async_read_some(buffer(data));
co_await async_write(socket, buffer(data, n));
}
}
我(从here)收集到协程类型大致class化为 3:
generator<> , task<>, and lazy<>
我的问题是:如果我想决定 return 类型,这三者之间有什么区别?
例如:对于延迟加载一组文件处理程序的协程,return class 是什么?我的实现将使用 task<FileHandler>
和 generator<Filehandler>
来实现相同的目的。
我查看了 'execution' 部分下的 this 关于 promise object
与协程交互时的限制。但我只能找到实现差异而不是 方法 差异。
我做了一些研究,我认为答案如下:
首先,C++中没有lazy<>
class。 https://en.cppreference.com/w/cpp/language/coroutines 错了。 (参考草稿确认)
因此,这是generator<T>
和task<T>
的return类型之间的区别。
TLDR;
最容易记住的方法是:
generators
are associated withco_yield
; whereas,tasks
are associated withco_await
发电机
与生成器 class 关联的 co_yield 机制与我们在 python 中遇到的完全相同(请参阅文档)并且与 thread_suspend
操作系统机制中的概念。
您可以选择同步或异步实现。 (参考 cppcoro library 示例。)
生成器类型 (kind-of) 如下所示:
struct generator {
struct promise_type;
using handle = std::coroutine_handle<promise_type>;
struct promise_type {
int current_value;
static auto get_return_object_on_allocation_failure() { return generator{nullptr}; }
auto get_return_object() { return generator{handle::from_promise(*this)}; }
auto initial_suspend() { return std::suspend_always{}; }
auto final_suspend() noexcept { return std::suspend_always{}; }
void unhandled_exception() { std::terminate(); }
void return_void() {}
auto yield_value(int value) {
current_value = value;
return std::suspend_always{};
}
};
bool move_next() { return coro ? (coro.resume(), !coro.done()) : false; }
int current_value() { return coro.promise().current_value; }
generator(generator const&) = delete;
generator(generator && rhs) : coro(rhs.coro) { rhs.coro = nullptr; }
~generator() { if (coro) coro.destroy(); }
private:
generator(handle h) : coro(h) {}
handle coro;
};
您将使用如下生成器类型:
generator f() { co_yield 1; co_yield 2; }
任务
另一方面,任务与 co_await
表达式相关联。它需要 Awaitable<T>
和 Awaiter<T>
概念,因此请确保正确使用与这两个概念关联的约束。 Awaiter 概念包括约束:await_ready
、await_suspend
和await_resume
。 Awaitable 概念具有约束条件:(1) co_await
specialization/overload 和 (2) 没有 await_transform
.[ ref ]
任务类型如下所示:
class task
{
public:
using promise_type = <unspecified>;
using value_type = T;
task() noexcept;
task(task&& other) noexcept;
task& operator=(task&& other);
task(const task& other) = delete;
task& operator=(const task& other) = delete;
bool is_ready() const noexcept;
Awaiter<T&> operator co_await() const & noexcept;
Awaiter<T&&> operator co_await() const && noexcept;
Awaitable<void> when_ready() const noexcept;
};
你可以选择使用这些概念(最好是我的方法),或者如果你还不熟悉这些概念的存在,那么你可能需要自己实现相关的约束来实现。
使用任务就像[ref]:
一样简单task<> tcp_echo_server() {
char data[1024];
for (;;) {
size_t n = co_await socket.async_read_some(buffer(data));
co_await async_write(socket, buffer(data, n));
}
}