
Andrey Semashev wrote:
As for states' allocation and deallocation, there may be other issues with it, besides of performance loss. I'm not quite sure you have read our discussion with Alexander Nasonov in this thread, where I pointed out that the fact that the state is deleted when being left may be inconvenient if the state has its local data. If the state is visited again all these data are lost.
Right, in my experience this is the behavior that is most commonly wanted. IIUC, what you want is something like the currently supported history but with the added feature that all local variables are also restored upon entry of the state. Such a feature has been suggested before but I've so far not found a satisfying way to implement it. IMO, you definitely need both options and sometimes a state even contains multiple variables with different needs of resetting/reconstitution.
Agreed, that's why I decided not to delete states during transitions. If some variables need to be reset when the state is being left, a user may implement such logic in the appropriate handler function.
In your library, is there any way to do this automatically, a la RAII? I'm asking because in my experience, you often need to acquire a resource upon state entry and release it upon state exit. It's error-prone when you need to release explicitly, just as it is error-prone when you need to manually release resources at the end of a block scope. That's why Boost.Statechart has this strict state-exit to destructor mapping. If you need a variable to live longer you push it into an outer state, just as you do with a variable allocated on the stack when you push it into an outer scope. Yes, this is suboptimal in the sense that other inner states of the outer state can then also access said variable, but the same is true for stack variables.
Just a thought of similar functionality support in Statechart. If there was an additional optional template parameter in the "state" or "simple_state" class template that would have a type of the state's context which lifetime would last from the first entrance into the state until the whole state machine destruction (let's call it a static state context), then a reference to the context might have been passed to the state's constructor. For example:
// This will be the static context struct State1Ctx { int n; };
struct State1 : public sc::simple_state< State1, OuterState, State1Ctx > { // This reference will hold the context State1Ctx& static_ctx_;
// In ctor we receive a reference to the static context. // No matter how many times we enter the state, // we get the reference to the same object here State1(State1Ctx& ctx) : static_ctx_(ctx) { } };
Or, alternatively, the static_ctx_ reference may even be in the "simple_state" class itself. It may be accessible via some public method.
I'd hate to add yet another template parameter, but the same could be achieved via specializations of a template (suggested long ago by Dave Abrahams): In simple-state.hpp: template<class State> class private_state_context {}; // Probably needs a better name template< class MostDerived, ... > class simple_state { // ... private_state_context< MostDerived > & private_context(); const private_state_context< MostDerived > & private_context() const; // ... }; If a user needs a private context for a state, she can specialize the private_state_context template for said state. It should be pretty easy to add something like this. The only problem is that every added feature also increases the opportunities for abuse and confusion. I'll think about this some more and then maybe add it to the to-do list.
Additionally as the data amount and states number raises the code of these data-holding classes gets more messy.
You've lost me here: Which data-holding classes? In Boost.Statechart there are only states.
By "data-holding classes" I meant collectively states and the state machine classes that can hold data of inner states that we were discussing above.
Ok.
I was saying that as the amount of states grows so does the amount of data. And if it is stored in some outer state, the state becomes overweightened and messy.
I agree for the cases when you need to push outward a variable due to lifetime-issues only (i.e. the variable is still only accessed by the state that originally contained it). However, IME, outer states are almost never pure data holding classes, they *usually* have behavior of their own and often need to access precisely the variables that were pushed outward from inner states (e.g. see the Stopwatch example).
To my mind this issue should be of special concern since Statechart is more targeted to creation of bigger state machines.
It has been a concern, I'm just unsure how it is best solved.
BTW, is there any way to make Boost.Statechart's machine not compiling if it doesn't expect some particular event type? IOW, to force the machine to support all event types being passed to it?
No, as that would require the implementation of the whole machine in one TU. IMO, the ability to compile parts of a large machine separately is more important in practice.
Ah, that's right. Modularity takes its toll.
Precisely, as do many other features. I have to accept that the library cannot satisfy all needs, e.g. especially parser-like FSMs are better implemented by other means. Regards, -- Andreas Huber When replying by private email, please remove the words spam and trap from the address shown in the header.