Boost Statechart - Local transitions
boost, boost-statechart, c++
Solution
Have you looked at the in-state reaction explained in the statechart tutorial? It seems to be doing what you are looking for.
Since you are asking for alternatives, in this period I am evaluating various C++ Harel statechart implementations. I looked at Boost statechart and Boost MSM. I wrote code with both. They hurt my feeble brain :-)
Then I found Machine Objects (Macho), very simple and small, and I love it. It supports hierarchical state machines, entry/exit actions, history, state machine snapshots, guards, internal transitions, event deferring, state-local storage (with optional persistence), so to me it is a satisfying Harel statechart implementation.
This code implements the FunctionMode part of the statechart with Macho:
#include "Macho.hpp"
#include <exception>
#include <iostream>
using namespace std;
namespace FunctionMode {
struct FunctionMode;
struct F1Mode;
struct F2Mode;
// The Top state, containing all the others.
TOPSTATE(Top) {
STATE(Top)
// All the events of the state machine are just virtual functions.
// Here we throw to mean that no inner state has implemented the event
// handler and we consider that an error. This is optional, we could
// just have an empty body or log the error.
virtual void evF1() { throw std::exception(); }
virtual void evF2() { throw std::exception(); }
// evF3 and so on...
private:
void init() { setState<FunctionMode>(); } // initial transition
};
SUBSTATE(FunctionMode, Top) {
STATE(FunctionMode)
virtual void evF1() { setState<F1Mode>(); }
virtual void evF2() { setState<F2Mode>(); }
// evF3, ...
private:
void entry() { cout << "FunctionMode::entry" << endl; }
void exit() { cout << "FunctionMode::exit" << endl; }
void init() { setState<F1Mode>(); } // initial transition
};
SUBSTATE(F1Mode, FunctionMode) {
STATE(F1Mode)
virtual void evF1() {} // make the event an internal transition (by swallowing it)
private:
void entry() { cout << "F1Mode::entry" << endl; }
void exit() { cout << "F1Mode::exit" << endl; }
};
SUBSTATE(F2Mode, FunctionMode) {
STATE(F2Mode)
virtual void evF2() {} // make the event an internal transition (by swallowing it)
private:
void entry() { cout << "F2Mode::entry" << endl; }
void exit() { cout << "F2Mode::exit" << endl; }
};
} // namespace FunctionMode
int main() {
Macho::Machine<FunctionMode::Top> sm;
// Now the machine is already in F1Mode.
// Macho has 2 methods for synchronous event dispatching:
// First method:
sm->evF1(); // <= this one will be swallowed by F1Mode::evF1()
// Second method:
sm.dispatch(Event(&FunctionMode::Top::evF2));
return 0;
}
Running it, the output is:
FunctionMode::entry
F1Mode::entry
F1Mode::exit
F2Mode::entry
F2Mode::exit
FunctionMode::exit
that shows that the transitions are internal.
In my opinion, clean, easy and compact code :-)
[EDIT1] The first version of the code didn't perform the initial transition `FunctionMode` -> `F1Mode`. Now it does.
Problem
I'm hoping that someone can help me out with this problem, or at least point out the error of my ways... As a simple illustration of my problem consider a part of an application where you can enter a "Functions Mode" state of operation. Four sub-modes are then available depending on which function key F1-F4 that the user presses. By default, F1 mode is entered. The state diagram starts off as follows: The user can press F1-F4 at any time to switch to the corresponding mode. Adding these transitions to the inner states leads to the following: Obviously this is (a) a mess, and (b) a lot of transitions to define. If at some point I want to add an F5Mode then... well, you get the picture. To avoid this I'd like to do the following: Boost Statechart allows me to define transitions from FunctionMode to any of the inner states, but the result isn't what I expected. The actual outcome is as follows: I.e. pressing F1-F4 to switch modes causes the outer FunctionMode state to be exited and re-entered along with triggering the unwanted exit and entry actions. Way back in 2006, this thread between the library author and a user seems to describe the same problem. I think that the author suggests doing the following as a work-around: However, that work-around doesn't seem very appealing to me: It has added an extra state level to be compiled, the code has become less readable, deep-history would have to be used to return to any of the function mode sub-states and the Intermediate state object is needlessly being destructed and constructed again. So... where am I going wrong? Or what are the alternatives? I've had a brief look at Boost Meta State Machine (msm) but from what I've seen so far I much prefer the look of Statechart. I'm surprised that more users haven't faced the same problem... which makes me think that perhaps my approach is completely wrong!