...one of the most highly
regarded and expertly designed C++ library projects in the
world.
— Herb Sutter and Andrei
Alexandrescu, C++
Coding Standards
The design of MSM tries to make front-ends and back-ends (later) to be as interchangeable as possible. Of course, no back-end will ever implement every feature defined by any possible front-end and inversely, but the goal is to make it as easy as possible to extend the current state of the library.
To achieve this, MSM divides the functionality between both sides: the front-end is a sort of user interface and is descriptive, the back-end implements the state machine engine.
MSM being based on a transition table, a concrete state machine (or a given front-end) must provide a transition_table. This transition table must be made of rows. And each row must tell what kind of transition it is and implement the calls to the actions and guards. A state machine must also define its regions (marked by initial states) And that is about the only constraints for front-ends. How the rows are described is implementer's choice.
Every row must provide:
A Source
typedef indicating, well, the type of the source
state.
A Target
typedef indicating, well, the type of the target
state.
A Evt
typedef indicating the type of the event triggering
the transition.
A row_type_tag
typedef indicating the type of the
transition.
Rows having a type requiring transition actions must provide a static
function action_call
with the following signature:
template <class Fsm,class SourceState,class TargetState,class
AllStates>
static void action_call (Fsm& fsm, Event const& evt,
SourceState&, TargetState&, AllStates&)
The function gets as parameters the (back-end) state machine, the
event, source and target states and a container (in the current
back-end, a fusion::set) of all the states defined in the state machine.
For example, as the back-end has the front-end as basic class,
action_call
is simply defined as
(fsm.*action)(evt)
.
Rows having a type requiring a guard must provide a static function
guard_call
with the following signature:
template <class Fsm,class SourceState,class TargetState,class
AllStates>
static bool guard_call (Fsm&, Event const&,
SourceState&, TargetState&, AllStates&)
The possible transition (row) types are:
a_row_tag: a transition with actions and no guard
g_row_type: a transition with a guard and no actions
_row_tag: a transition without actions or guard
row_tag: a transition with guard and actions
a_irow_tag: an internal transition (defined inside the
transition_table
) with actions
g_irow_tag: an internal transition (defined inside the
transition_table
) with guard
irow_tag: an internal transition (defined inside the
transition_table
) with actions and
guards
_irow_tag: an internal transition (defined inside the
transition_table
) without action or guard.
Due to higher priority for internal transitions, this is
equivalent to a "ignore event"
sm_a_i_row_tag: an internal transition (defined inside the
internal_transition_table
) with
actions
sm_g_i_row_tag: an internal transition (defined inside the
internal_transition_table
) with
guard
sm_i_row_tag: an internal transition (defined inside the
internal_transition_table
) with actions and
guards
sm__i_row_tag: an internal transition (defined inside the
internal_transition_table
) without action
or guard. Due to higher priority for internal transitions,
this is quivalent to a "ignore event"
Furthermore, a front-end must provide the definition of states and state machines. State machine definitions must provide (the implementer is free to provide it or let it be done by every concrete state machine. Different MSM front-ends took one or the other approach):
initial_state
: This typedef can be a single state or
a mpl container and provides the initial states defining one or
several orthogonal regions.
transition_table
: This typedef is a MPL sequence of
transition rows.
configuration
: this typedef is a MPL sequence of
known types triggering special behavior in the back-end, for example
if a concrete fsm requires a message queue or exception
catching.
States and state machines must both provide a (possibly empty) definition of:
flag_list
: the flags being active when this state or
state machine become the current state of the fsm.
deferred_events
: events being automatically deferred
when the state is the current state of the fsm.
internal_transition_table
: the internal transitions
of this state.
on_entry
and on_exit
methods.