Thanks for your quick reply Andreas, Andreas Huber wrote:
Hi Mick
Mick Hollins wrote:
I am currently using the history facility of boost::fsm. I've discovered that when I re-enter a state, X, via a history transition I don't actually re-enter the same instance of X that I was in before, but instead enter a newly constructed instance of X.
I just checked the UML spec regarding history transitions and it says that "Any necessary entry actions are performed", so given that boost::fsm uses constructors to represent entry actions I guess a new instance of X has to be constructed to ensure UML compliance.
Correct.
My actual concern is the effect this has on the "coolness" of state-local-storage (see http://boost-sandbox.sourceforge.net/libs/statechart/doc/faq.html#Whats_so_c...) It seems to me that state-local-storage, which I agree is generally cool, is particularly un-cool (and bug-inducing) in the presence of history transitions.
I'm not sure what a good alternative is, but just thought I'd raise the discussion.
When I implemented history, I had to make a choice between:
a) The current behavior b) Abandon the entry-action --> ctor / exit-action --> dtor mappings and introduce enter() and exit() functions instead (exit() already exists for unrelated reasons). In non-history transitions the state objects would be constructed and destructed as they are now (and enter() / exit() would be called after construction / before destruction). However, when a "historized" state is exited, exit() is called but the state is not destructed. Instead, it is stored in the state_machine object. When a transition to history is made later the state is retrieved and only enter() is called.
Given my recent experience, (b) sounds good to me :-) Would it be feasible to support both models somehow?
In my experience none of the above behaviors is entirely satisfying in practice because for some states you want a) and for others you'd want b).
Well, for states not involved in history transitions, (a) and (b) are more or less equivalent in terms of power of expression. I guess (a) is a little nicer as it doesn't involve 2-phase construction. For states that are involved in history transitions, it seems to me that (b) is the winner since it distinguishes between the two ways in which a state can be entered and is thereby more flexible. Om the subject of two phase construction, I often find that you need it for states anyway, as what you often want is the ability to provide parameters to the constructor of a state to which you are transitioning. The approach I've settled on for this is to post an "initialisation event" just before transitioning state, so that the initialisation event will be the first event handled by the state to which I am transitioning. Is there a better way to achieve this?
When a state contains two members you could even want a) for the first one and b) for the second one.
(b) allows you to have two members with different frequency of initialisation, but (a) makes that harder as it doesn't distinguish between time of state construction and time of (re)entry to a state.
Moreover, you can still get the desired behavior with a), by pushing the state-variables that you want preserved into outer states or the state machine object. As you observed, this essentially wrecks the benefits of state-local storage for those variables. I guess the main reasons that I settled with a) are: 1. It is simpler to explain and implement. 2. There were no votes in favor of b). In fact, you are the first user to bring this up.
Are you saying that I am the first person to attempt to use state local storage and history together?
3. History is used relatively rarely anyway.
I love the history facility (of both UML statecharts and boost::statechart). In fact, history ranks in my top 3 things I love about UML statecharts. The top 3 being: 1) heirarchical states 2) history states 3) deferred event delivery For the record, what I love about boost::statechart is: 1) It makes it trivial to map from a UML statechart to C++ code. When programming using boost::statechart I spend a great deal of my time working at the diagram level, rather than at the code level. That is great for productivity. I've never really experienced such a degree of productivity improvement from other bits of UML. 2) State local storage lets me constrain the scope of variables to exactly where I need them I find state local storage so nice that I like to use it for all my states. The problem is that if I realise down the track that a state needs to support history transitions then I need to rewrite the state to avoid using state local storage, possibly only after having tracked down a nasty bug caused by the history transitions.
Then again, almost all my experience with state machines stems from the machine control field. Other areas might well have different requirements / expectations, so I'm interested in your use-case and how much of a difference b) would make. Also, there might be other, less intrusive approaches that I haven't thought about.
I've started to think about other ways to represent state local storage that work well with history. If you have any suggestions, I'd love to hear them. cheers, mick