00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011 #if !defined(_XRB_STATEMACHINE_HPP_)
00012 #define _XRB_STATEMACHINE_HPP_
00013
00014 #include "xrb.hpp"
00015
00016 namespace Xrb
00017 {
00018
00019
00020 enum
00021 {
00022 SM_ENTER = static_cast<StateMachineInput>(-1),
00023 SM_EXIT = static_cast<StateMachineInput>(-2),
00024
00025 SM_HIGHEST_USER_INPUT_VALUE = static_cast<StateMachineInput>(-3)
00026 };
00027
00028
00029
00030 template <typename OwnerClass>
00031 class StateMachine
00032 {
00033 public:
00034
00035 typedef bool (OwnerClass::*State)(StateMachineInput);
00036
00037 StateMachine (OwnerClass *owner_class);
00038 ~StateMachine ();
00039
00040 bool IsInitialized () const { return m_current_state != NULL; }
00041 State CurrentState () const { return m_current_state; }
00042
00043 void Initialize (State initial_state);
00044 void RunCurrentState (StateMachineInput input);
00045 void Shutdown ();
00046
00047 void SetNextState (State state);
00048
00049 private:
00050
00051 void RunCurrentStatePrivate (StateMachineInput input);
00052
00053 OwnerClass *m_owner_class;
00054 bool m_is_running_a_state;
00055 State m_current_state;
00056 State m_next_state;
00057 };
00058
00059 template <typename OwnerClass>
00060 StateMachine<OwnerClass>::StateMachine (OwnerClass *const owner_class)
00061 {
00062 ASSERT1(owner_class != NULL);
00063 m_owner_class = owner_class;
00064 m_is_running_a_state = false;
00065 m_current_state = NULL;
00066 m_next_state = NULL;
00067 }
00068
00069 template <typename OwnerClass>
00070 StateMachine<OwnerClass>::~StateMachine ()
00071 {
00072 Shutdown();
00073 }
00074
00075 template <typename OwnerClass>
00076 void StateMachine<OwnerClass>::Initialize (State initial_state)
00077 {
00078
00079 ASSERT1(!m_is_running_a_state && "This method should not be used from inside a state");
00080 ASSERT1(m_current_state == NULL && "This state machine is already initialized");
00081
00082
00083 m_current_state = initial_state;
00084 RunCurrentStatePrivate(SM_ENTER);
00085 }
00086
00087 template <typename OwnerClass>
00088 void StateMachine<OwnerClass>::RunCurrentState (StateMachineInput const input)
00089 {
00090 ASSERT1(input <= SM_HIGHEST_USER_INPUT_VALUE && "Users are not allowed to send state-machine-defined input");
00091 RunCurrentStatePrivate(input);
00092 }
00093
00094 template <typename OwnerClass>
00095 void StateMachine<OwnerClass>::Shutdown ()
00096 {
00097 ASSERT1(!m_is_running_a_state);
00098
00099
00100 if (m_current_state != NULL)
00101 {
00102
00103 RunCurrentStatePrivate(SM_EXIT);
00104 m_current_state = NULL;
00105 }
00106 }
00107
00108 template <typename OwnerClass>
00109 void StateMachine<OwnerClass>::SetNextState (State state)
00110 {
00111 ASSERT1(m_is_running_a_state && "This method should only be used from inside a state");
00112 ASSERT1(m_current_state != NULL && "This state machine has not been initialized");
00113
00114
00115
00116 m_next_state = state;
00117 }
00118
00119 template <typename OwnerClass>
00120 void StateMachine<OwnerClass>::RunCurrentStatePrivate (StateMachineInput const input)
00121 {
00122 ASSERT1(!m_is_running_a_state && "This method should not be used from inside a state");
00123 ASSERT1(m_current_state != NULL && "This state machine has not been initialized");
00124
00125
00126 m_next_state = NULL;
00127
00128
00129 m_is_running_a_state = true;
00130 bool state_handled_the_input = (m_owner_class->*m_current_state)(input);
00131 m_is_running_a_state = false;
00132
00133
00134 if (input <= SM_HIGHEST_USER_INPUT_VALUE)
00135 ASSERT0(state_handled_the_input && "All user-defined state machine input must be handled");
00136
00137 if (input == SM_EXIT && m_next_state != NULL)
00138 ASSERT0(false && "You must not transition while exiting a state");
00139
00140
00141
00142 while (m_next_state != NULL)
00143 {
00144
00145
00146
00147 State real_next_state = m_next_state;
00148 m_next_state = NULL;
00149
00150 m_is_running_a_state = true;
00151 (m_owner_class->*m_current_state)(SM_EXIT);
00152 m_is_running_a_state = false;
00153
00154 ASSERT0(m_next_state == NULL && "You must not transition while exiting a state");
00155
00156
00157 m_current_state = real_next_state;
00158
00159 m_is_running_a_state = true;
00160 (m_owner_class->*m_current_state)(SM_ENTER);
00161 m_is_running_a_state = false;
00162 }
00163 }
00164
00165 }
00166
00167 #endif // !defined(_XRB_STATEMACHINE_HPP_)
00168