2011/10/28

boost.statechart を使ってみた。

まず普通に使ってみた。

例題は、
letsboost::statechart

ここを参考にさせていただきました、いつもお世話になってますw

まずシンプルな奴。

#include <iostream>

// boost
#include <boost/statechart/state_machine.hpp> // ステートマシン
#include <boost/statechart/simple_state.hpp> // ステート
#include <boost/statechart/event.hpp> // イベント
#include <boost/statechart/transition.hpp> // 遷移
#include <boost/statechart/in_state_reaction.hpp> // イベントアクション
#include <boost/mpl/list.hpp> // イベントテーブル

using namespace boost;

namespace gate
{


namespace events
{
// イベント
class InsertedCoin : public statechart::event< InsertedCoin > {};
class PassedThrough : public statechart::event< PassedThrough > {};
};

// 初期ステートのみを前方宣言
namespace states { class Locked; }
// ステートマシン < マシン, 初期ステート >
class Gate : public statechart::state_machine< Gate, states::Locked >
{
// メインスレッド

// イベントハンドラ
};


namespace states
{
class Locked;
class Unlocked;

// ロック状態 ステート < ステート, マシン >
class Locked
: public statechart::simple_state< states::Locked, Gate >
{
public:
// イベントを引数に取る
void alarm( const events::PassedThrough& event )
{
std::cout << "ウウウウウウウウウ ウウウウウウウウウウウウ" << std::endl;
}

// イベントテーブル
typedef boost::mpl::list<
// 遷移
// InsertedCoinイベントで、Unlockedへ遷移
statechart::transition < events::InsertedCoin, states::Unlocked >,

// リアクション
// 通過イベントで、アラームを発生 ステートは維持。
statechart::in_state_reaction<
events::PassedThrough, Locked, &Locked::alarm >
> reactions;
};

// ロック状態 ステート < ステート, マシン >
class Unlocked
: public statechart::simple_state< states::Unlocked, Gate >
{
public:
void thankYou( const events::InsertedCoin& event )
{
std::cout << "ありがとう・・・ありがとう・・・" << std::endl;
}

// イベントテーブル
typedef boost::mpl::list<
// InsertedCoinイベントで、Unlockedへ遷移
statechart::transition < events::PassedThrough, states::Locked >,

// リアクション
// コイン挿入イベントで、感謝を。ステートは維持
statechart::in_state_reaction<
events::InsertedCoin, Unlocked, &Unlocked::thankYou >
> reactions;

};

} // namespace states

} // namespace gate


int main()
{
gate::Gate gate;

// ステートマシンの初期化
gate.initiate();

// とりあえずイベントを発生させてみる
gate.process_event( gate::events::InsertedCoin() );
gate.process_event( gate::events::PassedThrough() );
gate.process_event( gate::events::PassedThrough() );
gate.process_event( gate::events::InsertedCoin() );
gate.process_event( gate::events::InsertedCoin() );

return 0;
}


内容は、そのまんまで、使ってるクラスは、大きくわけて三種類
  • イベント
  • ステートマシン
  • ステート
ステートを使ってるので当たり前だけど
ステートマシン が ステート を イベントをトリガに遷移していく動作をさせます。

イベント
boost::statechart::event テンプレート
イベントクラスがこのテンプレートクラスを継承する
テンプレートには、イベントクラスを渡す。

イベントハンドラの登録
boost::statechart::in_state_reaction テンプレート
イベントクラス、イベントハンドラを持っているクラス、イベントハンドラ を設定する。
後述するイベントテーブルに渡すことで、イベント発生時に、イベントハンドラが呼び出せる

イベントをトリガに別ステートへ遷移する
boost::statechart::transition テンプレート
イベントと遷移先のステートを設定すると、イベント発生時に指定したクラスに遷移する

ステートマシン
ステートマシン
boost::statechart::state_machine テンプレート
ステートマシンクラスが、このテンプレートクラスを継承する
テンプレートには、ステートマシンクラスと、初期ステートを設定する

ステート
ステート
boost::statechart::simple_state テンプレート
ステートクラスが、このテンプレートクラスを継承する
テンプレートには、ステートクラスと、ステートマシンを渡す。

イベントテーブル
boost::mpl::list テンプレート
ここに、イベントハンドラや、状態遷移 を登録する。

といったところです。

んで、いろいろ考えて、自分なりにごにょごにょやったのがこっち。
最終的にステート使う人が、書く量を減らせるようにというのを目標にしたんですが
いまはソースが一枚になってるので、結構ごちゃごちゃしてます。

イベントは分かる人が実装して、普通使う人が
ステートだけを実装していくような用途を想定しています。

/**
* @file statechart.cpp
* @author riskrisk
* @date Fri Oct 28 17:31:30 2011
*
* @brief statechart sample
*
* commandline : g++ -lboost_thread -lpthread statechart.cpp -o statechart.bin
*/

#include <iostream>
#include <queue>

// boost
#include <boost/thread.hpp>
#include <boost/bind.hpp>

#include <boost/any.hpp>

#include <boost/statechart/state_machine.hpp> // ステートマシン
#include <boost/statechart/simple_state.hpp> // ステート
#include <boost/statechart/event.hpp> // イベント
#include <boost/statechart/transition.hpp> // 遷移
#include <boost/statechart/in_state_reaction.hpp> // イベントアクション
#include <boost/mpl/list.hpp> // イベントテーブル

