7

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:

Diagram 1

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:

Diagram 2

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:

Diagram 3

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:

Diagram 4

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:

Diagram 5

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!

Grant
  • 133
  • 7

2 Answers2

1

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.

Community
  • 1
  • 1
marco.m
  • 4,573
  • 2
  • 26
  • 41
  • Many thanks for that and apologies for the delay in responding, it looks like I didn't have email notifications turned on. Unfortunately, in-state reactions have the same issue - calling Transit<> from the outer state to change the inner state still causes the outer state to be exited and re-entered. However, your suggested alternative looks pretty neat to me, so thanks for that! – Grant Aug 07 '12 at 17:15
1

I know this is an old question, these exit->enter on the same state is annoying.

It seems that to prevent reentry to self you need to: 1. Write custom handler in "self state" 2. Write guards in the parent handler that triggers the reentry to child state.

Imho it is a flaw in StateChart that I haven't found a nice solution for yet - a property call "skip reentry state transitions" on the statemachine object would be great.

Laro88
  • 71
  • 7