
Hi Dave, [snip]
After a brief glance, it looks like a very sophisticated and well-thought-out library.
Thanks :-)
I'd like to know if following state transitions require dynamic allocation or not.
By default (i.e. when you define states as demonstrated in the tutorial), a dynamic allocation happens in the course of every state entry. However, since users define all state classes themselves, they have full control over how each state will be allocated (see "Memory management customization" in the rationale). [snip]
Exception handling ------------------
Exceptions can be propagated from all user code except from state exit actions (mapped to destructors and destructors should virtually never throw in C++).
That seems like a bad limitation, and for me it calls into question the idea of mapping state exit to destructors. Can you explain why that's the right design?
Yes, there's a lot of evidence that state exit actions must not fail: Say you have a state_machine<> subclass S and an object of that class, which currently resides in state A and state B nested in A (see the picture under Error handling in ratinale.html). Inside S::~S, the state machine is terminated what leads to the exit actions of B and A being executed. If B's exit action fails, what can you do with the resulting exception? I guess we agree that you cannot propagate it out of S::~S. I think we also agree that despite the failure in B's exit action, we must still execute A's exit action before returning from S::~S. W.r.t. to handling said exception I believe that at this point there's pretty much nothing that can be done by the caller of B's exit action that couldn't be done inside the exit action before returning to its caller. That is, in this particular situation, there's no point in propagating an exception from B's exit action. I agree that this is a rather special situation, because exit actions are called from a destructor. However, even if exit actions are called in the course of a normal transition what are you going to do if B's exit action throws an exception? Technically, the state machine still resides in B, so you have pretty few options for continuing. You cannot exit A as that would violate the state machine invariant that inner states are always exited before outer states. You cannot make a transition to another state, as that would also require successful exit from B. So, the only sensible thing is to try to handle the error inside B's exit action. If the error cannot be handled there it must either be ignored or, in the case of a really serious error, be handled with a different mechanism than exceptions.
Out of the box, state_machine<> does the following:
1. The exception is caught
By the state machine framework?
Yes.
2. In the catch block, an exception_thrown event is allocated on the stack
3. Also in the catch block, an immediate dispatch of the exception_thrown event is attempted.
Hum. So basically, any throwing code that could be invoked from processing exception_thrown and from processing some other event had better be re-entrant?
Yes. However, I don't think this will happen in practice. Code that is executed as a result of the processing of an exception_thrown event is typically very different from code that is executed during normal state machine operation. Moreover, if code reacting to an exception_thrown event throws itself an exception then this exception will under all circumstances be propagated to the state machine client. That is, error handling code does not have to be reentrant.
That is, possibly remaining events in the queue are dispatched only after the exception has been handled successfully
I don't understand the relationship here.
A state machine can post internal events (simple_state<>::post_event()) while processing an event. Those internal events are pushed into a queue and processed as soon as the state machine has finished processing the current event. If an exception is thrown while processing any of these events, the resulting exception_thrown event "overtakes" the still queued events.
4. If the exception was handled successfully
What does that mean? Who signals "successful handling", and how do they do it?
This is explained a bit further down under the title Successful exception handling. I agree that this is probably a bit hard to find. I've added a link in my version. Regards, Andreas