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.
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
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With