本期實現的功能很簡單,協程的重頭都在co_await和異步操作上,generator本身是一個很輕的東西
如果你看到這行文字,說明這篇文章被無恥的盜用了(或者你正在選中文字),請前往 cnblogs.com/pointer-smq 支持原作者,謝謝
基本結構
generator和future/promise的模式的區別在於,future/promise的核心數據存在堆分配的shared_state里,協程對象(promise)、future共享引用shared_state,協程由異步操作的完成回調推動,協程完成后自行銷毀;而generator模式中,核心數據存在promise中,generator唯一引用協程對象(promise),協程由外部for循環使用者推動,generator控制協程銷毀。
一些設計要點
- 協程啟動后即暫停(initial_suspend)
- 協程結束前暫停(final_suspend),由generator析構控制協程destroy
- operator++負責控制協程恢復執行
- generator唯一引用協程,應當禁止拷貝
- generator的迭代器是input迭代器
一些需要注意的地方
- generator只能迭代一遍
- 銷毀generator會使迭代器失效
- 迭代器越界++會導致嚴重的UB(resume已經銷毀的協程)
- 拷貝的迭代器++其中一個,另一個的狀態也會變化
開始寫代碼
首先是generator的框架和對應的promise_type
template<class T>
class Generator {
struct Promise {
exp::suspend_always initial_suspend() { return {}; }
exp::suspend_always final_suspend() noexcept { return {}; }
Generator<T> get_return_object() { return {this}; }
void unhandled_exception() { std::terminate(); }
exp::suspend_always yield_value(T v) {
_current = std::move(v);
return {};
}
void return_void() {}
T _current;
};
public:
using promise_type = Promise;
Generator(const Generator& other) = delete;
Generator& operator=(const Generator& other) = delete;
Generator(Generator&& other) noexcept
: _promise(other._promise) {
other._promise = nullptr;
}
Generator& operator=(Generator&& other) noexcept = delete;
~Generator() {
if (_promise) {
exp::coroutine_handle<Promise>::from_promise(*_promise).destroy();
}
}
private:
Generator(Promise* sink)
: _promise(sink)
{}
Promise* _promise;
};
Generator強引用_promise,禁用拷貝,允許移動,移動賦值也應該允許的,但是太麻煩了索性delete了。
Generator析構函數里,通過promise指針拿到promise對應的協程,並調用destroy銷毀。
通過co_yield返回的對象通過Primise::yield_value存儲在Promise內部,等待Generator的迭代器來取出;yield_value返回suspend_always,表示每次yield之后協程都暫停,等下一次推動
這里面還差推動協程resume的iterator,現在補上
如果你看到這行文字,說明這篇文章被無恥的盜用了(或者你正在選中文字),請前往 cnblogs.com/pointer-smq 支持原作者,謝謝
template<class T>
class Generator {
// ...
// public
struct iterator_end_sentinel {};
struct iterator {
template<class>
friend class Generator;
using iterator_category = std::input_iterator_tag;
using value_type = T;
T operator*() {
return _promise->_current;
}
void operator++() {
exp::coroutine_handle<Promise>::from_promise(*_promise).resume();
}
bool operator!=(iterator_end_sentinel) {
return !exp::coroutine_handle<Promise>::from_promise(*_promise).done();
}
private:
iterator(Promise* promise)
: _promise(promise) {
}
Promise* _promise;
};
iterator begin() { return {_promise}; }
iterator_end_sentinel end() { return {}; }
// ...
};
這里begin和end返回的不是同一個迭代器類型,是C++17允許的來着?
iterator弱引用_promise。
operator*返回_promise中的當前值
operator++從_promise獲取coroutine_handle,使用resume恢復執行
operator!=(iterator_end_sentinel)從_promise獲取coroutine_handle,並判斷是否done(當協程暫停在final_suspend時即為done)
齊活了,就這么簡單,趕緊自己寫一個吧!
附錄
完整代碼
如果你看到這行文字,說明這篇文章被無恥的盜用了(或者你正在選中文字),請前往 cnblogs.com/pointer-smq 支持原作者,謝謝
#include <iostream>
#include <experimental/coroutine>
namespace exp = std::experimental;
template<class T>
class Generator {
struct Promise {
exp::suspend_always initial_suspend() { return {}; }
exp::suspend_always final_suspend() noexcept { return {}; }
Generator<T> get_return_object() { return {this}; }
void unhandled_exception() { std::terminate(); }
exp::suspend_always yield_value(T v) {
_current = std::move(v);
return {};
}
void return_void() {}
T _current;
};
public:
using promise_type = Promise;
struct iterator_end_sentinel {};
struct iterator {
template<class>
friend class Generator;
using iterator_category = std::input_iterator_tag;
using value_type = T;
T operator*() {
return _promise->_current;
}
void operator++() {
exp::coroutine_handle<Promise>::from_promise(*_promise).resume();
}
bool operator!=(iterator_end_sentinel) {
return !exp::coroutine_handle<Promise>::from_promise(*_promise).done();
}
private:
iterator(Promise* promise)
: _promise(promise) {
}
Promise* _promise;
};
iterator begin() { return {_promise}; }
iterator_end_sentinel end() { return {}; }
Generator(const Generator& other) = delete;
Generator& operator=(const Generator& other) = delete;
Generator(Generator&& other) noexcept
: _promise(other._promise) {
other._promise = nullptr;
}
Generator& operator=(Generator&& other) noexcept = delete;
~Generator() {
if (_promise) {
exp::coroutine_handle<Promise>::from_promise(*_promise).destroy();
}
}
private:
Generator(Promise* sink)
: _promise(sink)
{}
Promise* _promise;
};
Generator<int> func() {
for(int i = 0; i < 10; i++) {
co_yield i;
}
}
int main() {
for (int i : func()) {
std::cout << i << " ";
}
}