C ++ 20コルーチン
この記事はから来ています
https://lewissbaker.github.io/2017/09/25/coroutine-theory
https://blog.panicsoftware.com/coroutines-introduction/
https://kirit.com/How%20C%2B%2B%20coroutines%20work/My%20first%20coroutine
https://en.cppreference.com/w/cpp/language/coroutines
コルーチン
コルーチンは、機能が停止し、その後再開することを可能にする関数の一般化です。これは、実行されるステップのいくつかのうち分離することによって、機能の操作を一般 コール と リターン :3つの余分な操作に操作 サスペンド、 レジューム と 破壊を。
コルーチンは、アクティベーションフレームを破壊せずに懸濁させることができるので、私たちはもはや、その活性化フレームの寿命は、厳密に入れ子になったことを保証することはできません。これは、活性化フレームは、一般にスタックデータ構造を使用して割り当てることができないので、代わりに、ヒープ上に格納される必要があり得ることを意味します。
それはコルーチンをサポートするように言うことができるサスペンドおよびレジューム機能を、従って関数は、一般的スタック内に実装することができません。
スタックレスコルーチン差対stackful
stackful:関数呼び出しを処理するために使用することができ、各コルーチンのための別個のスタックを作成します。いくつかのコルーチンがある場合は、いくつかの独立したスタックが作成されます。
スタックレス:スタック全体を割り当てる必要はありません、少ないメモリを消費します。トップレベルから身を一時停止することができます
コルーチンは、2つを実装する別個のスタックまたはヒープで実現されてもよい、C ++は後者です。後者は唯一の最上階を一時停止することができます。
1)各コルーチンのスタックフレームは、スタックの別個の独立した機能、スタックポインタを切り替える通話時間を有します。
コルーチンヒープ2)空間分布
コルーチンは、実質的に一種類を達成する(以下、両方のスタックレスコルーチンを参照)
現在スタックポインタの位置に対応する1)通常の関数f、ESP(スタック・ロケーション・レジスタ)とEBP(スタック・ベース・ロケーション・レジスタ)
2)コルーチンのX、スタック空間の割り当てと同様の一般的な関数を呼び出します。ヒープに割り当てられた空間、およびスタックの内容をコピーしています。
3)コルーチン通常関数g、一般的な機能に割り当てられたスタックフレームを呼び出しました。コルーチンスタックの前に位置コルーチンのヒープ領域を保存
4)G機能が戻るが、結果値がヒープ領域に格納されます。
5)コルーチンリターン関数f、ヒープ領域は次のポイントを継続する、すなわち、)ポインタを再開(コルーチンとRPを維持した状態をxは。保存されたポインタ関数fコルーチン
6)関数hが、コルーチンハンドルを続行は、パラメータ渡しを満たすためにスタック空間を割り当てます。データヒープ領域の主な値。
データ構造は、一般的にコルーチン
一般に、上述した処理、コルーチンとデータを保存する必要があるように、空間データに対応するポイントを一時停止パラメータ、内部変数、?プロミスようにと。
前記約束は複数のメンバ関数を持つデータ型指定コルーチンに対応するC ++です。それらを介して、ユーザーがように、一時停止する方法、コルーチンの動作をカスタマイズ返すことができます。
get_return_object //戻りオブジェクトを作成するには initial_suspend //コルーチン体に入ります RETURN_VALUE //ときに呼び出さco_returnと呼ばれます コルーチン本体の端の前に呼び出さreturn_void // yield_value //ときに呼び出さco_yieldと呼ばれます final_suspend //コルーチンが終了と呼ばれます unhandled_exception //ハンドル例外
以下は、一般的な機能であります
Int関数(INT A、INT B){}
関数と呼ばれる関数との間の転送または交換は、主に、戻り値、コルーチンない除いて機能することによって達成されるからです。少なくとも補助関数ポインタは、知っているために、再度実行するために次の時間を必要としています。
このように、コルーチンの戻り値の型でC ++のタイプは、約束のコルーチンポインタが含まれている必要があります。
コルーチン関数の戻り値の型とコルーチン
典型的には、性能、co_returnを含むもの、co_yield、co_await API関数は、コルーチンです。型定義を返し、関連するAPIを使用しています。
co_return:呼び出し元の関数に戻りコルーチンを終了
// vs2015 書式#include <iostreamの> 書式#include <実験/コルーチン> 名前空間stdを使用。 テンプレート<クラスT> 構造体のテスト{ //インナータイプ 構造体promise_type。 handle_typeを使用して=のstd ::実験:: coroutine_handle <promise_type>。//タイプの別名 // 機能 試験(handle_typeのH):ハンドル(H){COUT << "#テストオブジェクト\ nを作成しました"。} 試験(CONSTテスト&S)=削除します。 試験&演算子=(CONSTテスト&)=削除します。 試験(試験&& S):ハンドル(s.handle){s.handle = nullptr。} 試験&演算子=(試験&& S){ハンドル= s.handle。s.handle = nullptr; *これを返します。} 〜テスト(){coutの<< "#Testなくなっ\ N"; もし(ハンドル)handle.destroy(); } T取得() { 裁判所未満<< "#ガット戻り値\ nを"; (もし!(this-> handle.done())) { handle.resume(); //履歴書 。handle.promise()値を返します。 } } 構造体promise_type { promise_type(){coutの<< "@のpromise_typeは、\ nを作成しました"。} 〜promise_type(){裁判所未満は<< "@のpromise_typeは、\ nは死にました"。} 自動get_return_objectは()//戻りオブジェクトを取得します { 裁判所未満<< "@のget_return_objectは\ nのいわゆる"; 試験を返す<T> {handle_type :: from_promise(*本)}; //パスハンドルは "リターンオブジェクト" を作成します } 実行コルーチン本体の前に呼ばれる自動initial_suspend()// { 裁判所未満<< "@のinitial_suspendは、\ nを呼ばれています"。 // STDを返す::実験:: suspend_never {}。//それを中断いけません STDを返す::実験:: suspend_always {}。 } 自動RETURN_VALUE(TのV)// co_return式があるときに呼び出さ { 裁判所未満<< "の@ RETURN_VALUEは、\ nを呼ばれています"。 値= V; 実験:: STDを返す:: suspend_never {}。//それを中断いけません // STDを返す::実験:: suspend_always {}。 } コルーチン本文の最後に呼ばれる自動final_suspend()// { 裁判所未満<< "@のfinal_suspendは、\ nを呼ばれています"。 STDを返す::実験:: suspend_always {}。 } 空unhandled_exception()//例外ハンドラ { STD ::出口(1)。 } //データ Tの値は、 }。 //メンバ変数 handle_typeハンドル。 }。 テスト<整数> coroutine01() { std :: coutの<< "coroutine01の\ nを開始します"; co_return 1; co_return 2; //ここに到達することはありません } メインint型() { { オートA = coroutine01()。 裁判所未満<<「corutineを作成し、値の\ nを取得しよう」。 = a.get INT()。 COUTは<< << <<てendl "の値は"; = a.get()。 COUTは<< << <<てendl "の値は"; } 0を返します。 }
戻り型として<整数>一つの試験上記で定義されており、このタイプでは、そのメンバ関数とpromise_typeを定義します。コンパイル時、コンパイラは、生産指示コルーチン実行に基づいて実現することができます。
大雑把に実行フローは、ログを表示することができ、最初のステップは、(私たちは、この関数が作成するオブジェクトを返し実現)get_return_objectを呼び出すことで、コルーチンはinitial_suspend-を入力してください>コルーチン関数本体 - > final_suspendコルーチン完全に完成。関数の本体は、遭遇したco_return RETURN_VALUEを起動します。
initial_suspendとfinal_suspend機能では真または偽リターンで中断するかどうかを決定することができます。
co_yield:一連の値を返します
書式#include <iostreamの> 書式#include <実験/コルーチン> 名前空間stdを使用。 構造体のテスト{ //インナータイプ 構造体promise_type。 handle_typeを使用して =のstd ::実験:: coroutine_handle <promise_type>。//タイプの別名 // 機能 試験(handle_typeのH):ハンドル(H){COUT << "#テストオブジェクト\ nを作成しました"。} 試験(CONSTテスト&S)=削除します。 試験&演算子=(CONSTテスト&)=削除します。 試験(試験&& S):ハンドル(s.handle){s.handle = nullptr。} 試験&演算子=(試験&& S){ハンドル= s.handle。s.handle = nullptr; *これを返します。} 〜テスト(){coutの<< "#Testなくなっ\ N"; もし(ハンドル)handle.destroy(); } INTの現在の値(){ 。handle.promise()値を返します。 } BOOL move_next() { handle.resume(); !handle.done()を返します。 } 構造体promise_type { promise_type(){coutの<< "@のpromise_typeは、\ nを作成しました"。} 〜promise_type(){裁判所未満は<< "@のpromise_typeは、\ nは死にました"。} 自動get_return_objectは()//戻りオブジェクトを取得します { 裁判所未満<< "@のget_return_objectは\ nのいわゆる"; 戻り検査{handle_type :: from_promise(*本)}; // "は、リターンオブジェクト" を作成するハンドル渡し } 実行コルーチン本体の前に呼ばれる自動initial_suspend()// { 裁判所未満<< "@のinitial_suspendは、\ nを呼ばれています"。 実験:: STDを返す:: suspend_never {}。//それを中断いけません // STDを返す::実験:: suspend_always {}。 } 自動return_void()//単にfinal_suspend前に呼び出され、RETURN_VALUEとの競合 { 裁判所未満<< "@のreturn_voidは、\ nを呼ばれています"。 実験:: STDを返す:: suspend_never {}。//それを中断いけません // STDを返す::実験:: suspend_always {}。 } 自動yield_value(int型T)// co_yieldによって呼び出されます() { std :: coutの<< "yield_valueと呼ばれる\ nを"; 値= T。 STDを返す::実験:: suspend_always {}。 } コルーチン本文の最後に呼ばれる自動final_suspend()// { 裁判所未満<< "@のfinal_suspendは、\ nを呼ばれています"。 STDを返す::実験:: suspend_always {}。 } 空unhandled_exception()//例外ハンドラ { STD ::出口(1)。 } //データ int型の値。 }。 //メンバ変数 handle_typeハンドル。 }。 テストcoroutine01(int型の数) { std :: coutの<< "coroutine01の\ nを開始します"; 以下のために(int型私= 0; iの数<;私は++) co_yield私は2 *; } メインint型() { { オートA = coroutine01(4)。 裁判所未満<<「corutineを作成し、値の\ nを取得しよう」。 行う { 裁判所未満<< "の値を取得し、" << a.current_value()<<てendl; }一方(a.move_next())。 } 0を返します。 }
この関数はco_yield一例であり、主な違いはyield_value関数を定義する約束である、と我々はコルーチンを複数回呼び出すことができるように、我々は、(約束によってインスタンス化)コルーチンポインタを定義します。
handle.doneによってコルーチンを終了するか否かを判断します
co_await表現
発現はawaitorに変換することができなければなりません
:Awaiterタイプはco_await式の一部として呼ばれる3つの特別なメソッドを実装タイプである await_ready、await_suspendとawait_resumeを。
オペランド式で表される計算の完了を待っている間、ターゲットが、準備ができていないときにコルーチンをサスペンド
約束型、Pは、その後await_transformという名前のメンバーを持っている場合、<exprを>最初の呼び出しに渡され promise.await_transform(<exprを>)を得た Awaitable 値
呼び出し元の関数に戻りコルーチンを中断、または継続するかどうかを決定するために式の値に基づいています。
式はAwaitable必要な型に変換されることができます。呼び出さawaitableはawait_ready、await_suspendとawait_resume 3つの機能を含むデータ構造の種類を指します。
具体的にどのようにawaitable型に次の式からの変換:
かどうかに基づいて、前記表現からオペレータco_awaitオペレータ定義await_transformとawaitable、プロセスの型がないawaitor同じかどうか約束があります。
awaitor(可等待对象)是awaitable类型的实例。co_await expression 等效于 co_await awaitor, 进而调用awaitor的各个成员函数来决定co_await的结果。
如上图,co_await awaitor 等效于 awaitor->await_ready() , awaitor->await_suspend() ...
其中根据await_suspend的返回类型可以是void bool or handle.
#include <iostream> #include <experimental/coroutine> using namespace std; template<class T> struct test { // inner types struct promise_type; using handle_type = std::experimental::coroutine_handle<promise_type>; //type alias // functions test(handle_type h) :handle(h) { cout << "# Created a Test object\n"; } test(const test & s) = delete; test & operator=(const test &) = delete; test(test && s) :handle(s.handle) { s.handle = nullptr; } test& operator=(test && s) { handle = s.handle; s.handle = nullptr; return *this; } ~test() { cout << "#Test gone\n"; if (handle) handle.destroy(); } T get() { cout << "# Got return value\n"; if (!(this->handle.done())) { handle.resume(); //resume return handle.promise().value; } } struct promise_type { promise_type() { cout << "@ promise_type created\n"; } ~promise_type() { cout << "@ promise_type died\n"; } auto get_return_object() //get return object { cout << "@ get_return_object called\n"; return test<T>{handle_type::from_promise(*this)};// pass handle to create "return object" } auto initial_suspend() // called before run coroutine body { cout << "@ initial_suspend is called\n"; // return std::experimental::suspend_never{}; // dont suspend it return std::experimental::suspend_always{}; } auto yield_value(int t) // called by co_yield() { std::cout << "yield_value called\n"; value = t; return std::experimental::suspend_always{}; } auto final_suspend() // called at the end of coroutine body { cout << "@ final_suspend is called\n"; return std::experimental::suspend_always{}; } void unhandled_exception() //exception handler { std::exit(1); } //T await_transform() {} // data T value; }; // member variables handle_type handle; }; struct AwaiableObj { int a; AwaiableObj() :a(0) {} bool await_ready() { cout << "@@ await_ready called\n"; return true; } auto await_suspend(std::experimental::coroutine_handle<> awaiting_handle) { cout << "@@ await_suspend called\n"; // return ; // return true; return false; // return awaiting_handle; } auto await_resume() { cout << "@@ await_resume called\n"; return a++; } }; test<int> await_routine() { auto a = AwaiableObj{}; for (int i = 0; i < 5; i++) { auto v = co_await a; co_yield v; } } int main() { { auto a = await_routine(); auto b = a.get(); cout << "value is " << b << endl; b = a.get(); cout << "value is " << b << endl; b = a.get(); cout << "value is " << b << endl; b = a.get(); cout << "value is " << b << endl; b = a.get(); cout << "value is " << b << endl; } return 0; }
推荐awaitable类型定义在promise 类型之内