User's Guide

Design

Msm is divided between front–ends and back-ends. At the moment, there is just one back-end. On the front-end side, there is more to see. Msm offers several state machine description languages with many more possible. Now, everybody can build his own description language (in case one gets bored with the ones provided) without changes to the library. For who feels like being a language writer this document adds a description of the interface between front-end and back-end.TODO.

There are, at the moment, three main front-ends. The basic one is an adaptation of the example provided in the MPL book (TODO add link) with actions defined as pointers to state or state machine methods. The second one is based on functors. The third, eUML (embedded UML) is an experimental language based on Boost.Proto and Boost.Typeof and hiding most of the metaprogramming to increase readability. Both eUML and the functor front-end also offer a functional library (a bit like Boost.Phoenix) for use as action language (UML defining none).

Basic front-end

This is the historical front-end, inherited from the MPL book. It provides a transition table made of rows of different names and functionality. Actions and guards are defined as methods and referenced through a pointer in the transition. This front-end provides a simple interface making easy state machines easier to define, but more complex state machines a bit harder. It also is slightly slower than the functor and eUML front-ends (about 20%). Not that it does more, it simply seems that the compiler better optimizes the functor actions.

A simple example

Let us have a look at a state machine diagram of the founding example:

We are now going to build it with MSM's basic front-end. An example is also provided.

Transition table

As previously stated, MSM is based on the transition table, so let us define one:

struct transition_table : mpl::vector<

// Start Event Next Action Guard
// +---------+ -------------+ ---------+ ---------------------+ ----------------------+
a_row < Stopped , play, Playing, &player_::start_playback >,
a_row < Stopped , open_close, Open, &player_::open_drawer >,
_row < Stopped , stop, Stopped >,
// +--------- -------------+ ---------+ ---------------------+ ----------------------+
a_row < Open , open_close , Empty , &player_::close_drawer >,
// +---------+ -------------+ ---------+ ---------------------+ ----------------------+
a_row < Empty , open_close , Open , &player_::open_drawer >,
row < Empty , cd_detected , Stopped , &player_::store_cd_info , &player_::good_disk_format >,
row < Empty , cd_detected , Playing , &player_::store_cd_info , &player_::auto_start >,
// +---------+ -------------+ ---------+ ---------------------+ ----------------------+
a_row < Playing , stop , Stopped , &player_::stop_playback >,
a_row < Playing , pause , Paused , &player_::pause_playback >,
a_row < Playing , open_close , Open , &player_::stop_and_open >,
// +---------+ -------------+ ---------+ ---------------------+ ----------------------+
a_row < Paused , end_pause , Playing , &player_::resume_playback >,
a_row < Paused , stop , Stopped , &player_::stop_playback >,
a_row < Paused , open_close , Open , &player_::stop_and_open >
// +---------+ -------------+ ---------+ ---------------------+ ----------------------+
> {};

You will notice that this is almost exactly our founding example. The only change in the transition table is the different types of transitions (rows). The founding example forces one to define an action method and offers no guards. You have 4 basic row types:

  • row takes 5 arguments: start state, event, next state, action and guard.

  • a_row (“a” for action) allows defining only the action and omit the guard condition.

  • g_row (“g” for guard) allows omitting the action method and defining only the guard.

  • _row allows omitting action and guard methods.

The signature for action methods is:

void stop_playback(stop const&)

Action methods return nothing and take the argument as const reference. Of course nothing forbids you from defining an action for several events:

template <class Event> void stop_playback(Event const&)

Guards have as only difference the return value, which is a boolean:

bool good_disk_format(cd_detected const& evt)

The transition table is actually a MPL vector (or list), which brings a limitation that the default maximum size of the table is 20. If you need more transitions, overriding this default behavior is necessary, for example add before any header:

#define BOOST_MPL_CFG_NO_PREPROCESSED_HEADERS

#define BOOST_MPL_LIMIT_VECTOR_SIZE 30 // or whatever you need

#define BOOST_MPL_LIMIT_MAP_SIZE 30 // or whatever you need

The other limitation is that the MPL types are defined only up to 50 entries. For the moment, the only solution to achieve more is to add headers to the MPL (luckily, this is not very complicated).TODO add them

Defining states with entry/exit actions

While states were enums in the MPL book, they now are structs, which allows them to hold data, provide entry, exit actions, different behaviors and be reusable (as they do not know anything about the containing state machine). To define a state, inherit from the correct state type:

struct Empty : public msm::front::state<> {};

They can optionally provide entry and exit behaviors:

struct Empty : public msm::front::state<> {

template <class Event, class Fsm>

void on_entry(Event const&, Fsm& ) {std::cout << "entering: Empty" << std::endl;}

template <class Event, class Fsm>

void on_exit(Event const&, Fsm& ) {std::cout << "leaving: Empty" << std::endl;}

};

There are more state types (terminate, interrupt, pseudo states, etc.) corresponding to the UML standard state types. These will be described in details in the next pages.

Defining a simple state machine

Declaring a state machine is straightforward and is done with a high signal / noise ratio. In our player example, we declare the state machine as:

struct player_ : public msm::front::state_machine_def<player_>{ /* see below */}

This declares a state machine using the basic front-end. We now declare inside the state machine structure the initial state:

typedef Empty initial_state;

And that is about all of what is absolutely needed. In the example, the states are declared inside the state machine for readability but this is not a requirements, states can be declared wherever you see fit.

All what is left to us is to pick a back-end (which is quite simple as there is only one at the moment):

typedef msm::back::state_machine<player_> player;

You now have a ready-to-use state machine with entry/exit actions, guards, transition actions, a message queue so that processing an event can generate another event. The state machine also adapted itself to your need and removed almost all features we didn't use in this simple example. Note that this is not per default the faster possible state machine. See the section "getting more speed" to know how to get the maximum speed. In a nutshell, MSM cannot know about your usage of some features so you will have to explicitly tell it.

TODO no_transition TODO start fct

Defining a submachine

We now want to extend our last state machine by making the Playing state a state machine itself (a submachine).

Again, an example is also provided.

A submachine really is a state machine itself, so we declare Playing as such, choosing a front-end and a back-end:

struct Playing_ : public msm::Front::state_machine_def<Playing_>{...}

typedef msm::back::state_machine<Playing_> Playing;

Like for any state machine, one also needs a transition table and an initial state:

struct transition_table : mpl::vector<

// Start Event Next Action Guard
// +---------+ -------------+ ---------+ ------------------------------+ ----------------------+
a_row < Song1 , NextSong, Song2, &Playing_::start_next_song >,
a_row < Song2 , PreviousSong, Song1, &Playing_::start_prev_song >,
a_row < Song2 , NextSong, Song3, &Playing_::start_next_song >,
a_row < Song3 , PreviousSong , Song2 , &Playing_::start_prev_song >
// +---------+ -------------+ ---------+ ------------------------------+ ----------------------+
> {};

typedef Song1 initial_state;

This is about all you need to do. MSM will now automatically recognize Playing as a submachine and all events handled by Playing (NextSong and PreviousSong) will now be automatically forwarded to Playing whenever this state is active. All other state machine features described later are also available.

Orthogonal regions, terminate state, event deferring

It is a very common problem in many state machines to have to handle errors. It usually involves defining a transition from all the states to a special error state. Translation: not fun. It is also not practical to find from which state the error originated. The following diagram shows an example of what clearly becomes not very readable:

This is neither very readable nor beautiful, clearly. And we do not even have any action on the transitions yet.

Luckily, UML provides a helpful concept, orthogonal regions. See them as lightweight state machines running at the same time in a same state machine and having the capability to influence one another. The effect is that you have several active states at any time. We can therefore keep our state machine from the previous example and just define a new region made of two states, AllOk and ErrorMode. AllOk is most of the time active. But the error_found error event makes the second region move to the new active state ErrorMode. This event does not interest the main region so it will simply be ignored. "no_transition" will be called only if no region at all handles the event.

