yield_to
coroutine::selfにyield_to()を実装しました。
まずはyield_to()の概要から説明しましょう。
typedef coro::coroutine<void()> coroutine1_type; typedef coro::coroutine<void()> coroutine2_type; void coroutine1_body(coroutine1_type::self& self); void coroutine2_body(coroutine2_type::self& self); coroutine1_type coroutine1(coroutine1_body); coroutine2_type coroutine2(coroutine2_body); void coroutine1_body(coroutine1_type::self& self) { // コルーチン2に制御を移す self.yield_to(coroutine2); } void coroutine2_body(coroutine1_type::self& self) { // コルーチン1の呼び出し元(メインルーチン)に制御を戻す // コルーチン1には戻らない self.yield(); } int main() { // コルーチン1を呼び出し coroutine1(); // コルーチン2からここへ直接戻ってくる }
通常のyield()は単に呼び出し元に戻るだけですが、yield_to()は指定したコルーチンに制御を移します。
yield_to()で呼ばれたコルーチンは、yield_to()を呼んだコルーチンの呼び出し元を覚えていて、yield()した際にそこへ戻ります。
次はコルーチンに引数や戻り値がある場合です。
typedef coro::coroutine<int()> coroutine1_type; typedef coro::coroutine<int(int)> coroutine2_type; void coroutine1_body(coroutine1_type::self& self); void coroutine2_body(coroutine2_type::self& self); coroutine1_type coroutine1(coroutine1_body); coroutine2_type coroutine2(coroutine2_body); int coroutine1_body(coroutine1_type::self& self) { // コルーチン2に制御を移す self.yield_to(coroutine2, 1); self.exit(); } int coroutine2_body(coroutine1_type::self& self, int arg) { // メインルーチンに制御と結果を戻す self.yield(arg*2); self.exit(); } int main() { // 2を出力 std::cout << coroutine1() << std::endl; }
コールチン1の変わりにコルーチン2が結果を返すため、2つのコルーチンは同じ戻り値を持つ必要があります。
一方、コルーチンの引数にはそのような制限がありません。
実装は意外と簡単で、
- yield_to()をする際に呼び出し元コンテキストを交換する
- yield_to()した先で元のコルーチンに結果を格納できるよう、結果を受け取るバッファを伝播させる
の2点だけでOKでした。
むしろBoost.Preprocessorと格闘するほうが大変でした。
hamigaki/coroutine/detail/coroutine_template.hppの差分
オブジェクト間のやり取りがごちゃごちゃしていて、pimpl_がpublicになってしまいました。
要調整です。