mirror of
https://github.com/boostorg/statechart.git
synced 2026-01-22 17:52:16 +00:00
- Added the function templates simple_state::clear_shallow_history() and simple_state::clear_deep_history() [SVN r22199]
1344 lines
60 KiB
HTML
1344 lines
60 KiB
HTML
<html>
|
||
|
||
<head>
|
||
<meta http-equiv="Content-Language" content="en-us">
|
||
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
|
||
<meta name="GENERATOR" content="Microsoft FrontPage 5.0">
|
||
<meta name="ProgId" content="FrontPage.Editor.Document">
|
||
<link rel="stylesheet" type="text/css" href="../../../boost.css">
|
||
<title>The boost::fsm library - Tutorial</title>
|
||
</head>
|
||
|
||
<body link="#0000ff" vlink="#800080">
|
||
|
||
<table border="0" cellpadding="7" cellspacing="0" width="100%" summary="header">
|
||
<tr>
|
||
<td valign="top" width="300">
|
||
<h3><a href="../../../index.htm">
|
||
<img alt="C++ Boost" src="../../../c++boost.gif" border="0" width="277" height="86"></a></h3>
|
||
</td>
|
||
<td valign="top">
|
||
<h1 align="center">The boost::fsm library</h1>
|
||
<h2 align="center">Tutorial</h2>
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
<hr>
|
||
<p>The Japanese translation of this tutorial can be found at
|
||
<a href="http://prdownloads.sourceforge.jp/jyugem/7127/fsm-tutorial-jp.pdf">
|
||
http://prdownloads.sourceforge.jp/jyugem/7127/fsm-tutorial-jp.pdf</a>. Kindly
|
||
contributed by Mitsuo Fukasawa.</p>
|
||
<h2>Contents</h2>
|
||
<dl class="page-index">
|
||
<dt><a href="#Introduction">Introduction</a></dt>
|
||
<dd><a href="#How to read this tutorial">How to read this tutorial</a></dd>
|
||
<dt><a href="#Hello World!">Hello World!</a></dt>
|
||
<dt><a href="#A stop watch">A stop watch</a></dt>
|
||
<dd><a href="#Defining states and events">Defining states and events</a></dd>
|
||
<dd><a href="#Adding reactions">Adding reactions</a></dd>
|
||
<dd><a href="#State-local storage">State-local storage</a></dd>
|
||
<dd><a href="#Getting state information out of the machine">Getting state
|
||
information out of the machine</a></dd>
|
||
<dt><a href="#A digital camera">A digital camera</a></dt>
|
||
<dd><a href="#Spreading a state machine over multiple translation units">
|
||
Spreading a state machine over multiple translation units</a></dd>
|
||
<dd><a href="#Guards">Guards</a></dd>
|
||
<dd><a href="#In-state reactions">In-state reactions</a></dd>
|
||
<dd><a href="#Transition actions">Transition actions</a></dd>
|
||
<dt><a href="#Advanced topics">Advanced topics</a></dt>
|
||
<dd><a href="#Specifying multiple reactions for a state">Specifying multiple
|
||
reactions for a state</a></dd>
|
||
<dd><a href="#Posting events">Posting events</a></dd>
|
||
<dd><a href="#Deferring events">Deferring events</a></dd>
|
||
<dd><a href="#History">History</a></dd>
|
||
<dd><a href="#Orthogonal states">Orthogonal states</a></dd>
|
||
<dd><a href="#State queries">State queries</a></dd>
|
||
<dd><a href="#State type information">State type information</a></dd>
|
||
<dd><a href="#Exception handling">Exception handling</a></dd>
|
||
<dd><a href="#Submachines & Parametrized States">Submachines & Parametrized
|
||
States</a></dd>
|
||
<dd><a href="#Asynchronous state machines">Asynchronous state machines</a></dd>
|
||
</dl>
|
||
<hr>
|
||
<h2><a name="Introduction">Introduction</a></h2>
|
||
<p>The boost::fsm library is a framework that allows you to quickly transform
|
||
a UML state chart into executable C++ code. This tutorial requires some
|
||
familiarity with the state machine concept and UML state charts. A nice
|
||
introduction to both can be found in
|
||
<a href="http://www.objectmentor.com/resources/articles/umlfsm.pdf">
|
||
http://www.objectmentor.com/resources/articles/umlfsm.pdf</a>. David Harel,
|
||
the inventor of state charts, presents an excellent tutorial-like but still
|
||
thorough discussion in his original paper:
|
||
<a href="http://www.wisdom.weizmann.ac.il/~dharel/SCANNED.PAPERS/Statecharts.pdf">
|
||
http://www.wisdom.weizmann.ac.il/~dharel/SCANNED.PAPERS/Statecharts.pdf</a>.
|
||
The UML specifications can be found at
|
||
<a href="http://www.omg.org/cgi-bin/doc?formal/03-03-01">
|
||
http://www.omg.org/cgi-bin/doc?formal/03-03-01</a> (see chapters 2.12 and
|
||
3.74).</p>
|
||
<p>All examples have been tested on the following platforms using boost
|
||
distribution 1.30.2:</p>
|
||
<ul>
|
||
<li>MSVC7.1 (the compiler coming with Visual Studio .NET 2003)</li>
|
||
<li>GCC3.2 (Mingw)</li>
|
||
</ul>
|
||
<h3><a name="How to read this tutorial">How to read this tutorial</a></h3>
|
||
<p>This tutorial was designed to be read linearly. First time users should
|
||
start reading right at the beginning and stop as soon as they know enough for
|
||
the task at hand. Specifically:</p>
|
||
<ul>
|
||
<li>The tutorial starts out with the <a href="#Hello World!">Hello World!</a>
|
||
and <a href="#A stop watch">stop watch</a> examples explaining the most
|
||
basic features that all users of the library should understand. Small and
|
||
simple machines with just a handful of states can be implemented reasonably
|
||
well by using just these features.</li>
|
||
<li>Afterwards, the <a href="#A digital camera">digital camera</a> example
|
||
explains the intermediate features most of which should be known by anyone
|
||
wanting to build larger machines with up to roughly a dozen states.</li>
|
||
<li>Finally, users wanting to create even more complex machines and project
|
||
architects evaluating boost::fsm should also read the
|
||
<a href="#Advanced topics">Advanced topics</a> section at the end. Moreover,
|
||
reading the <a href="rationale.html#Limitations">Limitations</a> section in
|
||
the Rationale is strongly suggested.</li>
|
||
</ul>
|
||
<h2><a name="Hello World!">Hello World!</a></h2>
|
||
<p>We follow the tradition and use the simplest possible program to make our
|
||
first steps. We will implement the following state chart:</p>
|
||
<p><img border="0" src="HelloWorld.gif" width="379" height="94"></p>
|
||
<pre>#include <boost/fsm/state_machine.hpp>
|
||
#include <boost/fsm/simple_state.hpp>
|
||
#include <iostream>
|
||
|
||
namespace fsm = boost::fsm;
|
||
|
||
struct Greeting;
|
||
struct Machine : fsm::state_machine< Machine, Greeting > {};
|
||
|
||
struct Greeting : fsm::simple_state< Greeting, Machine >
|
||
{
|
||
Greeting() { std::cout << "Hello World!\n"; } // entry
|
||
~Greeting() { std::cout << "Bye Bye World!\n"; } // exit
|
||
};
|
||
|
||
int main()
|
||
{
|
||
Machine myMachine;
|
||
myMachine.initiate();
|
||
return 0;
|
||
}</pre>
|
||
<p>This program prints <code>Hello World!</code> and <code>Bye Bye World!</code>
|
||
before exiting. The first line is printed as a result of calling <code>
|
||
initiate()</code>, which leads to the <code>Greeting</code> state begin
|
||
entered. At the end of <code>main()</code>, the <code>myMachine</code> object
|
||
is destroyed what automatically exits the <code>Greeting</code> state.</p>
|
||
<p>A few remarks:</p>
|
||
<ul>
|
||
<li>boost::fsm makes heavy use of the curiously recurring template pattern.
|
||
The deriving class must always be passed as the first parameter to the base
|
||
class template.</li>
|
||
<li>The machine is not yet running after construction. We start it by
|
||
calling <code>initiate()</code>.</li>
|
||
<li>Whenever the state machine enters a state it creates an object of the
|
||
corresponding state class. The object is then kept alive as long as the
|
||
machine remains in the state. Finally, the object is destroyed when the
|
||
state machine exits the state. Therefore, a state entry action can be
|
||
defined by adding a constructor and a state exit action can be defined by
|
||
adding a destructor.</li>
|
||
<li>All states reside in a <a href="definitions.html#Context">context</a>.
|
||
For the moment, this context is the state machine. That's why <code>Machine</code>
|
||
is passed as the second template parameter of <code>Greeting</code>'s base.</li>
|
||
<li>The state machine must be informed which state it has to enter when the
|
||
machine is initiated. That's why <code>Greeting</code> is passed as the
|
||
second template parameter of <code>Machine</code>'s base. We have to forward
|
||
declare <code>Greeting</code> for this purpose.</li>
|
||
<li>We are declaring all types as <code>struct</code>s only to avoid having
|
||
to type <code>public</code>. If you don't mind doing so, you can just as
|
||
well use <code>class</code>.</li>
|
||
</ul>
|
||
<h2><a name="A stop watch">A stop watch</a></h2>
|
||
<p>Next we will model a simple mechanical stop watch with a state machine.
|
||
Such watches typically have two buttons:</p>
|
||
<ul>
|
||
<li>Start/Stop</li>
|
||
<li>Reset</li>
|
||
</ul>
|
||
<p>And two states:</p>
|
||
<ul>
|
||
<li>Stopped: The hands reside in the position where they were last stopped.
|
||
<ul>
|
||
<li>Pressing the reset button moves the hands back to the 0 position. The
|
||
watch remains in the Stopped state.</li>
|
||
<li>Pressing the start/stop button leads to a transition to the Running
|
||
state.</li>
|
||
</ul>
|
||
</li>
|
||
<li>Running: The hands of the watch are in motion and continually show the
|
||
elapsed time.
|
||
<ul>
|
||
<li>Pressing the reset button moves the hands back to the 0 position and
|
||
leads to a transition to the Stopped state.</li>
|
||
<li>Pressing the start/stop button leads to a transition to the Stopped
|
||
state.</li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
<p>Here is one way to specify this in UML:</p>
|
||
<p><img border="0" src="StopWatch.gif" width="560" height="184"></p>
|
||
<h3><a name="Defining states and events">Defining states and events</a></h3>
|
||
<p>The two buttons are modeled by two events. Moreover, we also define the
|
||
necessary states and the initial state. <b>The following code is our starting
|
||
point, subsequent code snippets must be inserted</b>:</p>
|
||
<pre>#include <boost/fsm/event.hpp>
|
||
#include <boost/fsm/state_machine.hpp>
|
||
#include <boost/fsm/simple_state.hpp>
|
||
|
||
namespace fsm = boost::fsm;
|
||
|
||
struct EvStartStop : fsm::event< EvStartStop > {};
|
||
struct EvReset : fsm::event< EvReset > {};
|
||
|
||
struct Active;
|
||
struct StopWatch : fsm::state_machine< StopWatch, Active > {};
|
||
|
||
struct Stopped;
|
||
struct Active : fsm::simple_state< Active, StopWatch,
|
||
fsm::no_reactions, Stopped > {};
|
||
struct Running : fsm::simple_state< Running, Active > {};
|
||
struct Stopped : fsm::simple_state< Stopped, Active > {};
|
||
|
||
int main()
|
||
{
|
||
StopWatch myWatch;
|
||
myWatch.initiate();
|
||
return 0;
|
||
}</pre>
|
||
<p>This compiles but doesn't do anything observable yet. A few comments:</p>
|
||
<ul>
|
||
<li>The <code>simple_state</code> class template accepts up to four
|
||
parameters.
|
||
<ul>
|
||
<li>The third parameter specifies <a href="definitions.html#Reaction">
|
||
reactions</a> (explained in due course). Because there aren't any yet, we
|
||
pass <code>fsm::no_reactions</code>, which is also the default.</li>
|
||
<li>The fourth parameter specifies the inner initial state, if there is
|
||
one.</li>
|
||
</ul>
|
||
</li>
|
||
<li>A state is defined as an inner state simply by passing its outer state
|
||
as its context (where <a href="definitions.html#Outermost state">outermost
|
||
states</a> pass the state machine).</li>
|
||
<li>Because the context of a state must be a complete type (i.e. not forward
|
||
declared), a machine must be defined from "outside to inside". That is, we
|
||
always start with the state machine, followed by outermost states, followed
|
||
by the inner states of outermost states and so on. We can do so in a
|
||
breadth-first or depth-first way or employ a mixture of the two.<br>
|
||
Since the source and destination state of a transition often have the same
|
||
nesting depth, the pure depth-first approach tends to require a lot of
|
||
forward declarations for transition destinations while the pure
|
||
breadth-first approach tends to minimize the number of necessary forward
|
||
declarations.</li>
|
||
</ul>
|
||
<h3><a name="Adding reactions">Adding reactions</a></h3>
|
||
<p>For the moment we will use only one type of reaction: transitions. We <b>
|
||
insert</b> the bold parts of the following code:</p>
|
||
<pre><b>#include <boost/fsm/transition.hpp>
|
||
</b>
|
||
// ...
|
||
|
||
struct Stopped;
|
||
struct Active : fsm::simple_state< Active, StopWatch,
|
||
<b>fsm::transition< EvReset, Active ></b>, Stopped > {};
|
||
struct Running : fsm::simple_state< Running, Active<b>,
|
||
</b> <b>fsm::transition< EvStartStop, Stopped ></b> > {};
|
||
struct Stopped : fsm::simple_state< Stopped, Active<b>,
|
||
</b> <b>fsm::transition< EvStartStop, Running ></b> > {};
|
||
|
||
int main()
|
||
{
|
||
StopWatch myWatch;
|
||
myWatch.initiate();
|
||
<b>myWatch.process_event( EvStartStop() );
|
||
</b> <b>myWatch.process_event( EvStartStop() );
|
||
</b> <b>myWatch.process_event( EvStartStop() );
|
||
</b> <b>myWatch.process_event( EvReset() );
|
||
</b> return 0;
|
||
}</pre>
|
||
<p>A state can define an arbitrary number of reactions. That's why we have to
|
||
put them into an <code>mpl::list</code> as soon as there is more than one of
|
||
them (see <a href="#Specifying multiple reactions for a state">Specifying
|
||
multiple reactions for a state</a>).<br>
|
||
Now we have all the states and all the transitions in place and a number of
|
||
events are also sent to the stop watch. The machine dutifully makes the
|
||
transitions we would expect, but no actions are executed yet.</p>
|
||
<h3><a name="State-local storage">State-local storage</a></h3>
|
||
<p>Next we'll make the stop watch actually measure time. Depending on the
|
||
state the stop watch is in, we need different variables:</p>
|
||
<ul>
|
||
<li>Stopped: One variable holding the elapsed time</li>
|
||
<li>Running: One variable holding the elapsed time <b>and</b> one variable
|
||
storing the point in time at which the watch was last started.</li>
|
||
</ul>
|
||
<p>We observe that the elapsed time variable is needed no matter what state
|
||
the machine is in. Moreover, this variable should be reset to 0 when we send
|
||
an <code>EvReset</code> event to the machine. The other variable is only
|
||
needed while the machine is in the Running state. It should be set to the
|
||
current time of the system clock whenever we enter the Running state. Upon
|
||
exit we simply subtract the start time from the current system clock time and
|
||
add the result to the elapsed time.</p>
|
||
<pre><b>#include <ctime>
|
||
</b>
|
||
// ...
|
||
|
||
struct Stopped;
|
||
struct Active : fsm::simple_state< Active, StopWatch,
|
||
fsm::transition< EvReset, Active >, Stopped >
|
||
{
|
||
<b>public:
|
||
</b> <b>Active() : elapsedTime_( 0 ) {}
|
||
</b> <b>std::clock_t ElapsedTime() const { return elapsedTime_; }
|
||
</b> <b>std::clock_t & ElapsedTime() { return elapsedTime_; }
|
||
</b> <b>private:
|
||
</b> <b>std::clock_t elapsedTime_;
|
||
</b>};
|
||
|
||
struct Running : fsm::simple_state< Running, Active,
|
||
fsm::transition< EvStartStop, Stopped > >
|
||
{
|
||
<b>public:
|
||
</b> <b>Running() : startTime_( std::clock() ) {}
|
||
</b> <b>~Running()
|
||
</b> <b>{
|
||
</b> <b>context< Active >().ElapsedTime() +=
|
||
</b> <b>( std::clock() - startTime_ );
|
||
</b> <b>}
|
||
</b> <b>private:
|
||
</b> <b>std::clock_t startTime_;
|
||
</b>};
|
||
|
||
// ...</pre>
|
||
<p>Similar to when a derived class object accesses its base class portion,
|
||
<code>context<>()</code> is used to gain access to a direct or indirect outer
|
||
state object. The same function could be used to access the state machine
|
||
(here <code>context< StopWatch >()</code>). The rest should be mostly
|
||
self-explanatory. The machine now measures the time, but we cannot yet
|
||
retrieve it from the main program.</p>
|
||
<h3><a name="Getting state information out of the machine">Getting state
|
||
information out of the machine</a></h3>
|
||
<p>To retrieve the measured time, we need a mechanism to get state information
|
||
out of the machine. With our current machine design there are two ways to do
|
||
that. For the sake of simplicity we use the less efficient one: <code>
|
||
state_cast<>()</code>. As the name suggests, the semantics are very similar to
|
||
the ones of <code>dynamic_cast</code>. For example, when we call <code>
|
||
myWatch.state_cast< const Stopped & >()</code> <b>and</b> the machine is
|
||
currently in the Stopped state, we get a reference to the <code>Stopped</code>
|
||
state. Otherwise <code>std::bad_cast</code> is thrown. We can use this
|
||
functionality to implement a <code>StopWatch</code> member function that
|
||
returns the elapsed time. However, rather than ask the machine in which state
|
||
it is and then switch to different calculations for the elapsed time, we put
|
||
the calculation into the Stopped and Running states and use an interface to
|
||
retrieve the elapsed time:</p>
|
||
<pre><b>#include <iostream>
|
||
|
||
</b>// ...
|
||
|
||
<b>struct IElapsedTime
|
||
{
|
||
</b> <b>virtual std::clock_t ElapsedTime() const = 0;
|
||
};
|
||
|
||
</b>struct Active;
|
||
struct StopWatch : fsm::state_machine< StopWatch, Active >
|
||
{
|
||
<b>std::clock_t ElapsedTime() const
|
||
</b> <b>{
|
||
</b> <b>return state_cast< const IElapsedTime & >().ElapsedTime();
|
||
</b> <b>}
|
||
</b>};
|
||
<b>
|
||
</b>// ...
|
||
|
||
struct Running : <b>IElapsedTime,</b> fsm::simple_state<
|
||
Running, Active, fsm::transition< EvStartStop, Stopped > >
|
||
{
|
||
public:
|
||
Running() : startTime_( std::clock() ) {}
|
||
~Running()
|
||
{
|
||
<b>context< Active >().ElapsedTime() = ElapsedTime();
|
||
</b> }
|
||
<b>
|
||
</b> <b>virtual std::clock_t ElapsedTime() const
|
||
</b> <b>{
|
||
</b> <b>return context< Active >().ElapsedTime() +
|
||
</b> <b>std::clock() - startTime_;
|
||
</b> <b>}
|
||
</b> private:
|
||
std::clock_t startTime_;
|
||
};
|
||
|
||
struct Stopped : <b>IElapsedTime,</b> fsm::simple_state<
|
||
Stopped, Active, fsm::transition< EvStartStop, Running > >
|
||
{
|
||
<b>virtual std::clock_t ElapsedTime() const
|
||
</b> <b>{
|
||
</b> <b>return context< Active >().ElapsedTime();
|
||
</b> <b>}
|
||
</b>};
|
||
|
||
int main()
|
||
{
|
||
StopWatch myWatch;
|
||
myWatch.initiate();
|
||
<b>std::cout << myWatch.ElapsedTime() << "\n";
|
||
</b> myWatch.process_event( EvStartStop() );
|
||
<b>std::cout << myWatch.ElapsedTime() << "\n";
|
||
</b> myWatch.process_event( EvStartStop() );
|
||
<b>std::cout << myWatch.ElapsedTime() << "\n";
|
||
</b> myWatch.process_event( EvStartStop() );
|
||
<b>std::cout << myWatch.ElapsedTime() << "\n";
|
||
</b> myWatch.process_event( EvReset() );
|
||
<b>std::cout << myWatch.ElapsedTime() << "\n";
|
||
</b> return 0;
|
||
}</pre>
|
||
<p>To actually see time being measured, you might want to single-step through
|
||
the statements in <code>main()</code>. The StopWatch example extends this
|
||
program to an interactive console application.</p>
|
||
<h2><a name="A digital camera">A digital camera</a></h2>
|
||
<p>So far so good. However, the approach presented above has a few
|
||
limitations:</p>
|
||
<ul>
|
||
<li>Bad scalability: As soon as the compiler reaches the point where <code>
|
||
state_machine::initiate()</code> is called, a number of template
|
||
instantiations take place, which can only succeed if the full declaration of
|
||
each and every state of the machine is known. That is, the whole layout of a
|
||
state machine must be implemented in one single translation unit (actions
|
||
can be compiled separately, but this is of no importance here). For bigger
|
||
(and more real-world) state machines, this leads to the following
|
||
limitations:
|
||
<ul>
|
||
<li>At some point compilers reach their internal template instantiation
|
||
limits and give up. This can happen even for moderately-sized machines.
|
||
For example, in debug mode one popular compiler refused to compile earlier
|
||
versions of the BitMachine example for anything above 3 bits. This means
|
||
that the compiler reached its limits somewhere between 8 states, 24
|
||
transitions and 16 states, 64 transitions.</li>
|
||
<li>Multiple programmers can hardly work on the same state machine
|
||
simultaneously because every layout change will inevitably lead to a
|
||
recompilation of the whole state machine.</li>
|
||
</ul>
|
||
</li>
|
||
<li>Maximum one reaction per event: According to UML a state can have
|
||
multiple reactions triggered by the same event. This makes sense when all
|
||
reactions have mutually exclusive guards. The interface we used above only
|
||
allows for at most one unguarded reaction for each event. Moreover, the UML
|
||
concepts junction and choice point are not directly supported.</li>
|
||
<li>There is no way to specify <a href="definitions.html#In-state reaction">
|
||
in-state reactions</a>.</li>
|
||
</ul>
|
||
<p>All these limitations can be overcome with custom reactions. <b>Warning: It
|
||
is easy to abuse custom reactions up to the point of invoking undefined
|
||
behavior. Please study the documentation before employing them!</b></p>
|
||
<h3><a name="Spreading a state machine over multiple translation units">
|
||
Spreading a state machine over multiple translation units</a></h3>
|
||
<p>Let's say your company would like to develop a digital camera. The camera
|
||
has the following controls:</p>
|
||
<ul>
|
||
<li>Shutter button, which can be half-pressed and fully-pressed. The
|
||
associated events are <code>EvShutterHalf</code>, <code>EvShutterFull</code>
|
||
and <code>EvShutterReleased</code></li>
|
||
<li>Config button, represented by the <code>EvConfig</code> event</li>
|
||
<li>A number of other buttons that are not of interest here</li>
|
||
</ul>
|
||
<p>One use case for the camera says that the photographer can half-press the
|
||
shutter <b>anywhere</b> in the configuration mode and the camera will
|
||
immediately go into shooting mode. The following state chart is one way to
|
||
achieve this behavior:</p>
|
||
<p><img border="0" src="Camera.gif" width="544" height="317"></p>
|
||
<p>The Configuring and Shooting states will contain numerous nested states
|
||
while the Idle state is relatively simple. It was therefore decided to build
|
||
two teams. One will implement the shooting mode while the other will implement
|
||
the configuration mode. The two teams have already agreed on the interface
|
||
that the shooting team will use to retrieve the configuration settings. We
|
||
would like to ensure that the two teams can work with the least possible
|
||
interference. So, we put the two states in their own translation units so that
|
||
machine layout changes within the Configuring state will never lead to a
|
||
recompilation of the inner workings of the Shooting state and vice versa.</p>
|
||
<p><b>Unlike in the previous example, the excerpts presented here often
|
||
outline different options to achieve the same effect. That's why the code is
|
||
often not equal to the Camera example code.</b> Comments mark the parts where
|
||
this is the case.</p>
|
||
<p>Camera.hpp:</p>
|
||
<pre>#ifndef CAMERA_HPP
|
||
#define CAMERA_HPP
|
||
|
||
#include <boost/fsm/event.hpp>
|
||
#include <boost/fsm/state_machine.hpp>
|
||
#include <boost/fsm/simple_state.hpp>
|
||
#include <boost/fsm/custom_reaction.hpp>
|
||
|
||
namespace fsm = boost::fsm;
|
||
|
||
struct EvShutterHalf : fsm::event< EvShutterHalf > {};
|
||
struct EvShutterFull : fsm::event< EvShutterFull > {};
|
||
struct EvShutterRelease : fsm::event< EvShutterRelease > {};
|
||
struct EvConfig : fsm::event< EvConfig > {};
|
||
|
||
struct NotShooting;
|
||
struct Camera : fsm::state_machine< Camera, NotShooting >
|
||
{
|
||
bool IsMemoryAvailable() const { return true; }
|
||
bool IsBatteryLow() const { return false; }
|
||
};
|
||
|
||
struct Idle;
|
||
struct NotShooting : fsm::simple_state< NotShooting, Camera,
|
||
<b>fsm::custom_reaction< EvShutterHalf ></b>, Idle >
|
||
{
|
||
// ...
|
||
<b>fsm::result react( const EvShutterHalf & );</b>
|
||
};
|
||
|
||
struct Idle : fsm::simple_state< Idle, NotShooting,
|
||
<b>fsm::custom_reaction< EvConfig ></b> >
|
||
{
|
||
// ...
|
||
<b>fsm::result react( const EvConfig & );</b>
|
||
};
|
||
|
||
#endif</pre>
|
||
<p>Please note the bold parts in the code. With a custom reaction we only
|
||
specify that we <b>might</b> do something with a particular event, but the
|
||
actual reaction is defined in the <code>react</code> member function, which
|
||
can be implemented in the .cpp file.</p>
|
||
<p>Camera.cpp:</p>
|
||
<pre>#include "Camera.hpp"
|
||
#include "Configuring.hpp"
|
||
#include "Shooting.hpp"
|
||
|
||
// ...
|
||
|
||
// not part of the Camera example
|
||
fsm::result NotShooting::react( const EvShutterHalf & )
|
||
{
|
||
return transit< Shooting >();
|
||
}
|
||
|
||
fsm::result Idle::react( const EvConfig & )
|
||
{
|
||
return transit< Configuring >();
|
||
}</pre>
|
||
<p><b><font color="#FF0000">Caution: Any call to the <code>
|
||
simple_state::transit<>()</code> or <code>simple_state::terminate()</code>
|
||
(see <a href="reference.html#transit1">reference</a>) member functions will
|
||
inevitably destruct the state object (similar to <code>delete this;</code>)!
|
||
That is, code executed after any of these calls may invoke undefined behavior!</font></b>
|
||
That's why these functions should only be called as part of a return
|
||
statement.</p>
|
||
<h3><a name="Guards">Guards</a></h3>
|
||
<p>The inner workings of the Shooting state could look as follows:</p>
|
||
<p><img border="0" src="Camera2.gif" width="427" height="427"></p>
|
||
<p>When the user half-presses the shutter, Shooting and its inner initial
|
||
state Focusing are entered. In the Focusing entry action the camera instructs
|
||
the focusing circuit to bring the subject into focus. The focusing circuit
|
||
then moves the lenses accordingly and sends the EvInFocus event as soon as it
|
||
is done. Of course, the user can fully-press the shutter while the lenses are
|
||
still in motion. Without any precautions, the resulting EvShutterFull event
|
||
would simply be lost because the Focusing state does not define a reaction for
|
||
this event. As a result, the user would have to fully-press the shutter again
|
||
after the camera has finished focusing. To prevent this, the EvShutterFull
|
||
event is deferred inside the Focusing state. This means that all events of
|
||
this type are stored in a separate queue, which is emptied into the main queue
|
||
when the Focusing state is exited.</p>
|
||
<pre>struct Focusing : fsm::state< Focusing, Shooting, mpl::list<
|
||
fsm::custom_reaction< EvInFocus >,
|
||
<b>fsm::deferral< EvShutterFull ></b> > >
|
||
{
|
||
Focusing( my_context ctx );
|
||
fsm::result react( const EvInFocus & );
|
||
};</pre>
|
||
<p>Both transitions originating at the Focused state are triggered by the same
|
||
event but they have mutually exclusive guards. Here is an appropriate custom
|
||
reaction:</p>
|
||
<pre>// not part of the Camera example
|
||
fsm::result Focused::react( const EvShutterFull & )
|
||
{
|
||
if ( context< Camera >().IsMemoryAvailable() )
|
||
{
|
||
return transit< Storing >();
|
||
}
|
||
else
|
||
{
|
||
// The following is actually a mixture between an in-state
|
||
// reaction and a transition. See later on how to implement
|
||
// proper transition actions.
|
||
std::cout << "Cache memory full. Please wait...\n";
|
||
return transit< Focused >();
|
||
}
|
||
}</pre>
|
||
<p>Custom reactions can of course also be implemented directly in the state
|
||
declaration, which is often preferable for easier browsing.</p>
|
||
<p>Next we will use a guard to prevent a transition and let outer states react
|
||
to the event if the battery is low:</p>
|
||
<p>Camera.cpp:</p>
|
||
<pre>// ...
|
||
fsm::result NotShooting::react( const EvShutterHalf & )
|
||
{
|
||
if ( context< Camera >().IsBatteryLow() )
|
||
{
|
||
// We cannot react to the event ourselves, so we forward it
|
||
// to our outer state (this is also the default if a state
|
||
// defines no reaction for a given event).
|
||
<b>return forward_event();</b>
|
||
}
|
||
else
|
||
{
|
||
return transit< Shooting >();
|
||
}
|
||
}
|
||
// ...</pre>
|
||
<h3><a name="In-state reactions">In-state reactions</a></h3>
|
||
<p>The self-transition of the Focused state could also be implemented as an
|
||
<a href="definitions.html#In-state reaction">in-state reaction</a>, which has
|
||
the same effect as long as Focused does not have any entry or exit actions:</p>
|
||
<p>Shooting.cpp:</p>
|
||
<pre>// ...
|
||
fsm::result Focused::react( const EvShutterFull & )
|
||
{
|
||
if ( context< Camera >().IsMemoryAvailable() )
|
||
{
|
||
return transit< Storing >();
|
||
}
|
||
else
|
||
{
|
||
std::cout << "Cache memory full. Please wait...\n";
|
||
// Indicate that the event can be discarded. So, the
|
||
// dispatch algorithm will stop looking for a reaction
|
||
// and the machine remains in the Focused state.
|
||
<b>return discard_event();</b>
|
||
}
|
||
}
|
||
// ...</pre>
|
||
<h3><a name="Transition actions">Transition actions</a></h3>
|
||
<p>As an effect of every transition, actions are executed in the following
|
||
order:</p>
|
||
<ol>
|
||
<li>Starting from the innermost active state, all exit actions up to but
|
||
excluding the <a href="definitions.html#Innermost common outer state">
|
||
innermost common outer state</a>.</li>
|
||
<li>The transition action (if present).</li>
|
||
<li>Starting from the innermost common outer state, all entry actions down
|
||
to the target state followed by the entry actions of the initial states.</li>
|
||
</ol>
|
||
<p>Example:</p>
|
||
<p><img border="0" src="LCA.gif" width="604" height="304"></p>
|
||
<p>Here the order is as follows: ~D(), ~C(), ~B(), ~A(), t(), X(), Y(), Z().
|
||
The transition action t() is therefore executed in the context of the
|
||
InnermostCommonOuter state because the source state has already been left
|
||
(destructed) and the target state has not yet been entered (constructed).</p>
|
||
<p>With boost::fsm, a transition action can be a member of <b>any</b> common
|
||
outer context. That is, the transition between Focusing and Focused could be
|
||
implemented as follows:</p>
|
||
<p>Shooting.hpp:</p>
|
||
<pre>// ...
|
||
struct Focusing;
|
||
struct Shooting : fsm::simple_state< Shooting, Camera,
|
||
fsm::transition< EvShutterRelease, NotShooting >, Focusing >
|
||
{
|
||
// ...
|
||
<b>void DisplayFocused( const EvInFocus & );</b>
|
||
};
|
||
|
||
// ...
|
||
|
||
// not part of the Camera example
|
||
struct Focusing : fsm::simple_state< Focusing, Shooting,
|
||
fsm::transition< EvInFocus, Focused<b>,</b>
|
||
<b>Shooting, &Shooting::DisplayFocused</b> > > {};</pre>
|
||
<p><b>Or</b>, the following is also possible (here the state machine itself
|
||
serves as the outermost context):</p>
|
||
<pre>// not part of the Camera example
|
||
struct Camera : fsm::state_machine< Camera, NotShooting >
|
||
{
|
||
<b>void DisplayFocused( const EvInFocus & );</b>
|
||
};</pre>
|
||
<pre>// not part of the Camera example
|
||
struct Focusing : fsm::simple_state< Focusing, Shooting,
|
||
fsm::transition< EvInFocus, Focused<b>,</b>
|
||
<b>Camera, &Camera::DisplayFocused</b> > > {};</pre>
|
||
<p>Naturally, transition actions can also be invoked from custom reactions:</p>
|
||
<p>Shooting.cpp:</p>
|
||
<pre>// ...
|
||
fsm::result Focusing::react( const EvInFocus & evt )
|
||
{
|
||
return transit< Focused >( <b>&Shooting::DisplayFocused</b>, evt );
|
||
}</pre>
|
||
<p>Please note that we have to manually forward the event.</p>
|
||
<h2><a name="Advanced topics">Advanced topics</a></h2>
|
||
<h3><a name="Specifying multiple reactions for a state">Specifying multiple
|
||
reactions for a state</a></h3>
|
||
<p>Often a state must define reactions for more than one event. In this case,
|
||
an <code>mpl::list</code> must be used as outlined below:</p>
|
||
<pre>// ...
|
||
|
||
<b>#include <boost/mpl/list.hpp>
|
||
</b>
|
||
<b>namespace mpl = boost::mpl;
|
||
</b>
|
||
// ...
|
||
|
||
struct Playing : fsm::simple_state< Playing, Mp3Player,
|
||
<b>mpl::list<</b>
|
||
fsm::custom_reaction< EvFastForward >,
|
||
fsm::transition< EvStop, Stopped > <b>></b> > { /* ... */ };</pre>
|
||
<h3><a name="Posting events">Posting events</a></h3>
|
||
<p>Non-trivial state machines often need to post internal events. Here's an
|
||
example of how to do this: </p>
|
||
<pre>Pumping::~Pumping()
|
||
{
|
||
post_event( boost::intrusive_ptr< EvPumpingFinished >(
|
||
new EvPumpingFinished() ) );
|
||
}</pre>
|
||
<p>The event is pushed into the main queue, which is why it must be allocated
|
||
with <code>new</code>. The events in the queue are processed as soon as the
|
||
current reaction is completed. Events can be posted from inside <code>react</code>
|
||
functions, entry-, exit- and transition actions. However, posting from inside
|
||
entry actions is a bit more complicated (see e.g. <code>Focusing::Focusing()</code>
|
||
in <code>Shooting.cpp</code> in the Camera example): </p>
|
||
<pre>struct Pumping : <b>fsm::state</b>< Pumping, Purifier >
|
||
{
|
||
<b>Pumping( my_context ctx ) : my_base( ctx )</b>
|
||
{
|
||
post_event( boost::intrusive_ptr< EvPumpingStarted >(
|
||
new EvPumpingStarted() ) );
|
||
}
|
||
// ...
|
||
};</pre>
|
||
<p>Please note the bold parts. As soon as an entry action of a state needs to
|
||
contact the "outside world" (here: the event queue in the state machine), the
|
||
state must derive from <code>fsm::state</code> rather than from <code>
|
||
fsm::simple_state</code> and must implement a forwarding constructor as
|
||
outlined above (apart from the constructor, <code>fsm::state</code> offers the
|
||
same interface as <code>fsm::simple_state</code>). Hence, this must be done
|
||
whenever an entry action makes one or more calls to the following functions:</p>
|
||
<ul>
|
||
<li><code>simple_state::post_event()</code></li>
|
||
<li><code>simple_state::clear_shallow_history<>()</code></li>
|
||
<li><code>simple_state::clear_deep_history<>()</code></li>
|
||
<li><code>simple_state::outermost_context<>()</code></li>
|
||
<li><code>simple_state::context<>()</code></li>
|
||
<li><code>simple_state::state_cast<>()</code></li>
|
||
<li><code>simple_state::state_downcast<>()</code></li>
|
||
<li><code>simple_state::state_begin()</code></li>
|
||
<li><code>simple_state::state_end()</code></li>
|
||
</ul>
|
||
<p>In my experience, these functions are needed only rarely in entry actions
|
||
so this workaround should not uglify user code too much.</p>
|
||
<h3><a name="Deferring events">Deferring events</a></h3>
|
||
<p>To avoid a number of overheads, event deferral has one limitation: Only
|
||
events allocated with <code>new</code> <b>and</b> pointed to by a <code>
|
||
boost::intrusive_ptr</code> can be deferred. Any attempt to defer a
|
||
differently allocated event will result in a failing runtime assert. Example:</p>
|
||
<pre>struct Event : fsm::event< Event > {};
|
||
struct Initial;
|
||
struct Machine : fsm::state_machine<
|
||
Machine, Initial > {};
|
||
struct Initial : fsm::simple_state< Initial, Machine,
|
||
fsm::deferral< Event > > {};
|
||
|
||
int main()
|
||
{
|
||
Machine myMachine;
|
||
myMachine.initiate();
|
||
myMachine.process_event( Event() ); // error
|
||
myMachine.process_event(
|
||
*boost::shared_ptr< Event >( new Event() ) ); // error
|
||
myMachine.process_event(
|
||
*<b>boost::intrusive_ptr< Event >( new Event() )</b> ); // fine
|
||
return 0;
|
||
}</pre>
|
||
<h3><a name="History">History</a></h3>
|
||
<p>Photographers testing beta versions of our
|
||
<a href="#Spreading a state machine over multiple translation units">digital
|
||
camera</a> said that they really liked that half-pressing the shutter anytime
|
||
(even while the camera is being configured) immediately readies the camera for
|
||
picture-taking. However, most of them found it unintuitive that the camera
|
||
always goes into the idle mode after releasing the shutter. They would rather
|
||
see the camera go back into the state it had before half-pressing the shutter.
|
||
This way they can easily test the influence of a configuration setting by
|
||
modifying it, half- and then fully-pressing the shutter to take a picture.
|
||
Finally, releasing the shutter will bring them back to the screen where they
|
||
have modified the setting. To implement this behavior we'd change the state
|
||
chart as follows:</p>
|
||
<p><img border="0" src="CameraWithHistory1.gif" width="542" height="378"></p>
|
||
<p>As mentioned earlier, the Configuring state contains a fairly complex and
|
||
deeply nested inner machine. Naturally, we'd like to restore the previous
|
||
state down to the <a href="definitions.html#Innermost state">innermost state</a>(s)
|
||
in Configuring, that's why we use a deep history pseudo state. The associated
|
||
code looks as follows:</p>
|
||
<pre>// not part of the Camera example
|
||
struct NotShooting : fsm::simple_state< NotShooting, Camera,
|
||
/* ... */, Idle, <b>fsm::has_deep_history</b> > //
|
||
{
|
||
// ...
|
||
};
|
||
|
||
// ...
|
||
|
||
struct Shooting : fsm::simple_state< Shooting, Camera,
|
||
fsm::transition< EvShutterRelease,
|
||
<b>fsm::deep_history< Idle ></b> >, Focusing >
|
||
{
|
||
// ...
|
||
};
|
||
</pre>
|
||
<p>History has two phases: Firstly, when the state containing the history
|
||
pseudo state is exited, information about the previously active inner state
|
||
hierarchy must be saved. Secondly, when a transition to the history pseudo
|
||
state is made later, the saved state hierarchy information must be retrieved
|
||
and the appropriate states entered. The former is expressed by passing either
|
||
<code>fsm::has_shallow_history</code>, <code>fsm::has_deep_history</code> or
|
||
<code>fsm::has_full_history</code> (which combines shallow and deep history)
|
||
as the last parameter to the <code>simple_state</code> and <code>state</code>
|
||
templates. The latter is expressed by specifying either <code>
|
||
fsm::shallow_history</code> or <code>fsm::deep_history</code> as a transition
|
||
destination or, as we'll see in an instant, as an inner initial state. Because
|
||
it is possible that a state containing a history pseudo state has never been
|
||
entered before a transition to history is made, both class templates demand a
|
||
parameter specifying the default state to enter in such situations.</p>
|
||
<p>The redundancy necessary for using history is checked for consistency at
|
||
compile time. That is, the state machine wouldn't have compiled had we
|
||
forgotten to pass <code>fsm::has_deep_history</code> to the base of <code>
|
||
NotShooting</code>.</p>
|
||
<p>Another change request filed by a few beta testers says that they would
|
||
like to see the camera go back into the state it had before turning it off
|
||
when they turn it back on. Here's the implementation:</p>
|
||
<p><img border="0" src="CameraWithHistory2.gif" width="468" height="483"></p>
|
||
<pre>// ...
|
||
|
||
// not part of the Camera example
|
||
struct NotShooting : fsm::simple_state< NotShooting, Camera,
|
||
/* ... */, <b>mpl::list< fsm::deep_history< Idle > ></b>,
|
||
<b>fsm::has_deep_history</b> >
|
||
{
|
||
// ...
|
||
};
|
||
|
||
// ...</pre>
|
||
<p>Unfortunately, there is a small inconvenience due to some template-related
|
||
implementation details. When the inner initial state is a class template
|
||
instantiation we always have to put it into an <code>mpl::list</code>,
|
||
although there is only one inner initial state. Moreover, the current deep
|
||
history implementation has some <a href="rationale.html#Limitations">
|
||
limitations</a>.</p>
|
||
<h3><a name="Orthogonal states">Orthogonal states</a></h3>
|
||
<p><img border="0" src="OrthogonalStates.GIF" width="633" height="393"></p>
|
||
<p>To implement this state chart you simply specify more than one inner
|
||
initial state (see the Keyboard example):</p>
|
||
<pre>struct Active;
|
||
struct Keyboard : fsm::state_machine< Keyboard, Active > {};
|
||
|
||
struct NumLockOff;
|
||
struct CapsLockOff;
|
||
struct ScrollLockOff;
|
||
struct Active: fsm::simple_state<
|
||
Active, Keyboard, fsm::no_reactions,
|
||
<b>mpl::list< NumLockOff, CapsLockOff, ScrollLockOff ></b> > {};</pre>
|
||
<p>Active's inner states must declare which orthogonal region they belong to:</p>
|
||
<pre>struct EvNumLockPressed : fsm::event< EvNumLockPressed > {};
|
||
struct EvCapsLockPressed : fsm::event< EvCapsLockPressed > {};
|
||
struct EvScrollLockPressed :
|
||
fsm::event< EvScrollLockPressed > {};
|
||
|
||
struct NumLockOn : fsm::simple_state<
|
||
NumLockOn, Active<b>::orthogonal< 0 ></b>,
|
||
fsm::transition< EvNumLockPressed, NumLockOff > > {};
|
||
struct NumLockOff : fsm::simple_state<
|
||
NumLockOff, Active<b>::orthogonal< 0 ></b>,
|
||
fsm::transition< EvNumLockPressed, NumLockOn > > {};
|
||
|
||
struct CapsLockOn : fsm::simple_state<
|
||
CapsLockOn, Active<b>::orthogonal< 1 ></b>,
|
||
fsm::transition< EvCapsLockPressed, CapsLockOff > > {};
|
||
struct CapsLockOff : fsm::simple_state<
|
||
CapsLockOff, Active<b>::orthogonal< 1 ></b>,
|
||
fsm::transition< EvCapsLockPressed, CapsLockOn > > {};
|
||
|
||
struct ScrollLockOn : fsm::simple_state<
|
||
ScrollLockOn, Active<b>::orthogonal< 2 ></b>,
|
||
fsm::transition< EvScrollLockPressed, ScrollLockOff > > {};
|
||
struct ScrollLockOff : fsm::simple_state<
|
||
ScrollLockOff, Active<b>::orthogonal< 2 ></b>,
|
||
fsm::transition< EvScrollLockPressed, ScrollLockOn > > {};</pre>
|
||
<p><code>orthogonal< 0 ></code> is the default, so <code>NumLockOn</code> and
|
||
<code>NumLockOff</code> could just as well pass <code>Active</code> instead of
|
||
<code>Active::orthogonal< 0 ></code> to specify their context. The numbers
|
||
passed to the <code>orthogonal</code> member template must correspond to the
|
||
list position in the outer state. Moreover, the orthogonal position of the
|
||
source state of a transition must correspond to the orthogonal position of the
|
||
target state. Any violations of these rules lead to compile time errors.
|
||
Examples:</p>
|
||
<pre>// Example 1: does not compile because Active specifies
|
||
// only 3 orthogonal regions
|
||
struct WhateverLockOn: fsm::simple_state<
|
||
WhateverLockOn, Active<b>::</b>orthogonal< <b>3</b> > > {};
|
||
|
||
// Example 2: does not compile because Active specifies
|
||
// that NumLockOff is part of the "0th" orthogonal region
|
||
struct NumLockOff : fsm::simple_state<
|
||
NumLockOff, Active<b>::</b>orthogonal< <b>1</b> > > {};
|
||
|
||
// Example 3: does not compile because a transition between
|
||
// different orthogonal regions is not permitted
|
||
struct CapsLockOn : fsm::simple_state<
|
||
CapsLockOn, Active<b>::</b>orthogonal< <b>1</b> >,
|
||
fsm::transition< EvCapsLockPressed, CapsLockOff > > {};
|
||
struct CapsLockOff : fsm::simple_state<
|
||
CapsLockOff, Active<b>::</b>orthogonal< <b>2</b> >,
|
||
fsm::transition< EvCapsLockPressed, CapsLockOn > > {};</pre>
|
||
<h3><a name="State queries">State queries</a></h3>
|
||
<p>Often reactions in a state machine depend on the active state in one or
|
||
more orthogonal regions. This is because orthogonal regions are not completely
|
||
orthogonal or a certain reaction in an outer state can only take place if the
|
||
inner orthogonal regions are in particular states. For this purpose, the <code>
|
||
state_cast<>()</code> function introduced under
|
||
<a href="#Getting state information out of the machine">Getting state
|
||
information out of the machine</a> is also available within states.</p>
|
||
<p>As a somewhat far-fetched example, let's assume that our
|
||
<a href="#Orthogonal states">keyboard</a> also accepts <code>EvRequestShutdown</code>
|
||
events, the reception of which makes the keyboard terminate only if all lock
|
||
keys are in the off state. We would then modify the Keyboard state machine as
|
||
follows: </p>
|
||
<pre>struct EvRequestShutdown : fsm::event< EvRequestShutdown > {};
|
||
|
||
struct NumLockOff;
|
||
struct CapsLockOff;
|
||
struct ScrollLockOff;
|
||
struct Active: fsm::simple_state<
|
||
Active, Keyboard, fsm::custom_reaction< EvRequestShutdown >,
|
||
mpl::list< NumLockOff, CapsLockOff, ScrollLockOff > >
|
||
{
|
||
fsm::result react( const EvRequestShutdown & )
|
||
{
|
||
if ( ( state_downcast< const NumLockOff * >() != 0 ) &&
|
||
( state_downcast< const CapsLockOff * >() != 0 ) &&
|
||
( state_downcast< const ScrollLockOff * >() != 0 ) )
|
||
{
|
||
return terminate();
|
||
}
|
||
else
|
||
{
|
||
return discard_event();
|
||
}
|
||
}
|
||
};</pre>
|
||
<p>Passing a pointer type instead of reference type results in 0 pointers
|
||
being returned instead of <code>std::bad_cast</code> being thrown when the
|
||
cast fails. Note also the use of <code>state_downcast<>()</code> instead of
|
||
<code>state_cast<>()</code>. Similar to the differences between <code>
|
||
boost::polymorphic_downcast<>()</code> and <code>dynamic_cast</code>, <code>
|
||
state_downcast<>()</code> is a much faster variant of <code>state_cast<>()</code>
|
||
and can only be used when the passed type is a most-derived type. <code>
|
||
state_cast<>()</code> should only be used if you want to query an additional
|
||
base.</p>
|
||
<h4>Custom state queries</h4>
|
||
<p>It is often desirable to find out exactly which state(s) a machine
|
||
currently resides in. To some extent this is already possible with <code>
|
||
state_cast<>()</code> and <code>state_downcast<>()</code> but their utility is
|
||
rather limited because both only return a yes/no answer to the question "Are
|
||
you in state X?". It is possible to ask more sophisticated questions when you
|
||
pass an additional base class rather than a state class to <code>state_cast<>()</code>
|
||
but this involves more work (all states need to derive from and implement the
|
||
additional base), is slow (under the hood <code>state_cast<>()</code> uses
|
||
<code>dynamic_cast</code>), forces projects to compile with C++ RTTI turned on
|
||
and has a negative impact on state entry/exit speed.</p>
|
||
<p>Especially for debugging it would be so much more useful being able to ask
|
||
"In which state(s) are you?". For this purpose it is possible to iterate over
|
||
all active <b>innermost</b> states with <code>state_machine::state_begin()</code>
|
||
and <code>state_machine::state_end()</code>. Dereferencing the returned
|
||
iterator returns a reference to <code>const state_machine::state_base_type</code>,
|
||
the common base of all states. We can thus print the currently active state
|
||
configuration as follows (see the Keyboard example for the complete code):</p>
|
||
<pre>void DisplayStateConfiguration( const Keyboard & kbd )
|
||
{
|
||
char region = 'a';
|
||
|
||
for (
|
||
Keyboard::state_iterator pLeafState = kbd.state_begin();
|
||
pLeafState != kbd.state_end(); ++pLeafState )
|
||
{
|
||
std::cout << "Orthogonal region " << region << ": ";
|
||
std::cout << typeid( *pLeafState ).name() << "\n";
|
||
++region;
|
||
}
|
||
}</pre>
|
||
<p>If necessary, the outer states can be accessed with <code>
|
||
state_machine::state_base_type::outer_state_ptr()</code>, which returns a
|
||
pointer to <code>const state_machine::state_base_type</code>. When called on
|
||
an outermost state this function simply returns 0.</p>
|
||
<h3><a name="State type information">State type information</a></h3>
|
||
<p>To cut down on executable size some applications must be compiled with C++
|
||
RTTI turned off. This would render the ability to iterate over all active
|
||
states pretty much useless if it weren't for the following two functions:</p>
|
||
<ul>
|
||
<li><code>static <i>unspecified_type</i> simple_state::static_type()</code></li>
|
||
<li><code><i>unspecified_type<br>
|
||
</i> state_machine::state_base_type::dynamic_type() const</code></li>
|
||
</ul>
|
||
<p>Both return a value that is comparable via <code>operator==()</code> and
|
||
<code>std::less</code>. This alone would be enough to implement the <code>
|
||
DisplayStateConfiguration()</code> function above without the help of <code>
|
||
typeid</code> but it is still somewhat cumbersome as a map must be used to
|
||
associate the type information values with the state names.</p>
|
||
<h4><a name="Custom state type information">Custom state type information</a></h4>
|
||
<p>That's why the following functions are also provided (only available when
|
||
<a href="configuration.html#Application Defined Macros">
|
||
BOOST_FSM_USE_NATIVE_RTTI</a> is <b>not</b> defined):</p>
|
||
<ul>
|
||
<li><code>template< class T ><br>
|
||
static void simple_state::custom_static_type_ptr( const T * );</code></li>
|
||
<li><code>template< class T ><br>
|
||
static const T * simple_state::custom_static_type_ptr();</code></li>
|
||
<li><code>template< class T ><br>
|
||
const T * state_machine::<br>
|
||
state_base_type::custom_dynamic_type_ptr() const;</code></li>
|
||
</ul>
|
||
<p>These allow us to directly associate arbitrary state type information with
|
||
each state ...</p>
|
||
<pre>// ...
|
||
|
||
int main()
|
||
{
|
||
NumLockOn::custom_static_type_ptr( "NumLockOn" );
|
||
NumLockOff::custom_static_type_ptr( "NumLockOff" );
|
||
CapsLockOn::custom_static_type_ptr( "CapsLockOn" );
|
||
CapsLockOff::custom_static_type_ptr( "CapsLockOff" );
|
||
ScrollLockOn::custom_static_type_ptr( "ScrollLockOn" );
|
||
ScrollLockOff::custom_static_type_ptr( "ScrollLockOff" );
|
||
|
||
// ...
|
||
}</pre>
|
||
<p>... and rewrite the display function as follows:</p>
|
||
<pre>void DisplayStateConfiguration( const Keyboard & kbd )
|
||
{
|
||
char region = 'a';
|
||
|
||
for (
|
||
Keyboard::state_iterator pLeafState = kbd.state_begin();
|
||
pLeafState != kbd.state_end(); ++pLeafState )
|
||
{
|
||
std::cout << "Orthogonal region " << region << ": ";
|
||
std::cout <<
|
||
pLeafState->custom_dynamic_type_ptr< char >() << "\n";
|
||
++region;
|
||
}
|
||
}</pre>
|
||
<h3><a name="Exception handling">Exception handling</a></h3>
|
||
<p>Exceptions can be propagated from all user code except from state exit
|
||
actions (mapped to destructors and destructors should virtually never throw in
|
||
C++). Out of the box, <code>state_machine</code> does the following:</p>
|
||
<ol>
|
||
<li>The exception is caught.</li>
|
||
<li>In the catch block, an <code>fsm::exception_thrown</code> event is
|
||
allocated on the stack.</li>
|
||
<li>Also in the catch block, an <b>immediate</b> dispatch of the <code>
|
||
fsm::exception_thrown</code> event is attempted. That is, possibly remaining
|
||
events in the queue are dispatched only after the exception has been handled
|
||
successfully.</li>
|
||
<li>If the exception was handled successfully, the state machine returns to
|
||
the client normally. If the exception could not be handled successfully, the
|
||
original exception is rethrown so that the client of the state machine can
|
||
handle the exception.</li>
|
||
</ol>
|
||
<p>This behavior is implemented in the <code>exception_translator</code>
|
||
class, which is the default for the <code>ExceptionTranslator</code> parameter
|
||
of the <code>state_machine</code> class template. It was introduced because
|
||
users would want to change this on some platforms to work around buggy
|
||
exception handling implementations (see <a href="#Discriminating exceptions">
|
||
Discriminating exceptions</a>).</p>
|
||
<p>boost::fsm can also be used in applications compiled with C++ exception
|
||
handling turned off but doing so means losing <b>all</b> error handling
|
||
support, making proper error handling much more cumbersome (see
|
||
<a href="rationale.html#Error handling">Error handling</a> in the Rationale).</p>
|
||
<h4>Which states can react to an <code>fsm::exception_thrown</code> event?</h4>
|
||
<p>This depends on where the exception occurred. There are three scenarios:</p>
|
||
<ol>
|
||
<li>A <code>react</code> member function propagates an exception <b>before</b>
|
||
calling any of the reaction functions. The state that caused the exception
|
||
is first tried for a reaction, so the following machine will transit to
|
||
Defective after receiving an EvStart event:<br>
|
||
<br>
|
||
<img border="0" src="ThrowingInStateReaction.GIF" width="362" height="182"><br>
|
||
<br>
|
||
</li>
|
||
<li>A state entry action (constructor) propagates an exception. The outer
|
||
state of the state that caused the exception is first tried for a reaction,
|
||
so the following machine will transit to Defective after trying to enter
|
||
Stopped:<br>
|
||
<br>
|
||
<img border="0" src="ThrowingEntryAction.gif" width="438" height="241"><br>
|
||
</li>
|
||
<li>A transition action propagates an exception. The innermost common outer
|
||
state of the source and the target state is first tried for a reaction, so
|
||
the following machine will transit to Defective after receiving an
|
||
EvStartStop event:<br>
|
||
<br>
|
||
<img border="0" src="ThrowingTransitionAction.gif" width="422" height="362"></li>
|
||
</ol>
|
||
<p>As with a normal event, the dispatch algorithm will move outward to find a
|
||
reaction if the first tried state does not provide one (or if the reaction
|
||
explicitly returned <code>forward_event();</code>). However, <b>in contrast to
|
||
normal events, it will give up once it has unsuccessfully tried an outermost
|
||
state</b>, so the following machine will <b>not</b> transit to Defective after
|
||
receiving an EvNumLockPressed event:</p>
|
||
<p>
|
||
<img border="0" src="ExceptionsAndOrthogonalStates.gif" width="571" height="331"></p>
|
||
<p>Instead, the machine is terminated and the original exception rethrown.</p>
|
||
<h4>Successful exception handling</h4>
|
||
<p>An exception is considered handled successfully, if:</p>
|
||
<ul>
|
||
<li>an appropriate reaction for the <code>fsm::exception_thrown</code> event
|
||
has been found, <b>and</b></li>
|
||
<li>the state machine is in a stable state after the reaction has completed.</li>
|
||
</ul>
|
||
<p>The second condition is important for scenarios 2 and 3 in the last
|
||
section. In these scenarios, the state machine is in the middle of a
|
||
transition when the exception is handled. The machine would be left in an
|
||
invalid state, should the reaction simply discard the event without doing
|
||
anything else.</p>
|
||
<p>The out of the box behavior for unsuccessful exception handling is to
|
||
rethrow the original exception. The state machine is terminated before the
|
||
exception is propagated to the machine client.</p>
|
||
<h4><a name="Discriminating exceptions">Discriminating exceptions</a></h4>
|
||
<p>Because the <code>fsm::exception_thrown</code> object is dispatched from
|
||
within the catch block, we can rethrow and catch the exception in a custom
|
||
reaction:</p>
|
||
<pre>struct Defective : fsm::simple_state<
|
||
Defective, Purifier > {};
|
||
|
||
// Pretend this is a state deeply nested in the Purifier
|
||
// state machine
|
||
struct Idle : fsm::simple_state< Idle, Purifier,
|
||
mpl::list<
|
||
fsm::custom_reaction< EvStart >,
|
||
fsm::custom_reaction< fsm::exception_thrown > > >
|
||
{
|
||
fsm::result react( const EvStart & )
|
||
{
|
||
throw std::runtime_error( "" );
|
||
}
|
||
|
||
fsm::result react( const fsm::exception_thrown & )
|
||
{
|
||
try
|
||
{
|
||
<b>throw;</b>
|
||
}
|
||
catch ( const std::runtime_error & )
|
||
{
|
||
// only std::runtime_errors will lead to a transition
|
||
// to Defective ...
|
||
return transit< Defective >();
|
||
}
|
||
catch ( ... )
|
||
{
|
||
// ... all other exceptions are forwarded to our outer
|
||
// state(s). The state machine is terminated and the
|
||
// exception rethrown if the outer state(s) can't
|
||
// handle it either...
|
||
return forward_event();
|
||
}
|
||
|
||
// Alternatively, if we want to terminate the machine
|
||
// immediately, we can also either rethrow or throw
|
||
// a different exception.
|
||
}
|
||
};</pre>
|
||
<p><b>Unfortunately, this idiom (using <code>throw;</code> inside a <code>try</code>
|
||
block nested inside a <code>catch</code> block) does not work on at least one
|
||
very popular compiler.</b> If you have to use one of these platforms, you can
|
||
pass a customized exception translator class to the <code>state_machine</code>
|
||
class template. This will allow you to generate different events depending on
|
||
the type of the exception.</p>
|
||
<h3><a name="Submachines & parameterized states">Submachines &
|
||
parameterized states</a></h3>
|
||
<p>Submachines are to event-driven programming what functions are to
|
||
procedural programming, reusable building blocks implementing often needed
|
||
functionality. The associated UML notation is not entirely clear to me. It
|
||
seems to be severely limited (e.g. the same submachine cannot appear in
|
||
different orthogonal regions) and does not seem to account for obvious stuff
|
||
like e.g. parameters.</p>
|
||
<p>boost::fsm is completely unaware of submachines but they can be implemented
|
||
quite nicely with templates. Here, a submachine is used to improve the
|
||
copy-paste implementation of the keyboard machine discussed under
|
||
<a href="#Orthogonal states">Orthogonal states</a>:</p>
|
||
<pre>enum LockType
|
||
{
|
||
NUM_LOCK,
|
||
CAPS_LOCK,
|
||
SCROLL_LOCK
|
||
};
|
||
|
||
template< LockType lockType >
|
||
struct Off;
|
||
struct Active : fsm::simple_state<
|
||
Active, Keyboard, fsm::no_reactions, mpl::list<
|
||
Off< NUM_LOCK >, Off< CAPS_LOCK >, Off< SCROLL_LOCK > > > {};
|
||
|
||
template< LockType lockType >
|
||
struct EvPressed : fsm::event< EvPressed< lockType > > {};
|
||
|
||
template< LockType lockType >
|
||
struct On : fsm::simple_state<
|
||
On< lockType >, Active::orthogonal< lockType >,
|
||
fsm::transition< EvPressed< lockType >, Off< lockType > > > {};
|
||
|
||
template< LockType lockType >
|
||
struct Off : fsm::simple_state<
|
||
Off< lockType >, Active::orthogonal< lockType >,
|
||
fsm::transition< EvPressed< lockType >, On< lockType > > > {};</pre>
|
||
<h3><a name="Asynchronous state machines">Asynchronous state machines</a></h3>
|
||
<h4>Why asynchronous state machines are necessary</h4>
|
||
<p>As the name suggests, a synchronous state machine processes each event
|
||
synchronously. This behavior is implemented by the <code>state_machine</code>
|
||
class template, whose <code>process_event()</code> only returns after having
|
||
executed all reactions (including the ones provoked by internal events that
|
||
actions might have posted). Moreover, this function is also strictly
|
||
non-reentrant (just like all other member functions, so <code>state_machine</code>
|
||
is not thread-safe). This makes it difficult for two <code>state_machine</code>
|
||
subclasses to communicate via events in a bi-directional fashion correctly, <b>
|
||
even in a single-threaded program</b>. For example, state machine <code>A</code>
|
||
is in the middle of processing an external event. Inside an action, it decides
|
||
to send a new event to state machine <code>B</code> (by calling <code>
|
||
B::process_event</code> with an appropriate event). It then "waits" for B to
|
||
send back an answer via a boost::function-like call-back, which references
|
||
<code>A::process_event</code> and was passed as a data member of the event.
|
||
However, while <code>A</code> is "waiting" for <code>B</code> to send back an
|
||
event, <code>A::process_event</code> has not yet returned from processing the
|
||
external event and as soon as <code>B</code> answers via the call-back, <code>
|
||
A::process_event</code> is <b>unavoidably</b> reentered. This all really
|
||
happens in a single thread, that's why "wait" is in quotes.</p>
|
||
<h4>How it works</h4>
|
||
<p>In contrast to <code>state_machine</code>, <code>asynchronous_state_machine</code>
|
||
does not have a member function <code>process_event()</code>. Instead, there
|
||
is only <code>queue_event()</code>, which returns immediately after pushing
|
||
the event into a queue. A worker thread will later pop the event out of the
|
||
queue to have it processed. For applications using the boost::thread library,
|
||
the necessary locking, unlocking and waiting logic is readily available in
|
||
class <code>worker</code>.</p>
|
||
<p>Applications will usually first create a <code>worker</code> object and
|
||
then create one or more <code>asynchronous_state_machine</code> subclass
|
||
objects, passing the worker object to the constructor(s). Finally, <code>
|
||
worker<>::operator()()</code> is either called directly to let the machine(s)
|
||
run in the current thread, or, a <code>boost::function</code> object
|
||
referencing <code>operator()</code> is passed to a new <code>boost::thread</code>.
|
||
In the following code, we are running one state machine in a new boost::thread
|
||
and the other in the main thread (see the PingPong example for the full source
|
||
code):</p>
|
||
<pre>// ...
|
||
|
||
struct Waiting;
|
||
struct Player :
|
||
<b>fsm::asynchronous_state_machine</b>< Player, Waiting >
|
||
{
|
||
typedef fsm::asynchronous_state_machine< Player, Waiting >
|
||
BaseType;
|
||
|
||
Player( fsm::worker<> & myWorker ) :
|
||
BaseType( myWorker ) // ...
|
||
{
|
||
// ...
|
||
}
|
||
|
||
// ...
|
||
};
|
||
|
||
// ...
|
||
|
||
int main()
|
||
{
|
||
fsm::worker<> worker1;
|
||
fsm::worker<> worker2;
|
||
|
||
// each player runs in its own worker thread
|
||
Player player1( worker1 );
|
||
Player player2( worker2 );
|
||
|
||
// ...
|
||
|
||
// run first worker in a new thread
|
||
boost::thread otherThread(
|
||
boost::bind( &fsm::worker<>::operator(), &worker1 ) );
|
||
|
||
worker2(); // run second worker in this thread
|
||
otherThread.join();
|
||
|
||
return 0;
|
||
}</pre>
|
||
<p>We could just as well use two boost::threads:</p>
|
||
<pre>int main()
|
||
{
|
||
// ...
|
||
|
||
boost::thread thread1(
|
||
boost::bind( &fsm::worker<>::operator(), &worker1 ) );
|
||
boost::thread thread2(
|
||
boost::bind( &fsm::worker<>::operator(), &worker2 ) );
|
||
|
||
// do something else ...
|
||
|
||
thread1.join();
|
||
thread2.join();
|
||
|
||
return 0;
|
||
}</pre>
|
||
<p>Or, run both machines in the same worker thread:</p>
|
||
<pre>int main()
|
||
{
|
||
fsm::worker<> worker1;
|
||
|
||
Player player1( worker1 );
|
||
Player player2( worker1 );
|
||
|
||
// ...
|
||
|
||
worker1();
|
||
|
||
return 0;
|
||
}</pre>
|
||
<p><code>worker<>::operator()()</code> first initiates all machines and then
|
||
waits for events. Whenever <code>queue_event</code> is called on one of the
|
||
previously registered machines, the passed event is pushed into the worker's
|
||
queue and the worker thread is waked up to dispatch all queued events before
|
||
waiting again. <code>worker<>::operator()()</code> returns as soon as all
|
||
machines have terminated. <code>worker<>::operator()()</code> also throws any
|
||
exceptions that machines fail to handle. In this case all machines are
|
||
terminated before the exception is propagated.</p>
|
||
<p><b>Caution:</b></p>
|
||
<ul>
|
||
<li><b><code>asynchronous_state_machine</code> subclass objects must not be
|
||
destructed before <code>worker::operator()()</code> returns. Moreover, the
|
||
<code>worker</code> object may be destructed only after all of the
|
||
registered state machines have been destructed. Violations of these rules
|
||
will result in failing runtime asserts. </b></li>
|
||
<li><b>The interface of <code>asynchronous_state_machine</code> consists of
|
||
only the constructor and <code>queue_event()</code>. For technical reasons,
|
||
other functions like <code>initiate()</code>, <code>process_event()</code>,
|
||
etc. are nevertheless also publicly available, but it is not safe to call
|
||
these functions from any other thread than the worker (over which most users
|
||
have no control). <font color="#FF0000"><code>asynchronous_state_machine<>::queue_event()</code>
|
||
is the only function than can safely be called simultaneously from multiple
|
||
threads.</font></b></li>
|
||
</ul>
|
||
<hr>
|
||
<p>Revised
|
||
<!--webbot bot="Timestamp" s-type="EDITED" s-format="%d %B, %Y" startspan -->09 February, 2004<!--webbot bot="Timestamp" endspan i-checksum="40415" --></p>
|
||
<p><i>Copyright <20> <a href="mailto:ah2003@gmx.net">Andreas Huber D<>nni</a>
|
||
2003-2004. Use, modification and distribution are subject to the Boost
|
||
Software License, Version 1.0. (See accompanying file
|
||
<a href="../../../LICENSE_1_0.txt">LICENSE_1_0.txt</a> or copy at
|
||
<a href="http://www.boost.org/LICENSE_1_0.txt">
|
||
http://www.boost.org/LICENSE_1_0.txt</a>)</i></p>
|
||
|
||
</body>
|
||
|
||
</html>
|