Adding an orthogonal region is easy, one only needs to declare more states in the initial_state typedef. So, adding a new region with AllOk as the region's initial state is:

typedef mpl::vector<Empty,AllOk> initial_state;

Furthermore, when you detect an error, you usually do not want other events to be handled. To achieve this, we use another UML feature, terminate states. When any region moves to a terminate state, the state machine “terminates” (the state machine and all its states stay alive) and all further events are ignored. This is of course not mandatory, one can use orthogonal regions without terminate states. Also note that, as UML mandates, every region gets a chance of handling the event, in the order as declared by the initial_state type. MSM also provides a small extension to UML, interrupt states. If you declare ErrorMode as interrupt state instead of terminate state, the state machine will not handle any event other than the one which ends the interrupt. So it's like a terminate state, with the difference that you are allowed to resume the state machine when a condition (like handling of the original error) is met.

Last but not least, this example also shows here the handling of event deferring. Let's say someone puts a disc and immediately presses play. The event cannot be handled, yet you'd want it to be handled at a later point and not force the user to press play again. The solution is to define it as deferred in the Empty and Open states and get it handled in the first state where the event is not to be deferred. It can then be handled or rejected. In this example, when Stopped becomes active, the event will be handled because only Empty and Open defer the event.

Notice how UML defines event deferring as a state property. To accomodate this, MSM lets you specify this in states by providing a deferred_events type:

struct Empty : public msm::front::state<>

{

// if the play event is fired while in this state, defer it until a state

// handles or rejects it

typedef mpl::vector<play> deferred_events;

...};

Please have a look at the complete example.

While this is wanted by UML and is simple, it is not always practical because one could wish to defer only in certain conditions. One could also want to make this be part of a transition action with the added bonus of a guard. It would also be conform to the MSM philosophy to get as much as possible in the transition table, where you can have all the important state machine structure grouped together. This is also possible but not practical with this front-end so we will need to pick a different row from the functor front-end. For a complete description of the Row type, please have a look at the functor front-end.

First, as there is no state where MSM can detect the requirement of this feature, we need to require deferred events capability explicitly, by adding a type in the state machine definition:

struct player_ : public msm::front::state_machine_def<player_>

{

typedef int activate_deferred_events;

...};

We can now defer an event in any transition of the transition table by using as action the predefined msm::front::Defer functor, for example:

Row < Empty , play , none , Defer , none >

This is an internal transition row(see internal transitions) but you can ignore this for the moment. It just means that we are not leaving the Empty state. What matters is that we use Defer as action. This is roughly equivalent to the previous syntax but has the advantage of giving you all the information in the transition table.

The second difference is that as we now have a transition defined, this transition can play in the resolution of transition conflicts. For example, we could model an "if (condition2) move to Playing else if (condition1) defer play event":

Row < Empty , play , none , Defer , condition1 >,

g_row < Empty , play , Playing , &player_::condition2 >

Please have a look at this possible implementation.

History

UML defines two types of history, Shallow History and Deep History. What is it and when do you need it? In the previous examples, if the player was playing the second song and the user pressed pause, leaving Playing, at the next press on the play button, the Playing state would become active and the first song would play again. Soon would the first client complaints follow. They'd of course demand, that if the player was paused, then it should remember which song was playing. But it the player was stopped, then it should restart from the first song. Now, how can it be done? Of course, you could add a bit of programming logic and generate extra events to make the second song start if coming from Pause. Something like:

if (Event == end_pause) { for (int i=0;i< song number;++i) { player.process_event(NextSong()); } }

Not much to like in this example, isn't it? To solve this problem, you define what is called a shallow or a deep history. A shallow history reactivates the last active state of a submachine when this state machine becomes active again. The deep history does the same recursively, so if this last active state of the submachine was itself a submachine, its last active state would become active and this will continue until an active state is a normal state. For example, let us have a look at the following UML diagram:

Notice that the main difference compared to previous diagrams is that the initial state is gone and replaced by a History symbol (the H inside a circle).

As explained in the small UML tutorial, History is a good concept with a not completely satisfying specification. MSM kept the concept but not the specification. Msm goes another way and makes this a policy so you can define your own history types. Furthermore, History is a backend policy. This allows you to reuse the same state machine frontend with different history policies.

Concretely, your frontend stays unchanged:

struct Playing_ : public msm::front::state_machine_def<Playing_>

You then add the policy to the backend:

typedef msm::back::state_machine<Playing_,msm::back::ShallowHistory<mpl::vector<end_pause> > > Playing;

This states that a shallow history must be activated if the Playing state machine gets activated by the end_pause event and only this one (or any other event added to the mpl::vector). If the state machine was in the Stopped state and the event play was generated, the history would not be activated and the normal initial state would become active. By default, history is disabled. For your convenience the library provides in addition to ShallowHistory a non-UML standard AlwaysHistory policy (likely to be your main choice) which always activates history, whatever event triggers the submachine activation. Deep history is not directly available. The reason is that it would conflict with policies which submachines could define. Of course, if for example, Song1 were a state machine itself, it could use the ShallowHistory policy itself thus creating Deep History.

Anonymous transitions

The following diagram shows an example making use of this feature:

Anonymous transitions are transitions without a named event. This means that the transition automatically fires when the predecessor state is entered (to be exact, after the entry action). Otherwise it is a normal transition with actions and guards. Why would you need something like that? A possible case would be if a part of your state machine implements some algorithm, where states are steps of the algorithm implementation. Then, using several anonymous transitions with different guard conditions, you are actually implementing some if/else statement. Another possible use would be a real-time system called at regular intervals and always doing the same thing, meaning implementing the same algorithm. The advantage is that once you know how long a transition takes to execute on the system, by calculating the longest path (the number of transitions from start to end), you can pretty much know how long your algorithm will take in the worst case, which in turns tells you how big of a time frame you are to request from a scheduler.

If you are using Executable UML (a good book describing it is "Executable UML, a foundation for Model-Driven Architecture"), you will notice that it is common for a state machine to generate an event to itself only to leave a state. Anonymous transitions free you from this constraint.

If you do not use this feature in a concrete state machine, MSM will deactivate it and you will not pay for it. If you use it, there is however a small performance penalty as MSM will try to fire a compound event (the other UML name for anonymous transitions) after every taken transition. This will therefore double the event processing cost, which is not as bad as it sounds as MSM’s execution speed is very high anyway.

To define such a transition, use “none” as event in the transition table, for example:

row < State3 , none , State4 , &p::State3ToState4 , &p::always_true >

An implementation of the state machine diagram is also provided.

Internal transitions

Internal transitions are transitions executing in the scope of the active state, being a simple state or a submachine. One can see them as a self-transition of this state, without an entry or exit action called. This is useful when all you want is to execute some code for a given event in a given state.

Internal transitions are specified as having a higher priority than normal transitions. While it makes sense for a submachine with exit points, it is surprising for a simple state. MSM lets you define the transition priority by setting the transition’s position inside the transition table (see internals ). The difference between "normal" and internal transitions is that internal transitions have no target state, therefore we need new row types. We had a_row, g_row, _row and row, we now add a_irow, g_irow, _irow and irow which are like normal transitions but define no target state. For, example an internal transition with a guard condition could be:

g_irow < Empty /*state*/ , cd_detected /*event*/,&p::internal_guard /* guard */ >

These new row types can be placed anywhere in the transition table so that you can still have your state machine structure grouped together. The only difference of behavior with the UML standard is the missing notion of priority. Please have a look at the example.

It is also possible to do it the UML-conform way by declaring a transition table called internal transition_table inside the state itself and using internal row types. For example:

struct Empty : public msm::front::state<> {

struct internal_transition_table : mpl::vector<

a_internal < cd_detected , Empty, &Empty::internal_action >

> {};

};

