有限状態機械(Finite State Machine:FSM)の実装
マルチスレッドなアプリケーションの状態遷移が楽になるかも?
ということで、ステートマシンを使ってみます。
参考サイト、書籍
階層型有限状態機械(Hierarchical Finite State Machine)
今回の目的はHFSMを実装することです。(上の図のようなネストされた状態遷移)
boostにもステートマシンクラスが用意されていますが(boost::statechart::state_machine)
いきなり使うのはちょっとハードルが高そうだったので、Stateパターンを使った単純なものから実装していきたいと思います。
Stateパターン
stateインターフェイス
- entry
- 入場動作。状態が開始される時
- exe
- 実行活動。状態継続中
- exit
- 退場動作。状態が終了する時
template < typename ContextType > class state_interface { public: virtual void entry( ContextType* context ) = 0; virtual void exe( ContextType* context ) = 0; virtual void exit( ContextType* context ) = 0; };
state_machineクラス
- CRTP
template < typename ContextType > class state_machine { protected: typedef ContextType context_t; typedef state_interface< ContextType > state_t; private: //開始状態 struct start : state_interface< context_t > , singleton< start > { void entry( context_t* /*context*/ ){} void exe( context_t* /*context*/ ){} void exit( context_t* /*context*/ ){} }; //終了状態 struct end : state_interface< context_t > , singleton< end > { void entry( context_t* /*context*/ ){} void exe( context_t* /*context*/ ){} void exit( context_t* /*context*/ ){} }; private: state_t* current; public: state_machine() : current( start::get_instance() ) {} virtual ~state_machine() { change_state( end::get_instance() ); } public: void update() { current->exe( derived() ); } void change_state( state_t* new_state ) { current->exit( derived() ); current = new_state; current->entry( derived() ); } ContextType* derived() { return static_cast< ContextType* >( this ); } };
singletonクラス
各状態はsingletonクラスを継承します。
template < typename T > class singleton { protected: singleton(){} singleton( singleton const& ); ~singleton(){} singleton& operator=( singleton const& ); public: static T* get_instance() { static T instance; return &instance; } };
Hello World
struct hoge : state_machine< hoge > { struct greeting : state_interface< hoge > , singleton< greeting > { void entry( context_t* /*context*/ ); void exe( context_t* /*context*/ ){} void exit( context_t* /*context*/ ); }; public: hoge(); }; hoge::hoge() { change_state( hoge::greeting::get_instance() ); } void hoge::greeting::entry( context_t* /*context*/ ) { std::cout << _T( "hello world" ); } void hoge::greeting::exit( context_t* /*context*/ ) { std::cout << _T( "bye bye world" ); } int main() { hoge h; }
実行結果
hello world bye bye world