【问题标题】:C++20 coroutines: need a function that uses co_yieldC++20 协程:需要一个使用 co_yield 的函数
【发布时间】:2020-10-15 05:54:47
【问题描述】:

此 MCVE 无法在 g++10 中编译(使用 -std=c++21 -fcoroutines 选项)。

#include <iostream>

int f() { for (int i = 0; i < 10; ++i) co_yield i; }

int main ()
{
    for (int i : f())
        std::cout << i << ' ';

    return 0;
}

f 的第一行应该是什么样子,所以我得到了一个工作协程?还是需要做其他事情来制作一个工作协程?它需要是 C++20 的一部分(g++10supposed to fully support),而不是添加的库。

【问题讨论】:

  • 您的f 例程看起来不像这里的例程:en.cppreference.com/w/cpp/language/coroutines ... 这相关吗? (我还不熟悉 C++20 协程。我还在 C++14 领域。)
  • f 需要返回一个满足协程自定义点的特殊类型。 int 不是这样的类型。 github.com/lewissbaker/cppcoro 有一些我听说过适用于这类事情的类型。这种特殊用途听起来像generator&lt;int&gt; 或类似的东西。
  • 感谢您的评论。唉,生成器(这将是完美的!)不是 C++ 标准的一部分。我已经编辑了这个问题,以澄清我想要的是 c++20,而不是在标准可用之前构建的库。
  • @TopologicalSort 你的previous question 已经明确表示C++20 中没有generator。如果您不打算接受其他库,那么我不知道您在寻找什么。
  • @Barry:他在找你写generator 或类似的东西给他。基本上,他想了解协程机制是如何工作的。

标签: c++ coroutine c++20 c++-coroutine


【解决方案1】:

这是一个使用 gcc 10.1 的协程 myCoroutineFunction 的最小工作示例,它仅使用 co_yield 而没有 co_return

它使用计数的 for 循环。范围 for 循环需要更多的工作来编写迭代器。

协程的返回类型是一个生成器对象,需要一个promise对象来跟踪协程的当前值。

因为生成器和promise相互依赖,所以把promise做成生成器的嵌套类很方便。

每次协程产生一个值时,该值由promise维护,同时协程被挂起

客户端代码与生成器交互,生成器提供访问当前协程值和在暂停后推进协程的方法

// coroutine.cpp

// g++ -ocoroutine -g -O0 -Wall -Werror -Wextra
// -std=c++20 -fcoroutines -fno-exceptions coroutine.cpp

#include <stdio.h>
#include <coroutine>

using namespace std;

struct Generator {
  struct Promise;

// compiler looks for promise_type
  using promise_type=Promise;
  coroutine_handle<Promise> coro;

  Generator(coroutine_handle<Promise> h): coro(h) {}

  ~Generator() {
    if(coro)
      coro.destroy();
  }

// get current value of coroutine
  int value() {
    return coro.promise().val;
  }

// advance coroutine past suspension
  bool next() {
    coro.resume();
    return !coro.done();
  }

  struct Promise {
// current value of suspended coroutine
    int val;

// called by compiler first thing to get coroutine result
    Generator get_return_object() {
      return Generator{coroutine_handle<Promise>::from_promise(*this)};
    }

// called by compiler first time co_yield occurs
    suspend_always initial_suspend() {
      return {};
    }

// required for co_yield
    suspend_always yield_value(int x) {
      val=x;
      return {};
    }

// called by compiler for coroutine without return
    suspend_never return_void() {
      return {};
    }

// called by compiler last thing to await final result
// coroutine cannot be resumed after this is called
    suspend_always final_suspend() {
      return {};
    }
  };

};

Generator myCoroutineFunction(int n) {

  for(int i = 0; i < n; ++i) {
    co_yield i;
  }

}
int main ()
{
  int n=10;

  Generator myCoroutineResult = myCoroutineFunction(n);

  for(int i=0; i < n; ++i) {
    myCoroutineResult.next();
    printf("%d ", myCoroutineResult.value());
  }

  return 0;
}

编译运行


$ g++ -ocoroutine -g -O0 -Wall -Werror -Wextra \
-std=c++20 -fcoroutines -fno-exceptions coroutine.cpp
$ ./coroutine
0 1 2 3 4 5 6 7 8 9

【讨论】:

  • 用 g++-10 测试了你的例子。根据它,你的例子不能工作,除非......:1)你添加到你的struct Promiseunhandle_exception。类似:void unhandled_exception() { std::terminate(); }。 2)如果你想使用std::terminate(),你有两个选择:a)或者你添加#include&lt;exception&gt;(如果你想保留&lt;stdio.h&gt;(更好cstdio)。b)或者你不添加@ 987654334@,但将 &lt;stdio.h&gt; 替换为 &lt;iostream&gt;(控制台输出的 main 更改:cout 而不是 printf)。
  • 使用 gcc 10.2 就像一个魅力。谢谢一百万,这是一个非常重要的答案。希望我能投票十几次,为我节省了大量的工作。
【解决方案2】:

根据您的问题:

f 的最上面一行应该是什么样子,所以我得到了一个工作协程?


我已将您的代码修改如下。请特别注意 f 的返回类型。

Couroutine_Example_Generator<int> f() { for (int i = 0; i < 10; ++i) co_yield i; }

int main ()
{
    for (auto i = f(); i.move_next(); )
    {
        std::cout << i.current_value() << ' ';
    }

    return 0;
}

您可以查看我对您的代码所做的更改的完整 Godbolt 代码。检查here

或者是否需要做其他事情来制作一个工作协程?


是的。 C++20 规范要求提供 promise_type。 所以,我在头类中定义了生成器的模板化版本,您可以查看here。或者在我的repository 中查看稍微不同的代码示例。


如果您有兴趣,请提供其他相关详细信息。 以下是 C++20 规范的相关摘录

协程的承诺类型是 std::coroutine_traits::promise_type。 协程的行为就好像它的函数体被替换为:


{
  promise-type promise promise-constructor-arguments ;
  try {
  co_await promise .initial_suspend() ;
  function-body

【讨论】:

    猜你喜欢
    • 2021-09-03
    • 2021-08-13
    • 2021-12-27
    • 1970-01-01
    • 2023-03-09
    • 2017-06-03
    • 2019-04-17
    • 2021-09-17
    • 2020-06-26
    相关资源
    最近更新 更多