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を返しています。
より複雑なコマンドを処理する場合は、もう少しマシな手段を考えたほうが良いかもしれません。
まぁ、サンプルですのでこの辺で。