Boost.Coroutineでスクリプト

yotto-kさんのLuaサンプルをBoost.Coroutineでやってみました。
http://www12.ocn.ne.jp/~dante98/src/non_visual_novel.cpp
スクリプト部分はほぼ同じように書けます。

void main()
{
    msg("まってくださいませ。");
    int count = 0;
    while (true)
    {
        ++count;
        write_ln((boost::format("(%1% 回目)") % count).str().c_str());
        write_ln("その あなたの たびに 口一ラも おともしとうございます。");
        write_ln("この口一ラも つれてって くださいますわね?");
        if (select("はい", "いいえ") == 0)
            break;
        msg("そんな ひどい……。");
    }
    msg("うれしゅうございます。 ぽっ");
}

今回はboost::coroutines::generatorでなく、boost::coroutines::coroutineを使っています。

namespace coro = boost::coroutines;
typedef coro::coroutine<void(int)> coroutine_type;

スクリプトから直接yield()を呼ぶと格好悪いので、

class script
{
public:
    static void run(coroutine_type::self& self, int)
    {
        script scr(self);
        scr.main();
    }

private:
    coroutine_type::self& self_;

    script(coroutine_type::self& self) : self_(self)
    {
    }

    void msg(const char* str)
    {
        ::msg(str); // 実際の仕事をする関数を呼ぶ
        self_.yield();
    }

    void main(); // 上に書いたmain
};

のようにして、スクリプトの見た目上はyield()の呼び出しが消えているわけです。
Windowsのメッセージ処理からスクリプトに戻る箇所はこうなります。

switch(command)
{
  case START:
    (*TheCroutine)(0);
    break;
  case WAIT:
    (*TheCroutine)(0);
    break;
  case SELECTION:
    {
        int selection = TheSelection.get_selection();
        TheSelection.clear();
        (*TheCroutine)(selection);
    }
    break;
  default:
    assert(0);
    break;
}

STARTとWAITは特に返す値もありませんが、intを返さないといけないので0を返しています。
より複雑なコマンドを処理する場合は、もう少しマシな手段を考えたほうが良いかもしれません。
まぁ、サンプルですのでこの辺で。