This declares an internal transition table called internal_transition_table and reacting on the event cd_detected by calling internal_action on Empty. Let us note a few points:

  • internal tables are NOT called transition_table but internal_transition_table

  • they use different but similar row types: a_internal, g_internal, _internal and internal.

  • These types take as first template argument the triggering event and then the action and guard method. Note that the only real difference to classical rows is the extra argument before the function pointer. This is the type on which the function will be called.

  • This also allows you, if you wish, to define actions and guards in another state of the state machine or in the state machine itself.

  • submachines can have an internal transition table and a classical transition table.

The following example makes use of an a_internal. It also uses functor-based internal transitions which will be explained in the functor front-end, please ignore them for the moment. Also note that the state-defined internal transitions, having following the UML standard the highest priority, are tried before those defined inside the state machine transition table.

Which method should you use? It depends on what you need:

  • the first version (using irow) is simpler and likely to compile faster. It also lets you choose the priority of your internal transition.

  • the second version is more logical from a UML perspective and lets you make states more useful and reusable. It also allows you to call actions and guards on any state of the state machine

Note: There is an added possibility coming from this feature. The internal_transition_table transitions being added directly inside the main state machine's transition table, it is possible, if it is more to your state, to distribute your state machine definition a bit like Boost.Statechart, leaving the state machine itself the only task of declaring the states it wants to use using the explicit_creation type definition. While this is not the author's favorite way, it is still possible. A simplified example using only two states will make it clearer:

There is an added bonus offered for submachines, which can have both the standard transition_table and an internal_transition_table (which has higher priority). This makes it easier if you decide to make a full submachine from a state. It is also slightly faster than the standard alternative, adding orthogonal regions, because event dispatching will, if accepted by the internal table, not continue to the subregions. This gives you a O(1) dispatch instead of O(number of regions). While the example is with eUML, the same is also possible with this front-end.

more row types

It is also possible to write transitions using actions and guard conditions not just from the state machine but also from its contained states. In this case, one must specify not just a method pointer but also the object on which to call it. This transition row is called, not very originally, row2. They come, like normal transitions in four flavors: a_row2, g_row2, _row2 and row2. For example, a transition calling an action from the state Empty could be:

a_row2 < Stopped , open_close , Open , Empty /*action source*/ , &Empty::open_drawer /*action*/>

The same capabilities are also available for internal transitions so that we have: a_irow2, g_irow2, _irow2 and row2. For transitions defined as part of the internal_transition_table, you can use the a_internal, g_internal, _internal, internal row types.

These row types allow us to distribute the state machine code among states, making them reusable and more valuable. Using transition tables inside states also contributes to this possibility. An example of these new tows is also provided.

Explicit entry / entry and exit pseudo-state / fork

MSM (almost) fully supports these features described in the small UML tutorial. Almost because there are currently two limitations:

  • it is only possible to explicitly enter a sub- state of the target but not a sub-sub state.

  • it is not possible to explicitly exit. Exit points must be used.

Let us see a concrete example:

We find in this diagram:

  • A “normal” entering into SubFsm2 triggered by event1 and back to State1 using the same event. In each zone is the initial state activated, i.e. SubState1 and SubState1b.

  • An explicit entry into SubFsm2::SubState2 for zone “a” with event2 as trigger, meaning that in region “b” the initial state, SubState1b, activated.

  • A fork into zones “a” and “b” to the explicit entries SubState2 and SubState2b, triggered by event3. Both states become active so no zone is default activated (if we had a third zone, it would be).

  • A connection of two transitions through an entry pseudo state, SubFsm2::PseudoEntry1, triggered by event4 and triggering also the second transition on the same event (both transitions must be triggered by the same event). Zone “b” gets default-activated and SubState1b becomes active.

  • An exit from SubFsm2 using an exit pseudo-state, PseudoExit1, triggered by event5 and connecting two transitions using the same event. Again, the event is forwarded to the second transition and both regions are exited, as SubFsm2 becomes inactive. Note that if no transition is defined from PseudoExit1, an error (as defined in the UML standard) will be detected and no_transition called.

The example is also fully implemented.

This sounds complicated but the syntax is simple.

Explicit entry

First, to define that a state is an explicit entry, you have to make it a state and mark it as explicit, giving as template parameters the zone id (the zone id starts with 0 and corresponds to the first initial state of the initial_state type sequence).

struct SubState2 : public msm::front::state<> , public msm::front::explicit_entry<0>

And define the submachine as:

typedef msm::back::state_machine<SubFsm2_> SubFsm2;

You can then use it as target in a transition with State1 as source:

_row < State1, Event2, SubFsm2::direct< SubFsm2_::SubState2> >

The syntax deserves some explanation. SubFsm2_ is a front end. SubState2 is a nested state, therefore the SubFsm2_::SubState2 syntax. The containing machine (containing State1 and SubFsm2) refers to the backend instance (SubFsm2). SubFsm2::direct states that a direct entry is desired.

Note (also valid for forks): in order to make compile time more bearable for the more standard cases, and unlike initial states, explicit entry states which are also not found in the transition table of the entered submachine (a rare case) do NOT get automatically created. To explicitly create such states, you need to add in the state machine containing the explicit states a simple typedef giving a sequence of states to be explicitly created like:

typedef mpl::vector<SubState2,SubState2b> explicit_creation;

Fork

Need a fork instead of an explicit entry? Then, as a fork is an explicit entry into states of different regions, we do not change the state definition compared to the explicit entry and specify as target a list of explicit entry states:

_row < State1, Event3, mpl::vector<SubFsm2::direct< SubFsm2_::SubState2>, SubFsm2::direct <SubFsm2_::SubState2b> >

With SubState2 defined as before and SubState2b defined as being in the second region (Caution: MSM does not check that the region is correct):

struct SubState2b : public msm::front::state<> , public msm::front::explicit_entry<1>

Entry pseudo states

To define an entry pseudo state, you need derive from the corresponding class and give the region id:

struct PseudoEntry1 : public msm::front::entry_pseudo_state<0>

And add the corresponding transition in Fsm's transition table:

_row < State1, Event4, SubFsm2::entry_pt<SubFsm2_::PseudoEntry1> >

And another in the SubFsm2_ submachine definition (remember that UML defines an entry point as a connection between two transitions), for example this time with an action method:

_row < PseudoEntry1, Event4, SubState3, &SubFsm2_::entry_action >

Exit pseudo states

And finally, exit pseudo states are to be used almost the same way, but defined differently: it takes as template argument the event to be forwarded (no region id is necessary):

struct PseudoExit1 : public exit_pseudo_state<event6>

And you need, as for entry pseudo states, two transitions, one in the submachine:

_row < SubState3, Event5, PseudoExit1 >

And one in the containing state machine:

_row < SubFsm2::exit_pt<SubFsm2_::PseudoExit1>, Event6, State2 >

Important note 1: UML defines transiting to an entry pseudo state and having either no second transition or one with a guard as an error but defines no error handling. MSM will tolerate this behavior; the entry pseudo state will simply be the newly active state.

Important note 2: UML defines transiting to an exit pseudo state and having no second transition as an error, and also defines no error handling. Therefore, it was decided to implement exit pseudo state as terminate states and the containing composite not properly exited will stay terminated as it was technically “exited”.

Important note 3: UML states that for the exit point, the same event must be used in both transitions. MSM relaxes this rule and only wants the event on the inside transition to be convertible to the one of the outside transition. In our case, event6 is convertible from event5. Notice that the forwarded event must be named in the exit point definition. For example, we could define event6 as simply as:

struct event6 { event6(){} template <class Event> event6(Event const&){} };//convertible from any event

Flags

This tutorial is devoted to a concept not defined in UML: flags. It has been added into MSM after proving itself useful on many occasions. Please, do not be frightened as we are not talking about ugly shortcuts made of an improbable collusion of Booleans.

