yohhoyの日記

技術的メモをしていきたい日記

C++コルーチン拡張メモ(N4736)

プログラミング言語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演算子オーバーロード
  • 新しいヘッダ:<experimental/coroutine> *1
  • コルーチン(coroutine) == 関数途中での中断(suspend)/再開(resume)を追加サポートする関数(function)*2
  • コルーチン(coroutine) == 本体にco_yield/co_return/co_awaitキーワードを含む関数*3
    • 制限:main関数、コンストラクタ、デストラクタはコルーチンになれない。関数戻り値型推論とコルーチンは併用不可。コルーチンへのconstexpr指定不可。returnco_returnは排他的。
  • 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_yieldco_awaitによって中断(suspend)され、“コルーチンハンドル(coroutine handle)” を介して再開(resume)される。
    • コルーチン(coroutine)とスレッド(thread)は直交した機能。あるスレッドで中断(suspend)したコルーチンを、他スレッド上にて再開(resume)可能。*6
  • スタックレス・コルーチン(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

*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