E-MailRelay

- Main Page
- Namespaces
- Classes
- Files

- File List
- File Members

gstatemachine.h
Go to the documentation of this file.
1 //
2 // Copyright (C) 2001-2018 Graeme Walker <graeme_walker@users.sourceforge.net>
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
16 // ===
17 ///
18 /// \file gstatemachine.h
19 ///
20
21 #ifndef G_STATE_MACHINE_H
22 #define G_STATE_MACHINE_H
23
24 #include "gdef.h"
25 #include "gexception.h"
26 #include <map>
27
28 namespace G
29 {
30
31 G_EXCEPTION_CLASS( StateMachine_Error , "invalid state machine transition" ) ;
32
33 /// \class StateMachine
34 /// A finite state machine class template.
35 ///
36 /// The finite state machine has a persistant 'state'. When an 'event' is
37 /// apply()d to the state machine, it undergoes a state 'transition'
38 /// and then calls the associated 'action' method.
39 ///
40 /// Any action method can return a boolean predicate value which is used to
41 /// select between two transitions -- the 'normal' transition if the predicate
42 /// is true, and an 'alternative' transition if false.
43 ///
44 /// Transition states can be implemented by having the relevant action
45 /// method call apply() on the state-machine. The state machine's state
46 /// is always changed before any action method is called -- although only
47 /// using the 'normal' transition, not the 'alternative' -- so this sort
48 /// of reentrancy is valid, as long as the action method going into
49 /// the transition state returns a predicate value of 'true'.
50 ///
51 /// Default transitions for a given state are not supported directly. But
52 /// note that protocol errors do not invalidate the state machine and
53 /// do not result in a change of state. This means that client code can
54 /// achieve the effect of default transitions by handling protocol errors
55 /// for that state in a special manner.
56 ///
57 /// Special states 'same' and 'any' can be defined to simplify the
58 /// definition of state transitions. A transition with a 'source'
59 /// state of 'any' will match any state. This is typically used
60 /// for error events or timeouts. A transition with a 'destination'
61 /// state of 'same' will not result in a state change. This is
62 /// sometimes used when handling predicates -- the predicate can
63 /// be used to control whether the state changes, or stays the
64 /// same. The 'any' state is also used as a return value from
65 /// apply() to signal a protocol error.
66 ///
67 /// If the 'any' state is numerically the largest then it can be used
68 /// to identify a default transition for the given event; transitions
69 /// identified by an exact match with the current state will be
70 /// chosen in preference to the 'any' transition.
71 ///
72 /// The 'end' state is special in that predicates are ignored for
73 /// transitions which have 'end' as their 'normal' destintation
74 /// state. This is because of a special implementation feature
75 /// which allows the state machine object to be deleted within the
76 /// action method which causes a transition to the 'end' state.
77 /// (This feature also means that transitions with an 'alternative'
78 /// state of 'end' are not valid.)
79 ///
80 /// Usage:
81 /// \code
82 /// class Protocol
83 /// {
84 /// struct ProtocolError {} ;
85 /// enum State { s_Any , s_Same , sFoo , sBar , sEnd } ;
86 /// enum Event { eFoo , eBar , eError } ;
87 /// typedef StateMachine<Protocol,State,Event> Fsm ;
88 /// Fsm m_fsm ;
89 /// void doFoo( const std::string & , bool & ) {}
90 /// void doBar( const std::string & , bool & ) { delete this ; }
91 /// Event decode( const std::string & ) const ;
92 /// public:
93 /// Protocol() : m_fsm(sFoo,sBar,s_Same,s_Any)
94 /// {
95 /// m_fsm.addTransition(eFoo,sFoo,sBar,&Protocol::doFoo) ;
96 /// m_fsm.addTransition(eBar,sBar,sEnd,&Protocol::doBar) ;
97 /// }
98 /// void apply( const std::string & event_string )
99 /// {
100 /// State s = m_fsm.apply( *this , decode(event_string) , event_string ) ;
101 /// if( s == sEnd ) return ; // this already deleted by doBar()
102 /// if( s == sAny ) throw ProtocolError() ;
103 /// }
104 /// } ;
105 /// \endcode
106 ///
107 template <typename T, typename State, typename Event, typename Arg>
108 class StateMachine
109 {
110 public:
111 typedef void (T::*Action)(Arg, bool &) ;
112 typedef StateMachine_Error Error ;
113
114 StateMachine( State s_start , State s_end , State s_same , State s_any ) ;
115 ///< Constructor.
116
117 void addTransition( Event event , State from , State to , Action action ) ;
118 ///< Adds a transition. Special semantics apply if 'from' is
119 ///< 's_any', or if 'to' is 's_same'.
120
121 void addTransition( Event event , State from , State to , Action action , State alt ) ;
122 ///< An overload which adds a transition with predicate support.
123 ///< The 'alt' state is taken as an alternative 'to' state
124 ///< if the action's predicate is returned as false.
125
126 State apply( T & t , Event event , Arg arg ) ;
127 ///< Applies an event. Calls the appropriate action method
128 ///< on object "t" and changes state. The state change
129 ///< takes into account the predicate returned by the
130 ///< action method.
131 ///<
132 ///< If the event is valid then the new state is returned.
133 ///< If the event results in a protocol error the StateMachine's
134 ///< state is unchanged, no action method is called, and
135 ///< this method returns 's_any' (see ctor).
136 ///<
137 ///< As a special implementation feature the StateMachine
138 ///< object may be deleted during the last action method
139 ///< callback (ie. the one which takes the state to the
140 ///< 's_end' state).
141
142 State state() const ;
143 ///< Returns the current state.
144
145 State reset( State new_state ) ;
146 ///< Sets the current state. Returns the old state.
147
148 private:
149 struct Transition /// A private structure used by G::StateMachine<>.
150 {
151 State from ;
152 State to ;
153 State alt ; // alternate "to" state if predicate false
154 Action action ;
155 Transition(State s1,State s2,Action a,State s3) :
156 from(s1) , to(s2) , alt(s3) , action(a) {}
157 } ;
158 typedef std::multimap<Event,Transition> Map ;
159 typedef typename Map::value_type Map_value_type ;
160 Map m_map ;
161 State m_state ;
162 State m_end ;
163 State m_same ;
164 State m_any ;
165 } ;
166
167 template <typename T, typename State, typename Event, typename Arg>
168 StateMachine<T,State,Event,Arg>::StateMachine( State s_start , State s_end , State s_same , State s_any ) :
169 m_state(s_start) ,
170 m_end(s_end) ,
171 m_same(s_same) ,
172 m_any(s_any)
173 {
174 }
175
176 template <typename T, typename State, typename Event, typename Arg>
177 void StateMachine<T,State,Event,Arg>::addTransition( Event event , State from , State to , Action action )
178 {
179 addTransition( event , from , to , action , to ) ;
180 }
181
182 template <typename T, typename State, typename Event, typename Arg>
183 void StateMachine<T,State,Event,Arg>::addTransition( Event event , State from , State to , Action action , State alt )
184 {
185 if( to == m_any || alt == m_any )
186 throw Error( "\"to any\" is invalid" ) ;
187
188 if( from == m_same )
189 throw Error( "\"from same\" is invalid" ) ;
190
191 if( to == m_end && alt != to )
192 throw Error( "predicates on end-state transitions are invalid" ) ;
193
194 if( alt == m_end && to != m_end )
195 throw Error( "false predicates cannot take you to the end state" ) ;
196
197 m_map.insert( Map_value_type( event , Transition(from,to,action,alt) ) ) ;
198 }
199
200 template <typename T, typename State, typename Event, typename Arg>
201 State StateMachine<T,State,Event,Arg>::reset( State new_state )
202 {
203 State old_state = m_state ;
204 m_state = new_state ;
205 return old_state ;
206 }
207
208 template <typename T, typename State, typename Event, typename Arg>
209 State StateMachine<T,State,Event,Arg>::state() const
210 {
211 return m_state ;
212 }
213
214 template <typename T, typename State, typename Event, typename Arg>
215 State StateMachine<T,State,Event,Arg>::apply( T & t , Event event , Arg arg )
216 {
217 State state = m_state ;
218 typename Map::iterator p = m_map.find(event) ; // look up in the multimap keyed on event + current-state
219 for( ; p != m_map.end() && (*p).first == event ; ++p )
220 {
221 if( (*p).second.from == m_any || (*p).second.from == m_state )
222 {
223 State old_state = m_state ; // change state
224 if( (*p).second.to != m_same )
225 state = m_state = (*p).second.to ;
226
227 State end = m_end ; // (avoid using members after the action method call)
228
229 bool predicate = true ;
230 (t.*((*p).second.action))( arg , predicate ) ; // perform action
231
232 if( state != end && !predicate ) // respond to predicate
233 {
234 State alt_state = (*p).second.alt ;
235 state = m_state = alt_state == m_same ? old_state : alt_state ;
236 }
237 return state ;
238 }
239 }
240 return m_any ;
241 }
242
243 }
244
245 #endif
246
G::StateMachine::apply
State apply(T &t, Event event, Arg arg)
Applies an event.
Definition: gstatemachine.h:215
G
Low-level classes.
Definition: garg.h:29
G::StateMachine::StateMachine
StateMachine(State s_start, State s_end, State s_same, State s_any)
Constructor.
Definition: gstatemachine.h:168
gexception.h
G::Arg
A class which holds a represention of the argc/argv command line array, and supports simple command-l...
Definition: garg.h:43
G::StateMachine::addTransition
void addTransition(Event event, State from, State to, Action action)
Adds a transition.
Definition: gstatemachine.h:177
G::StateMachine::reset
State reset(State new_state)
Sets the current state. Returns the old state.
Definition: gstatemachine.h:201
G::StateMachine::state
State state() const
Returns the current state.
Definition: gstatemachine.h:209
G::StateMachine
A finite state machine class template.
Definition: gstatemachine.h:108