来源:https://blog.panicsoftware.com/your-first-coroutine/
std::future<int> foo();
这是个普通函数,还是协程?这是由其具体实现决定的。
If any of those keywords occur in the function, then it becomes a coroutine.
co_await
co_return
co_yield
So the operator co_await
is a unary operator, which takes the Awaitable
object as its argument.
Why do we need to define additional types?
So the object used to communicate with the coroutine is the object of the coroutine’s return type.
我们使用协程的返回值,和协程进行沟通。
If our coroutine is able to suspend, we need to be able to somehow resume it, so our return type needs to have some resume()
method.
协程可以暂停,所以协程的返回类型需要有 resume()
等接口。
How will the resumable object be created (there is no return statement)? Basically, the compiler generates some additional code for the coroutines. Every coroutine function is transformed by the compiler to the form similar to this:
1 | Promise promise; |
What is more, the resumable object will be created before the initial_suspend
through a call to the:
promise.get_return_object();
So what we need to do is to create the Promise
type.
如何创建这个返回对象呢?由编译器生成的样板代码约束我们得实现 Promise
类型,它得包含以下接口:
resumable get_return_object()
// 创建返回对象auto initial_suspend()
auto final_suspend()
void return_void()
void unhandled_exception()
Unfortunately, we are not yet ready to define functions of our first promise type. To operate on the coroutine, we need to have some sort of handle to the coroutine, which we will manage. There already is a built-in object for this exact purpose.
如何实现上述接口呢?我们需要某种句柄来管理协程。
The coroutine_handle
The coroutine_handle
is an object, that refers to the coroutine’s dynamically allocated state.
很关键的类型,TODO 稍后整理
Deeper into promise type.
Handling co_return
First of all, if you use co_return
keyword without any expression on its right side (or void expression), the compiler generates:
promise.return_void(); goto final_suspend;
But if there is some non-void expression, the compiler generates a slightly different code:
promise.return_value(expression); goto final_suspend;
Making the use of co_yield operator
Now to implement our promise_type correctly, we need to know what kind of code compiler generates when it encounters the co_yield keyword. And the following snippet shows precisely this.
co_await promise.yield_value(expression);
So what is missing is the yield_value
member function. Also worth to note is, that no co_await
keyword appears, but we will be talking about the co_await
keyword later on. For now, the knowledge, that co_await
+ suspend_always
suspends the coroutine is enough.
Summary
So as you can see, the coroutines are hard to learn, after all, it’s still not everything about the coroutines since we only touched Awaitables and Awaiter objects. Fortunately because of the level of difficulty the coroutines the coroutines are very flexible.
幸运的是,由于协程的难度,协程非常灵活。
The good thing is that the whole complicated part of this feature is not to be known by the regular C++ developer. In fact, the regular C++ developer should know how to write the body of the coroutine, but not coroutine objects themselves.
好消息是常规C ++开发人员不知道此功能的整个复杂部分。实际上,常规的C ++开发人员应该知道如何编写协程的主体,而不是协程对象本身。
Developers definitely should use already defined coroutines objects from the standard library (which later will come into the standard) or third-party libraries (like cppcoro).