We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF or read online on Scribd
1B] A Bo. 8 8)
rtrd
State Machine Design in C++
Its not all that hard to implement a finite-state machine, unless its very large, and you have to worry about multithreading, and .
May 01,2000
[URL hw cr. comennlsale machine deson-e-lTBA40%236
‘A.common design technique in the repertoire of most programmers isthe venerable state machine, Designers use this programming construct to break
‘complex problems into manageable states and state eansitions. There are innumerable ways to implement a state machine, Some ae simple and elegant,
folhers are more complex bu offer inereased eror checking and Dexibility.
‘A switch statoment provides one ofthe easiest to implement and most common version of state machine. Here, cach case within the switch statement
becomes a state, implemented something ike:
suiteh(currentstate)
11 do sorething in the idle stave
breaks
case’ Stst0:
7/ a3 sorething in the stop state
breaks
wee
>
‘This method i cesinly appropriate fo solving many different design problems, When employed on an even driven, multithreaded project, however,
sale machines ofthis form ean be quite limiting
“The first problem revolves around controlling what state ranstions are valid and which ones are invalid. There is no wey to enforce the stte transition
rules. Any transition is allowed at any time, which is not pertculrly desirable. For most designs only afew transition patterns ae valid. Keay the
software design should enforce these predefined sate sequences and prevent the unwanted transitions, Another problem arises when trying to send data to
specific stave. Since the entire state machine is located within a single function, sending additional dats to any given sate proves difficult. And lastly
these designs are rarely suitable for use in a mulltheaded system. The designer must ensure the state machine is called froma single thread of contol,
‘This article explores state machine design and implements a particular one using C~*, The particular implementation solves the aforementioned problems
by including support for bo internal and extemal events, event dat, and state transition validation. I is multhread-sfe. Using this simple sate machine
base class, a programmer can easly employ state machines ona system-wide bassin a uniform and thread-safe manner.
Why Use a State Maching
Implementing code using a state machines isan extremely handy design technique for solving complex engineering problems. State machines break down
the design into a series of steps, or what are called states in sate-machine ling. Each sate performs some narrowly defined task. Events, on the other
Jang ae the timull which cause the stale machine to move, or transition, between states To take a simple example, which [will use throughout this
acl, les say we are designing motor-contol software. We want to start and stop the motor, as well as change the moto’ speed. Simple enough. The
‘motor contol evens to be exposed to the elient software will be a follows:
1. Set Speed — sets the motor going at a specific speed
2. Halt — stops the motor.
‘These events provide the ability to start the motor at whatever speed desired, which aso implies changing the speed of an already moving motor. Or we
can stop the motor altogether. To the motor-control class, these two events, or function, are considered extemal events. To a cient using our code,
however, these are just plain fanetions within a elas, Thats how we want it— the client blissflly unaware of the aca implementation
“These events are not state mechine states The steps required to handle these two events are differen. In this case the states ae:
1. Tle — the motor isnot spinning bu is at rest.
+ Do nothing
2, Sart —starts the motor from a dead stop
‘+ Turn ox motor power.
+ Set motor speed
3. Change Speed — adjust the speed ofan already moving motor.‘+ Change motor speed,
4, Stop — stop a moving motor
+ Tum off motor power
+ Goto the idle state,
Bach state cates out afew specific tasks. The Start state stars the motor by fist tuming onthe power, then adjusting the speed. When changing the
speed of an already moving motor, we don't need totum the power on (its aleady on) so we just change the speed. To stop the motor we turn off the
power and transition to the Te state aaiting another command. Therefor, by breaking the motor consol into discret states, as opposed to having one
‘monolithic function, we can mors easily manage the rules of how to operate the motor.
‘To graphically illustrate the states and events, we can use a state diagram. Figure 1 shows the state transitions forthe motor contol class. A box denotes
state anda connecting arrow indicates the event transitions. Arrows with the event name listed are external events, whereas unadorned lines are
considered internal evens. (I cover the differences between internal and extemal events later in the article.)
‘Sesveed
sre 1: State transitions for the motor control class
‘As you can se, when an event comes in the state tasition that oceurs depends on state machine's curent state, When a setSpeed event comes in, for
instance, and the motor is i the Tate stat, i transitions to the start state. However, tha same SetSpeed event generated while the current state is start
‘transitions the motor to the ChangeSpeed sat. You can also so that not li sate transitions are valid. For instance, the motor can ansition from
‘angespees to Tale without fist going through the stop stat.
In short using a state machine captures and enforces complex interactions which might atherwise be difficult to convey and implement.
‘Nomenclature
[Now that Ive touched on some state machine design isues and nomenclature, I want to clarify some ofthe more important attributes of a state machine
Every state machine has the concept ofa "current state." This isthe sae the state machine curently oesupies. At any given moment i time, the state
machine canbe in only a single sate. Every instance of particular state machine class will ave the same originating state. That origination state,
however, des not execute during object eatin, Only an event sent o the state machine causes a state function fo execute,
"State fonctions" implement each sate — one state function per state-machine state, Ia thi
state-function signatures, which ae as follows
implementation al state functions must adhere to one of two
vod and efune> are the particular class and function name respectively. For example, you might choose signatures such as wok
ryclase:St_Fune(voie). The important thing here is tat the fonction return no dats (has avoid etum type) and that it has al most one input argument of|
type Eventostas (or a derived class thereof). The Cventoata pointer can designate an abject of a class that drives from EventOata, Deriving event data
‘fom the eventoata class allows the siate-machine engine to delete the data once it has been used.
“The state functions never retum a value. There is no concept ofretuming an error code when a sate function executes because the state machine is
designed to handle the event at any time, Therefore, a sate machine must always be ready to accept events
Internal and External Events
‘As mentioned earlier, an event isthe stimulus that causes a state machine to transition between sate, For instance, a button press could be an event.
Events ean be broken out int two categories: external and intemal. The extemal event, tits most basic level, sa function call ino a state-machine
‘object. These functions are public snd are called fom the outside, or from code external othe state-machine object, Any thread or task within a system
‘an generate an extemal event. If the external event Function call causes a state transition to accu, the sate will execute synchronously within the caller's
thread of control. An internal event, on the other hand, is seli-generated by the state machine itself during state execution.A typical scenario consists of an exteral event being generated, which, again, boils down toa function call nt the class's public iterface. Based upon
‘the event being generated and the state machine's eurent sate, a lookup is performed to determine ia transition is required. If so, the state machine
transitions to the new state and the code for that state executes. AL the end of the state function, a ebeck is performed wo determine whether an internal
{vent was generated, Iso, another transition is performed andthe new state ets a chance to execute. This process continues until the state machine is no
Tonger generating intemal events, at whieh time the original extemal event function call returns. The external event and all internal events, ifany, execute
‘within he callers thread of contrl
‘Once the extemal event stars the state machine executing, it cannot be interrupted by another extemal event until the external event and all internal events
have completed execution. This provides a mulithread-safe environment forthe state transitions. Semaphores or mutexes canbe usod in the state machine
engine to block other threads that might be trying tbe simultancously access the same object,
Event Data
‘When an event is generated it can optionally attach event data to be used by te sate during execution. Once the sate has completed execution, the event
data is considered used up and mast be delet, Therefore, any event data sent fo a state machine mist be created onthe heap via operator new 50 that
the state machine an delete it once wed. Tn edition, for our particular implementation the event data must inherit from the Eventbata base elas (see
Listing One). This gives the state machine engine a common base class for which to delet all event data,
Listing One: The EventData class
‘ifndef event DATA.
‘deine EVENTDRTACH
Glass Evertoata
virtual ~eventoatad) 05
fendse //event_oaTAM
‘Creating event data on the heap may seem lke @ needless step, but it allows a pointer tothe event data o travel through operating system message queues
ntl it arives ais destination. AL that point, the daa wil be used by the stale machine and subsequently deleted. This eliminates the need to send the
entire data stricture through the queue when just a pointer will do, Is aso another reason shat event function calls donot return dat, such asa status
‘ode. When the event finally aries at its destination, the call being made may have been intited from a diferent ask, or even a diferent processor,
such that asynchronous return cade will have no meaning to the calling thread,
State Transitions
‘When an extemal event i generated, a lookup is performed to determine the state transition course of action. There ave thee possible outcomes to an
event: new sate, event ignored, o cannot happen. A new sate causes a transition to a new state where its allowed to execute. Transitions to the existing
state are alo possible, which means the curent state is e-executed. Fr an ignored event, no state executes. However, the event daa, i any, i deleted
‘The last possiblity, cannot happen, i reserved for situations where the event isnot valid given the eureat sate ofthe state machine, If his occurs, the
software faults,
In this implementation, internal events are not required to perform a validating transition Jookup. The sate transition i assumed to be valid, You could
check for both valid intemal and external event transitions, but in practice this ust takes more storage space and generates busywork for very litle bene
‘The real need for validating transitions Ties inthe synchronous, extemal events where a client ean cause an event to occur at a inappropriate time, Once
the state machine is execuling, i canaot be interupled. Iie under the control of he clase’ private implementation, thereby making transition checks
"unnecessary: This gives the designer the freedoms to change states, via internal evens, without the burden of updating transition tables.
State Machine Implementation
Two base classes are necessary to use a sate machine object: Statetachine and cventoata, A class inherits from Statetachine to obtain the necessary
mechanisms to support sate transitions and event handling, The stateHacnine class alsa contains various preprocessor macros to ease implementation of|
the state machin. To send data structures or classes tothe state funetions, the structure must inherit fom the Eventoata base class
| fist present look atthe intemals ofthe Statetaenine clas, Then I show how to use it correctly, statesachine isthe hase class used for handling state
‘wansitions (se Listings Two and Three). Any clas implemented asa state machine inberits from ths class. The interface is contained within three
fanetions:
void ExternalEvent(unsigned char, FventDatat = WIL)
vod rnternalevent(unsigned char, Evereoatar = NLL)
Vintual const Statestruce® cetstatemsa0) = 8
isting Two: Defines base class for state machines
‘indo STATE MACHINE 4
fedatine STATERACHENEDS
‘include “Eventbata.
Uf base class for state machines‘
pubite:
‘Statevachine(int naxseates);
‘intent sstaterachineG) ()
protectea
fsrun { EVENT TGWORED ~ OxFE, OMNOT HAPPEN 5
Unsigned char curreneseates
oie! bxternaicvent(wisigned char, EventDatat = NULL):
‘oid Internalevent (unsigned char, EventDatar ~ NULL):
virtual const statestruct™ Getststetap() = 8;
private:
‘Const int _saxStates;
bool _evertsenerated;
Eventutar _peventoata;
vote stateehgine(voia);
typoder void (Statedachine:stateFune) Eventbata #5
statetune pstateFune;
»
‘define BEIK_STATE HAP \
pubic:
Const Statestruct® Getstatekap() (\
‘static const stateservct statenapl] =
‘dine STATE MP _ENTRVCentey)\
{Ueinterpret_easteseaterume> (entry) )y
AHdefEng EAD_STATE MAP \
(Creincerpret_eaetestaterunc>(WLL) )\
BN
Fetten astatenep[@]5
AtdefEne SEGIN_TRANSITION HP
static const unsigned char TRANSITIONS[T = (\
AideFEne TRANSITION MAP_eNTAYCentry)\
AideFEne ENO_TRANSETION, MAO (data) \
eo:
Externalevent(TRANsiTIONs[currentstate], data);
endif //STATE_PACHINE A
List
1g Three: Implements StateMachine class
sinetude cassert-hy
‘ineluge “Seaterachane.h”
Statotochine::Statetachine( int maxstates)
anaestates(raystetes),
Gineeneseateta),
receneratedt alse),
TpeversDaraqwitt)
<
}
11 generates an external event. called ence por external event
U1 fo'stare the state nacnine executing
‘od stetamachine:sexternalevent(ursighed char neaState,
ventosta™ posts)
‘
1/3 we ave supposed to ignore this event
EF (nentate == EVENT TenoKeD) {
71 just delete tne event cata, if any
4 (poata)
delete posta;
»
ase (
U1 generate the event and execute the state engine
Irceralevert(nenstate, pO3"3);
‘statetngine():
>
>
UJ generates an internal event. catted fro
11 Einetion to tranestion toa new state
‘roid Statemacnine:Taternalevent (ursigned char newState,
ventoata” poata)
vsthin a state
_pfventbata = posta;
TeversGeneratee = t¢51/ the state engine executes the state machine states
osd stataiachinesistatetngine (vote)
« 5
11 18 ~ dock semaphore here
Uf ite events are being generated Keep executing states
hale CoventGenerated)
oakatenp = -peventOata; 7 copy of event data pointer
peventData = NLL; // event dota used up, reset ptr
Teventcenenated = False; // event used up, reset #05
assert(currentstate < naxstates);
11 execute the state passing in event deta, if any
Const statestruct" pstatemap = cetstaterap();
(ihis-stpstatota[currentstate) pstateFune)(e0ataTenp);
11 Af event cata was used, then delete it
{Se (gostatenp)
delete poocaterp;
poatatenp = NULL;
>
}
11 To ~ uniock senaphoce here
?
[ExternalE5vent generates an external event to the sate machine using as arguments the new state anda pointer to an Eventoata object, ifany. The
InternalEvent function generates internal events using the same set of arguments, The GetStatevsp function retumns an array of statefunction pointers
‘which willbe retrieved by the sate engine when appropriate. This function must be implemented bythe inheriting class sine itis pure virtual. However,
:macros are provided to implement this function for us, as Iwill demonstrate shorty
StateMachine Usage
StateMachine is used as an inherited base class. The notor class i an example of how to use it (se Listings Four and Five). Yotor implements our
Ibypothetical motor-contol state machine, where clients can start the motor, at & specific spesd, and stop the motor. The SetSpeed and int public
Tunetions are the extemal events into the otor sate machine, Note that to the caller an extemal event is just function eal. Te state machine
{implementation details are hidden from the cliem’ view ofthis class. Setspeed takes event data, as evidenced by the pointer tothe ntonbata structure,
‘whieh contains the motor speed. This data structure willbe deleted upon completion of the state processing o ts imperative thatthe structure inerit
{rom tventData and be created using operator new before the function cal s made
Listing Four: Defines Motor state machine class
#5indef nor08 4
‘define MOTOR
‘include "seaekachine.t"
UY stoucture to hold event data passed into state achine
Steet Motoroata's public eventoote
nt speeds
»
Uf the wotor state machine class
‘lass motor = pubite Statekachine
‘
public:
otor() + Statetachine(ST_MaK STATES) <)
17 external events taken by this state machine
foie mares
oid SetSpeed(Motorostat);
private!
71 state machine state functions
‘oie st-tale()
ose stost090):
oie SIastart(otoroatat):
Voss StLchangespeed(Wotorbata’);
11 state nap to
‘STATE.MaP TRY (ST_1dle)
Statt_pap_eurav{stostop)
STATE_MaP_enTRY(ST-stare)
ine state function order
‘STATE_paP ENTRY (ST_changespeed)
c_ state pa
11 state enumeration order must match the order of state
1) method entries in She state TP
ee able 6x ED,
&
fendey 1100108
Listing Five: Implements Motor class
‘ineluge *hotor-A
11 ant motor external event
‘oid Motor: aletoose)
«
11 piven the Watt event, transition to now state based upon
U1 Bre current. state of the state nachine
BEGIN TRANSTTZON HAP 11 = currant state
TRANSITION WaP-ENTAY (EVENT_TenoReD) // ST_Idle
ToansrizOn_pap_ewiay (CaMOT HAPPEN) /7 SITStOP
Taansizon_pae_ewtaY (St-ST0P) U1 sistant
Teak TZ0n-pae~evTaY (SILSTOP) 11 Si-enangespees
e_ TRANSITION MAPCWULL)
UV set motor speed external event
‘roid Motors setspecd(Metordata posta)
THBNSITEON MAP ENTRY (SI_START) Jf SIIale .
Teas TLOWoaerentay (CAIMOTAHAPPEN) 77 SITStOP
TaaNsITION_waP_esTaY (Sr_CaNNce_SPEED)// STstart
TaaNsiTZOW-Wae~ewTRY {SI_CARNGE_SPEED)// SIchangespeed
n_TANSITTOLMAP(pD3t3)
11 state nacnine sits hore shen notor As not running
oid moter! 51 Edie)
<
}
11 stop the rotor
‘ose nator :51-St0p0)
«
11 perform the stop motor processing here
ij Reansition to st tle via an incernal. evert
Snvernalevent(St_i002);,
>
11 start the motor going
‘sd NotorssiStart(Hetoroatat posta)
11 set inition motor speed processing here
?
11 changes the rotor speed once the motor 1s moving
‘old Motor: :51_Changespeed(hatordats™ posta)
11 perform the change nator speed to poata-repeed here
‘Whoa the Hotor elas is created, its intial stat is ST_tte. The first call to Set Speed transitions the state machine tothe St_star® state, where the motor is
initially se into motion, Subsequent sevspeed events transition tothe st_changespeed state, where the speed ofan already moving motor is adjusted. The
ait event transitions to ST_stop, where, during state execution, an internal even is genericd to transition back to the St_saie state
‘The state-machine engine knows which state function to call by using the state map, The state map maps the currentstate varable toa specific state
‘unetion. For instance, if currentstate is 2, then the tir state-map function pointer entry will be called (counting (rom zero). The state map is ereated
using these thece macros:
aon STATE pa
‘zn, STATE_ya® starts the stale map sequence. Each STATE_fao_en7RY that follows has as an argument a slate function, whichis added tothe sate map,
xp_STATE_pa? terminates the map. The completed state map jut implements the pure virtual function Geeseatekap dafined within the Ststemachine base
class, Now the Statevachine base class ca ask fr all the state Function pointes Va this eal
[Notice that we have to use the dastardly reinterpret_cast> operator within the STATE_M4P_eNTRY macro to cast the derived clas member function pointer
{oa statevachine member function pointer. I is necessary to perform this upeast since the Statehacnine base class has 20 idea what the derived classi
So, it is imperative tht the entries provided to STATz_taP_ENTRY are really member functions of an inheriting class and that they conform tothe state
unetion signature discussed eater. Otherwise bad things will happen,“Each state function must have an enumeration associated with it. These enumeration are used to store the current state of the state machine. In Hator,
«states provides these enumerations, which are used later in the transition map. Is important thatthe enumeration order matches the order provided
Within the state map. This way, we can tie a state enumeration to a particular state function call EVENT_ToNoRED and cAMWOT_MAPPEN are two other constants
‘used in conjunction with these state enumerations.EveNT_TGIORED tells the state engine not to execute any state, just returs and do nothing, CANNOT. HAPPEN
fells the state engine to ful. This is an abnormal catastrophic fllure condition that is never suppose to occu
“The last detail to altend to ere the state transition rules. How does the state machine know whet transitions should occur? The answer isthe transition map,
‘whieh is eeated using these macros:
4, TRANSITION HAP
Exp. TRANSITION
[Each enteral even function has transition map. BEGIN_TRANSLTZON, MAP starts the map. Each TRANSITZON_MaP_eNTRY tat follows indicates whet the state
machine should do based upon the current state, The umber of entees in each tansition map must match the number of state functions exactly. In our
‘example we have four state functions, so we need four entries. The location of each entry matches the order of state functions defined within the state
‘ap. Thus, the first entry within the Hate funetion indicates an Event_1GAo2eD. This is interpreted to mean, "Ifa Halt event occurs while the current state is
Sale Ie, just ignore the eves." Similarly, the thied entry means, "L'a Halt event occurs while current is state Start, thea transition to slate Stop.”
ENO_TRANGITION_M4P terminates the map. The argument to this end macro is the event dat, if any. vat has no event data so the argument is a nll pointer,
Dut enangespeed has data soit is pessed in here
“The transition map is basically a lookup table indexed by the currentstate variable to determine the course of action, Ths information is passed tothe
xternalevent fuetion as an argument slong with the event data. When the Statetngine Function exceutes, it looks up the correct state function pointer
by calling Geeseatenap:
pstatetap = etStaterep();
“Then, based upon the curventstate variable, it calls one of the state functions inthe map:
(this->epstatenplcurrentstate].pstateFunc) (pbstsTeng)
‘After the state funetion has a chance to execute it deletes the event data, iTany, before checking to see if any intemal events were generated
Generating Events
At this point we have a working state machine class. Let's sce how to generate evens to it, An extemal event is generated by ercatng the event data
structure on th heap using ne, assigning the structure member variables, and calling the exteral event Function. Obviously, if te exteral event doesnt
fake event data then a data structure isnot created, The following code fragment shows how a synchronous calle made. You could, ofcourse, send the
pointe to the data structure along with some means of identifying the even, through an operating system message queue tobe hanled asynchronously
by the destination tsk:
otordatat posta
Doata-sspeed = 3
fotor.setSpeed(pData)s
‘To generate an intemal event ftom within a state function, call Internalevent. Ifthe destination doesn't accept event data, then the Function is called with
conly the state you want to transition te:
Internalevent(St_2018)
In the example above, once the state funetion completes execution the state machine will transition tothe St_tdle state If, on the other hand, event data
needs tobe fen tothe destination stat, then the data structure needs tobe crested onthe heap and passed in as an argument
Potorbatat pbata = new Motorbata:
poate-ospeed = 1
nternalfvent(sT UNE SPCED, pata);
Multthread Safety
‘To prevent preemption by another tread when the state machine is nthe process af execution, the StateMachine clas uses semaphores within the
statetngine function. Before the external event is allowed fo execute, the semaphore is locked. When the exierua event and all internal events have been
processed, the semaphore is unlocked, allowing another external even to enter the state machine instance.
‘Comments indicate where the semaphore lock and unlock should be placed ifthe application is multithreaded. Note that cach Staterachéne object should
have ils own instance of a semaphore. This prevents a singe instance from locking & semaphore and preveating all ober Statetachine objects fora
exeeuting.
Benefits
Timplementng a state machine using this method as opposed tothe old switch statement style may seem lke extea effort, However, the payosTis in a more
robust design that is capable of being employed uniformly over an entre multithreaded system, Having each sate in is own function provides easier
reading than a single huge switch statement, and allows unique event data tobe sen to each sat, In addition, validating state transitions prevents client
misuse by eliminating the side effects caused by unswanted state transitions,‘This implementation offers easy use for the inheriting classes. With the macros it lets you just "ur the crank" without much thought given tothe
underlying mectanies of how the state engine operates This allows you more time to concentrate on more important things, lke the design ofthe state
transitions and state function implementation
Reference
[1] Sally Shacr. Object Lifecycles (Prentice
Englewood Cliffs, ND), 1992.
David Lafreniere has designed hardware and software over the past ten years. He currently works at PropHtcad Development, Inc. a software-consulting
frm, designing software for a variety of embedded and PC-based applications. He can be reached a afrenipacbell net.