If you look into the Boost.Statechart documentation you'll find some code like:

if ( ( state_downcast< const NumLockOff * >() != 0 ) &&

( state_downcast< const CapsLockOff * >() != 0 ) &&

( state_downcast< const ScrollLockOff * >() != 0 ) )

While correct and found in many UML books, this can be error-prone and a potential time-bomb when your state machine grows and you add new states or orthogonal regions.

And most of all, it hides the real question, which would be “does my state machine's current state define a special property”? In this special case “are my keys in a lock state”? So let's apply the Fundamental Theorem of Software Engineering and move one level of abstraction higher.

In our player example, let's say we need to know if the player has a loaded CD. We could do the same:

if ( ( state_downcast< const Stopped * >() != 0 ) &&

( state_downcast< const Open * >() != 0 ) &&

( state_downcast< const Paused * >() != 0 ) &&

( state_downcast< const Playing * >() != 0 )

)

Or flag these 4 states as CDLoaded-able. You add a flag_list type into each flagged state:

typedef mpl::vector1<CDLoaded> flag_list;

You can even define a list of flags, for example in Playing:

typedef mpl::vector2<PlayingPaused,CDLoaded> flag_list;

This means that Playing supports both properties. Now to check if your player has a loaded CD, check if your flag is active in the current state:

player p; if (p.is_flag_active<CDLoaded>()) ...

And what if you have orthogonal regions? How to decide if a state machine is in a flagged state? By default, you keep the same code and the current states will be OR'ed, meaning if one of the active states has the flag, then is_flag_active returns true. Of course, in some cases, you might want that all of the active states are flagged for the state to be active. You can also AND the active states:

if (p.is_flag_active<CDLoaded,player::Flag_AND>()) ...

The following diagram displays the flag situation in the tutorial.

Event Hierarchy

There are cases where transitions with many related but different events are needed. An example is text parsing. Let's say you want to parse a string and use a state machine to handle you parsing state. You want to parse 4 digits and decide to use a state for every matched digit. Your state machine could look like:

But how to detect the digit event? We would like to avoid having to define 10 transitions on char_0, char_1... between two states as it would force us to write 4 x 10 transitions and the compile-time would suffer. To solve this problem, MSM supports the triggering of a transition on a subclass event. For example, if we define digits as:

struct digit {};

struct char_0 : public digit {};

And to the same for other digits, we can now fire char_0, char_1 events and this will cause a transition with "digit" as trigger to be taken.

An example with performance measurement, taken from the documentation of Boost.Xpressive illustrates this example (TODO). You might notice that the performance is actually very good (better).

Containing state machine (deprecated)

This feature is still supported in MSM for backward compatibility but made obsolete by the fact that every guard/action/entry action/exit action get the state machine passed as argument and might be removed at a later time.

All of the states defined in the state machine are created upon state machine construction. This has the huge advantage of a reduced syntactic noise. The cost is a small loss of control of the user on the state creation and access. But sometimes you needed a way for a state to get access to its containing state machine. Basically, a state needs to change its declaration to:

struct Stopped : public msm::front::state<sm_ptr>

And to provide a set_sm_ptr function: void set_sm_ptr(player* pl)

to get a pointer to the containing state machine. The same applies to terminate_state / interrupt_state and entry_pseudo_state / exit_pseudo_state.

Functor front-end

The functor front-end is the preferred front-end at the moment. It is more powerful than the standard front-end, slightly faster and has a more readable transition table. It also makes it easier to reuse parts of state machines. Like eUML, il also comes with a good deal of predefined actions. Actually, eUML generates a functor front-end through Boost.Typeof and Boost.Proto so both offer the same functionality.

The rows which MSM offers come in different flavors. We saw the a_row, g_row, _row, row, not counting internal rows. This is already much to know, so why define new rows? These types have some disadvantages:

  • They are more typing and information than we would wish. This means syntactic noise.

  • Function pointers are weird in C++.

  • The action/guard signature is limited and does not allow for more variations of parameters (source state, target state, current state machine, etc.)

  • It is not easy to reuse action code from a state machine to another.

Transition table

We can change the definition of the simple tutorial's transition table to:

// Start Event Next Action Guard
// +---------+ -------------+ ---------+ ---------------------+ ----------------------+
Row < Stopped , play, Playing, start_playback >,
Row < Stopped , open_close, Open, open_drawer, none >,
Row < Stopped , stop, Stopped, none >,
// +--------- -------------+ ---------+ ---------------------+ ----------------------+
Row < Open , open_close , Empty , close_drawer, none >,
// +---------+ -------------+ ---------+ ---------------------+ ----------------------+
Row < Empty , open_close , Open , open_drawer >,
Row < Empty , cd_detected , Stopped , store_cd_info , good_disk_format >,
g_row < Empty , cd_detected , Playing , store_cd_info , &player_::auto_start >,
// +---------+ -------------+ ---------+ ---------------------+ ----------------------+
Row < Playing , stop , Stopped , stop_playback, none >,
Row < Playing , pause , Paused , pause_playback, none >,
Row < Playing , open_close , Open , stop_and_open, none >,
// +---------+ -------------+ ---------+ ---------------------+ ----------------------+
Row < Paused , end_pause , Playing , resume_playback, none >,
Row < Paused , stop , Stopped , stop_playback, none >,
Row < Paused , open_close , Open , stop_and_open, none >
// +---------+ -------------+ ---------+ ---------------------+ ----------------------+
> {};

Transitions are now of type "Row" with exactly 5 template arguments: source state, event, target state, action and guard. Wherever there is nothing (for example actions and guards), write "none". Actions and guards are no more methods but functors getting as arguments the detected event, the state machine, source and target state:

struct store_cd_info {

template <class Fsm,class Evt,class SourceState,class TargetState>

void operator()(Evt const&, Fsm& fsm, SourceState&, TargetState& )

{

cout << "player::store_cd_info" << endl; fsm.process_event(play());

}

};

The advantage to functors compared to functions are that they are generic and reusable. They also allow passing more parameters than just events. The guard functors are the same but have an operator() returning a bool.

It is also possible to mix rows from different front-ends. To show this, a g_row has been left in the transition table. Note: in case the action functor is used in the transition table of a state machine contained inside a top-level state machine, the “fsm” parameter refers to the lowest-level state machine (referencing this action), not the top-level one.

To illustrate the reusable point, MSM comes with a whole set of predefined functors. Please refer to eUML for the full list (TODO). For example, we are going to replace the first action by an action sequence and the guard by a more complex functor.

We decide we now want to execute 2 actions in the first transition (Stopped -> Playing). We only need to change the action start_playback to (TODO) ActionSequence_< mpl::vector<some_action, start_playback> > and now will execute some_action and start_playback every time the transition is taken. ActionSequence_ is a functor callinng each element of the mpl::vector in sequence.

We also want to replace good_disk_format by a condition of the type: “good_disk_format && (some_condition || some_other_condition)”. We can achieve this using And_ and Or_ functors: And_<good_disk_format,Or_< some_condition , some_other_condition>. It even starts looking like functional programming. MSM ships with functors for operators, state machine usage, STL algorithms or container methods.

Defining states with entry/exit actions

You probably noticed that we just showed a different transition table and that we even mixed rows from different front-ends. This means that you can do this and leave the definitions for states unchanged. Most examples are doing this as it is the simplest solution. You still enjoy the simplicity of the first front-end with the extended power of the new transition types. This tutorial, adapted from the earlier example does just this.

Of course, it is also possible to define states where entry and exit actions are also provided as functors as these are generated by eUML and both front-ends are equivalent. For example, we can define a state as:

struct Empty_Entry {

template <class Event,class Fsm,class State>

void operator()(Event const&,Fsm&,State&)

{

...

}

}; // same for Empty_Exit

struct Empty : public msm::front::euml::func_state<Empty_Entry,Empty_Exit>{};

