例題は、
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 件のコメント:
コメントを投稿