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_readyawait_suspendawait_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));
  }
}