This also means that you can, like in the transition table, write entry / exit actions made of more complicated action combinations. The previous example can therefore be rewritten.

Usually, however, one will probably use the standard state definition as it provides the same capabilities as this front-end state definition, unless one needs some of the shipped predefined functors.

Defining a simple state machine

Like states, state machines can be defined using the previous front-end, as the previous example showed, or with the functor front-end, which allows you to define a state machine entry and exit functions as functors, as in this example.

Anonymous transitions

Anonymous (compound) transitions are transition withour a named event, taken automatically. We saw how this front-end uses none when no action or guard is required. We can also use none instead of an event to mark an anonymous transition. For example, the following transition makes an immediate transition from State1 to State2:

Row < State1 , none , State2 >

The following transition does the same but calling an action in the process:

Row < State1 , none , State2 , State1ToState2, none >

The following diagram shows an example and its implementation:

Internal transitions

The following example uses internal transitions with the functor front-end. As for the simple standard front-end, both methods of defining internal transitions are supported:

  • defining a Row in the state machine's transition table with none as target state defines an internal transition

  • defining an internal_transition_table made of Internal rows inside a state defines UML-conform internal transitions with higher priority

  • transitions defined inside internal_transition_table require no source state either as the source state is known.

Like for the standard front-end internal transitions, internal transition tables are added into the main state machine's table, thus allowing you to distribute the transition table definition and reuse states.

There is an added bonus offered for submachines, which can have both the standard transition_table and an internal_transition_table (which has higher priority). This makes it easier if you decide to make a full submachine from a state. It is also slightly faster than the standard alternative, adding orthogonal regions, because event dispatching will, if accepted by the internal table, not continue to the subregions. This gives you a O(1) dispatch instead of O(number of regions). While the example is with eUML, the same is also possible with this front-end.

eUML (experimental)

Important note: eUML requires a compiler supporting the C++0x decltype/typeof feature (from example VC >= 9, g++ >= 4.3. VC8 is partially supported). More generally, eUML has experimental status because most compilers will start crashing when a state machine becomes too big. Only g++ 4.3 (unfortunately not 4.4 which shows a serious regression) seems perfectly resilient.

The previous front-ends are simple to write but still force an amount of noise, mostly MPL types, so it would be nice to write code looking like C++ (with a C++ action language) directly inside the transition table, like UML designers like to do on their state machine diagrams. This is what eUML is for.

eUML is a Boost.Proto-based compile-time domain specific embedded language. It provides grammars which allow the definition of actions/guards directly inside the transition table or entry/exit in the state definition. It is defined in the namespace msm::front::euml. There are grammars for actions, guards, flags, attributes, deferred events, initial states.

It also relies on Boost.Typeof as a wrapper around the new decltype C++0x feature to provide a compile-time evaluation of all the grammars. Unfortunately, all the underlying Boost libraries are not Typeof-enabled, so for the moment, you will need a compiler where Typeof is natively implemented (like VC8-9-10, g++ >= 4.3).

Examples will be provided in the next paragraphs. You need to include eUML basic features:

#include <msm/front/euml/euml.hpp>

To add STL support (at possible cost of longer compilation times), include:

#include <msm/front/euml/stl.hpp>

Transition table

A transition can be defined using eUML as:

source + event [guard] / action == target or as

target == source + event [guard] / action

The first version looks like a drawn transition in a diagram, the second one seems natural to a C++ developper.

The simple transition table written with the previous front-end can now be written as:

typedef BOOST_TYPEOF(build_stt((
Stopped + play [DummyGuard] / (TestFct,start_playback) == Playing
Stopped + open_close/ open_drawer == Open
Stopped + stop == Stopped
Open + open_close / close_drawer == Empty
Empty + open_close / open_drawer == Open
Empty + cd_detected [good_disk_format] / store_cd_info == Stopped
) ) ) transition_table;

Or, using the alternative notation, it can be:

typedef BOOST_TYPEOF(build_stt((
Playing == Stopped + play [DummyGuard] / (TestFct,start_playback)
Open == Stopped + open_close/ open_drawer
Stopped == Stopped + stop
Empty == Open + open_close / close_drawer
Open == Empty + open_close / open_drawer
Stopped == Empty + cd_detected [good_disk_format] / store_cd_info
) ) ) transition_table;

The transition table now looks like a list of (readable) rules with little noise.

UML defines guards between “[ ]” and actions after a “/”, so this is already more readable for UML designers. UML also allows designers to define several actions sequentially (our previous ActionSequence) separated by a comma. The first transition does just this: two actions separated by a comma and enclosed inside parenthesis to respect C++ operator precedence.

If this seems to you like it will cost you run-time performance, don't worry, typeof (decltype) only evaluates the build_stt function and no run-time cost occurs. Actually, eUML is only a metaprogramming layer on top of "standard" MSM metaprogramming and this first layer generates the previously-presented functor front-end.

UML also allows designers to define more complicated guards, like [good_disk_format && (some_condition || some_other_condition)]. This was possible with our previously defined functors, but using a complicated template syntax. This syntax is now possible exactly as written, which means without syntactic noise.

Defining events, actions and states with entry/exit actions

Events must be proto-enabled. To achieve this, they must inherit from euml_event:

struct play : euml_event<play>{};

Actions (returning void) and guards (returning a bool) are defined like previous functors, with the difference that they also must be proto-enabled:

struct some_condition : euml_action< some_condition >

{

template <class Fsm,class Evt,class SourceState,class TargetState>

bool operator()(Evt const& ,Fsm& ,SourceState& ,TargetState& ) { return true; }

};

It is also possible to use the same action grammar as for the transition table for state entry and exit actions:

typedef BOOST_TYPEOF(euml::build_state( (Empty_Entry,Dummy_Entry)/*2 entry actions*/,Empty_Exit/*1 exit action*/ )) Empty;

This means that Empty is defined as a state with an entry action made of two sub-actions, Empty_Entry and Dummy_Entry (enclosed inside parenthesis), and an exit action, Empty_Exit.

There are several overloads of the build_state function:

  • build_state(): state without entry or exit action.

  • build_state(Expr1): state with entry but no exit action.

  • build_state(Expr1,Expr2): state with entry and exit action.

  • build_state(Expr1,Expr2,Attributes): state with entry and exit action, defining some attributes (read further on).

  • build_state(Expr1,Expr2,Attributes,Configure): state with entry and exit action, defining some attributes (read further on) and flags (standard MSM flags) or deferred events (standard MSM deferred events).

  • build_state(Expr1,Expr2,Attributes,Configure,Base): state with entry and exit action, defining some attributes (read further on), flags and deferred events (plain msm deferred events) and a non-default base state (as defined in standard MSM).

A NoAction is also defined, which does, well, nothing except being a placeholder (needed for example as entry action if we have no entry but an exit). Expr1 and Expr2 are a sequence of actions, obeying the same action grammar as in the transition table (following the “/” symbol).

The state functors have a slightly different signature as there is no source and target state but only a current state (entry/exit actions are transition-independent), for example:

struct Empty_Entry : euml_action< Empty_Entry >

{

template <class Evt,class Fsm,class State>

void operator()(Evt const& ,Fsm& ,State& ) { ... }

};

Notice again the euml_action, to make the functor play nice with the grammar.

Defining a simple state machine

