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になってしまいました。
要調整です。