C++20协程的标准目前为止似乎还未确定, 现在按VS2019(10.0.18362.0)的具体实现做例子, 下面给出一个惰性计算的例子.
注意目前开启C++协程需要打开选项 /await
.
为方便理解起见, 下面将promise对象称为协程守护器, awaiter对象称为同步器.
所需头文件如下
#include
#include
class HandleDeliver {
HandleDeliver() = delete;
HandleDeliver(const HandleDeliver&) = delete;
HandleDeliver& operator= (const HandleDeliver&) = delete;
HandleDeliver& operator= (HandleDeliver &&) = delete;
private:
std::experimental::coroutine_handle<>& phandle;
public:
HandleDeliver(std::experimental::coroutine_handle<>& phandle) noexcept :
phandle(phandle) {}
HandleDeliver(HandleDeliver &&self) noexcept :
phandle(self.phandle) {}
bool await_ready() const noexcept {
return false;
}
void await_suspend(std::experimental::coroutine_handle<> handle) noexcept {
phandle = handle;
}
void await_resume() const noexcept {}
};
class Lazy {
Lazy() = delete;
Lazy(const Lazy&) = delete;
Lazy& operator= (const Lazy&) = delete;
Lazy& operator= (Lazy &&) = delete;
public:
class promise_type {
... // 见下文守护器
};
private:
class iterator_type {
... // 见下文迭代器
};
private:
promise_type & promise;
bool is_overed;
int retv;
protected:
bool _next() {
if(is_overed) {
return false;
}
promise.resume();
// promise.rethrow_if_has_exception();
if(promise.overed()) {
retv = promise.value();
promise.destroy();
is_overed = true;
return false;
}
return true;
}
int _get() const noexcept {
if(is_overed) {
return -1;
} else {
return promise.value();
}
}
public:
explicit Lazy(promise_type& promise) noexcept :
promise(promise),
is_overed(false),
retv() {
}
Lazy(Lazy &&self) noexcept :
promise(self.promise),
is_overed(self.is_overed) {
}
iterator_type begin() noexcept {
return iterator_type(*this);
}
iterator_type end() noexcept {
return iterator_type(*this);
}
int get_return() noexcept {
if(is_overed) {
return retv;
} else {
return -1;
}
}
};
class promise_type {
promise_type(const promise_type&) = delete;
promise_type(promise_type &&) = delete;
promise_type& operator= (const promise_type&) = delete;
promise_type& operator= (promise_type &&) = delete;
private:
std::experimental::coroutine_handle<> handle;
int v;
bool is_overed;
public:
std::experimental::suspend_never initial_suspend() noexcept {
return std::experimental::suspend_never();
}
promise_type() noexcept : v(-1), is_overed(false) {}
Lazy get_return_object() noexcept {
return Lazy(*this);
}
HandleDeliver yield_value(int v) noexcept {
this->v = v;
return HandleDeliver(handle);
}
void return_value(int v) noexcept {
this->v = v;
}
HandleDeliver final_suspend() noexcept {
is_overed = true;
return HandleDeliver(handle);
}
void unhandled_exception() {
try {
std::rethrow_exception(std::current_exception());
} catch(std::exception& e) {
std::cout << "exception: " << e.what() << std::endl;
} catch(...) {
std::cout << "unknow exception" << std::endl;
}
}
public:
void resume() const noexcept {
handle.resume();
}
bool overed() const noexcept {
return is_overed;
}
int value() const noexcept {
return v;
}
void destroy() noexcept {
handle.destroy();
}
};
class iterator {
iterator_type() = delete;
iterator_type(const iterator_type&) = delete;
iterator_type& operator= (const iterator_type&) = delete;
iterator_type& operator= (iterator_type &&) = delete;
private:
Lazy &z;
public:
explicit iterator_type(Lazy& z) noexcept : z(z) {}
iterator_type(iterator_type &&self) noexcept : z(self.z) {}
iterator_type& operator++ () noexcept {
z._next();
return *this;
}
bool operator!= (iterator_type const& other) const noexcept {
return !z.is_overed;
}
int operator* () const noexcept {
return z._get();
}
};
Lazy f() {
for(int i = 0; i < 5; ++i) {
co_yield i;
}
co_return 10;
}
int main() {
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
{
Lazy result = f();
for(int i : result) {
std::cout << ">" << i << std::endl;
}
std::cout << result.get_return() << std::endl;
}
_CrtDumpMemoryLeaks();
return 0;
}
>0
>1
>2
>3
>4
10
若给每个核心函数调用加上字符串输出, 则如下:
00E893D8 Lazy::promise_type::initial_suspend
00E893D8 Lazy::promise_type::promise_type
00E893D8 Lazy::promise_type::get_return_object
00E893D8 Lazy::promise_type::yield_value
00E893F8 HandleDeliver::HandleDeliver
00E893F8 HandleDeliver::await_ready
00E893F8 HandleDeliver::await_suspend
>0
00E893F8 HandleDeliver::await_resume
00E893F8 HandleDeliver::~HandleDeliver
00E893D8 Lazy::promise_type::yield_value
00E893F8 HandleDeliver::HandleDeliver
00E893F8 HandleDeliver::await_ready
00E893F8 HandleDeliver::await_suspend
>1
00E893F8 HandleDeliver::await_resume
00E893F8 HandleDeliver::~HandleDeliver
00E893D8 Lazy::promise_type::yield_value
00E893F8 HandleDeliver::HandleDeliver
00E893F8 HandleDeliver::await_ready
00E893F8 HandleDeliver::await_suspend
>2
00E893F8 HandleDeliver::await_resume
00E893F8 HandleDeliver::~HandleDeliver
00E893D8 Lazy::promise_type::yield_value
00E893F8 HandleDeliver::HandleDeliver
00E893F8 HandleDeliver::await_ready
00E893F8 HandleDeliver::await_suspend
>3
00E893F8 HandleDeliver::await_resume
00E893F8 HandleDeliver::~HandleDeliver
00E893D8 Lazy::promise_type::yield_value
00E893F8 HandleDeliver::HandleDeliver
00E893F8 HandleDeliver::await_ready
00E893F8 HandleDeliver::await_suspend
>4
00E893F8 HandleDeliver::await_resume
00E893F8 HandleDeliver::~HandleDeliver
00E893D8 Lazy::promise_type::return_value
00E893D8 Lazy::promise_type::final_suspend
00E89408 HandleDeliver::HandleDeliver
00E89408 HandleDeliver::await_ready
00E89408 HandleDeliver::await_suspend
00E89408 HandleDeliver::~HandleDeliver
00E893D8 Lazy::promise_type::~promise_type
10
其中, 红框的函数是语法上要求必须存在的函数.
C++20标准中要求守护器的初始化在initial_suspend之前. 但VS的实际实现却颠倒过来了. initial_suspend被调用时, 守护器竟然尚未初始化!(具体见上文实例输出部分)
协程抛出异常后, 由协程守护器的unhandled_exception函数接收异常. 若需将异常抛出给主线程, 则该异常需为由throw new
创建. 否则在协程栈上创建的异常抛出协程后会立即失效.