Like for a functor front-end, you can reuse the state machine definition method from the standard front-end. You can also use eUML to define a state machine "on the fly" (if, for example, you need to provide an on_entry/on_exit for this state machine as a functor). For this, there is also a function, build_sm, which has up to 8 arguments:

  • build_sm(Stt, Init): simplest state machine where only the transition table and initial state(s) are defined.

  • build_sm(Stt, Init, Expr1): state machine where the transition table, initial state and entry action are defined.

  • build_sm(Stt, Init, Expr1, Expr2): state machine where the transition table, initial state, entry and exit actions are defined.

  • build_sm(Stt, Init, Expr1, Expr2, Attributes): state machine where the transition table, initial state, entry and exit actions are defined. Furthermore, some attributes are added (read further on).

  • build_sm(Stt, Init, Expr1, Expr2, Attributes, Configure): state machine where the transition table, initial state, entry and exit actions are defined. Furthermore, some attributes (read further on), flags, deferred events and configuration capabilities TODO link (no message queue / no exception catching) are added.

  • build_sm(Stt, Init, Expr1, Expr2, Attributes, Flags, Deferred , Base): state machine where the transition table, initial state, entry and exit actions are defined. Furthermore, attributes (read further on), flags , deferred events and configuration capabilities (no message queue / no exception catching) are added and a non-default base state (see base state TODO) is defined.

For example, a minimum state machine could be defined like:

typedef BOOST_TYPEOF(build_stt((
... ) ) ) transition_table;

typedef BOOST_TYPEOF(build_sm( transition_table(),init_ << Empty )) player_;

Note for VC9: This defined build_sm syntax works most of the time. But once in a while, VC9 will display the problem shown in the next section (not enough heap space). For example, this simple performance test (TODO link), while really simple, will display the bug. To correct it, the following solution works:

#ifndef BOOST_MSVC

typedef BOOST_TYPEOF(build_sm( transition_table(),init_ << Empty << AllOk )) player_;

#else

struct player_ : public BOOST_TYPEOF(build_sm( transition_table(),init_ << Empty << AllOk )) {};

#endif

Please have a look at the player tutorial written using eUML's first and second syntax. Please ignore for the moment the BOOST_MSM_EUML_DECLARE_ATTRIBUTE macros, we come back to it very soon.

Defining a submachine

Defining a submachine (see tutorial) with other front-ends simply means using a state which is a state machine in the transition table of another state machine. This is the same with eUML. One only needs define a second state machine and reference it in the transition table of the containing state machine.

Note: the previous #ifdef trick has to be used for submachine definition because the VC9 bug occurs more often when submachines are involved.

Attributes / Function call

We now want to make our grammar more useful. Very often, one needs only very simple action methods, for example ++Counter or Counter > 5 where Counter is usually defined as some attribute of the class containing the state machine. Furthermore, unlike many expensive tools which are on the market, states within MSM are also classes so they can have attributes, and we would also like to provide them with attributes.

If you look back at our examples using the first and second syntaxes, you will find some unexplained BOOST_MSM_EUML_DECLARE_ATTRIBUTE macros. Let us go back to them. We have:

BOOST_MSM_EUML_DECLARE_ATTRIBUTE(std::string,cd_name)

BOOST_MSM_EUML_DECLARE_ATTRIBUTE(DiskTypeEnum,cd_type)

This declares two attributes: cd_name of type std::string and cd_type of type DiskTypeEnum. These attributes are not part of any event or state in particular, we just declared a name and a type. Now, we can add attributes to our cd_detected event:

typedef BOOST_TYPEOF(build_attributes(attributes_ << cd_name << cd_type )) cd_detected_attributes;

struct cd_detected : euml_event<cd_detected>,cd_detected_attributes {

cd_detected(std::string name, DiskTypeEnum diskType) {

get_attribute(cd_name)=name;get_attribute(cd_type)=diskType;}

};

The two left shift of the first line add both attributes into the helper cd_detected_attributes structure. As cd_detected inherits from the helper, it now has these two attributes. The function get_attribute returns a reference to the required attributes so that we can easily write and set them.

Ok, great, we now have a two liner to add attributes to a class, which we could have done more easily, so what is the point? The point is that we can now do what was not possible, reference these attributes directly, at compile-time, in the transition table. For example, in the example, you will find this transition:

Stopped == Empty + cd_detected [good_disk_format && (event_(cd_type)==Int_<DISK_CD>())]

Read event_(cd_type) as event_->cd_type with event_ a type generic for events, whatever the concrete event is (in this particular case, it happens to be a cd_detected as the transition shows).

The main advantage of this feature is that you do not need to define a new functor and you do not need to look inside the functor to know what it does, you have all at hand.

MSM provides more generic objects for state machine types:

  • event_ : used inside any action, the event triggering the transition

  • state_: used inside entry and exit actions, the entered / exited state

  • source_: used inside a transition action, the source state

  • target_: used inside a transition action, the target state

  • fsm_: used inside any action, the (lowest-level) state machine processing the transition

  • Int_<int value>: a functor representing an int

  • Char_<value>: a functor representing a char

  • Size_t_<value>: a functor representing a size_t

  • String_<mpl::string> (boost >= 1.40): a functor representing a string.

These helpers can be used in two different ways:

  • helper(attribute_name) returns the attribute with name attribute_name

  • helper returns the state / event type itself.

The second form is helpful if you want to use states with self-created functors. In the above tutorial, we provide Empty with an activate_empty method. We would like to create a eUML functor and call it from inside the transition table. This is done using the MSM_EUML_METHOD / MSM_EUML_FUNCTION macros. The first creates a functor to a method, the second to a free function. In the tutorial, we write:

MSM_EUML_METHOD(ActivateEmpty_ , activate_empty , activate_empty_ , void , void )

The first parameter is the functor name, for use either directly or with the functor front-end. The second is the name of the method which will be called. The third is the function name for use in the transition table, the fourth is the return type of the function if used in the context of a transition action, the fifth is the result type if used in the context of a state entry / exit action (usually fourth and fifth are the same). We now have a new eUML function calling a method of "something", and this "something" is one of the five previously explained helpers. We can now use this in a transition, for example:

Empty == Open + open_close / (close_drawer,activate_empty_(target_))

The action is now defined as a sequence of two actions: close_drawer and activate_empty called on the target itself. The target being Empty (the state defined left), this really will call Empty.activate_empty(). This method could also have an (or several) argument(s), for example the event, we could then call activate_empty_(target_ , event_).

More examples can be found in the terrible compiler stress test, the timer example or in the iPodSearch with eUML (for String_ and more).

Orthogonal regions, flags, event deferring

To define orthogonal regions really means defining more initial states. To add more initial states, “shift left” some, for example, if we had another initial state named AllOk :

typedef BOOST_TYPEOF(build_sm( transition_table(),init_ << Empty << AllOk )) player_;

You remember from the build_state and build_sm signatures that just after attributes, we can define flags, like in the basic MSM frontend. To do this, we have another "shift-left" grammar, for example:

typedef BOOST_TYPEOF(build_state(NoAction,NoAction, attributes_ << no_attributes_,

/* flags */ configure_<< PlayingPaused << CDLoaded )) Paused;

We now defined that Paused will get two flags, PlayingPaused and CDLoaded, defined, for example as:

struct CDLoaded : euml_flag<CDLoaded> {};

This corresponds to the following basic front-end definition of Paused:

struct Paused : public msm::front::state<>

{ typedef mpl::vector2<PlayingPaused,CDLoaded> flag_list; };

Under the hood, what you get really is a mpl::vector2.

Note: As we use the version of build_state with 4 arguments, we need to tell eUML that we need no attributes. Similarly to a cout << endl, we need a attributes_ << no_attributes_ syntax.

You can use the flag with the is_flag_active method of a state machine. You can also use the provided helper function is_flag_ (returning a bool) for state and transition actions. For example, in the iPod implementation with eUML (TODO link), you find the following transition:

ForwardPressed == NoForward + EastPressed [!is_flag_(NoFastFwd)]

The function also has an optional second parameter which is the state machine on which the function is called. by default, fsm_ is used (the current state machine) but you could provide a functor returning a reference to another state machine.

eUML also supports defining deferred events in the state (state machine) definition. To this aim, we can reuse the flag grammar. For example:

typedef BOOST_TYPEOF(build_state(Empty_Entry,Empty_Exit, attributes_ << no_attributes_,

/* flags */ configure_<< play )) Empty;

