This document covers the use of yake::fsm2! It does not cover the deprecated code of Yake 0.4 and earlier!
yake::fsm implements a generic flexible state machine which allows runtime configuration. The latter is a requirement and therefore the reason that we did not take one of the existing compile-time state machine libraries (e.g. boost).
TODO Why didn't we choose a different runtime-configurable lib?
Let's create a very simple state machine where states are identified by strings and events by integers.
typedef fsm::machine<std::string, int> machine_type; machine_type m1;
Let's fill the machine with information: states and transitions. We keep it simple in this example so there'll be just a few of both types.
First, the states:
m1.addState("alive"); m1.addState("the_inevitable");
Now, the transitions in the form of (from, event, to):
const int kEvtAxe = 1; // We create a constant for easier reference later. m1.addTransition("alive", kEvtAxe, "the_inevitable");
Initial state. Do we need to set it? By default, the machine sets the initial state to the 'null' state. For strings it's defined by a template specialization of fsm::get_null_state<state_type>() and is by default the string “null”.
We can either add the state to the machine and add a transition from “null” to “alive” or we simply set the initial state to “alive”. We do the latter in this example:
m1.setState("alive");
Let's make sure the current state is “alive”:
YAKE_ASSERT( m1.current() == "alive" ); // YAKE_ASSERT is a macro doing a check of the condition at runtime. std::cout << m1.current(); // prints 'alive' to the console window.
Now let's transition between states by posting an event to the machine:
m1.processEvent( kEvtAxe ); std::cout << m1.current(); // now prints 'the_inevitable'
In the next part we discuss how to handle notifications of state changes.
It is very easy to trigger callbacks when a state machine changes state.
Normally, you call the machine's member function processEvent(). If you want to do pre and/or post processing on successful state change, you can call processEventCb() with the 2nd and 3rd parameters being executable objects.
It can look like this:
typedef fsm::machine<std::string,int> machine_t; // state: string, event: int void cout_op_enter(const machine_t&, const machine_t::state_type& state) { std::cout << "enter state: " << state << "\n"; } void cout_op_exit(const machine_t&, const machine_t::state_type& state) { std::cout << "exit state: " << state << "\n"; } m1.processEventCb( 12, cout_op_enter, cout_op_exit );
If event '12' results in a state change from “alive” to “dead” the output looks like this:
exit state: alive enter state: dead
Or we could use a struct with operator():
typedef fsm::machine<std::string,int> machine_t; // state: string, event: int struct cout_op { cout_op( const std::string& prefix ) : prefix_(prefix) {} void operator(const machine_t&, const machine_t::state_type& state) { std::cout << prefix_ << ": " << state << "\n"; } private: std::string prefix_; } m1.processEventCb( 12, cout_op("enter state"), cout_op("exit state") );
If event '12' results in a state change from “alive” to “dead” the output looks like this:
exit state: alive enter state: dead
The function machine::processEventCb<>() accepts any callbacks that implement operator()(…) (using the machine's types), e.g. it works with boost::function objects.