C++ Boost

The Boost Statechart Library

Frequently Asked Questions (FAQs)


Does Boost.Statechart support polymorphic events?
How can I hide the inner workings of a state machine from its clients?
Is it possible to inherit from a given state machine and modify its layout in the subclass?
Why are exit-actions called in the wrong order when I use multiple inheritance?
Is Boost.Statechart suitable for embedded applications?
Is your library suitable for applications with hard real-time requirements?

Does Boost.Statechart support polymorphic events?

No. Although events can be derived from each other to write common code only once, reactions can only be defined for most-derived events.

Example:

template< class MostDerived >
struct EvButtonPressed : sc::event< MostDerived >
{
  // common code
};

struct EvPlayButtonPressed :
  EvButtonPressed< EvPlayButtonPressed > {};
struct EvStopButtonPressed :
  EvButtonPressed< EvStopButtonPressed > {};
struct EvForwardButtonPressed :
  EvButtonPressed< EvForwardButtonPressed > {};

/* ... */

// We want to turn the player on, no matter what button we
// press in the Off state. Although we can write the reaction
// code only once, we must mention all most-derived events in
// the reaction list.
struct Off : sc::simple_state< Off, Mp3Player, mpl::list<
  sc::custom_reaction< EvPlayButtonPressed >,
  sc::custom_reaction< EvStopButtonPressed >,
  sc::custom_reaction< EvForwardButtonPressed > > >
{
  template< class MostDerived >
  sc::result react( const EvButtonPressed< MostDerived > & )
  {
    // ...
  }
};

How can I hide the inner workings of a state machine from its clients?

To see why and how this is possible it is important to recall the following facts:

The class template member function state_machine<>::initiate() creates an object of the initial state. So, the definition of this state must be known before the compiler reaches the point where initiate() is called. To be able to hide the initial state of a state machine in a .cpp file we must therefore no longer let clients call initiate(). Instead, we do so in the .cpp file, at a point where the full definition of the initial state is known.

Example:

StopWatch.hpp:

// define events ...

struct Active; // the only visible forward
struct StopWatch : sc::state_machine< StopWatch, Active >
{
  StopWatch();
};

StopWatch.cpp:

struct Stopped;
struct Active : sc::simple_state< Active, StopWatch,
  sc::transition< EvReset, Active >, Stopped > {};
struct Running : sc::simple_state< Running, Active,
  sc::transition< EvStartStop, Stopped > > {};
struct Stopped : sc::simple_state< Stopped, Active,
  sc::transition< EvStartStop, Running > > {};

StopWatch::StopWatch()
{
  // For example, we might want to ensure that the state
  // machine is already started after construction.
  // Alternatively we could also provide the client with
  // a Start() function...
  initiate();
}

Is it possible to inherit from a given state machine and modify its layout in the subclass?

Yes, but contrary to what some FSM code generators allow, Boost.Statechart machines can do so only in a way that was foreseen by the designer of the base state machine:

struct EvStart : sc::event< EvStart > {};

struct Idle;
struct PumpBase : sc::state_machine< PumpBase, Idle >
{
  virtual sc::result react(
    Idle & idle, const EvStart & ) const;
};

struct Idle : sc::simple_state< Idle, PumpBase,
  sc::custom_reaction< EvStart > >
{
  sc::result react( const EvStart & evt )
  {
    return context< PumpBase >().react( *this, evt );
  }
};

struct Running : sc::simple_state< Running, PumpBase > {};

sc::result PumpBase::react(
  Idle & idle, const EvStart & ) const
{
  return idle.transit< Running >();
}


struct MyRunning : sc::simple_state< MyRunning, PumpBase > {};

struct MyPump : PumpBase
{
  virtual sc::result react(
    Idle & idle, const EvStart & ) const
  {
    return idle.transit< MyRunning >();
  }
};

Why are exit-actions called in the wrong order when I use multiple inheritance?

Update: The implementation has changed considerably in this area. It is still possible to get this behavior under rare circumstances (when an action propagates an exception in a state machine with orthogonal regions and if the statechart layout satisfies certain conditions), but it can no longer be demonstrated with the example program below. However, the described workaround is still valid and ensures that this behavior will never show up.

They definitely aren't for the simple_state and state subclasses, but the destructors of additional bases might be called in construction order (rather than the reverse construction order):

#include <boost/statechart/state_machine.hpp>
#include <boost/statechart/simple_state.hpp>

namespace sc = boost::statechart;

class EntryExitDisplayer
{
  protected:
    EntryExitDisplayer( const char * pName ) :
      pName_( pName )
    {
      std::cout << pName_ << " entered\n";
    }

    ~EntryExitDisplayer()
    {
      std::cout << pName_ << " exited\n";
    }

  private:
    const char * const pName_;
};

struct Outer;
struct Machine : sc::state_machine< Machine, Outer > {};
struct Inner;
struct Outer : EntryExitDisplayer, sc::simple_state<
  Outer, Machine, sc::no_reactions, Inner >
{
  Outer() : EntryExitDisplayer( "Outer" ) {}
};

struct Inner : EntryExitDisplayer,
  sc::simple_state< Inner, Outer >
{
  Inner() : EntryExitDisplayer( "Inner" ) {}
};

int main()
{
  Machine myMachine;
  myMachine.initiate();
  return 0;
}

This program will produce the following output:

Outer entered
Inner entered
Outer exited
Inner exited

That is, the EntryExitDisplayer base class portion of Outer is destructed before the one of Inner although Inner::~Inner() is called before Outer::~Outer(). This somewhat counter-intuitive behavior is caused by the following facts:

So, when the Outer destructor is called the call stack looks as follows:

Outer::~Outer()
simple_state< Inner, ... >::~simple_state()
Inner::~Inner()

Note that Inner::~Inner() did not yet have a chance to destroy its EntryExitDisplayer base class portion, as it first has to call the destructor of the second base class. Now Outer::~Outer() will first destruct its simple_state< Outer, ... > base class portion and then do the same with its EntryExitDisplayer base class portion. The stack then unwinds back to Inner::~Inner(), which can then finally finish by calling EntryExitDisplayer::~EntryExitDisplayer().

Luckily, there is an easy work-around: Always let simple_state<> and state<> be the first base class of a state. This ensures that destructors of additional bases are called before recursion employed by state base destructors can alter the order of destruction.

Is Boost.Statechart suitable for embedded applications?

It depends. As explained under Speed versus scalability tradeoffs in the Rationale, the virtually limitless scalability offered by this library does have its price. Especially small and simple FSMs can easily be implemented so that they consume fewer cycles and less memory and occupy less code space in the executable. Here are some obviously very rough estimates:

As mentioned above, these are very rough estimates derived from the use of the library on a desktop PC, so they should only be used to decide whether there is a point in making your own performance tests on your target platform.

Is your library suitable for applications with hard real-time requirements?

Yes. Out of the box, the only operations taking potentially non-deterministic time that the library performs are calls to global operator new and dynamic_casts. Global operator new calls can be avoided by passing a custom allocator to  state_machine<> and by giving all state classes a custom operator new. dynamic_casts can be avoided by not calling the state_cast<> member functions of state_machine<>, simple_state<> and state<> but using the deterministic variant state_downcast<> instead.


Revised 10 May, 2005

© Copyright Andreas Huber Dönni 2003-2005. The link refers to a spam honeypot. Please remove the words spam and trap to obtain my real address.

Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)