The configure_ left shift is also responsible for deferring events. Shit inside a flag and it will be seen as a flag, shift an event and it will be a deferred event. This replaces the basic front-end definition:

typedef mpl::vector<play> deferred_events;

In this tutorial, player is defining a second orthogonal region with AllOk as initial state. The Empty and Open states also defer the event play. Open, Stopped and Pause also support the flag CDLoaded using the same left shift into configure_.

In the functor front_end, we also had the possibility to defer an event inside a transition, which makes possible conditional deferring. This is also possible with eUML through the use of the defer_ order, as shown in this tutorial. You will find the foillowing transition:

Open + play / defer_

This is an internal transition. Ignore it for the moment. Interesting is, that when the event play is fired and Open is active, the event will be deferred. Now add a guard and you can conditionally defer the event, for example:

Open + play [ some_condition ] / defer_

This is similar to what we did with the functor front-end. This means that we have the same limitations. Using defer_ instead of a state declaration, we need to tell MSM that we have deferred events in this state machine. We do this (again) using a configure_ declaration in the state machine definition in which we shift the deferred_events configuration flag using the build_sm function:

typedef BOOST_TYPEOF(build_sm( transition_table(),init_ << Empty << AllOk,

Entry_Action, Exit_Action, attributes_ << no_attributes_, configure_<< deferred_events )) player_;

A tutorial illsutrates this possibility.

Customizing a state machine / Getting more speed

We just saw how to use configure_ to define deferred events or flags. We can also use it to configure our state machine like we did with other front-ends (TODO add in standard):

  • configure_ << no_exception: disables exception handling

  • configure_ << no_msg_queue deactivates the message queue

Deactivating these features if not needed greatly improves the event dispatching speed of your state machine. Our speed testing example with eUML use this feature.

Anonymous transitions

Anonymous transitions (See UML tutorial) are transitions without a named event which are therefore triggered immediately when the source state becomre active, provided a guard allows it. As there is no event, to define such a transition, simply omit the “+” part of the transition (the event), for example:

State3 == State4 [always_true] / State3ToState4

State4 [always_true] / State3ToState4 == State3

Please have a look at this example, which implements the previously defined state machine with eUML.

Internal transitions

Like both other front-ends, eUML supports two ways of defining internal transitions:

  • in the state machine's transition table. In this case, you need to specify a source state, event, actions and guards but no target state, which eUML will interpret as an internal transition, for example this defines a transition internal to Open, on the event open_close:

    Open + open_close [internal_guard1] / internal_action1

    A full example is also provided.

  • in a state's internal_transition_table. For example:

    typedef BOOST_TYPEOF(build_state( Open_Entry(),Open_Exit() )) Open_def;

    struct Open : public Open_def {

    typedef BOOST_TYPEOF(build_internal_stt((

    open_close [internal_guard1] / internal_action1

    ) ) ) internal_transition_table;

    };

    Notice how we do not need to repeat that the transition originates from Open as we already are in the context of Open.

    The implementation also shows the added bonus offered for submachines, which can have both the standard transition_table and an internal_transition_table (which has higher priority). This makes it easier if you decide to make a full submachine from a state. It is also slightly faster than the standard alternative, adding orthogonal regions, because event dispatching will, if accepted by the internal table, not continue to the subregions. This gives you a O(1) dispatch instead of O(number of regions).

Direct entry / entry and exit pseudo-state / fork

We saw the build_state function, which creates a simple state. Likewise, eUML provides other state-building functions for other types of states:

  • build_terminal_state takes the same arguments as build_state and defines, well, a terminate state.

  • build_interrupt_state takes the same arguments as build_state and defines an interrupt state.

  • build_exit_state takes the same arguments as build_state and defines an exit pseudo state.

  • build_entry_state takes the same arguments as build_state and defines an interrupt state. The region index to be entered is defined as an int template argument, so build_entry_state<0> defines an entry state into the first region of a submachine.

  • build_explicit_entry_state takes the same arguments as build_state and defines an explicit entry state. The region index to be entered is defined as an int template argument, so build_explicit_entry_state<0> defines an explicit entry state into the first region of a submachine.

Using these states in the transition table is like in any other front end using fsm_name::exit_pt<>, fsm_name::direct<> and fsm_name::entry_pt<>. For example, a direct entry could be:

SubFsm2::direct<SubState2> == State1 + event2

Forks being a list on direct entries, eUML supports a logical syntax (state1, state2, ...), for example:

(SubFsm2::direct<SubState2>, SubFsm2::direct<SubState2b>, SubFsm2::direct<SubState2c>) == State1 + event3

The entry tutorial is also available with eUML.

Helper functions

We saw a few helpers but there are more, so let us have a more complete description:

  • event_ : used inside any action, the event triggering the transition

  • state_: used inside entry and exit actions, the entered / exited state

  • source_: used inside a transition action, the source state

  • target_: used inside a transition action, the target state

  • fsm_: used inside any action, the (lowest-level) state machine processing the transition

  • The previous objects can also return an attribute, for example event_(cd_name)

  • Int_<int value>: a functor representing an int

  • Char_<value>: a functor representing a char

  • Size_t_<value>: a functor representing a size_t

  • True_ and False_ functors returning true and false respectively

  • String_<mpl::string> (boost >= 1.40): a functor representing a string.

  • if_then_else_(guard, action, action) where action can be an action sequence

  • if_then_(guard, action) where action can be an action sequence

  • while_(guard, action) where action can be an action sequence

  • do_while_(guard, action) where action can be an action sequence

  • for_(action, guard, action, action) where action can be an action sequence

  • process_(some_event [, some state machine] [, some state machine] [, some state machine] [, some state machine]) will call process_event (some_event) on the current state machine or on the one(s) passed as 2nd , 3rd, 4th, 5th argument. This allow sending events to several external machines

  • process2_(some_event,Value [, some state machine] [, some state machine] [, some state machine]) will call process_event (some_event(Value)) on the current state machine or on the one(s) passed as 3rd, 4th, 5th argument

  • is_ flag_(some_flag[, some state machine]) will call is_flag_active on the current state machine or on the one passed as 2nd argument

  • Predicate_<some predicate>: Used in STL algorithms. Wraps unary/binary functions to make them eUML-compatible so that they can be used in STL algorithms

This can make for quite some fun. For example,

/( if_then_else_(--fsm_(m_SongIndex) > Int_<0>(),/*if clause*/

show_playing_song(), /*then clause*/

(fsm_(m_SongIndex)=Int_<1>(),process_(EndPlay())) /*else clause*/ ) ) means:

