7b7fa20fdf
that use it are valid. Merged revisions 53047-53048 via svnmerge from https://svn.boost.org/svn/boost/trunk ........ r53047 | danieljames | 2009-05-16 15:17:20 +0100 (Sat, 16 May 2009) | 1 line Fix some validation errors. ........ r53048 | danieljames | 2009-05-16 15:23:59 +0100 (Sat, 16 May 2009) | 1 line Use a local copy of the valid HTML 4.01 icon. ........ [SVN r53258]
1839 lines
68 KiB
HTML
1839 lines
68 KiB
HTML
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
|
|
|
<html>
|
|
<head>
|
|
<meta http-equiv="Content-Language" content="en-us">
|
|
<meta http-equiv="Content-Type" content="text/html; charset=us-ascii">
|
|
<meta name="GENERATOR" content="Microsoft FrontPage 6.0">
|
|
<meta name="ProgId" content="FrontPage.Editor.Document">
|
|
<link rel="stylesheet" type="text/css" href="../../../boost.css">
|
|
|
|
<title>The Boost Statechart 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=
|
|
"../../../boost.png" border="0" width="277" height="86"></a></h3>
|
|
</td>
|
|
|
|
<td valign="top">
|
|
<h1 align="center">The Boost Statechart Library</h1>
|
|
|
|
<h2 align="center">Tutorial</h2>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
<hr>
|
|
|
|
<p>A Japanese translation of an earlier version 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="#HowToReadThisTutorial">How to read this tutorial</a></dd>
|
|
|
|
<dt><a href="#HelloWorld">Hello World!</a></dt>
|
|
|
|
<dt><a href="#BasicTopicsAStopWatch">Basic topics: A stop watch</a></dt>
|
|
|
|
<dd><a href="#DefiningStatesAndEvents">Defining states and
|
|
events</a></dd>
|
|
|
|
<dd><a href="#AddingReactions">Adding reactions</a></dd>
|
|
|
|
<dd><a href="#StateLocalStorage">State-local storage</a></dd>
|
|
|
|
<dd><a href="#GettingStateInformationOutOfTheMachine">Getting state
|
|
information out of the machine</a></dd>
|
|
|
|
<dt><a href="#IntermediateTopicsADigitalCamera">Intermediate topics: A
|
|
digital camera</a></dt>
|
|
|
|
<dd><a href=
|
|
"#SpreadingAStateMachineOverMultipleTranslationUnits">Spreading a state
|
|
machine over multiple translation units</a></dd>
|
|
|
|
<dd><a href="#DeferringEvents">Deferring events</a></dd>
|
|
|
|
<dd><a href="#Guards">Guards</a></dd>
|
|
|
|
<dd><a href="#InStateReactions">In-state reactions</a></dd>
|
|
|
|
<dd><a href="#TransitionActions">Transition actions</a></dd>
|
|
|
|
<dt><a href="#AdvancedTopics">Advanced topics</a></dt>
|
|
|
|
<dd><a href="#SpecifyingMultipleReactionsForAState">Specifying multiple
|
|
reactions for a state</a></dd>
|
|
|
|
<dd><a href="#PostingEvents">Posting events</a></dd>
|
|
|
|
<dd><a href="#History">History</a></dd>
|
|
|
|
<dd><a href="#OrthogonalStates">Orthogonal states</a></dd>
|
|
|
|
<dd><a href="#StateQueries">State queries</a></dd>
|
|
|
|
<dd><a href="#StateTypeInformation">State type information</a></dd>
|
|
|
|
<dd><a href="#ExceptionHandling">Exception handling</a></dd>
|
|
|
|
<dd><a href="#SubmachinesAndParameterizedStates">Submachines &
|
|
Parametrized States</a></dd>
|
|
|
|
<dd><a href="#AsynchronousStateMachines">Asynchronous state
|
|
machines</a></dd>
|
|
</dl>
|
|
<hr>
|
|
|
|
<h2><a name="Introduction" id="Introduction">Introduction</a></h2>
|
|
|
|
<p>The Boost Statechart library is a framework that allows you to quickly
|
|
transform a UML statechart into executable C++ code, <b>without</b> needing
|
|
to use a code generator. Thanks to support for almost all UML features the
|
|
transformation is straight-forward and the resulting C++ code is a nearly
|
|
redundancy-free textual description of the statechart.</p>
|
|
|
|
<h3><a name="HowToReadThisTutorial" id="HowToReadThisTutorial">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>Small and simple machines with just a handful of states can be
|
|
implemented reasonably well by using the features described under
|
|
<a href="#BasicTopicsAStopWatch">Basic topics: A stop watch</a></li>
|
|
|
|
<li>For larger machines with up to roughly a dozen states the features
|
|
described under <a href="#IntermediateTopicsADigitalCamera">Intermediate
|
|
topics: A digital camera</a> are often helpful</li>
|
|
|
|
<li>Finally, users wanting to create even more complex machines and
|
|
project architects evaluating Boost.Statechart should also read the
|
|
<a href="#AdvancedTopics">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="HelloWorld" id="HelloWorld">Hello World!</a></h2>
|
|
|
|
<p>We will use the simplest possible program to make our first steps. The
|
|
statechart ...</p>
|
|
|
|
<p><img alt="HelloWorld" src="HelloWorld.gif" border="0" width="379"
|
|
height="94"></p>
|
|
|
|
<p>... is implemented with the following code:</p>
|
|
<pre>
|
|
#include <boost/statechart/state_machine.hpp>
|
|
#include <boost/statechart/simple_state.hpp>
|
|
#include <iostream>
|
|
|
|
namespace sc = boost::statechart;
|
|
|
|
// 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>
|
|
|
|
// We need to forward-declare the initial state because it can
|
|
// only be defined at a point where the state machine is
|
|
// defined.
|
|
struct Greeting;
|
|
|
|
// Boost.Statechart makes heavy use of the curiously recurring
|
|
// template pattern. The deriving class must always be passed as
|
|
// the first parameter to all base class templates.
|
|
//
|
|
// The state machine must be informed which state it has to
|
|
// enter when the machine is initiated. That's why Greeting is
|
|
// passed as the second template parameter.
|
|
struct Machine : sc::state_machine< Machine, Greeting > {};
|
|
|
|
// For each state we need to define which state machine it
|
|
// belongs to and where it is located in the statechart. Both is
|
|
// specified with Context argument that is passed to
|
|
// simple_state<>. For a flat state machine as we have it here,
|
|
// the context is always the state machine. Consequently,
|
|
// Machine must be passed as the second template parameter to
|
|
// Greeting's base (the Context parameter is explained in more
|
|
// detail in the next example).
|
|
struct Greeting : sc::simple_state< Greeting, Machine >
|
|
{
|
|
// 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.
|
|
Greeting() { std::cout << "Hello World!\n"; } // entry
|
|
~Greeting() { std::cout << "Bye Bye World!\n"; } // exit
|
|
};
|
|
|
|
int main()
|
|
{
|
|
Machine myMachine;
|
|
// The machine is not yet running after construction. We start
|
|
// it by calling initiate(). This triggers the construction of
|
|
// the initial state Greeting
|
|
myMachine.initiate();
|
|
// When we leave main(), myMachine is destructed what leads to
|
|
// the destruction of all currently active states.
|
|
return 0;
|
|
}
|
|
</pre>
|
|
|
|
<p>This prints <code>Hello World!</code> and <code>Bye Bye World!</code>
|
|
before exiting.</p>
|
|
|
|
<h2><a name="BasicTopicsAStopWatch" id="BasicTopicsAStopWatch">Basic
|
|
topics: 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 alt="StopWatch" src="StopWatch.gif" border="0" width="560" height=
|
|
"184"></p>
|
|
|
|
<h3><a name="DefiningStatesAndEvents" id="DefiningStatesAndEvents">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/statechart/event.hpp>
|
|
#include <boost/statechart/state_machine.hpp>
|
|
#include <boost/statechart/simple_state.hpp>
|
|
|
|
namespace sc = boost::statechart;
|
|
|
|
struct EvStartStop : sc::event< EvStartStop > {};
|
|
struct EvReset : sc::event< EvReset > {};
|
|
|
|
struct Active;
|
|
struct StopWatch : sc::state_machine< StopWatch, Active > {};
|
|
|
|
struct Stopped;
|
|
|
|
// The simple_state class template accepts up to four parameters:
|
|
// - The third parameter specifies the inner initial state, if
|
|
// there is one. Here, only Active has inner states, which is
|
|
// why it needs to pass its inner initial state Stopped to its
|
|
// base
|
|
// - The fourth parameter specifies whether and what kind of
|
|
// history is kept
|
|
|
|
// Active is the outermost state and therefore needs to pass the
|
|
// state machine class it belongs to
|
|
struct Active : sc::simple_state<
|
|
Active, StopWatch, Stopped > {};
|
|
|
|
// Stopped and Running both specify Active as their Context,
|
|
// which makes them nested inside Active
|
|
struct Running : sc::simple_state< Running, Active > {};
|
|
struct Stopped : sc::simple_state< Stopped, Active > {};
|
|
|
|
// 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 direct
|
|
// 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.
|
|
|
|
int main()
|
|
{
|
|
StopWatch myWatch;
|
|
myWatch.initiate();
|
|
return 0;
|
|
}
|
|
</pre>
|
|
|
|
<p>This compiles but doesn't do anything observable yet.</p>
|
|
|
|
<h3><a name="AddingReactions" id="AddingReactions">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/statechart/transition.hpp>
|
|
</b>
|
|
// ...
|
|
|
|
struct Stopped;
|
|
struct Active : sc::simple_state< Active, StopWatch, Stopped >
|
|
{
|
|
<b>typedef sc::transition< EvReset, Active > reactions;</b>
|
|
};
|
|
|
|
struct Running : sc::simple_state< Running, Active >
|
|
{
|
|
<b>typedef sc::transition< EvStartStop, Stopped > reactions;</b>
|
|
};
|
|
|
|
struct Stopped : sc::simple_state< Stopped, Active >
|
|
{
|
|
<b>typedef sc::transition< EvStartStop, Running > reactions;</b>
|
|
};
|
|
|
|
// A state can define an arbitrary number of reactions. That's
|
|
// why we have to put them into an mpl::list<> as soon as there
|
|
// is more than one of them
|
|
// (see <a href=
|
|
"#SpecifyingMultipleReactionsForAState">Specifying multiple reactions for a state</a>).
|
|
|
|
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>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="StateLocalStorage" id="StateLocalStorage">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 : sc::simple_state< Active, StopWatch, Stopped >
|
|
{
|
|
<b>public:</b>
|
|
typedef sc::transition< EvReset, Active > reactions;
|
|
|
|
<b>Active() : elapsedTime_( 0.0 ) {}
|
|
</b> <b>double ElapsedTime() const { return elapsedTime_; }
|
|
</b> <b>double & ElapsedTime() { return elapsedTime_; }
|
|
</b> <b>private:
|
|
</b> <b>double elapsedTime_;
|
|
</b>};
|
|
|
|
struct Running : sc::simple_state< Running, Active >
|
|
{
|
|
<b>public:</b>
|
|
typedef sc::transition< EvStartStop, Stopped > reactions;
|
|
|
|
<b>Running() : startTime_( std::time( 0 ) ) {}
|
|
</b> <b>~Running()
|
|
</b> <b>{</b>
|
|
// Similar to when a derived class object accesses its
|
|
// base class portion, context<>() is used to gain
|
|
// access to the direct or indirect context of a state.
|
|
// This can either be a direct or indirect outer state
|
|
// or the state machine itself
|
|
// (e.g. here: context< StopWatch >()).
|
|
<b>context< Active >().ElapsedTime() +=
|
|
</b> <b>std::difftime( std::time( 0 ), startTime_ );
|
|
</b> <b>}
|
|
</b> <b>private:
|
|
</b> <b>std::time_t startTime_;
|
|
</b>};
|
|
|
|
// ...
|
|
</pre>
|
|
|
|
<p>The machine now measures the time, but we cannot yet retrieve it from
|
|
the main program.</p>
|
|
|
|
<p>At this point, the advantages of state-local storage (which is still a
|
|
relatively little-known feature) may not yet have become apparent. The FAQ
|
|
item "<a href="faq.html#StateLocalStorage">What's so cool about state-local
|
|
storage?</a>" tries to explain them in more detail by comparing this
|
|
StopWatch with one that does not make use of state-local storage.</p>
|
|
|
|
<h3><a name="GettingStateInformationOutOfTheMachine" id=
|
|
"GettingStateInformationOutOfTheMachine">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> (StopWatch2.cpp shows the slightly
|
|
more complex alternative). 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 double ElapsedTime() const = 0;
|
|
};
|
|
|
|
</b>struct Active;
|
|
struct StopWatch : sc::state_machine< StopWatch, Active >
|
|
{
|
|
<b>double ElapsedTime() const
|
|
</b> <b>{
|
|
</b> <b>return state_cast< const IElapsedTime & >().ElapsedTime();
|
|
</b> <b>}
|
|
</b>};
|
|
<b>
|
|
</b>// ...
|
|
|
|
struct Running : <b>IElapsedTime,</b>
|
|
sc::simple_state< Running, Active >
|
|
{
|
|
public:
|
|
typedef sc::transition< EvStartStop, Stopped > reactions;
|
|
|
|
Running() : startTime_( std::time( 0 ) ) {}
|
|
~Running()
|
|
{
|
|
<b>context< Active >().ElapsedTime() = ElapsedTime();
|
|
</b> }
|
|
<b>
|
|
</b> <b>virtual double ElapsedTime() const
|
|
</b> <b>{
|
|
</b> <b>return context< Active >().ElapsedTime() +
|
|
</b> <b>std::difftime( std::time( 0 ), startTime_ );
|
|
</b> <b>}
|
|
</b> private:
|
|
std::time_t startTime_;
|
|
};
|
|
|
|
struct Stopped : <b>IElapsedTime,</b>
|
|
sc::simple_state< Stopped, Active >
|
|
{
|
|
typedef sc::transition< EvStartStop, Running > reactions;
|
|
|
|
<b>virtual double 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="IntermediateTopicsADigitalCamera" id=
|
|
"IntermediateTopicsADigitalCamera">Intermediate topics: 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>
|
|
</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="SpreadingAStateMachineOverMultipleTranslationUnits" id=
|
|
"SpreadingAStateMachineOverMultipleTranslationUnits">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 statechart is one way to
|
|
achieve this behavior:</p>
|
|
|
|
<p><img alt="Camera" src="Camera.gif" border="0" 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_INCLUDED
|
|
#define CAMERA_HPP_INCLUDED
|
|
|
|
#include <boost/statechart/event.hpp>
|
|
#include <boost/statechart/state_machine.hpp>
|
|
#include <boost/statechart/simple_state.hpp>
|
|
#include <boost/statechart/custom_reaction.hpp>
|
|
|
|
namespace sc = boost::statechart;
|
|
|
|
struct EvShutterHalf : sc::event< EvShutterHalf > {};
|
|
struct EvShutterFull : sc::event< EvShutterFull > {};
|
|
struct EvShutterRelease : sc::event< EvShutterRelease > {};
|
|
struct EvConfig : sc::event< EvConfig > {};
|
|
|
|
struct NotShooting;
|
|
struct Camera : sc::state_machine< Camera, NotShooting >
|
|
{
|
|
bool IsMemoryAvailable() const { return true; }
|
|
bool IsBatteryLow() const { return false; }
|
|
};
|
|
|
|
struct Idle;
|
|
struct NotShooting : sc::simple_state<
|
|
NotShooting, Camera, Idle >
|
|
{
|
|
// 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 react member function, which can be
|
|
// implemented in the .cpp file.
|
|
<b>typedef sc::custom_reaction< EvShutterHalf > reactions;</b>
|
|
|
|
// ...
|
|
<b>sc::result react( const EvShutterHalf & );</b>
|
|
};
|
|
|
|
struct Idle : sc::simple_state< Idle, NotShooting >
|
|
{
|
|
<b>typedef sc::custom_reaction< EvConfig > reactions;</b>
|
|
|
|
// ...
|
|
<b>sc::result react( const EvConfig & );</b>
|
|
};
|
|
|
|
#endif
|
|
</pre>
|
|
|
|
<p>Camera.cpp:</p>
|
|
<pre>
|
|
#include "Camera.hpp"
|
|
|
|
// The following includes are only made here but not in
|
|
// Camera.hpp
|
|
// The Shooting and Configuring states can themselves apply the
|
|
// same pattern to hide their inner implementation, which
|
|
// ensures that the two teams working on the Camera state
|
|
// machine will never need to disturb each other.
|
|
#include "Configuring.hpp"
|
|
#include "Shooting.hpp"
|
|
|
|
// ...
|
|
|
|
// not part of the Camera example
|
|
sc::result NotShooting::react( const EvShutterHalf & )
|
|
{
|
|
return transit< Shooting >();
|
|
}
|
|
|
|
sc::result Idle::react( const EvConfig & )
|
|
{
|
|
return transit< Configuring >();
|
|
}
|
|
</pre>
|
|
|
|
<p><b><font color="#FF0000">Caution: Any call to
|
|
<code>simple_state<>::transit<>()</code> or
|
|
<code>simple_state<>::terminate()</code> (see <a href=
|
|
"reference.html#transit1">reference</a>) 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="DeferringEvents" id="DeferringEvents">Deferring
|
|
events</a></h3>
|
|
|
|
<p>The inner workings of the Shooting state could look as follows:</p>
|
|
|
|
<p><img alt="Camera2" src="Camera2.gif" border="0" 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 : sc::state< Focusing, Shooting >
|
|
{
|
|
typedef mpl::list<
|
|
sc::custom_reaction< EvInFocus >,
|
|
<b>sc::deferral< EvShutterFull ></b>
|
|
> reactions;
|
|
|
|
Focusing( my_context ctx );
|
|
sc::result react( const EvInFocus & );
|
|
};
|
|
</pre>
|
|
|
|
<h3><a name="Guards" id="Guards">Guards</a></h3>
|
|
|
|
<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
|
|
sc::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>
|
|
// ...
|
|
sc::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="InStateReactions" id="InStateReactions">In-state
|
|
reactions</a></h3>
|
|
|
|
<p>The self-transition of the Focused state could also be implemented as an
|
|
<a href="definitions.html#InStateReaction">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>
|
|
// ...
|
|
sc::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>
|
|
|
|
<p>Because the in-state reaction is guarded, we need to employ a
|
|
<code>custom_reaction<></code> here. For unguarded in-state reactions
|
|
<code><a href=
|
|
"reference.html#ClassTemplatein_state_reaction">in_state_reaction</a><></code>
|
|
should be used for better code-readability.</p>
|
|
|
|
<h3><a name="TransitionActions" id="TransitionActions">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#InnermostCommonContext">innermost
|
|
common context</a></li>
|
|
|
|
<li>The transition action (if present)</li>
|
|
|
|
<li>Starting from the innermost common context, all entry actions down to
|
|
the target state followed by the entry actions of the initial states</li>
|
|
</ol>
|
|
|
|
<p>Example:</p>
|
|
|
|
<p><img alt="LCA" src="LCA.gif" border="0" 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.Statechart, 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 : sc::simple_state< Shooting, Camera, Focusing >
|
|
{
|
|
typedef sc::transition<
|
|
EvShutterRelease, NotShooting > reactions;
|
|
|
|
// ...
|
|
<b>void DisplayFocused( const EvInFocus & );</b>
|
|
};
|
|
|
|
// ...
|
|
|
|
// not part of the Camera example
|
|
struct Focusing : sc::simple_state< Focusing, Shooting >
|
|
{
|
|
typedef sc::transition< EvInFocus, Focused<b>,</b>
|
|
<b>Shooting, &Shooting::DisplayFocused</b> > reactions;
|
|
};
|
|
</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 : sc::state_machine< Camera, NotShooting >
|
|
{
|
|
<b>void DisplayFocused( const EvInFocus & );</b>
|
|
};
|
|
</pre>
|
|
<pre>
|
|
// not part of the Camera example
|
|
struct Focusing : sc::simple_state< Focusing, Shooting >
|
|
{
|
|
typedef sc::transition< EvInFocus, Focused<b>,</b>
|
|
<b>Camera, &Camera::DisplayFocused</b> > reactions;
|
|
};
|
|
</pre>
|
|
|
|
<p>Naturally, transition actions can also be invoked from custom
|
|
reactions:</p>
|
|
|
|
<p>Shooting.cpp:</p>
|
|
<pre>
|
|
// ...
|
|
sc::result Focusing::react( const EvInFocus & evt )
|
|
{
|
|
// We have to manually forward evt
|
|
return transit< Focused >( <b>&Shooting::DisplayFocused</b>, evt );
|
|
}
|
|
</pre>
|
|
|
|
<h2><a name="AdvancedTopics" id="AdvancedTopics">Advanced topics</a></h2>
|
|
|
|
<h3><a name="SpecifyingMultipleReactionsForAState" id=
|
|
"SpecifyingMultipleReactionsForAState">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 : sc::simple_state< Playing, Mp3Player >
|
|
{
|
|
typdef <b>mpl::list<</b>
|
|
sc::custom_reaction< EvFastForward >,
|
|
sc::transition< EvStop, Stopped > <b>></b> reactions;
|
|
|
|
/* ... */
|
|
};
|
|
</pre>
|
|
|
|
<h3><a name="PostingEvents" id="PostingEvents">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( EvPumpingFinished() );
|
|
}
|
|
</pre>
|
|
|
|
<p>The event is pushed into the main queue. 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>sc::state</b>< Pumping, Purifier >
|
|
{
|
|
<b>Pumping( my_context ctx ) : my_base( ctx )</b>
|
|
{
|
|
post_event( EvPumpingStarted() );
|
|
}
|
|
// ...
|
|
};
|
|
</pre>
|
|
|
|
<p>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>state<></code> rather than from
|
|
<code>simple_state<></code> and must implement a forwarding
|
|
constructor as outlined above (apart from the constructor,
|
|
<code>state<></code> offers the same interface as
|
|
<code>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="History" id="History">History</a></h3>
|
|
|
|
<p>Photographers testing beta versions of our <a href=
|
|
"#SpreadingAStateMachineOverMultipleTranslationUnits">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 alt="CameraWithHistory1" src="CameraWithHistory1.gif" border="0"
|
|
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#InnermostState">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 : sc::simple_state<
|
|
NotShooting, Camera, Idle, <b>sc::has_deep_history</b> >
|
|
{
|
|
// ...
|
|
};
|
|
|
|
// ...
|
|
|
|
struct Shooting : sc::simple_state< Shooting, Camera, Focusing >
|
|
{
|
|
typedef sc::transition<
|
|
EvShutterRelease, <b>sc::deep_history< Idle ></b> > reactions;
|
|
|
|
// ...
|
|
};
|
|
</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>has_shallow_history</code>,
|
|
<code>has_deep_history</code> or <code>has_full_history</code> (which
|
|
combines shallow and deep history) as the last parameter to the
|
|
<code>simple_state</code> and <code>state</code> class templates. The
|
|
latter is expressed by specifying either
|
|
<code>shallow_history<></code> or <code>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>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 alt="CameraWithHistory2" src="CameraWithHistory2.gif" border="0"
|
|
width="468" height="483"></p>
|
|
<pre>
|
|
// ...
|
|
|
|
// not part of the Camera example
|
|
struct NotShooting : sc::simple_state< NotShooting, Camera,
|
|
<b>mpl::list< sc::deep_history< Idle > ></b>,
|
|
<b>sc::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="OrthogonalStates" id="OrthogonalStates">Orthogonal
|
|
states</a></h3>
|
|
|
|
<p><img alt="OrthogonalStates" src="OrthogonalStates.gif" border="0" width=
|
|
"633" height="393"></p>
|
|
|
|
<p>To implement this statechart you simply specify more than one inner
|
|
initial state (see the Keyboard example):</p>
|
|
<pre>
|
|
struct Active;
|
|
struct Keyboard : sc::state_machine< Keyboard, Active > {};
|
|
|
|
struct NumLockOff;
|
|
struct CapsLockOff;
|
|
struct ScrollLockOff;
|
|
struct Active: sc::simple_state< Active, Keyboard,
|
|
<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 : sc::event< EvNumLockPressed > {};
|
|
struct EvCapsLockPressed : sc::event< EvCapsLockPressed > {};
|
|
struct EvScrollLockPressed :
|
|
sc::event< EvScrollLockPressed > {};
|
|
|
|
struct NumLockOn : sc::simple_state<
|
|
NumLockOn, Active<b>::orthogonal< 0 ></b> >
|
|
{
|
|
typedef sc::transition<
|
|
EvNumLockPressed, NumLockOff > reactions;
|
|
};
|
|
|
|
struct NumLockOff : sc::simple_state<
|
|
NumLockOff, Active<b>::orthogonal< 0 ></b> >
|
|
{
|
|
typedef sc::transition<
|
|
EvNumLockPressed, NumLockOn > reactions;
|
|
};
|
|
|
|
struct CapsLockOn : sc::simple_state<
|
|
CapsLockOn, Active<b>::orthogonal< 1 ></b> >
|
|
{
|
|
typedef sc::transition<
|
|
EvCapsLockPressed, CapsLockOff > reactions;
|
|
};
|
|
|
|
struct CapsLockOff : sc::simple_state<
|
|
CapsLockOff, Active<b>::orthogonal< 1 ></b> >
|
|
{
|
|
typedef sc::transition<
|
|
EvCapsLockPressed, CapsLockOn > reactions;
|
|
};
|
|
|
|
struct ScrollLockOn : sc::simple_state<
|
|
ScrollLockOn, Active<b>::orthogonal< 2 ></b> >
|
|
{
|
|
typedef sc::transition<
|
|
EvScrollLockPressed, ScrollLockOff > reactions;
|
|
};
|
|
|
|
struct ScrollLockOff : sc::simple_state<
|
|
ScrollLockOff, Active<b>::orthogonal< 2 ></b> >
|
|
{
|
|
typedef sc::transition<
|
|
EvScrollLockPressed, ScrollLockOn > reactions;
|
|
};
|
|
</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: sc::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 : sc::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 : sc::simple_state<
|
|
CapsLockOn, Active<b>::</b>orthogonal< <b>1</b> > >
|
|
{
|
|
typedef sc::transition<
|
|
EvCapsLockPressed, CapsLockOff > reactions;
|
|
};
|
|
|
|
struct CapsLockOff : sc::simple_state<
|
|
CapsLockOff, Active<b>::</b>orthogonal< <b>2</b> > >
|
|
{
|
|
typedef sc::transition<
|
|
EvCapsLockPressed, CapsLockOn > reactions;
|
|
};
|
|
</pre>
|
|
|
|
<h3><a name="StateQueries" id="StateQueries">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="#GettingStateInformationOutOfTheMachine">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=
|
|
"#OrthogonalStates">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 : sc::event< EvRequestShutdown > {};
|
|
|
|
struct NumLockOff;
|
|
struct CapsLockOff;
|
|
struct ScrollLockOff;
|
|
struct Active: sc::simple_state< Active, Keyboard,
|
|
mpl::list< NumLockOff, CapsLockOff, ScrollLockOff > >
|
|
{
|
|
typedef sc::custom_reaction< EvRequestShutdown > reactions;
|
|
|
|
sc::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 << ": ";
|
|
// The following use of typeid assumes that
|
|
// BOOST_STATECHART_USE_NATIVE_RTTI is defined
|
|
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="StateTypeInformation" id="StateTypeInformation">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="CustomStateTypeInformation" id=
|
|
"CustomStateTypeInformation">Custom state type information</a></h4>
|
|
|
|
<p>That's why the following functions are also provided (only available
|
|
when <a href=
|
|
"configuration.html#ApplicationDefinedMacros">BOOST_STATECHART_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="ExceptionHandling" id="ExceptionHandling">Exception
|
|
handling</a></h3>
|
|
|
|
<p>Exceptions can be propagated from all user code except from state
|
|
destructors. Out of the box, the state machine framework is configured for
|
|
simple exception handling and does not catch any of these exceptions, so
|
|
they are immediately propagated to the state machine client. A scope guard
|
|
inside the <code>state_machine<></code> ensures that all state
|
|
objects are destructed before the exception is caught by the client. The
|
|
scope guard does not attempt to call any <code>exit</code> functions (see
|
|
<a href="#TwoStageExit">Two stage exit</a> below) that states might define
|
|
as these could themselves throw other exceptions which would mask the
|
|
original exception. Consequently, if a state machine should do something
|
|
more sensible when exceptions are thrown, it has to catch them before they
|
|
are propagated into the Boost.Statechart framework. This exception handling
|
|
scheme is often appropriate but it can lead to considerable code
|
|
duplication in state machines where many actions can trigger exceptions
|
|
that need to be handled inside the state machine (see <a href=
|
|
"rationale.html#ErrorHandling">Error handling</a> in the Rationale).<br>
|
|
That's why exception handling can be customized through the
|
|
<code>ExceptionTranslator</code> parameter of the
|
|
<code>state_machine</code> class template. Since the out-of-the box
|
|
behavior is to <b>not</b> translate any exceptions, the default argument
|
|
for this parameter is <code>null_exception_translator</code>. A
|
|
<code>state_machine<></code> subtype can be configured for advanced
|
|
exception handling by specifying the library-supplied
|
|
<code>exception_translator<></code> instead. This way, the following
|
|
happens when an exception is propagated from user code:</p>
|
|
|
|
<ol>
|
|
<li>The exception is caught inside the framework</li>
|
|
|
|
<li>In the catch block, an <code>exception_thrown</code> event is
|
|
allocated on the stack</li>
|
|
|
|
<li>Also in the catch block, an <b>immediate</b> dispatch of the
|
|
<code>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>On platforms with buggy exception handling implementations users would
|
|
probably want to implement their own model of the <a href=
|
|
"reference.html#ExceptionTranslator">ExceptionTranslator concept</a> (see
|
|
also <a href="#DiscriminatingExceptions">Discriminating
|
|
exceptions</a>).</p>
|
|
|
|
<h4>Successful exception handling</h4>
|
|
|
|
<p>An exception is considered handled successfully, if:</p>
|
|
|
|
<ul>
|
|
<li>an appropriate reaction for the <code>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 next
|
|
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. <code>exception_translator<></code> simply rethrows
|
|
the original exception if the exception handling was unsuccessful. Just as
|
|
with simple exception handling, in this case a scope guard inside the
|
|
<code>state_machine<></code> ensures that all state objects are
|
|
destructed before the exception is caught by the client.</p>
|
|
|
|
<h4>Which states can react to an <code>exception_thrown</code> event?</h4>
|
|
|
|
<p>Short answer: If the state machine is stable when the exception is
|
|
thrown, the state that caused the exception is first tried for a reaction.
|
|
Otherwise the outermost <a href="definitions.html#UnstableState">unstable
|
|
state</a> is first tried for a reaction.</p>
|
|
|
|
<p>Longer answer: 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 or the action
|
|
executed during an in-state reaction propagates an exception. 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 alt="ThrowingInStateReaction" src="ThrowingInStateReaction.gif"
|
|
border="0" width="362" height="182"><br>
|
|
<br></li>
|
|
|
|
<li>A state entry action (constructor) propagates an exception:<br>
|
|
|
|
<ul>
|
|
<li>If there are no orthogonal regions, the direct 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 alt="ThrowingEntryAction" src="ThrowingEntryAction.gif" border=
|
|
"0" width="438" height="241"><br></li>
|
|
|
|
<li>If there are orthogonal regions, the outermost <a href=
|
|
"definitions.html#UnstableState">unstable state</a> is first tried
|
|
for a reaction. The outermost unstable state is found by first
|
|
selecting the direct outer state of the state that caused the
|
|
exception and then moving outward until a state is found that is
|
|
unstable but has no direct or indirect outer states that are
|
|
unstable. This more complex rule is necessary because only reactions
|
|
associated with the outermost unstable state (or any of its direct or
|
|
indirect outer states) are able to bring the machine back into a
|
|
stable state. Consider the following statechart:<br>
|
|
<br>
|
|
<img alt="OutermostUnstableState" src="OutermostUnstableState.gif"
|
|
border="0" width="467" height="572"><br>
|
|
<br>
|
|
Whether this state machine will ultimately transition to E or F after
|
|
initiation depends on which of the two orthogonal regions is
|
|
initiated first. If the upper orthogonal region is initiated first,
|
|
the entry sequence is as follows: A, D, B, (exception is thrown).
|
|
Both D and B were successfully entered, so B is the outermost
|
|
unstable state when the exception is thrown and the machine will
|
|
therefore transition to F. However, if the lower orthogonal region is
|
|
initiated first, the sequence is as follows: A, B, (exception is
|
|
thrown). D was never entered so A is the outermost unstable state
|
|
when the exception is thrown and the machine will therefore
|
|
transition to E.<br>
|
|
In practice these differences rarely matter as top-level error
|
|
recovery is adequate for most state machines. However, since the
|
|
sequence of initiation is clearly defined (orthogonal region 0 is
|
|
always initiated first, then region 1 and so forth), users <b>can</b>
|
|
accurately control when and where they want to handle
|
|
exceptions<br></li>
|
|
</ul>
|
|
</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 alt="ThrowingTransitionAction" src="ThrowingTransitionAction.gif"
|
|
border="0" 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 alt="ExceptionsAndOrthStates" src="ExceptionsAndOrthStates.gif"
|
|
border="0" width="571" height="331"></p>
|
|
|
|
<p>Instead, the machine is terminated and the original exception
|
|
rethrown.</p>
|
|
|
|
<h4><a name="DiscriminatingExceptions" id=
|
|
"DiscriminatingExceptions">Discriminating exceptions</a></h4>
|
|
|
|
<p>Because the <code>exception_thrown</code> event is dispatched from
|
|
within the catch block, we can rethrow and catch the exception in a custom
|
|
reaction:</p>
|
|
<pre>
|
|
struct Defective : sc::simple_state<
|
|
Defective, Purifier > {};
|
|
|
|
// Pretend this is a state deeply nested in the Purifier
|
|
// state machine
|
|
struct Idle : sc::simple_state< Idle, Purifier >
|
|
{
|
|
typedef mpl::list<
|
|
sc::custom_reaction< EvStart >,
|
|
sc::custom_reaction< sc::exception_thrown >
|
|
> reactions;
|
|
|
|
sc::result react( const EvStart & )
|
|
{
|
|
throw std::runtime_error( "" );
|
|
}
|
|
|
|
sc::result react( const sc::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>
|
|
|
|
<h4><a name="TwoStageExit" id="TwoStageExit">Two stage exit</a></h4>
|
|
|
|
<p>If a <code>simple_state<></code> or <code>state<></code>
|
|
subtype declares a public member function with the signature <code>void
|
|
exit()</code> then this function is called just before the state object is
|
|
destructed. As explained under <a href="rationale.html#ErrorHandling">Error
|
|
handling</a> in the Rationale, this is useful for two things that would
|
|
otherwise be difficult or cumbersome to achieve with destructors only:</p>
|
|
|
|
<ol>
|
|
<li>To signal a failure in an exit action</li>
|
|
|
|
<li>To execute certain exit actions <b>only</b> during a transition or a
|
|
termination but not when the state machine object is destructed</li>
|
|
</ol>
|
|
|
|
<p>A few points to consider before employing <code>exit()</code>:</p>
|
|
|
|
<ul>
|
|
<li>There is no guarantee that <code>exit()</code> will be called:
|
|
|
|
<ul>
|
|
<li>If the client destructs the state machine object without calling
|
|
<code>terminate()</code> beforehand then the currently active states
|
|
are destructed without calling <code>exit()</code>. This is necessary
|
|
because an exception that is possibly thrown from <code>exit()</code>
|
|
could not be propagated on to the state machine client</li>
|
|
|
|
<li><code>exit()</code> is not called when a previously executed
|
|
action propagated an exception and that exception has not (yet) been
|
|
handled successfully. This is because a new exception that could
|
|
possibly be thrown from <code>exit()</code> would mask the original
|
|
exception</li>
|
|
</ul>
|
|
</li>
|
|
|
|
<li>A state is considered exited, even if its <code>exit</code> function
|
|
propagated an exception. That is, the state object is inevitably
|
|
destructed right after calling <code>exit()</code>, regardless of whether
|
|
<code>exit()</code> propagated an exception or not. A state machine
|
|
configured for advanced exception handling is therefore always unstable
|
|
while handling an exception propagated from an <code>exit</code>
|
|
function</li>
|
|
|
|
<li>In a state machine configured for advanced exception handling the
|
|
processing rules for an exception event resulting from an exception
|
|
propagated from <code>exit()</code> are analogous to the ones defined for
|
|
exceptions propagated from state constructors. That is, the outermost
|
|
unstable state is first tried for a reaction and the dispatcher then
|
|
moves outward until an appropriate reaction is found</li>
|
|
</ul>
|
|
|
|
<h3><a name="SubmachinesAndParameterizedStates" id=
|
|
"SubmachinesAndParameterizedStates">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.Statechart 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="#OrthogonalStates">Orthogonal states</a>:</p>
|
|
<pre>
|
|
enum LockType
|
|
{
|
|
NUM_LOCK,
|
|
CAPS_LOCK,
|
|
SCROLL_LOCK
|
|
};
|
|
|
|
template< LockType lockType >
|
|
struct Off;
|
|
struct Active : sc::simple_state<
|
|
Active, Keyboard, mpl::list<
|
|
Off< NUM_LOCK >, Off< CAPS_LOCK >, Off< SCROLL_LOCK > > > {};
|
|
|
|
template< LockType lockType >
|
|
struct EvPressed : sc::event< EvPressed< lockType > > {};
|
|
|
|
template< LockType lockType >
|
|
struct On : sc::simple_state<
|
|
On< lockType >, Active::orthogonal< lockType > >
|
|
{
|
|
typedef sc::transition<
|
|
EvPressed< lockType >, Off< lockType > > reactions;
|
|
};
|
|
|
|
template< LockType lockType >
|
|
struct Off : sc::simple_state<
|
|
Off< lockType >, Active::orthogonal< lockType > >
|
|
{
|
|
typedef sc::transition<
|
|
EvPressed< lockType >, On< lockType > > reactions;
|
|
};
|
|
</pre>
|
|
|
|
<h3><a name="AsynchronousStateMachines" id=
|
|
"AsynchronousStateMachines">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>
|
|
function only returns after having executed all reactions (including the
|
|
ones provoked by internal events that actions might have posted). This
|
|
function is 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> subtype objects 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>). It then "waits" for B to send back an
|
|
answer via a <code>boost::function<></code>-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>The <code>asynchronous_state_machine</code> class template has none of
|
|
the member functions the <code>state_machine</code> class template has.
|
|
Moreover, <code>asynchronous_state_machine<></code> subtype objects
|
|
cannot even be created or destroyed directly. Instead, all these operations
|
|
must be performed through the <code>Scheduler</code> object each
|
|
asynchronous state machine is associated with. All these
|
|
<code>Scheduler</code> member functions only push an appropriate item into
|
|
the schedulers' queue and then return immediately. A dedicated thread will
|
|
later pop the items out of the queue to have them processed.</p>
|
|
|
|
<p>Applications will usually first create a
|
|
<code>fifo_scheduler<></code> object and then call
|
|
<code>fifo_scheduler<>::create_processor<>()</code> and
|
|
<code>fifo_scheduler<>::initiate_processor()</code> to schedule the
|
|
creation and initiation of one or more
|
|
<code>asynchronous_state_machine<></code> subtype objects. Finally,
|
|
<code>fifo_scheduler<>::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>.
|
|
Alternatively, the latter could also be done right after constructing the
|
|
<code>fifo_scheduler<></code> object. In the following code, we are
|
|
running one state machine in a new <code>boost::thread</code> and the other
|
|
in the main thread (see the PingPong example for the full source code):</p>
|
|
<pre>
|
|
struct Waiting;
|
|
struct Player :
|
|
sc::asynchronous_state_machine< Player, Waiting >
|
|
{
|
|
// ...
|
|
};
|
|
|
|
// ...
|
|
|
|
int main()
|
|
{
|
|
// Create two schedulers that will wait for new events
|
|
// when their event queue runs empty
|
|
sc::fifo_scheduler<> scheduler1( true );
|
|
sc::fifo_scheduler<> scheduler2( true );
|
|
|
|
// Each player is serviced by its own scheduler
|
|
sc::fifo_scheduler<>::processor_handle player1 =
|
|
scheduler1.create_processor< Player >( /* ... */ );
|
|
scheduler1.initiate_processor( player1 );
|
|
sc::fifo_scheduler<>::processor_handle player2 =
|
|
scheduler2.create_processor< Player >( /* ... */ );
|
|
scheduler2.initiate_processor( player2 );
|
|
|
|
// the initial event that will start the game
|
|
boost::intrusive_ptr< BallReturned > pInitialBall =
|
|
new BallReturned();
|
|
|
|
// ...
|
|
|
|
scheduler2.queue_event( player2, pInitialBall );
|
|
|
|
// ...
|
|
|
|
// Up until here no state machines exist yet. They
|
|
// will be created when operator()() is called
|
|
|
|
// Run first scheduler in a new thread
|
|
boost::thread otherThread( boost::bind(
|
|
&sc::fifo_scheduler<>::operator(), &scheduler1, 0 ) );
|
|
scheduler2(); // Run second scheduler 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(
|
|
&sc::fifo_scheduler<>::operator(), &scheduler1, 0 ) );
|
|
boost::thread thread2( boost::bind(
|
|
&sc::fifo_scheduler<>::operator(), &scheduler2, 0 ) );
|
|
|
|
// do something else ...
|
|
|
|
thread1.join();
|
|
thread2.join();
|
|
|
|
return 0;
|
|
}
|
|
</pre>
|
|
|
|
<p>Or, run both machines in the same thread:</p>
|
|
<pre>
|
|
int main()
|
|
{
|
|
sc::fifo_scheduler<> scheduler1( true );
|
|
|
|
sc::fifo_scheduler<>::processor_handle player1 =
|
|
scheduler1.create_processor< Player >( /* ... */ );
|
|
sc::fifo_scheduler<>::processor_handle player2 =
|
|
scheduler1.create_processor< Player >( /* ... */ );
|
|
|
|
// ...
|
|
|
|
scheduler1();
|
|
|
|
return 0;
|
|
}
|
|
</pre>
|
|
|
|
<p>In all the examples above,
|
|
<code>fifo_scheduler<>::operator()()</code> waits on an empty event
|
|
queue and will only return after a call to
|
|
<code>fifo_scheduler<>::terminate()</code>. The <code>Player</code>
|
|
state machine calls this function on its scheduler object right before
|
|
terminating.</p>
|
|
<hr>
|
|
|
|
<p><a href="http://validator.w3.org/check?uri=referer"><img border="0" src=
|
|
"../../../doc/images/valid-html401.png" alt="Valid HTML 4.01 Transitional"
|
|
height="31" width="88"></a></p>
|
|
|
|
<p>Revised
|
|
<!--webbot bot="Timestamp" s-type="EDITED" s-format="%d %B, %Y" startspan -->03 December, 2006<!--webbot bot="Timestamp" endspan i-checksum="38512" --></p>
|
|
|
|
<p><i>Copyright © 2003-<!--webbot bot="Timestamp" s-type="EDITED" s-format="%Y" startspan -->2006<!--webbot bot="Timestamp" endspan i-checksum="770" -->
|
|
<a href="contact.html">Andreas Huber Dönni</a></i></p>
|
|
|
|
<p><i>Distributed under 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>
|