First of all, consider the difference between junction pseudo state and choice pseudo state.
These two diagrams are similar. The only difference is that one uses junction pseudo state and the other uses choice pseudo state.
Look at the diagram that uses junction pseudo state. When Event1 occurs, guard conditions are evaluated prior to invoke the action corresponding to Event1. Hence the else branch is chosen. And then actions are invoked along with the transition sequence. The diagram above (case of junction pseudo state) indicates that the action val=1 is invoked first, and then the action ElseBranch is invoked. Consequently, val related to the action ElseBranch is equal to 1.
Then, look at the diagram that uses choice pseudo state. When Event1 occurs, guard conditions are evaluated after invoke the action corresponding to Event1. Hence the [val==1] branch is chosen. And then action Val1Branch is invoked. This means that when a transition reaches a state, the action that corresponds to the transition is evaluated one by one.
Let's see how choice pseudo state may be implemented on Boost.Msm. Boost.Msm doesn't support choice pseudo states directly but you can use the normal state instead of the choice pseudo state. The replaced model is described as the following diagram.And then, translate the model above into the following code directly.
#include <iostream> #include <boost/msm/back/state_machine.hpp> #include <boost/msm/front/state_machine_def.hpp> #include <boost/msm/front/functor_row.hpp> namespace { namespace msm = boost::msm; namespace msmf = boost::msm::front; namespace mpl = boost::mpl; // Events struct Event1 {}; // ----- State machine struct Sm1_:msmf::state_machine_def<Sm1_> { struct State1_:msmf::state<>{ template <class Event, class Fsm, class SourceState, class TargetState> void operator()(Event const&, Fsm& f, SourceState&, TargetState&) const { f.val = 0; std::cout << "val = " << f.val << std::endl; } }; struct Choice_:msmf::state<>{}; // Set initial state typedef State1_ initial_state; // Guards struct GuardVal1 { template <class Event, class Fsm, class SourceState, class TargetState> bool operator()(Event const&, Fsm& f, SourceState&, TargetState&) const { if (f.val == 1) return true; return false; } }; // Actions struct ActionVal1Assign { template <class Event, class Fsm, class SourceState, class TargetState> void operator()(Event const&, Fsm& f, SourceState&, TargetState&) const { f.val = 1; std::cout << "ActionVal1Assign val = " << f.val << std::endl; } }; struct ActionVal1Branch { template <class Event, class Fsm, class SourceState, class TargetState> void operator()(Event const&, Fsm& f, SourceState&, TargetState&) const { std::cout << "ActionVal1Branch val = " << f.val << std::endl; } }; struct ActionElseBranch { template <class Event, class Fsm, class SourceState, class TargetState> void operator()(Event const&, Fsm& f, SourceState&, TargetState&) const { std::cout << "ActionElseBranch val = " << f.val << std::endl; } }; // Transition table struct transition_table:mpl::vector< // Start Event Next Action Guard msmf::Row < State1_, Event1, Choice_, ActionVal1Assign, msmf::none >, msmf::Row < Choice_, msmf::none, State1_, ActionElseBranch, msmf::none >, // else msmf::Row < Choice_, msmf::none, State1_, ActionVal1Branch, GuardVal1 > > {}; private: int val; }; // back-end typedef msm::back::state_machine<Sm1_> Sm1; void test() { Sm1 sm1; sm1.start(); std::cout << "> Send Event1" << std::endl; sm1.process_event(Event1()); } } int main() { test(); return 0; } // Output: // // > Send Event1 // ActionVal1Assign val = 1 // ActionVal1Branch val = 1
There is an issue with this approach. If we swap the 2nd and 3th row inside the transition table, then it is actually going throw the else branch!!!
The output will be:
// > Send Event1
// ActionVal1Assign val = 1
// ActionElseBranch val = 1
Thank you for your comment.
Please check the section 'If/else branch' in the previous page http://redboltz.wikidot.com/junction-pseudo-state
You need to place the else branch on the top of outgoing transitions from the choice state.
Boost.Msm evaluates outgoing transitions' guards from bottom to top.
You stated "And then action Val1Action is invoked." But there is no Val1Action in these figures, only Val1Branch.
Thank you for the comment. I replaced "Val1Action" with "Val1Branch".