Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++20 coroutines: need a function that uses co_yield

This MCVE won't compile in g++10 (with -std=c++21 -fcoroutines options).

#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;
}

What should that top line of f look like, so I get a working coroutine? Or does something else need to be done to make a working coroutine? It needs to be part of C++20 (which g++10 is supposed to fully support), not an added library.

like image 625
Topological Sort Avatar asked Oct 22 '25 07:10

Topological Sort


2 Answers

Here's a minimal working example with gcc 10.1 of a coroutine myCoroutineFunction that uses just co_yield and does not have a co_return

It uses a counted for loop. A range for loop requires a little more work to write an iterator.

The return type of a coroutine is a generator object that needs a promise object to track the current value of the coroutine.

Because the generator and promise depend on each other, it's convenient to make the promise a nested class of the generator.

Every time the coroutine yields a value, the value is maintained by the promise while the coroutine is suspended

Client code interacts with the generator that provides methods to access the current coroutine value and to advance the coroutine after suspension

// 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;
}

Compile and run


$ 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
like image 180
Zartaj Majeed Avatar answered Oct 23 '25 21:10

Zartaj Majeed


From your question:

What should that top line of f look like, so I get a working coroutine?


I have modified your code as below. Please pay special attention to return type of 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;
}

You can see complete Godbolt code for the changes that I did to your code. Check here.

Or does something else need to be done to make a working coroutine?


Yes. C++20 specification requires promise_type to be provided. So, I have defined a templatized version of generator in a header class which you can look here. Or see a slightly different code example in my repository.


Additional relevant details in case you are interested. Here are relevant excerpts from C++20 specification

The promise type of a coroutine is std::coroutine_traits<R, P1, . . . , Pn>::promise_type. A coroutine behaves as if its function-body were replaced by:


{
  promise-type promise promise-constructor-arguments ;
  try {
  co_await promise .initial_suspend() ;
  function-body
like image 35
Atul Mehra Avatar answered Oct 23 '25 20:10

Atul Mehra



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!