using namespace boost;

namespace gate
{

// イベント
namespace events
{
// コイン挿入
// イベント( event< Event > )
class InsertedCoin : public statechart::event< InsertedCoin > {};
// イベントハンドラ
class InsertedCoinHandler
{
public:
// イベントハンドラ( onXxx( const Event& ) )
virtual void onInsertCoin( const InsertedCoin& event ) = 0;
// ハンドラ登録( in_state_reaction< Event, HandlerClass, EventHandler > )
typedef statechart::in_state_reaction< InsertedCoin,
InsertedCoinHandler,
&InsertedCoinHandler::onInsertCoin > EventHandler;
};
// 遷移( transition< Event, TransitionState > )
template< class TARGET_STATE >
class InsertedCoinTransition
: public statechart::transition< InsertedCoin, TARGET_STATE >
{};

// 通過
// イベント( event< Event > )
class PassedThrough : public statechart::event< PassedThrough > {};
// イベントハンドラ
class PassedThroughHandler
{
public:
// イベントハンドラ( onXxx( const Event& ) )
virtual void onPassThrough( const PassedThrough& event ) = 0;
// ハンドラ登録( in_state_reaction< Event, HandlerClass, EventHandler > )
typedef statechart::in_state_reaction< PassedThrough,
PassedThroughHandler,
&PassedThroughHandler::onPassThrough > EventHandler;
};
// 遷移( transition< Event, TransitionState > )
template< class TARGET_STATE >
class PassedThroughTransition
: public statechart::transition< PassedThrough, TARGET_STATE >
{};

// イベントのシリアライズ用
typedef boost::shared_ptr< statechart::event_base > EventPtr;
};

// 初期ステートのみを宣言
namespace states { class Locked; }
// ステートマシン( state_machine < StateMachine, InitialState > )
class Gate : public statechart::state_machine< Gate, states::Locked >
{
private:

bool isExit_; // スレッドの終了
std::queue< events::EventPtr > events_; // イベントのシリアライズ

public:
// コンストラクタ
Gate()
: isExit_( false )
{}

// スレッドの終了要求
void requestExit()
{
isExit_ = true;
}

// メインスレッド
void run() {

std::cout << "machine entry" << std::endl;

// ステートマシンの初期化
initiate();

// メインループ
while( !isExit_ ) {
// イベントハンドラ
eventHandler();

// 登録イベントが無い場合は、少し待ってみる
if( isNoEvent() ) {
usleep( 100 * 1000 ); // 100msec
}
}

std::cout << "machine exit" << std::endl;
}

// イベントが無い場合に真を返す
bool isNoEvent() { return events_.empty(); }

// イベントの追加
void addEvent( events::EventPtr event ) {
events_.push( event );
}

private:

// イベントハンドラ( とても適当 )
void eventHandler() {
if( !isNoEvent() ) {
process_event( *( events_.front() ) );
events_.pop();
}
}
};

// ステート
namespace states
{
class Locked;
class Unlocked;

// ロック状態( simple_state< State, Machine > )
class Locked
: public statechart::simple_state< Locked, Gate >
, public events::PassedThroughHandler
{
public:

// イベントハンドラ
// 通過
virtual void onPassThrough( const events::PassedThrough& event )
{
// アラームを鳴らそう
std::cout << "ウウウウウウウウウ ウウウウウウウウウウウウ" << std::endl;
}

// イベントテーブル
typedef mpl::list<

// 遷移
// InsertedCoinイベント -> Unlockedステート
events::InsertedCoinTransition< Unlocked >,

// イベントハンドラ
// PassedThroughイベント
events::PassedThroughHandler::EventHandler
> reactions;
};

// アンロック状態( simple_state< State, Machine > )
class Unlocked
: public statechart::simple_state< Unlocked, Gate >
, public events::InsertedCoinHandler
{
public:

// イベントハンドラ
// コイン挿入
virtual void onInsertCoin( const events::InsertedCoin& event )
{
std::cout << "ありがとう・・・ありがとう・・・" << std::endl;
}

// イベントテーブル
typedef mpl::list<
// PassedThroughイベント ->、Unlockedステート
events::PassedThroughTransition< Locked >,

// イベントハンドラコール
// InsertedCoinイベント
events::InsertedCoinHandler::EventHandler
> reactions;
};

} // namespace states

} // namespace gate


int main()
{
gate::Gate gate;

// ざっくりとイベントを登録
gate.addEvent( gate::events::EventPtr( new gate::events::InsertedCoin ) );
gate.addEvent( gate::events::EventPtr( new gate::events::PassedThrough ) );
gate.addEvent( gate::events::EventPtr( new gate::events::PassedThrough ) );
gate.addEvent( gate::events::EventPtr( new gate::events::InsertedCoin ) );
gate.addEvent( gate::events::EventPtr( new gate::events::InsertedCoin ) );


std::cout << "start" << std::endl;

// ステートマシンを起動
boost::thread gateThread( boost::bind( &gate::Gate::run, &gate ) );

// ちょっとだけ待っておく
sleep( 1 );

// 終了要求
gate.requestExit();
// 終了待ち
gateThread.join();

std::cout << "end" << std::endl;

return 0;
}

// EOF


0 件のコメント:

コメントを投稿