Boost

2012年2月 4日 (土)

boost.contextを使ってみました。

以前、setjmp / longjmp関数を使用してコルーチンを実現してみましたが、スタックの関係上、コルーチン内部では、クラスインスタンスが使えないという問題がありました。

今回、boost.contextというものがあるのを知りましたので、試してみました。

以下、ソースになります。

#include <iostream>
#include <string>
#include <boost/context/all.hpp>
using namespace std;
namespace APP {
class CSub {
public:
    void print( int n ) {
        cout << "(" << m_name << ") -> " << n << endl;
    }
public:
    CSub( const string& name )
        : m_name( name )
    { cout << "CSub() : " << m_name << endl; }
    ~CSub() { cout << "~CSub() : " << m_name << endl; }
private:
    string m_name;
};
class CMain {
private:
    static void torampoline( CMain* self ) {
        self->function();
    }
private:
    void yield( void ) {
        m_context.suspend();
    }
    void yield_break( void ) {
        m_complete = true;
        m_context.suspend();
    }
private:
    void function( void ) {
        CSub sub_1( "_1" );
        for( int n=0; n<10; ++n ) {
            sub_1.print( n );
            yield();
        }
        for( int n=0; n<10; ++n ) {
            CSub( "_2" ).print( n );
            if( n != 5 ) {
                yield();
            }
            else {
                yield_break();
            }
        }
    }
public:
    int run( void ) {
        m_context = boost::contexts::context(
            &CMain::torampoline, this,
            boost::contexts::default_stacksize(),
            boost::contexts::no_stack_unwind, boost::contexts::return_to_caller );
        m_complete  = false;
        m_context.start();
        while( !m_complete && !m_context.is_complete() ) {
            m_context.resume();
        }
        if( !m_context.is_complete() ) {
            m_context.unwind_stack();
        }
        return 0;
    }
public:
    CMain() { ; }
    ~CMain() { ; }
private:
    boost::contexts::context m_context;
    bool                     m_complete;
};
}   /* APP */
static APP::CMain g_MainInst;
int main( int argc, char* argv[] ) {
    return g_MainInst.run();
}
実行結果です。
CSub() : _1
(_1) -> 0
(_1) -> 1
(_1) -> 2
(_1) -> 3
(_1) -> 4
(_1) -> 5
(_1) -> 6
(_1) -> 7
(_1) -> 8
(_1) -> 9
CSub() : _2
(_2) -> 0
~CSub() : _2
CSub() : _2
(_2) -> 1
~CSub() : _2
CSub() : _2
(_2) -> 2
~CSub() : _2
CSub() : _2
(_2) -> 3
~CSub() : _2
CSub() : _2
(_2) -> 4
~CSub() : _2
CSub() : _2
(_2) -> 5
~CSub() : _2
~CSub() : _1
すばらしい!!

| | コメント (0) | トラックバック (0)

2009年12月19日 (土)

ちょっと書式指定文字列を調べた

int A = 10; という変数があって、このAの内容を3桁0埋めで文字列化したいとします。つまるところ、『010』となればOKです。

ぱっと思い浮かぶのは、以下のようなものでしょうか。

char temp[256];
::sprintf( "%03d", A );

う~ん、いかにも、C言語ですね。やっぱり、文字列を扱うならstd::stringを使いたいですね。では、もう一歩前進して、C++っぽくいきます。

std::stringstream stream;
stream << std::setw( 3 ) << std::setfill('0') << A;
std::string temp = stream.str();

ストリームを作ってデータを流し込んであげます。このコードは、::sprintf()を使うのと比べて、大きな利点があります。なんといっても、文字列の容量を気にしなくても良い点でしょう。::sprintf()を使用する場合、文字列を入れるバッファを指定しますが、文字列がバッファの容量を超えてしまってはいけないのです。

でも、書式指定がちょっとわかりにくいですね。std::setw()で出力幅を指定して、std::setfill()で空きには、0を入れるというような長いコードをかかなくてはいけないですから。上記コードは1変数の変換のみですけど、いろんな書式を10個ぐらい変換するといったら、軽くパニックですね。::sprintf()だったら、『%d』とか『%3d』とか『%02X』とか指定すればいいので、楽かつ、わかりやすいのですが。

では、さらに、もう一歩進みましょう。

ここで、Boostのformatを使ってみます。以下、コードです。

std::stringstream stream;
stream << boost::format( "%03d" ) % A;
std::string temp = stream.str();

書式指定部分が::sprintf()みたいになり、すっきりしました。boost::format()で書式を指定したあとに、変換対象物を流し込むのですが、ここで、『<<』ではなく、『%』になっています(Aの送り先は、streamではなく、boost::format()になります)ので、注意が必要です。

すっきりしたので、今回はここまで。

ちなみに、Boostというのは、『boost wiki』みたいな感じでググってみてください。

| | コメント (0) | トラックバック (0)