## チュートリアル

### コルーチン

コルーチンとは、処理を中断したり、再開したりすることのできるサブルーチンの変種である。

Hamigaki.Coroutineはコルーチンを作成するためのクラステンプレート`coroutine`を提供している。ここでは`coroutine`を用いて掛け算九九の答えを表示するプログラムを考える。

    #include <hamigaki/coroutine/coroutine.hpp>

これは`coroutine`を使用するために必要である。

    namespace coro = hamigaki::coroutines;

    typedef coro::coroutine<int()> coroutine_type;

`coroutine`は名前空間`hamigaki::coroutines`で定義される。唯一のテンプレート引数はコルーチンの型を指定するためのものである。ここでは`int`型の戻り値を持ち、引数を持たないコルーチンを使用する。

    int kuku_body(coroutine_type::self& self)
    {
        for (int i = 1; i <= 9; ++i)
            for (int j = 1; j <= 9; ++j)
                self.yield(i*j);

        self.exit();
    }

関数`kuku_body()`はコルーチンの本体である。この関数の型は`coroutine`のテンプレート引数に指定したものに`coroutine_type::self&`型の引数を追加したものになる。この追加された引数はコルーチンの制御を行うために使用される。

メンバ関数`yield()`はコルーチンの呼び出し側に制御を戻し、引数をコルーチンの計算結果として返す関数である。

また、メンバ関数`exit()`はコルーチンの実行を中断し、呼び出し側に制御を戻す関数である。

    #include <iostream>

    int main()
    {
        try
        {
            coroutine_type kuku(kuku_body);
            while (true)
                std::cout << kuku() << std::endl;
        }
        catch (const coro::coroutine_exited&)
        {
        }
    }

`coroutine`のコンストラクタの引数にはコルーチンの本体を指定する。

作成された`coroutine`は関数オブジェクトとして機能する。コルーチンを呼び出すと、制御がコルーチン本体へと移る。コルーチン側は`yield()`が呼ばれるか、`return`されるまで実行され、その結果が呼び出し側に返される。再びコルーチンを呼び出すと、前回`yield()`を呼んだ箇所から実行が再開される。

メンバ関数`exit()`が呼ぶか、コルーチンから`return`すると、コルーチンは終了する。終了したコルーチンを呼び出すと、例外`coroutine_exited`が発生する。コルーチンはその性質上、呼び出すまで終了したかどうか分からないので、この例外を捕捉することで終了を検知する。

別の方法として、コルーチンの呼び出し時に`std::nothrow`を引数として指定することもできる。この場合、戻り値が`boost::optional`になり、終了を検知した場合は空となる。

    #include <iostream>

    int main()
    {
        coroutine_type kuku(kuku_body);
        while (boost::optional<int> next = kuku(std::nothrow))
            std::cout << *next << std::endl;
    }

完全なプログラムは以下のようになる。

    #include <hamigaki/coroutine/coroutine.hpp>

    namespace coro = hamigaki::coroutines;

    typedef coro::coroutine<int()> coroutine_type;

    int kuku_body(coroutine_type::self& self)
    {
        for (int i = 1; i <= 9; ++i)
            for (int j = 1; j <= 9; ++j)
                self.yield(i*j);

        self.exit();
    }

    #include <iostream>

    int main()
    {
        try
        {
            coroutine_type kuku(kuku_body);
            while (true)
                std::cout << kuku() << std::endl;
        }
        catch (const coro::coroutine_exited&)
        {
        }
    }

### ジェネレータ

オブジェクトの列を生成し、一要素ずつ順に返すサブルーチンをジェネレータと呼ぶ。

Hamigaki.Coroutineはジェネレータを作成するためにクラステンプレート`generator`を提供しており、これはコルーチンを用いて実装されている。今度は、先程の九九のプログラムをジェネレータを用いて書き直すことを考える。

    #include <hamigaki/coroutine/generator.hpp>

