プログラミング言語C++のコルーチン(Coroutines)拡張に関するメモ。2018年4月現在、C++2a(C++20)標準規格での正式採択に向けた検討が進んでいる。
2019-08-09追記:2019年2月会合にてP0912R5が採択され、C++2aへのコルーチン導入が決定している。id:yohhoy:20190906も参照のこと。
本記事の内容は(PDF)N4736 Working Draft, C++ Extensions for Coroutinesに基づく。
C++コルーチン拡張はコルーチンに関するC++言語仕様と低レベルAPIのみを規定し、他プログラミング言語のような具体的な “コルーチン” 機能は何も定義しない。
- C++コルーチンライブラリ実装者は、低レベルAPIを用いてジェネレータ(Generator)や非同期タスク(Asynchronous task)といった具象機能を提供する。
- C++アプリケーション開発者は、
co_xxx
キーワードを介してコルーチンライブラリを利用する。
要約:
- 新しいキーワード:
co_yield
,co_return
,co_await
- 新しい構文:yield式, co_return文, await式,
co_await範囲for文,co_await演算子オーバーロード- 2019-08-09追記:co_await範囲for文はC++2aコルーチンから削除された。P0664R8 C++ Coroutine TS Issues, #35 Remove
for co_await
statement.
- 2019-08-09追記:co_await範囲for文はC++2aコルーチンから削除された。P0664R8 C++ Coroutine TS Issues, #35 Remove
- 新しいヘッダ:<experimental/coroutine> *1
- コルーチン(coroutine) == 関数途中での中断(suspend)/再開(resume)を追加サポートする関数(function)*2
- コルーチン(coroutine) == 本体に
co_yield
/co_return
/co_await
キーワードを含む関数*3 - C++コルーチン拡張では、コルーチン動作に関するユーザ定義型のカスタマイゼーション・ポイント(customization point)を定める。*4
- yield文とco_return文はユーザ定義 “コルーチン型(coroutine type)” を介して、await文と
co_await範囲for文はユーザ定義 “Awaitable型” を介してその振る舞いを定義する。 - コルーチン外部仕様としては、コルーチン制御フローのための “コルーチンハンドル(coroutine handle)*5” を内包した “コルーチン型(coroutine type)” を返す。
- コルーチン内部実装では、ユーザ定義 “プロミス型(promise type)” の “プロミスオブジェクト(promise object)” が暗黙に生成され、“コルーチン型(coroutine type)” 戻り値との紐付けを行う。
- コルーチン制御フローは
co_yield
やco_await
によって中断(suspend)され、“コルーチンハンドル(coroutine handle)” を介して再開(resume)される。 - コルーチン(coroutine)とスレッド(thread)は直交した機能。あるスレッドで中断(suspend)したコルーチンを、他スレッド上にて再開(resume)可能。*6
- yield文とco_return文はユーザ定義 “コルーチン型(coroutine type)” を介して、await文と
- スタックレス・コルーチン(stackless coroutine)のみサポート。
- 非対称(asymmetric)/対称(symmetric)コルーチンいずれもサポート。*7
- コルーチン内部実装では、暗黙に “コルーチン・フレーム(corotuine frame)” 動的メモリ確保/解放処理が行われる。ただし、一定条件をみたせばコンパイラによるヒープ確保省略最適化(heap allocation elision optimization; HALO)を期待できる。*8
- 2022-06-27追記:HALOは "coroutine elision" とも呼称される。*9
ノート:アプリケーション開発者視点でのC++コルーチン拡張の恩恵は、lewissbaker/cppcoroライブラリやP0975R0 Impact of coroutines on current and upcoming library facilitiesが参考になる。低レベルAPIを用いたナイーブ実装はC++コルーチン拡張メモ参照。
関連URL
- P0978R0 A Response to "P0973r0: Coroutines TS Use Cases and Design Issues"
- (PDF)await/yield: C++ coroutines
- (PDF)C++ Coroutines - Under The Covers, CppCon 2016
- (PDF)Putting Coroutines to Work with the Windows Runtime, CppCon 2016
- (PDF)Concurrency, Parallelism and Coroutines, CppCon 2017
- (PDF)Coroutines What Can't They Do, CppCon 2017
- (PDF)Naked Coroutines Live, CppCon 2017
- How C++ coroutines work — kirit.com
- C++ Coroutines: Understanding operator co_await
*1:TS(Technical Specification)から正式機能に昇格すれば、ヘッダ <coroutine> に変更される。P0912R0参照。
*2:通常関数(サブルーチン; subroutine)制御フローは呼び出し(call)/戻り(return)のみサポートするのに対し、コルーチン(coroutine)制御フローは呼び出し(call)/中断(suspend)/再開(resume)/戻り(return)に拡張されている。つまりコルーチンはより汎化された関数(サブルーチン)と解釈される。
*3:関数プロトタイプ宣言からは、通常の関数かコルーチンのいずれかを判断できない。
*4:本記事における「ユーザ定義」のユーザは、通常のアプリケーション開発者ではなく、コルーチンライブラリの実装者を意味する。
*5:std::experimental::coroutine_handle<Promise>(Promiseはプロミス型(promise type))
*6:コルーチンとスレッド併用時のスレッド安全性(thread safety)担保は、これまで同様にコルーチンライブラリ開発者およびアプリケーション開発者の責任となる。
*7:http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0913r1.html
*8:http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0981r0.html
*9:https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p2477r0.html