if (fsm.SongIndex > 0, call show_playing_song else {fsm.SongIndex=1; process EndPlay on fsm;}

A few examples are using these features:

  • the iPod example introduced at the BoostCon09 has been rewritten with eUML (weak compilers please move on...)

  • the iPodSearch example also introduced at the BoostCon09 has been rewritten with eUML. In this example, you will also find some examples of STL functor usage.

  • A simpler timer example is a good starting point.

There is unfortunately a small catch. Defining a functor using MSM_EUML_METHOD or MSM_EUML_FUNCTION will create a correct functor. Your own eUML functors written as described at the beginning of this section will also work well, except with the while_, if_then_, if_then_else_ functions which will require a bit of writing. TODO macro.

Phoenix-like STL support

As we saw, eUML supports most C++ operators (except address-of). For example it is possible to write event_(some_attribute)++ or [source_(some_bool) && fsm_(some_other_bool)]. But a programmer needs more than operators in his daily programming. The STL is clearly a must have. Therefore, eUML comes in with a lot of functors to simplify your day-to-day programming. For almost every algorithm or container method of the STL, a corresponding eUML function is defined. Like Boost.Phoenix, “.” And “->” of call on objects are replaced by a functional programming paradigm, for example:

  • begin_(container), end_(container): returns iterators of a container.

  • empty_(container): returns container.empty()

  • clear_(container): container.clear()

  • transform_ : std::transform

In a nutshell, almost every STL method or algorithm is matched by a corresponding functor, which can then be used in the transition table or state actions. The reference (TODO link) explains in detail the usage and the underlying functor (so that this possibility is not reserved to eUML but also to the functor-based front-end). The file structure of this Phoenix-like library matches the one of Boost.Phoenix. All STL algorithm functors are to be found in:

#include <msm/front/euml/algorithm.hpp>

The algorithms are also divided into sub-headers, matching the phoenix structure for simplicity:

#include < msm/front/euml/iteration.hpp>

#include < msm/front/euml/transformation.hpp>

#include < msm/front/euml/querying.hpp>

Container methods can be found in:

#include < msm/front/euml/container.hpp>

Or one can simply include the whole STL support (you will also need to include euml.hpp):

#include < msm/front/euml/stl.hpp>

A few examples (to be found in this tutorial):

  • push_back_(fsm_(m_tgt_container),event_(m_song)): the state machine has an attribute m_tgt_container of type std::vector<OneSong> and the event has an attribute m_song of type OneSong. The line therefore pushes m_song at the end of m_tgt_container

  • if_then_( state_(m_src_it) != end_(fsm_(m_src_container)), process2_(OneSong(),*(state_(m_src_it)++)) ): the current state has an attribute m_src_it (an iterator). If this iterator != fsm.m_src_container.end(), process OneSong on fsm, copy-constructed from state.m_src_it which we post-increment

Back-end

There is, at the moment, one back-end. This back-end contains the library engine and defines the performance and functionality tradeoffs. The currently available back-end implements most of the functionality defined by the UML 2.0 standard at very high runtime speed, in exchange for longer compile-time. The runtime speed is due to a constant-time double-dispatch and self-adapting capabilities allowing the framework to adapt itself to the features used by a given concrete state machine. All unneeded features either disable themselves or can be manually disabled. See section 5.1 for a complete description of the run-to-completion algorithm.

MSM Backend features

TODO

Creation

MSM being divided betwenn front and back-end, one needs to first define a front-end. Then, to create a real state machine, the back-end must be created: typedef msm::back::state_machine<my_front_end> my_fsm;

We now have a fully functional state machine. The next sections will describe what can be done with it.

Event dispatching

The main reason to exist for a state machine is to dispatch events. For MSM, events are objects of a given event type. The object itself can contain data, but the event type is what decides of the transition to be taken. For MSM, if some_event is a given type (a simple struct for example) and e1 and e2 concrete events, e1 and e2 are equivalent, from a transition perspective. Of course, e1 and e2 can have different values and you can use them inside actions. Events are dispatched as const reference, so actions cannot modify events for obvious side-effect reasons. To dispatch an event of type some_event, you can simply write:

my_fsm fsm; fsm.process_event(some_event());

some_event e1; fsm.process_event(e1)

Creating an event on the fly will be optimized by the compiler so the performance will not degrade.

Active state(s)

The backend also offers a way to know which state is active, though you will normally only need this for debugging purposes. If what you need simply is doing something with the active state, internal transitions or visitors are a better alternative. If you need to know what state is active, const int* current_state() will return an array of state ids. Please refer to the internals section to know how state ids are generated.

Base state type

Sometimes, one needs to customize states to avoid repetition and provide a common functionality, for example in the form of a virtual method. You might also want to make your states polymorphic so that you can call typeid on them for logging or debugging. It is also useful if you need a visitor, like the next section will show. You will notice that all front-ends offer the possibility of adding a base type. Not that all states and state machines must have the same base state, so this could reduce reuse. For example, using the basic front end, you need to:

  • Add the non-default base state in your msm::front::state<> definition, as first template argument (except for interrupt_states for which it is the second argument, the first one being the event ending the interrupt), for example, my_base_state being your new base state for all states in a given state machine: struct Empty : public msm::front::state<my_base_state> Now, my_base_state is your new base state. If it has a virtual function, your states become polymorphic. MSM also provides a default polymorphic base type for your convenience, msm::front::polymorphic_state

  • Add the user-defined base state in the state machine frontend definition, as a second template argument, for example: struct player_ : public msm::front::state_machine<player_,my_base_state>

You can also ask for a state with a given id (which you might have gotten from current_state()) using const base_state* get_state_by_id(int id) const where base_state is the one you just defined. You can now do something ploymorphically. For example using the visitor.

Visitor

In some cases, having a pointer-to-base of the currently active states is not enough. You might want to call non-virtually a method of the currently active states. It will not be said that MSM forces the virtual keyword down your throat!

To achieve this goal, MSM provides its own variation of a visitor pattern using the previously described user-defined state technique. If you add to your user-defined base state an accept_sig typedef giving the return value (unused for the moment) and signature and provide an accept method with this signature, calling visit_current_states will cause accept to be called on the currently active states. Typically, you will also want to provide an empty default accept in your base state in order in order not to force all your states to implement accept. For example your base state could be:

struct my_visitable_state{

// signature of the accept function

typedef args<void> accept_sig;

// we also want polymorphic states

virtual ~my_visitable_state() {}

// default implementation for states who do not need to be visited

void accept() const {}

};

This makes your states polymorphic and visitable. In this case, accept is made const and takes no argument. It could also be:

struct SomeVisitor {…};

struct my_visitable_state{

// signature of the accept function

typedef args<void,SomeVisitor&> accept_sig;

// we also want polymorphic states

virtual ~my_visitable_state() {}

// default implementation for states who do not need to be visited

void accept(SomeVisitor&) const {}

};

And now, accept will take one argument (it could also be non-const). By default, accept takes up to 2 arguments. To get more, add a #define BOOST_MSM_VISITOR_ARG_SIZE to another value before including state_machine.hpp. For example:

#define BOOST_MSM_VISITOR_ARG_SIZE 3

#include <boost/msm/back/state_machine.hpp>

Note that accept will be called on ALL active states and also automatically on sub-states of a submachine.

Important warning: The method visit_current_states takes its parameter by value, so if the signature of the accept function is to contain a parameter passed by reference, pass this parameter with a boost:ref/cref to avoid undesired copies or slicing. So, for example, in the above case, call:

SomeVisitor vis; sm.visit_current_states(boost::ref(vis));

This example uses a visiting function with 2 arguments.

Flags

Flags is a MSM-only concept, supported by all front-ends, which base themselves on the functions:

template <class Flag> bool is_flag_active() and

template <class Flag,class BinaryOp> bool is_flag_active()

These functions return true if the currently active state(s) support the Flag property. The first variant ORs the result if there are several orthogonal regions, the second one expects OR or AND, for example:

my_fsm.is_flag_active<MyFlag>()

my_fsm.is_flag_active<MyFlag,my_fsm_type::Flag_OR>()

Please refer to the front-ends sections for more information.

Getting a state

It is sometimes necessary to have the client code get access to the states' data. After all, the states are created once for good and hang around as long as the state machine does so why not use it? You simply just need sometimes to get information about any state, even inactive ones. An example is if you want to write a coverage tool and know how many times a state was visited. To get a state, use the get_state method giving the state name, for example:

player::Stopped* tempstate = p.get_state<player::Stopped*>(); or

player::Stopped& tempstate2 = p.get_state<player::Stopped&>(); depending on your personal taste.

State machine constructor with arguments

You might want to define a state machine with a non-default constructor. For example, you might want to write:

struct player_ : public msm::front::state_machine_def<player_> { player_(int some_value){…} }

This is possible, using the back-end as forwarding object:

typedef msm::back::state_machine<player_ > player; player p(3); The back-end will call the corresponding front-end constructor upon creation.

You can pass arguments up to the value of the BOOST_MSM_CONSTRUCTOR_ARG_SIZE macro (currently 5) arguments. Change this value before including any header if you need to change it.