これは`generator`を使用するために必要である。

    namespace coro = hamigaki::coroutines;

    typedef coro::generator<int> generator_type;

`generator`のテンプレート引数はコルーチンの型ではなく、戻り値の型である。

    int kuku_body(generator_type::self& self)
    {
        for (int i = 1; i <= 9; ++i)
            for (int j = 1; j <= 9; ++j)
                self.yield(i*j);

        self.exit();
    }

関数`kuku_body()`はコルーチンの本体である。これは`coroutine`が`generator`に置き換わっただけである。

メインプログラムは次のように書ける。

    #include <algorithm>
    #include <iostream>
    #include <iterator>

    int main()
    {
        std::copy(
            generator_type(kuku_body), generator_type(),
            std::ostream_iterator<int>(std::cout, "\n")
        );
    }

`generator`のコンストラクタにコルーチンの本体を渡すことでジェネレータを作成する。省略時初期化された`generator`は終端を表すために利用される。

`generator`は入力反復子の要件を満たすので、入力反復子を受け取るアルゴリズムに渡すことができる。

### コールバック関数

C言語でオブジェクトを列挙する場合、主に次のインタフェースが利用される。

- 列挙用のハンドルを通して、順次問い合わせる(プル型)
- 各要素に対し、コールバック関数を呼ぶ(プッシュ型)

前者の例としては、POSIXの`opendir()`/`readdir()`/`closedir()`を用いたディレクトリの走査が挙げられる。このインタフェースは列挙を進めることも中断することも自由に行うことができるため、反復子で表現するのが容易である。

一方、後者のインタフェースはMicrosoft Windowsで頻繁に用いられるものである。このインタフェースは、列挙の主体がユーザー側にないため、反復子で表現することは困難である。しかし、Hamigaki.Coroutineを用いることでこれが可能となる。

例として、ウィンドウハンドル(HWND)を列挙する`EnumWindows()`関数を反復子で表現することを考える。

    #include <hamigaki/coroutine/generator.hpp>
    #include <algorithm>
    #include <iostream>
    #include <windows.h>

    namespace coro = hamigaki::coroutines;

    typedef coro::generator<HWND> enum_windows_iterator;

    BOOL CALLBACK enum_windows_callback(HWND hwnd, LPARAM lParam)
    {
        try
        {
            enum_windows_iterator::self& self =
                *reinterpret_cast<enum_windows_iterator::self*>(lParam);

            self.yield(hwnd);
            return TRUE;
        }
        catch (...)
        {
        }
        return FALSE;
    }

    HWND enum_windows_body(enum_windows_iterator::self& self)
    {
        ::EnumWindows(&enum_windows_callback, reinterpret_cast<LPARAM>(&self));
        self.exit();
    }

`enum_windows_body()`がコルーチンの本体である。`EnumWindows()`はこの中で呼び出す。`EnumWindows()`には各ウィンドウハンドル毎に呼び出すコールバック関数とそれに渡すデータを引数として渡すようになっている。コールバック関数`enum_windows_callback()`は`self`を必要とするので、ここではそのポインタを渡している。

`enum_windows_callback()`では、`yield()`でウィンドウハンドルを返し、制御を呼び出し側に戻す。

反復子が進められると処理がコルーチン側に戻り、コールバック関数からは`TRUE`が返される。これは列挙の継続を意味している。

反復子を末尾まで進めることなく破棄した場合は、`yield()`が例外`exit_exception`を送出するので、これを捕捉した後に`FALSE`を返す。これにより列挙も中断される。

`enum_windows_iterator`は入力反復子なので、次のように利用できる。

    void print_hwnd(HWND hwnd)
    {
        std::cout << static_cast<void*>(hwnd) << std::endl;
    }

    int main()
    {
        std::for_each(
            enum_windows_iterator(enum_windows_body), enum_windows_iterator(),
            print_hwnd
        );
    }

| 製作著作 © 2006-2008 Takeshi Mouri