Boost.Coroutine
Boost.CoroutineがBoost 1.55で新しいインターフェイスを搭載したとの事なので、何をどう書けばどう動くかを書いてみます。
pull_type/push_type?
今までのBoost.Coroutineは何か良く分からないシグネチャを持ったcoroutine関連らしきクラスを引数として、それを双方向にぐにゃぐにゃやる必要があって非常に面倒でした。
しかし流石に、作者の人も使いにくいのが分かっていたらしく、今回大幅に整理してくれたようです。
まず、双方向なアクセスが出来たクラス(caller_type)を廃止し、値を外に返す側(push_type)と値を取ってくる側(pull_type)の片方向x2に変更されました。
基本的には、
// 値を渡す側 boost::coroutines::coroutine<渡したい値の型>::push_type // 値を受け取る側 boost::coroutines::coroutine<受け取りたい値の型>::pull_type // ※当然ながら、「渡したい値の型==受け取りたい値の型」であること。
の2つを対にして使う、と覚えておけばいいと思います。
それでは、値を受け取る方向のサンプルコードと、値を渡す方向のサンプルコードを書いてみます。
ジェネレータ
まず、Coroutineの一般的な用途でよく使われそうな【値を1個ずつ生成する関数】(値を受け取る方向)を書く方法から。
いわゆる「ジェネレータ」という奴で、必要になる度に1個ずつ値を取ってきたい時に使う奴ですね。
#include <iostream> #include <boost/coroutine/all.hpp> using namespace std; // 実行する関数 void Action(boost::coroutines::coroutine<int>::push_type& yield) { // 1回呼ばれる毎に0,1,2,3,4 と値を1つずつ返す for (int i = 0; i < 5; i++) { yield(i); } } // range-based for void Sample1() { boost::coroutines::coroutine<int>::pull_type co(Action); cout << "start loop." << endl; for (auto& item : co) { cout << item << endl; } } // イテレータアクセス void Sample2() { boost::coroutines::coroutine<int>::pull_type co(Action); cout << "start loop." << endl; for (auto it = boost::begin(co); it != boost::end(co); ++it) { cout << (*it) << endl; } } // 値取れるかチェック->get->次のyield void Sample3() { boost::coroutines::coroutine<int>::pull_type co(Action); cout << "start loop." << endl; while (co.has_result()) { cout << co.get() << endl; co(); } } int main() { cout << "- - - - - - - " << endl; Sample1(); cout << "- - - - - - - " << endl; Sample2(); cout << "- - - - - - - " << endl; Sample3(); cout << "- - - - - - - " << endl; return 0; }
出力結果:
- - - - - - - start loop. 0 1 2 3 4 - - - - - - - start loop. 0 1 2 3 4 - - - - - - - start loop. 0 1 2 3 4 - - - - - - -
数値を渡していくやつ
次は、(こういうアレの名前知らないんですが…)値を渡していく奴を書いてみます。
処理としては、値を1つずつ関数に渡していって、関数は受け取った値と、今までの合計を標準出力に吐き出し続ける感じ(値を渡す方向)です。
先ほどのコードとは、pull_typeとpush_typeの位置が逆転してる事が分かります。
#include <iostream> #include <boost/coroutine/all.hpp> using namespace std; // 実行する関数1 void Sample1(boost::coroutines::coroutine<int>::pull_type& co) { int total = 0; for(auto& item : co) { cout << item << endl; total += item; cout << "total:" << total << endl; } } // 実行する関数2 void Sample2(boost::coroutines::coroutine<int>::pull_type& co) { int total = 0; for (auto it = boost::begin(co); it != boost::end(co); ++it) { cout << (*it) << endl; total += (*it); cout << "total:" << total << endl; } } // 実行する関数3 void Sample3(boost::coroutines::coroutine<int>::pull_type& co) { int total = 0; while (co.has_result()){ cout << co.get() << endl; total += co.get(); cout << "total:" << total << endl; co(); } } int main() { cout << "- - - - - - - " << endl; boost::coroutines::coroutine<int>::push_type yield1(Sample1); cout << "start loop." << endl; for (int i = 1; i <= 5; i++) { yield1(i); } cout << "- - - - - - - " << endl; boost::coroutines::coroutine<int>::push_type yield2(Sample2); cout << "start loop." << endl; for (int i = 1; i <= 5; i++) { yield2(i); } cout << "- - - - - - - " << endl; boost::coroutines::coroutine<int>::push_type yield3(Sample3); cout << "start loop." << endl; for (int i = 1; i <= 5; i++) { yield3(i); } return 0; }
出力結果:
- - - - - - - start loop. 1 total:1 2 total:3 3 total:6 4 total:10 5 total:15 - - - - - - - start loop. 1 total:1 2 total:3 3 total:6 4 total:10 5 total:15 - - - - - - - start loop. 1 total:1 2 total:3 3 total:6 4 total:10 5 total:15
実に対照的なので分かりやすい。
動的ゲームでよく使いたい奴
そして(個人的には)本題の、動的ゲームでよく使うような使い方。
DXライブラリ導入してコピペで動くヤツが以下。
#include "DxLib.h" #include <boost/coroutine/all.hpp> class Actor{ public: Actor() : co(Action) {} void Update() { // カウントが0以下の時、Actionを実行 if (--count <= 0) { co(this); } } void Draw() { DrawCircle(x, y, 30, GetColor(0, 0, 255), TRUE); } private: static void Action(boost::coroutines::coroutine<Actor*>::pull_type& yield) { while (true) { // x方向に5秒使って100px進む for (size_t i = 0; i < 5 * 60; i++) { yield.get()->x++; yield(); } // 1秒止まる yield.get()->count = 60; yield(); // y方向に5秒使って100px進む for (size_t i = 0; i < 5 * 60; i++) { yield.get()->y++; yield(); } // 1秒止まる yield.get()->count = 60; yield(); // x方向に5秒使って100px戻る for (size_t i = 0; i < 5 * 60; i++) { yield.get()->x--; yield(); } // 1秒止まる yield.get()->count = 60; yield(); // y方向に5秒使って100px戻る for (size_t i = 0; i < 5 * 60; i++) { yield.get()->y--; yield(); } // 1秒止まる yield.get()->count = 60; yield(); } } private: int count = 0; int x = 100; int y = 100; boost::coroutines::coroutine<Actor*>::push_type co; }; int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { // ウインドウモードに変更 ChangeWindowMode(TRUE); if (DxLib_Init() == -1) // DXライブラリ初期化処理 { return -1; // エラーが起きたら直ちに終了 } SetDrawScreen(DX_SCREEN_BACK); // Actor実体化 Actor actor; // Zキーの入力待ち while (CheckHitKey(KEY_INPUT_Z) == 0 && ProcessMessage() != -1) { ClearDrawScreen(); // □な感じに移動し続ける actor.Update(); actor.Draw(); ScreenFlip(); } DxLib_End(); // DXライブラリ使用の終了処理 return 0; // ソフトの終了 }
"yield.get()->"が凄く汚くて嫌なので、たぶん実際にゲーム用のコード書くときはもっとマシな方法模索すると思いますが…。
まあ、そんな感じで一応動くんじゃないでしょうか!