Boost.Context ファイルの継続的な読み込み

「Boost.Contextの使い道がわからない」という声をちらほら聞くので、ちょっと実用的なサンプル。
わかりやすいのがリアルタイムゲームだと思ったので、ゲームループ(1/60秒での定期実行)で少しずつファイルを読んでいく処理を書きました。これと同じようにして、シューティングゲームやパチンコなどでの弾道なんかにも使えますね。


【a.txt】

aaa
bbb
ccc
#include <iostream>
#include <fstream>
#include <boost/bind.hpp>
#include <boost/ref.hpp>
#include <boost/asio.hpp>
#include <boost/asio/steady_timer.hpp>
#include "continuation.hpp"

namespace asio = boost::asio;
namespace chrono = boost::chrono;

class Game {
    std::vector<std::string> data;
    continuation cont;
public:
    Game()
        : cont(boost::bind(&Game::load_file, this))
    {
    }

    void update()
    {
        if (!cont.is_complete()) {
            cont.resume();
            std::cout << data.back() << std::endl;
        }
    }

    void draw()
    {
    }

private:
    void load_file()
    {
        std::ifstream file("a.txt");
        std::string line;
        while (std::getline(file, line)) {
            data.push_back(line);
            if (file.peek() != EOF) {
                cont.suspend();
            }
        }
    }
};

const chrono::milliseconds  
    timer_duration(static_cast<int>(1.0 / 60.0 * 1000));

void on_timer(Game& game, asio::steady_timer& timer)
{
    game.update();
    game.draw();

    timer.expires_from_now(timer_duration);
    timer.async_wait(
        boost::bind(&on_timer, boost::ref(game), boost::ref(timer)));
}

int main()
{
    Game game;

    asio::io_service io_service;
    asio::steady_timer timer(io_service);

    timer.expires_from_now(timer_duration);
    timer.async_wait(
        boost::bind(&on_timer, boost::ref(game), boost::ref(timer)));

    io_service.run();
}
aaa
bbb
ccc

ここでは、fstreamの変数をローカル変数にして、Boost.Contextでsuspendしてスタックを保存し、次回呼ばれたときにfstreamの変数が前回呼ばれた状態から再開するようにしてます。


continuationクラスは、このブログで何回か使ってきたので省略します。ファイルリソースの破棄をする必要があるので、stack_unwindにしてデストラクタが自動的に呼ばれるようにしてます。


追記 2012/03/13 11:14
継続(Boost.Context)を使わなかった場合:

#include <iostream>
#include <fstream>
#include <boost/bind.hpp>
#include <boost/ref.hpp>
#include <boost/asio.hpp>
#include <boost/asio/steady_timer.hpp>
#include <boost/utility/value_init.hpp>

namespace asio = boost::asio;
namespace chrono = boost::chrono;

class Game {
    std::vector<std::string> data;
    std::ifstream file;
    boost::initialized<bool> is_loaded;
public:
    void update()
    {
        if (!is_loaded.data()) {
            load_file();
            std::cout << data.back() << std::endl;
        }
    }

    void draw()
    {
    }

private:
    void load_file()
    {
        if (is_loaded.data()) {
            return;
        }

        if (!file.is_open()) {
            file.open("a.txt");
        }

        std::string line;
        if (!std::getline(file, line)) {
            is_loaded.data() = true;
            file.close();
            return;
        }
        data.push_back(line);

        if (file.peek() == EOF) {
            is_loaded.data() = true;
            file.close();
        }
    }
};

const chrono::milliseconds  
    timer_duration(static_cast<int>(1.0 / 60.0 * 1000));

void on_timer(Game& game, asio::steady_timer& timer)
{
    game.update();
    game.draw();

    timer.expires_from_now(timer_duration);
    timer.async_wait(
        boost::bind(&on_timer, boost::ref(game), boost::ref(timer)));
}

int main()
{
    Game game;

    asio::io_service io_service;
    asio::steady_timer timer(io_service);

    timer.expires_from_now(timer_duration);
    timer.async_wait(
        boost::bind(&on_timer, boost::ref(game), boost::ref(timer)));

    io_service.run();
}