
Dear Boosters I think boost::fsm has finally reached a state that is worthy of a formal review. The .zip can be found here: http://boost-sandbox.sf.net/fsm.zip The docs are also available online: http://boost-sandbox.sf.net/libs/fsm As always: Feedback is highly welcome. Regards, Andreas

"Andreas Huber" <ah2003@gmx.net> writes:
Dear Boosters
I think boost::fsm has finally reached a state that is worthy of a formal review.
The .zip can be found here:
http://boost-sandbox.sf.net/fsm.zip
The docs are also available online:
http://boost-sandbox.sf.net/libs/fsm
As always: Feedback is highly welcome.
After a brief glance, it looks like a very sophisticated and well-thought-out library. I'd like to know if following state transitions require dynamic allocation or not. I also have some questions about this:
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?
Out of the box, state_machine<> does the following:
1. The exception is caught
By the state machine framework?
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?
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.
4. If the exception was handled successfully
What does that mean? Who signals "successful handling", and how do they do it?
the state machine returns to the client normally. If the exception could not be handled successfully, the original exception is rethrown so that the client of the state machine can handle the exception
This behavior is implemented in the exception_translator class template, which is the default for the ExceptionTranslator parameter of the state_machine class template. It was introduced because users would want to change this on some platforms to work around buggy exception handling implementations (see Discriminating exceptions).
Interesting; I'll have to look at that... -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

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

"Andreas Huber" <ah2003@gmx.net> writes:
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.
You miss my point. I know destructors must not fail. I am suggesting that it may have been the wrong decision to represent state exit actions with destructors; you could've used some other protocol. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

David Abrahams wrote:
"Andreas Huber" <ah2003@gmx.net> writes:
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.
You miss my point. I know destructors must not fail. I am suggesting that it may have been the wrong decision to represent state exit actions with destructors; you could've used some other protocol.
I don't think I have missed your point and I know that I'm talking to an exception handling expert. Have you read that whole paragraph? Agreed, after posting I noticed that the first half isn't exactly the best explanation of why exit actions must not fail. However, I believe that the second half is a good argument why throwing exit actions are a bad idea: <quote> ... 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. </quote> A maybe more convincing argument: In the course of a transition a state entry action throws an exception. Note that the state machine is in an invalid state (unstable) when this happens. If the state machine does not successfully handle the exception, the exception must be propagated to the state machine client. Because the state machine is unstable, it *must* be terminated before propagating to the client. Termination calls the exit actions of all currently active states. What are you going to do with exceptions thrown by these exit actions? Regards, Andreas

"Andreas Huber" <ah2003@gmx.net> writes:
I don't think I have missed your point and I know that I'm talking to an exception handling expert. Have you read that whole paragraph?
Yep
Agreed, after posting I noticed that the first half isn't exactly the best explanation of why exit actions must not fail. However, I believe that the second half is a good argument why throwing exit actions are a bad idea: <quote> ... 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?
Stay at B, of course (?)
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.
I don't understand why. The user of the FSM could handle the exception on the outside if its state doesn't change.
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. </quote>
A maybe more convincing argument: In the course of a transition a state entry action throws an exception. Note that the state machine is in an invalid state (unstable) when this happens. If the state machine does not successfully handle the exception, the exception must be propagated to the state machine client. Because the state machine is unstable, it *must* be terminated before propagating to the client.
OK so far...
Termination calls the exit actions of all currently active states.
that sounds like it could be the wrong design decision, to me.
What are you going to do with exceptions thrown by these exit actions?
-- Dave Abrahams Boost Consulting http://www.boost-consulting.com

David Abrahams wrote:
"Andreas Huber" <ah2003@gmx.net> writes:
I don't think I have missed your point and I know that I'm talking to an exception handling expert. Have you read that whole paragraph?
Yep
Agreed, after posting I noticed that the first half isn't exactly the best explanation of why exit actions must not fail. However, I believe that the second half is a good argument why throwing exit actions are a bad idea: <quote> ... 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?
Stay at B, of course (?)
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.
I don't understand why. The user of the FSM could handle the exception on the outside if its state doesn't change.
Ok, I seem to finally see your point. You would want to leave the state machine in a possibly *invalid* state when propagating the exception to the state machine client, right? So what is the user then doing with the state machine object after handling that exception? Since the machine might be unstable he cannot process any more events. What's even more disturbing is the fact that he cannot even successfully destroy the state machine. Or are you proposing that the exit actions of the still active states should not be called upon destruction?
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. </quote>
A maybe more convincing argument: In the course of a transition a state entry action throws an exception. Note that the state machine is in an invalid state (unstable) when this happens. If the state machine does not successfully handle the exception, the exception must be propagated to the state machine client. Because the state machine is unstable, it *must* be terminated before propagating to the client.
OK so far...
Termination calls the exit actions of all currently active states.
that sounds like it could be the wrong design decision, to me.
Ok, but how do you propose should the state machine then be terminated? By simply not calling the exit actions of still active states? Regards, Andreas

"Andreas Huber" <ah2003@gmx.net> writes:
David Abrahams wrote:
"Andreas Huber" <ah2003@gmx.net> writes:
I don't think I have missed your point and I know that I'm talking to an exception handling expert. Have you read that whole paragraph?
Yep
Agreed, after posting I noticed that the first half isn't exactly the best explanation of why exit actions must not fail. However, I believe that the second half is a good argument why throwing exit actions are a bad idea: <quote> ... 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?
Stay at B, of course (?)
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.
I don't understand why. The user of the FSM could handle the exception on the outside if its state doesn't change.
Ok, I seem to finally see your point. You would want to leave the state machine in a possibly *invalid* state when propagating the exception to the state machine client, right?
No. I said it should stay in the same state it was already in. Presumably the exit action is the first thing that would happen on a transition (?), so if it fails, you haven't gone anywhere and the machine should still be in a valid state.
So what is the user then doing with the state machine object after handling that exception? Since the machine might be unstable he cannot process any more events. What's even more disturbing is the fact that he cannot even successfully destroy the state machine. Or are you proposing that the exit actions of the still active states should not be called upon destruction?
I am proposing that. Leaving a state via transition seems fundamentally different from what must happen when you're simply done using a state machine and it is destroyed.
that sounds like it could be the wrong design decision, to me.
Ok, but how do you propose should the state machine then be terminated? By simply not calling the exit actions of still active states?
Yes, why not? -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

David Abrahams <dave <at> boost-consulting.com> writes:
Ok, I seem to finally see your point. You would want to leave the state machine in a possibly *invalid* state when propagating the exception to the state machine client, right?
No. I said it should stay in the same state it was already in. Presumably the exit action is the first thing that would happen on a transition (?)
Right.
, so if it fails, you haven't gone anywhere and the machine should still be in a valid state.
This can only be guaranteed if your state machine is flat and does not have orthogonal regions. If you either have nested states and/or orthogonal regions you frequently need to exit more than one state. If the first exit action fails then, yes, the machine is still in a valid state. However, if the first one succeeds and any of the other exit actions fails then you're doomed. Your state machine is now in an invalid state and there's nothing you can do to bring it back into a valid state, short of terminating by not calling exit actions (see below).
So what is the user then doing with the state machine object after handling that exception? Since the machine might be unstable he cannot process any more events. What's even more disturbing is the fact that he cannot even successfully destroy the state machine. Or are you proposing that the exit actions of the still active states should not be called upon destruction?
I am proposing that. Leaving a state via transition seems fundamentally different from what must happen when you're simply done using a state machine and it is destroyed.
that sounds like it could be the wrong design decision, to me.
Ok, but how do you propose should the state machine then be terminated? By simply not calling the exit actions of still active states?
Yes, why not?
Because that would essentially be the same as destructing an object by not calling its destructor (i.e. by only deallocating the memory). I'm not only saying this because an active state happens to be an object in boost::fsm. State machines frequently acquire resources in entry actions and dispose of them in exit actions. Those resources would simply be leaked if there's no guarantee that exit actions are called under all non-fatal error situations. Regards, Andreas

Andreas Huber <ah2003@gmx.net> writes:
Ok, but how do you propose should the state machine then be terminated? By simply not calling the exit actions of still active states?
Yes, why not?
Because that would essentially be the same as destructing an object by not calling its destructor (i.e. by only deallocating the memory).
That's circular logic.
I'm not only saying this because an active state happens to be an object in boost::fsm. State machines frequently acquire resources in entry actions and dispose of them in exit actions. Those resources would simply be leaked if there's no guarantee that exit actions are called under all non-fatal error situations.
It seems reasonable that well-designed state objects should also deallocate any resources they own in their destructors. It doesn't neccessarily seem reasonable as a consequence to force all exit actions into the state's destructor. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

David Abrahams <dave <at> boost-consulting.com> writes:
I'm not only saying this because an active state happens to be an object in boost::fsm. State machines frequently acquire resources in entry actions and dispose of them in exit actions. Those resources would simply be leaked if there's no guarantee that exit actions are called under all non-fatal error situations.
It seems reasonable that well-designed state objects should also deallocate any resources they own in their destructors. It doesn't neccessarily seem reasonable as a consequence to force all exit actions into the state's destructor.
Are you proposing that an active state should still be represented by an object, which acquires resources and executes entry actions in its constructor, releases resources in its destructor but executes exit actions in a separate function, which is called just before destructing the state object? Regards, Andreas

Andreas Huber <ah2003@gmx.net> writes:
David Abrahams <dave <at> boost-consulting.com> writes:
I'm not only saying this because an active state happens to be an object in boost::fsm. State machines frequently acquire resources in entry actions and dispose of them in exit actions. Those resources would simply be leaked if there's no guarantee that exit actions are called under all non-fatal error situations.
It seems reasonable that well-designed state objects should also deallocate any resources they own in their destructors. It doesn't neccessarily seem reasonable as a consequence to force all exit actions into the state's destructor.
Are you proposing that an active state should still be represented by an object,
I don't have enough knowledge of the FSM domain to say whether that's a good design or not; I have to trust you that it makes sense to have a correspondence between states and distinct types whose instances' lifetimes correspond to the time spent in the state.
which acquires resources and executes entry actions in its constructor, releases resources in its destructor but executes exit actions in a separate function, which is called just before destructing the state object?
Yes, that's basically what I was saying. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

David Abrahams <dave <at> boost-consulting.com> writes:
Are you proposing that an active state should still be represented by an object,
I don't have enough knowledge of the FSM domain to say whether that's a good design or not; I have to trust you that it makes sense to have a correspondence between states and distinct types whose instances' lifetimes correspond to the time spent in the state.
That an active state is represented by an object is crucial to satisfy the scalability requirements. Otherwise adding a variable that is used only in a part of the machine would lead to the recompilation of the whole state machine.
which acquires resources and executes entry actions in its constructor, releases resources in its destructor but executes exit actions in a separate function, which is called just before destructing the state object?
Yes, that's basically what I was saying.
This is certainly doable. However, I'm unsure whether the current design allows a you-don't-pay-for-what-you-don't-use implementation of such a feature. Plus, I'm still not convinced that this feature would be used more than rarely. Do you have some real-world use cases in mind? If this is needed only rarely I'd rather have users work around the problem by setting a boolean and posting an error event in a failing exit action and then testing that boolean in all following exit actions. Regards, Andreas

Andreas Huber <ah2003@gmx.net> writes:
Are you proposing that an active state should still be represented by an object,
I don't have enough knowledge of the FSM domain to say whether that's a good design or not; I have to trust you that it makes sense to have a correspondence between states and distinct types whose instances' lifetimes correspond to the time spent in the state.
That an active state is represented by an object is crucial to satisfy the scalability requirements. Otherwise adding a variable that is used only in a part of the machine would lead to the recompilation of the whole state machine.
OK.
which acquires resources and executes entry actions in its constructor, releases resources in its destructor but executes exit actions in a separate function, which is called just before destructing the state object?
Yes, that's basically what I was saying.
This is certainly doable. However, I'm unsure whether the current design allows a you-don't-pay-for-what-you-don't-use implementation of such a feature. Plus, I'm still not convinced that this feature would be used more than rarely. Do you have some real-world use cases in mind?
Unfortunately I'm not so familiar with what exit actions in FSMs are typically used for, but from an abstract point of view I don't think of resource releasing as an "action". If you were writing this stuff in a GC'd language, for the most part, you wouldn't devote any exit action code to resource releases. So I figure there must be some other reason people want exit actions, and since they seem symmetrical to entry actions in some ways, I don't see why you'd want to prohibit exceptions there. If anything, I'd want to prohibit exceptions in entry actions, since those can lead to the FSM being in a state from which it's impossible to continue. At least with exit actions, you can stay where you are and keep all your FSM's abstract invariants intact.
If this is needed only rarely I'd rather have users work around the problem by setting a boolean and posting an error event in a failing exit action and then testing that boolean in all following exit actions.
Eeeeew, that's gross. Sorry, but that just smells like an awful hack. Why not just call the state object's exit() member function if it has one? -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

David Abrahams <dave <at> boost-consulting.com> writes:
This is certainly doable. However, I'm unsure whether the current design allows a you-don't-pay-for-what-you-don't-use implementation of such a feature. Plus, I'm still not convinced that this feature would be used more than rarely. Do you have some real-world use cases in mind?
Unfortunately I'm not so familiar with what exit actions in FSMs are typically used for, but from an abstract point of view I don't think of resource releasing as an "action". If you were writing this stuff in a GC'd language, for the most part, you wouldn't devote any exit action code to resource releases.
I don't think so. A non-shareable resource (e.g. a file) you almost always need to release immediately after you are done using it (because someone else might want to access it while your program is still running). The same is true for shareable resources that are limited by other factors than memory (e.g. SQL connections).
So I figure there must be some other reason people want exit actions ...
In my experience you typically do the following stuff in exit actions: For the most part you release resources, sometimes bump a counter here and there and, yes, under rare circumstances you also execute stuff that can fail. I guess we agree that the same is true for destructors.
... , and since they seem symmetrical to entry actions in some ways, I don't see why you'd want to prohibit exceptions there.
For the same reasons that class constructors and destructors are asymmetrical in this regard as well.
If anything, I'd want to prohibit exceptions in entry actions, since those can lead to the FSM being in a state from which it's impossible to continue.
Interesting, it seems "failure-proof" entry actions would be an inevitable consequence when you allow failing exit actions.
At least with exit actions, you can stay where you are and keep all your FSM's abstract invariants intact.
As I have already pointed out: Only if your machines are flat and non- orthogonal, right? My approach is almost symmetrical to yours, isn't it? Since I allow failing entry actions I am forced to disallow failing exit actions. With your approach exit actions can fail what forces you to disallow failing entry actions. In both approaches allowing the failure-proof action to suddenly fail can result in an FSM having an invalid state from where it is impossible to recover. However, I consider my approach superior because I can guarantee that for every successfully completed entry action there will always be a matching call to the exit action. You cannot give such a guarantee.
If this is needed only rarely I'd rather have users work around the problem by setting a boolean and posting an error event in a failing exit action and then testing that boolean in all following exit actions.
Eeeeew, that's gross. Sorry, but that just smells like an awful hack.
I very much agree. But don't we have to do the same when we want to signal a destructor failure?
Why not just call the state object's exit() member function if it has one?
That might be a solution but I'd rather avoid this complication as long as there are no concrete use-cases for it. Regards, Andreas

Andreas Huber <ah2003@gmx.net> writes:
If anything, I'd want to prohibit exceptions in entry actions, since those can lead to the FSM being in a state from which it's impossible to continue.
Interesting, it seems "failure-proof" entry actions would be an inevitable consequence when you allow failing exit actions.
Not AFAICT. Even if you don't allow exit actions to fail, how will you recover from a failed entry action? I am assuming here that when going A->B, you exit A before you enter B. If you're doing it in the other order, of course, my arguments don't apply.
At least with exit actions, you can stay where you are and keep all your FSM's abstract invariants intact.
As I have already pointed out: Only if your machines are flat and non- orthogonal, right?
I don't understand what you mean. Certainly w.r.t. "flat", only one exit action in a stack of nested states can be allowed to fail.
My approach is almost symmetrical to yours, isn't it? Since I allow failing entry actions I am forced to disallow failing exit actions.
I don't understand that. You can't neccessarily re-enter a valid state if you've already exited and entry throws an exception.
With your approach exit actions can fail what forces you to disallow failing entry actions. In both approaches allowing the failure-proof action to suddenly fail can result in an FSM having an invalid state from where it is impossible to recover.
However, I consider my approach superior because I can guarantee that for every successfully completed entry action there will always be a matching call to the exit action. You cannot give such a guarantee.
If this is needed only rarely I'd rather have users work around the problem by setting a boolean and posting an error event in a failing exit action and then testing that boolean in all following exit actions. Eeeeew, that's gross. Sorry, but that just smells like an awful hack.
I very much agree. But don't we have to do the same when we want to signal a destructor failure?
Circular argument again ;-) -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

David Abrahams wrote:
Andreas Huber <ah2003@gmx.net> writes:
If anything, I'd want to prohibit exceptions in entry actions, since those can lead to the FSM being in a state from which it's impossible to continue.
Interesting, it seems "failure-proof" entry actions would be an inevitable consequence when you allow failing exit actions.
Not AFAICT. Even if you don't allow exit actions to fail, how will you recover from a failed entry action?
Please have a look at the state chart at http://tinyurl.com/yrbee Let us assume that the lower orthogonal region is entered first (in boost::fsm this would be region 0). When we initiate this state machine, The states are entered in the following sequence: A, B, (exception is thrown while trying to enter C). When that exception is thrown then, as you rightly point out, we have a state machine that is in an invalid state. To bring this machine back into a stable state, boost::fsm now does the following: 1. The outermost unstable state (see definitions.html) is determined. In this example this is A, because the state machine never had the chance to enter the second orthogonal region. 2. An exception_thrown event is allocated on the stack 3. The state machine searches for a reaction for the exception_thrown event. It starts by examining the outermost unstable state. If that state does not have an appropriate reaction, it works its way outward. 4. Since A does have a reaction for the exception_thrown event, that reaction is executed. 5. The execution of said reaction leads to the exit of B, the exit of A and finally to the entry of E. Do we agree that the state machine is now in a valid (stable) state (assuming that the entry action of E did not throw)? If the upper orthogonal region is entered first, the entry sequence is as follows: A, D, B, (exception is thrown while trying to enter C). Now B is the outermost unstable state and consequently B is exited and F entered. Again, we're stable, right? In both cases if the state machine hadn't found a suitable reaction then it would have been terminated (by exiting all remaining states) and the original exception would have been propagated to the state machine client. In both cases if another exception had been thrown during transition to either E or F, the state machine would have been terminated and the new exception would have been propagated to the state machine client. I think it is obvious that this algorithm will ensure under *all* circumstances that the state machine becomes stable again after having encountered an exception (a terminated state machine is stable too). This is only possible when exit actions cannot throw. If you allow exit actions to throw, even if you prohibit throwing entry actions, this is possible only if you accept that a call to an entry action might not be matched with a corresponding call to an exit action.
I am assuming here that when going A->B, you exit A before you enter B.
Right.
At least with exit actions, you can stay where you are and keep all your FSM's abstract invariants intact.
As I have already pointed out: Only if your machines are flat and non- orthogonal, right?
I don't understand what you mean. Certainly w.r.t. "flat", only one exit action in a stack of nested states can be allowed to fail.
Please have a look at the state chart again. We now assume that nothing fails upon initiating the state machine (i.e. the C entry action does not exist). Let us further assume that the state machine is handed a normal event, EvX and the transition to E has been selected (i.e. the transition trigger is EvX and not exception_thrown). Before we can enter E, we first need to exit C, B, D and A in this sequence. If exit actions are allowed to throw and if either the B, D, or A exit action throws, then the state machine is now in an invalid state and there's no way how we can bring it back into a stable state again, right?
My approach is almost symmetrical to yours, isn't it? Since I allow failing entry actions I am forced to disallow failing exit actions.
I don't understand that. You can't neccessarily re-enter a valid state if you've already exited and entry throws an exception.
As I have pointed out above, if the entry action of either E or F also throws then, yes, all is lost and we need to terminate the machine an propagate the exception to the state machine client. However, one would certainly try to make sure that the entry actions of E and F will not throw, right?
With your approach exit actions can fail what forces you to disallow failing entry actions. In both approaches allowing the failure-proof action to suddenly fail can result in an FSM having an invalid state from where it is impossible to recover.
However, I consider my approach superior because I can guarantee that for every successfully completed entry action there will always be a matching call to the exit action. You cannot give such a guarantee.
If this is needed only rarely I'd rather have users work around the problem by setting a boolean and posting an error event in a failing exit action and then testing that boolean in all following exit actions. Eeeeew, that's gross. Sorry, but that just smells like an awful hack.
I very much agree. But don't we have to do the same when we want to signal a destructor failure?
Circular argument again ;-)
Sorry, I don't get it. Why exactly is that circular? I'm not talking about state destructors, I'm talking about C++ destructors in general. If you happen to be in the situation of somehow needing to signal a destructor failure to the outside world you are in the same situation as someone that needs to signal a exit action failure in a FSM framework that does not allow to throw from exit actions (whether the FSM framework maps exit actions to destructors or exit() functions is irrelevant). My argument was that in C++ you can already find yourself in a situation where signaling a failure is, well, quite difficult. So, it isn't all that surprising that as a user of a library you might find yourself in more (or other) such situations. While I do see that one does not exactly follow from the other (it was never my intention to suggest that) I really don't see why this reasoning is circular. Regards, Andreas

"Andreas Huber" <ah2003@gmx.net> writes:
Please have a look at the state chart at
Let us assume that the lower orthogonal region is entered first (in boost::fsm this would be region 0). When we initiate this state machine, The states are entered in the following sequence: A, B, (exception is thrown while trying to enter C). When that exception is thrown then, as you rightly point out, we have a state machine that is in an invalid state. To bring this machine back into a stable state, boost::fsm now does the following: 1. The outermost unstable state (see definitions.html) is determined. In this example this is A, because the state machine never had the chance to enter the second orthogonal region. 2. An exception_thrown event is allocated on the stack 3. The state machine searches for a reaction for the exception_thrown event. It starts by examining the outermost unstable state. If that state does not have an appropriate reaction, it works its way outward. 4. Since A does have a reaction for the exception_thrown event, that reaction is executed. 5. The execution of said reaction leads to the exit of B, the exit of A and finally to the entry of E.
Do we agree that the state machine is now in a valid (stable) state (assuming that the entry action of E did not throw)?
Sure, if every state whose entry action can fail has a superstate, you can bail out and leave the machine in the superstate. Are you requiring that? Is that superstate always a reasonable place to end up upon failure? FWIW, I don't know what an orthogonal region is, and I don't have time to learn right now, so I'm probably missing a lot. If you're still convinced you have everything right, I'll assume I'm missing something important and stop poking at this design decision. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

David Abrahams wrote:
Sure, if every state whose entry action can fail has a superstate, you can bail out and leave the machine in the superstate. Are you requiring that?
No, this is not a requirement. But, you are right in your observation that the failure of an entry action of an outermost state cannot be handled anywhere. Consequently, the error handling algorithm will not find a suitable reaction in such a situation and bail out by rethrowing the original exception (the state machine is terminated already).
Is that superstate always a reasonable place to end up upon failure?
Well, you don't excatly end up there, right? The superstate (more accurately the outermost unstable state) is only a "container" for reaction(s) to the exception_thrown event. If such a reaction is found and executed it *must* make a transition to another state (sort of a safe haven) or terminate the state machine itself. Only then are we stable again. To answer your question: Yes, I believe so.
FWIW, I don't know what an orthogonal region is, and I don't have time to learn right now, so I'm probably missing a lot.
I don't think you are missing that much. I hope I have managed to convince you that everything works fine for state machines without orthogonal regions. Orthogonal regions do complicate the matter but not by a lot.
If you're still convinced you have everything right,
Yes, now I believe even more so than before our discussion. Regards, Andreas

"Andreas Huber" <ah2003@gmx.net> writes:
Is that superstate always a reasonable place to end up upon failure?
Well, you don't excatly end up there, right? The superstate (more accurately the outermost unstable state) is only a "container" for reaction(s) to the exception_thrown event. If such a reaction is found and executed it *must* make a transition to another state (sort of a safe haven) or terminate the state machine itself.
What does "terminate the state machine" mean?
Only then are we stable again. To answer your question: Yes, I believe so.
FWIW, I don't know what an orthogonal region is, and I don't have time to learn right now, so I'm probably missing a lot.
I don't think you are missing that much. I hope I have managed to convince you that everything works fine for state machines without orthogonal regions.
I wish you had; I'll just have to take your word for it because I'm out of time for discussion. Your method of handling errors as described above seems (with my very shallow understanding) a bit capricious and hard-to-manage to me, and it seems strange to keep that system while ruling out a chance for strong-guarantee behavior (where an exit action may throw).
Orthogonal regions do complicate the matter but not by a lot.
OK. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

David Abrahams <dave <at> boost-consulting.com> writes:
What does "terminate the state machine" mean?
It means exiting all remaining active states.
I wish you had; I'll just have to take your word for it because I'm out of time for discussion. Your method of handling errors as described above seems (with my very shallow understanding) a bit capricious and hard-to-manage to me, and it seems strange to keep that system while ruling out a chance for strong-guarantee behavior (where an exit action may throw).
Ok, opinion taken. I'll update the rationale section and try to very clearly explain: - How one typically handles errors in traditional FSM frameworks (which don't allow for failing actions) and how this led to the design of the error handling mechanism in boost::fsm, which essentially only automates what you have to do manually otherwise. - Why failing exit actions are a bad idea. I'm looking forward to convince you in the formal review :-). Regards, Andreas

Andreas Huber <ah2003 <at> gmx.net> writes:
David Abrahams <dave <at> boost-consulting.com> writes:
I wish you had; I'll just have to take your word for it because I'm out of time for discussion. Your method of handling errors as described above seems (with my very shallow understanding) a bit capricious and hard-to-manage to me, and it seems strange to keep that system while ruling out a chance for strong-guarantee behavior (where an exit action may throw).
Ok, opinion taken. I'll update the rationale section and try to very clearly explain: - How one typically handles errors in traditional FSM frameworks (which
don't
allow for failing actions) and how this led to the design of the error handling mechanism in boost::fsm, which essentially only automates what you have to do manually otherwise. - Why failing exit actions are a bad idea.
I'm looking forward to convince you in the formal review .
Maybe I'm just being thick, but the whole idea of a failing exit action seems rather odd to me. This has nothing to do with whether the exit action is a destructor or not. The state is trying to take a transition, it basically has done so, the transition action has run, and - whammo - we fail and want to get back into the state being exited? Why? So we can fail to exit it on the next event? Alternatively, we want to go to a new state because of this failure - not to the one we have already launched into the transition for - once again - this seems ugly, we have now turned the failing exit action into some sort of guard. If that is what it is supposed to be, why not just write it as one? I think it is important to note that it is perfectly possible (afaik) to implement an arbitrary FSM without using entry or exit actions at all. It doesn't seem like a bad idea to reserve these for RAII use, and do all other work in actions. On a vaguely related note - I have a question about in-state transitions. There is an example of a custom reaction to do this. However, there is no provision for an action associated with this transition. Obviously it is possible to write the action (or call it) as part of the custom reaction, but this seems irregular for a library that is trying to map directly from/to UML. My eyes glazed over a bit trying to follow the transition handling code to add some sort of an in_state_transition<> but the concept seems trivial enough. Having this facility means that the same action can be used for an in-state transition as for other transitions, which seems only reasonable, and the similar declaration should make developing/modifying a state's transitions easier. For that matter, I suppose the transition could actually specialise for the particular case of a transition back to the source state, and no special declaration would be needed? Regards Darryl.

----- Original Message ----- From: "Darryl Green" <darryl.green@unitab.com.au> [...]
Maybe I'm just being thick, but the whole idea of a failing exit action
seems
rather odd to me. This has nothing to do with whether the exit action is a destructor or not.
The same could be said about entry action. I've just finished a project with a Moore type state machine. If the entry or exit action throws, the state machine is in undefined state... unless you define the throw event as a legitimate event which causes a transition. However I don't see how this can be done generically. In our project, we decided: 1. The state machine *framework* never catches user exceptions. 2. The framework doesn't have a concept of failed actions at all. Any action has to be completed. In this model, any user exception is sent upstairs and it is up to higher intelligence to decide whether to trigger the stack unwinding or do something else. All fault situations during an action are *expected* and it is a responsibility of this action to generate an appropriate event that will cause a transition to a fault handling state or stop the process. In other words, failed actions are part of the state machine design. Eugene

E. Gladyshev <eegg <at> comcast.net> writes:
If the entry or exit action throws, the state machine is in undefined state... unless you define the throw event as a legitimate event which causes a transition. However I don't see how this can be done generically.
boost::fsm does exactly this and in a generic fashion.
In our project, we decided: 1. The state machine *framework* never catches user exceptions. 2. The framework doesn't have a concept of failed actions at all. Any action has to be completed.
This is the traditional way of dealing with failures in state machines. It works, but it is cumbersome.
All fault situations during an action are *expected* and it is a responsibility of this action to generate an appropriate event that will cause a transition to a fault handling state or stop the process.
Why not let the exception slip out of the action and let the state machine framework: 1. catch the exception 2. generate an appropriate event 3. dispatch that event to the appropriate state (see docs for details) 4. check that the event has indeed been processed and that the machine is back in a stable state ? Regards, Andreas

--- Andreas Huber <ah2003@gmx.net> wrote:
E. Gladyshev <eegg <at> comcast.net> writes:
If the entry or exit action throws, the state machine is in undefined state... unless you define the throw event as a legitimate event which causes a transition. However I don't see how this can be done generically.
boost::fsm does exactly this and in a generic fashion.
Does it mean that the state machine framework allows me to specify what event it will generate for a particular exception type? If so does it mean that all unspecified exceptions won't be caught by the framework?
In our project, we decided: 1. The state machine *framework* never catches user exceptions. 2. The framework doesn't have a concept of failed actions at all. Any action has to be completed.
This is the traditional way of dealing with failures in state machines. It works, but it is cumbersome.
Why is it cumbersome?
All fault situations during an action are *expected* and it is a responsibility of this action to generate an appropriate event that will cause a transition to a fault handling state or stop the process.
Why not let the exception slip out of the action and let the state machine framework: 1. catch the exception 2. generate an appropriate event 3. dispatch that event to the appropriate state (see docs for details) 4. check that the event has indeed been processed and that the machine is back in a stable state
If the framework knows all possible exception types and appropriate events associated with them, then this should work just fine. The only problem I have is #4. If all exceptions are expected, why do you need any special checks that the event has indeed been processed. In this case, any exception is just a normal event (just like any other event) and the state machine is never in an unstable state. What do you mean exactly by stable/unstable state? It sounds kind of disturbing to me when a generic state machine framework defines some sort of unstable states on its own. Eugene __________________________________ Do you Yahoo!? Friends. Fun. Try the all-new Yahoo! Messenger. http://messenger.yahoo.com/

E. Gladyshev <egladysh <at> yahoo.com> writes:
boost::fsm does exactly this and in a generic fashion.
Does it mean that the state machine framework allows me to specify what event it will generate for a particular exception type?
Yes, you can do that if you want (it is a per state machine policy). The default is to always dispatch an exception_thrown event.
If so does it mean that all unspecified exceptions won't be caught by the framework?
Yes, you can do that. The default is to catch all exceptions.
This is the traditional way of dealing with failures in state machines. It works, but it is cumbersome.
Why is it cumbersome?
Because you have to write lots boiler-plate code yourself (catch the exception and post the error event in a lot of actions). The framework can automate this for you.
Why not let the exception slip out of the action and let the state machine framework: 1. catch the exception 2. generate an appropriate event 3. dispatch that event to the appropriate state (see docs for details) 4. check that the event has indeed been processed and that the machine is back in a stable state
If the framework knows all possible exception types and appropriate events associated with them, then this should work just fine.
It doesn't typically have to. I think the default behavior is ok for most projects. See http://tinyurl.com/2uzs5, Discriminating exceptions.
The only problem I have is #4. If all exceptions are expected, why do you need any special checks that the event has indeed been processed. In this case, any exception is just a normal event (just like any other event) and the state machine is never in an unstable state. What do you mean exactly by stable/unstable state?
See http://tinyurl.com/2bjjw, Unstable state machine
It sounds kind of disturbing to me when a generic state machine framework defines some sort of unstable states on its own.
I don't think so, the behavior is clearly defined and just automates what you'd do manually anyway. Regards, Andreas

----- Original Message ----- From: "Andreas Huber" <ah2003@gmx.net> To: <boost@lists.boost.org> Sent: Thursday, May 27, 2004 9:51 AM Subject: [boost] Re: [prereview request][fsm] [...]
Yes, you can do that if you want (it is a per state machine policy). The default is to always dispatch an exception_thrown event.
Interesting solution! catch { try { throw; } catch( type1 ) { ... } } never thought about it. This is why I didn't know how to make it generic. It makes sense now.
See http://tinyurl.com/2bjjw, Unstable state machine
It sounds kind of disturbing to me when a generic state machine framework defines some sort of unstable states on its own.
I don't think so, the behavior is clearly defined and just automates what you'd do manually anyway.
As for unstable states. Does this idiom mean that any exception event handling state must be considered as an implicit *inner* state of any other state that throws? So that *only* the exception handling states can be considered as *most inner* states. I think that one of the most important goals of the state machines design is to achieve a completely deterministic behavior. Doesn't your default exception handling idiom compromise this objective? However as soon as you can customize or disable the default exception handling. I am fine with that. Eugene

----- Original Message ----- From: "Andreas Huber" <ah2003@gmx.net> To: <boost@lists.boost.org> Sent: Thursday, May 27, 2004 9:51 AM Subject: [boost] Re: [prereview request][fsm]
[...]
Yes, you can do that if you want (it is a per state machine policy). The default is to always dispatch an exception_thrown event.
Interesting solution! catch { try { throw; } catch( type1 ) { ... } }
never thought about it. This is why I didn't know how to make it generic. It makes sense now.
I've written code like that. It lets you centralize a bunch of exception handling: try { ...code that throws } catch ( ... ) { HandleExceptions (); } void HandleExceptions () { try { throw; } catch ( type1 ) {} catch ( type2 ) {} catch ( type3 ) {} and so on. } -- -- Marshall Marshall Clow Idio Software <mailto:marshall@idio.com> I want a machine that thinks I'm more important than it is, and acts like it. -- Eric Herrmann

E. Gladyshev wrote:
As for unstable states. Does this idiom mean that any exception event handling state must be considered as an implicit *inner* state of any other state that throws? So that *only* the exception handling states can be considered as *most inner* states.
No. Please have a look at http://tinyurl.com/2uzs5 (the whole section on Exception Handling) again. I think it clearly explains how it all works.
I think that one of the most important goals of the state machines design is to achieve a completely deterministic behavior.
Yes, this is important for me also.
Doesn't your default exception handling idiom compromise this objective?
Do you think so because finding a handler for an exception might take non-deterministic time? That is true on some platforms. On others you *can* establish upper limits for finding a handler. I chose the default this way because I believe that a majority of the users will not have deterministic reaction time requirements. In real-time projects you often disable exceptions anyway so the problem won't arise. I will add a remark that real-time projects should consider not using exceptions or check that the compilers exception handling implementation is deterministic. Regards, Andreas

"Andreas Huber" <ah2003@gmx.net> wrote in message news:c95sjg$a4o$1@sea.gmane.org...
Why not let the exception slip out of the action and let the state machine framework: 1. catch the exception 2. generate an appropriate event 3. dispatch that event to the appropriate state (see docs for details) 4. check that the event has indeed been processed and that the machine is back in a stable state
[...]
No. Please have a look at http://tinyurl.com/2uzs5 (the whole section on Exception Handling) again. I think it clearly explains how it all works.
It does. The reason I asked is that in #4, you are saing that an exception renders the state unstable (if I understood it right). The definition of unstable state is "A state is unstable from the moment when it has been entered until just before its last direct inner state is entered. Once a state is stable it can only become unstable again by being the innermost common outer state in a transition." So for the state to be unstable there must be an inner state. So if an *innermost* state action generates an exception, how can it become unstable?
I think that one of the most important goals of the state machines design is to achieve a completely deterministic behavior.
Yes, this is important for me also.
Doesn't your default exception handling idiom compromise this objective?
Do you think so because finding a handler for an exception might take non-deterministic time?
I meant a behavioral determinism (not temporal) where all states and transition are *explicitly* defined by the state machine designer. For instance if the state machine is in some state I can tell exactly what events might have caused it and what state the client is in and what is the next possible state may be. My concern was is that if the framework is adding some *implicit* transitions upon exceptions the above explicit determinism is gone? Eugene

E. Gladyshev wrote:
It does. The reason I asked is that in #4, you are saing that an exception renders the state unstable (if I understood it right). The definition of unstable state is
"A state is unstable from the moment when it has been entered until just before its last direct inner state is entered. Once a state is stable it can only become unstable again by being the innermost common outer state in a transition."
So for the state to be unstable there must be an inner state.
No, the term is "innermost common outer state", not "innermost state". The innermost common outer state is only defined during a transition and is exactly the same as the LCA, see definitions.html.
I meant a behavioral determinism (not temporal) where all states and transition are *explicitly* defined by the state machine designer.
This still applies in boost::fsm. If you don't define any reactions for the exception_thrown event, your state machine will simply terminate when an exception is thrown. If you want to react to such exceptions you must explicitly define exception_thrown reactions. What makes you think otherwise?
For instance if the state machine is in some state I can tell exactly what events might have caused it and what state the client is in and what is the next possible state may be. My concern was is that if the framework is adding some *implicit* transitions upon exceptions the above explicit determinism is gone?
Well, since the state machine is terminated when you fail to handle the exception_thrown event, you could argue that the exception handling system does add one reaction (the termination). Is that what you are referring to? Regards, Andreas

----- Original Message ----- From: "Andreas Huber" <ah2003@gmx.net> To: <boost@lists.boost.org> Sent: Thursday, May 27, 2004 11:55 PM Subject: [boost] Re: Re: Re: [prereview request][fsm]
E. Gladyshev wrote:
It does. The reason I asked is that in #4, you are saing that an exception renders the state unstable (if I understood it right). The definition of unstable state is
"A state is unstable from the moment when it has been entered until just before its last direct inner state is entered. Once a state is stable it can only become unstable again by being the innermost common outer state in a transition."
So for the state to be unstable there must be an inner state.
No, the term is "innermost common outer state", not "innermost state". The innermost common outer state is only defined during a transition and is exactly the same as the LCA, see definitions.html.
The definition of "innermost common outer state" is "The innermost common outer state of two states is the first direct or indirect outer state that both states have in common". It doesn't say anything about transitions? In your watch sample, is the Active state is innermost common outer state of the Stopped and Running states? Are the Stopped and Running states are "innermost" states? [...]
This still applies in boost::fsm. If you don't define any reactions for
the
exception_thrown event, your state machine will simply terminate when an exception is thrown. If you want to react to such exceptions you must explicitly define exception_thrown reactions. What makes you think otherwise?
I understand that. My concernt is that your exception handing model has this internal if/else mechanism. "If the state machine is stable when the exception is thrown, the state that caused the exception is first tried for a reaction. Otherwise the outermost unstable state is first tried for a reaction." How do I incorporate this if/else behavior in my traditional state machine design process? For instance if I add an exception_thrown reaction to a state, how will I know whether this reaction is called as the result of an exception that occured in this state or in one of its inner states? How can I tell whether it is a failed react function failed entry action of failed transition?
For instance if the state machine is in some state I can tell exactly what events might have caused it and what state the client is in and what is the next possible state may be. My concern was is that if the framework is adding some *implicit* transitions upon exceptions the above explicit determinism is gone?
Well, since the state machine is terminated when you fail to handle the exception_thrown event, you could argue that the exception handling system does add one reaction (the termination). Is that what you are referring to?
Yes, plus those implicit transitions that depend on where the exception is thrown (react/entry action/transition)? It seems to me that with your exception handling process, the well established state machine design techniques will need to be revisited. I am not saying that it is a bad thing, it just needs some theoretical work. Like you mentioned before, all the FSM theories aren't addressing the exception handling stuff. You are suggesting a practical solution but a good theory is absent. Best, Eugene

E. Gladyshev <eegg <at> comcast.net> writes:
No, the term is "innermost common outer state", not "innermost state". The innermost common outer state is only defined during a transition and is exactly the same as the LCA, see definitions.html.
The definition of "innermost common outer state" is "The innermost common outer state of two states is the first direct or indirect outer state that both states have in common".
It doesn't say anything about transitions?
Sorry for the confusion. Yes, it is defined for any two states in your state chart. What I wanted to say is that it has little meaning outside of transitions.
In your watch sample, is the Active state is innermost common outer state of the Stopped and Running states?
Yes.
Are the Stopped and Running states are "innermost" states?
Yes.
My concernt is that your exception handing model has this internal if/else mechanism.
"If the state machine is stable when the exception is thrown, the state that caused the exception is first tried for a reaction. Otherwise the outermost unstable state is first tried for a reaction."
How do I incorporate this if/else behavior in my traditional state machine design process?
The idea behind this if/else mechanism is that the event resulting from the exception is always sent to exactly the state from where a full recovery is possible. If an in-state reaction throws, then obviously the machine is stable and the exception_event can be processed by the state that caused the exception. For failing entry actions this is impossible so from the point where the exception actually happened the dispatcher moves outward until it finds a state that *could* bring the state machine back into a stable state (by a transitioning to an other state or by terminating).
For instance if I add an exception_thrown reaction to a state, how will I know whether this reaction is called as the result of an exception that occured in this state or in one of its inner states?
How can I tell whether it is a failed react function failed entry action of failed transition?
I assume you mean that you cannot distinguish between failing in-state reactions of the outer state, failing entry actions of the inner states and failing transition actions between the inner states. That is true. I have never though about this. Is that a problem for you?
Well, since the state machine is terminated when you fail to handle the exception_thrown event, you could argue that the exception handling system does add one reaction (the termination). Is that what you are referring to?
Yes, plus those implicit transitions that depend on where the exception is thrown (react/entry action/transition)?
Those aren't transitions, are they? The *reaction search* algorithm just acts differently in the face of failure. If it wouldn't, it is possible that your state machine is left in an invalid state when you make a mistake while handling the exception_thrown event.
It seems to me that with your exception handling process, the well established state machine design techniques will need to be revisited. I am not saying that it is a bad thing, it just needs some theoretical work.
I believe that the current way of handling exceptions in boost::fsm is not far away from what you can possibly hope for. However, I agree that I'll need to justify it much more thoroughly. As I mentioned before I will update the Rationale (as soon as this thread has died down).
Like you mentioned before, all the FSM theories aren't addressing the exception handling stuff. You are suggesting a practical solution but a good theory is absent.
True. I hope some of the rationale I will write can establish that theory. Regards, Andreas

--- Andreas Huber <ah2003@gmx.net> wrote:
E. Gladyshev <eegg <at> comcast.net> writes: [...]
The idea behind this if/else mechanism is that the event resulting from the exception is always sent to exactly the state from where a full recovery is possible. If an in-state reaction throws, then obviously the machine is stable and the exception_event can be processed by the state that caused the exception. For failing entry actions this is impossible so from the point where the exception actually happened the dispatcher moves outward until it finds a state that *could* bring the state machine back into a stable state (by a transitioning to an other state or by terminating).
Right. So the process of bringing the state machine back into a stable state is not explicitly defined by the designer. Right? This is what I meant when I said that in this case, the determinism of the traditional state machines is gone.
For instance if I add an exception_thrown reaction to a state, how will I know whether this reaction is called as the result of an exception that occured in this state or in one of its inner states?
How can I tell whether it is a failed react function failed entry action of failed transition?
I assume you mean that you cannot distinguish between failing in-state reactions of the outer state, failing entry actions of the inner states and failing transition actions between the inner states. That is true.
That is exactly what I mean.
I have never though about this. Is that a problem for you?
I don't know yet. Like I said I am not yet sure how to use your exception handling in practice but it seems like it is changing the traditional approach to the state machine design process. One suggestion that I have is this. Perhaps before the suggested exception handling is fully researched, boost::fsm should follow the well established standards by default and have the new exception handling as a predefined policy that user would have to select explicitly and not the other way around?
Well, since the state machine is terminated when you fail to handle
the
exception_thrown event, you could argue that the exception handling system does add one reaction (the termination). Is that what you are referring to?
Yes, plus those implicit transitions that depend on where the exception is thrown (react/entry action/transition)?
Those aren't transitions, are they?
Right. Sorry. I should have said the reaction selection process. [...]
I believe that the current way of handling exceptions in boost::fsm is not
far
away from what you can possibly hope for.
No, it is not far. I personally cannot think of anything better.
However, I agree that I'll need to justify it much more thoroughly. As I mentioned before I will update the Rationale (as soon as this thread has died down).
Like you mentioned before, all the FSM theories aren't addressing the exception handling stuff. You are suggesting a practical solution but a good theory is absent.
True. I hope some of the rationale I will write can establish that theory.
It'd be great. I believe that it should be more like an academic research. Perhaps you might consider producing an academic paper on this subject... Best, Eugene

E. Gladyshev wrote:
One suggestion that I have is this. Perhaps before the suggested exception handling is fully researched, boost::fsm should follow the well established standards by default and have the new exception handling as a predefined policy that user would have to select explicitly and not the other way around?
Yes, I have come to that conclusion also. And I don't need to change much, just the default for the ExceptionTranslator.
It'd be great. I believe that it should be more like an academic research. Perhaps you might consider producing an academic paper on this subject...
I don't have a clue how to do that (especially not how/where to publish) because I've never studied in an university. But I will write a paper. Regards, Andreas

From: "Andreas Huber" <ah2003@gmx.net> To: <boost@lists.boost.org> Sent: Thursday, May 27, 2004 9:51 AM Subject: [boost] Re: [prereview request][fsm]
E. Gladyshev <egladysh <at> yahoo.com> writes:
boost::fsm does exactly this and in a generic fashion.
Does it mean that the state machine framework allows me to specify what event it will generate for a particular exception type?
Yes, you can do that if you want (it is a per state machine policy). The default is to always dispatch an exception_thrown event.
After thinking about your solution a bit more, I don't think that it is generic enough. Your solution try{...} catch(...) { try { throw; } catch( type1 ) { ... } } is very different from try{...} catch( type1 ) {...} One of the differences is that in the first case, the stack unwinding will be triggered for any exception. In the second case, it is not necessarily the case. IMO, a truly generic solution should work like the second case. So I seem to be back to my previous conclusion: there is no way to properly discriminate exception types in boost::fsm. Eugene

From: "E. Gladyshev" <eegg@comcast.net>
Your solution
try{...} catch(...) { try { throw; } catch( type1 ) { ... } }
is very different from
try{...} catch( type1 ) {...}
One of the differences is that in the first case, the stack unwinding will be triggered for any exception. In the second case, it is not necessarily the case.
Not at all. From 15.2/1: As control passes from a throwexpression to a handler, destructors are invoked for all automatic objects constructed since the try block was entered. In order to enter any handler, stack unwinding occurs. In the first case, stack unwinding occurs immediately because "catch (...)" matches all exceptions. In the second case, only if there wasn't a handler for a particular exception would stack unwinding be delayed while the search continued for another handler (including unexpected()). IOW, stack unwinding will occur for both forms, it's just a question as to where the handler will be found. Let me make this more concrete. Assume the only expected excceptions are type1, type2, and type3. Then this: try { ... } catch (...) { // stack unwinding has occurred by the time we get here // this is actually in a separate function, of course try { throw; } catch (type1 const &) { ... } catch (type2 const &) { ... } catch (type3 const &) { ... } catch (...) { throw; } } does exactly the same thing, wrt stack unwinding, as this: try { ... } catch (type1 const &) { // stack unwinding has occurred by the time we get here ... } catch (type2 const &) { // stack unwinding has occurred by the time we get here ... } catch (type3 const &) { // stack unwinding has occurred by the time we get here ... } If the function called by the first form handles a different set of exceptions, then the only change is where the handler is for the active exception. Stack unwinding will have occurred by the time that handler is entered. If both forms provide the same set of handlers, then the only difference is where the handler is found. Given that the former approach captures common exception handling in a single function that rethrows the exception, it is merely shorthand for having to write the same handlers repeatedly. -- Rob Stewart stewart@sig.com Software Engineer http://www.sig.com Susquehanna International Group, LLP using std::disclaimer;

--- Rob Stewart <stewart@sig.com> wrote:
From: "E. Gladyshev" <eegg@comcast.net>
try{...} catch(...) { try { throw; } catch( type1 ) { ... } }
is very different from
try{...} catch( type1 ) {...}
[...]
IOW, stack unwinding will occur for both forms, it's just a question as to where the handler will be found.
15.5.1/2 Note: in the situation where no matching handler is found, it is implementation-defined whether or not the stack is unwound before terminate() is called. In all other situations, the stack shall not be unwound before terminate() is called Your claim that the stack unwinding will occur in both cases is based on an implementation-defined behavior. In fact, all implementations that I know about allows you disable the stack unwinding for unhandled exceptions. So it is not strictly portable nor generic. BTW: for the same matter, the famous RAII idiom isn't a portable idiom either. Best, Eugene

From: "E. Gladyshev" <eegg@comcast.net>
--- Rob Stewart <stewart@sig.com> wrote:
From: "E. Gladyshev" <eegg@comcast.net>
try{...} catch(...) { try { throw; } catch( type1 ) { ... } }
is very different from
try{...} catch( type1 ) {...}
[...]
IOW, stack unwinding will occur for both forms, it's just a question as to where the handler will be found.
15.5.1/2 Note: in the situation where no matching handler is found, it is implementation-defined whether or not the stack is unwound before terminate() is called. In all other situations, the stack shall not be unwound before terminate() is called
Your claim that the stack unwinding will occur in both cases is based on an implementation-defined behavior. In fact, all implementations that I know about allows you disable the stack unwinding for unhandled exceptions. So it is not strictly portable nor generic.
That's non-normative text, but it turns out that 15.3/9 covers it: If no matching handler is found in a program, the function terminate() is called; whether or not the stack is unwound before this call to terminate() is implementationdefined (15.5.1). So, OK, there is that slight difference, but why would you care? Why would this -- no stack unwinding for an unhandled exception -- matter in boost::fsm? Put another way, what benefit does this provide and does it outweight the benefits of the approach being used now? -- Rob Stewart stewart@sig.com Software Engineer http://www.sig.com Susquehanna International Group, LLP using std::disclaimer;

----- Original Message ----- From: "Rob Stewart" <stewart@sig.com>
From: "E. Gladyshev" <eegg@comcast.net>
--- Rob Stewart <stewart@sig.com> wrote:
From: "E. Gladyshev" <eegg@comcast.net>
try{...} catch(...) { try { throw; } catch( type1 ) { ... } }
is very different from
try{...} catch( type1 ) {...}
[...]
IOW, stack unwinding will occur for both forms, it's just a question as to where the handler will be found.
15.5.1/2 Note: in the situation where no matching handler is found, it is implementation-defined whether or not the stack is unwound before terminate() is called. In all other situations, the stack shall not be unwound before terminate() is called
Your claim that the stack unwinding will occur in both cases is based on an implementation-defined behavior. In fact, all implementations that I know about allows you disable the stack unwinding for unhandled exceptions. So it is not strictly portable nor generic.
That's non-normative text, but it turns out that 15.3/9 covers it:
I don't understant what you mean by "15.3/9 covers it"?
If no matching handler is found in a program, the function terminate() is called; whether or not the stack is unwound before this call to terminate() is implementationdefined (15.5.1).
So, OK, there is that slight difference, but why would you care?
I don't think that it is a slight difference. If you make an assumption that all unhandled exceptions will eventually trigger stack unwinding, but a specific implementation doesn't do that, it is huge difference. Such an implementation (legal implementation, btw) may break a bunch of RAII based designs.
Why would this -- no stack unwinding for an unhandled exception -- matter in boost::fsm?
Because boost::fsm doesn't allow me to discriminate exception types and keep the stack from unwinding (for unhandled exceptions) at the same time.
Put another way, what benefit does this provide and does it outweight the benefits of the approach being used now?
Sorry, what outweighs what? Eugene

From: "E. Gladyshev" <eegg@comcast.net>
From: "Rob Stewart" <stewart@sig.com>
15.5.1/2 Note: in the situation where no matching handler is found, it is implementation-defined whether
or
not the stack is unwound before terminate() is called. In all
other
situations, the stack shall not be unwound before terminate()
is
called
Your claim that the stack unwinding will occur in both cases is based on an implementation-defined behavior. In fact, all implementations that I know about allows you disable the stack unwinding for unhandled exceptions. So it is not strictly portable nor generic.
That's non-normative text, but it turns out that 15.3/9 covers it:
I don't understant what you mean by "15.3/9 covers it"?
If no matching handler is found in a program, the function terminate() is called; whether or not the stack is unwound before this call to terminate() is implementationdefined (15.5.1).
15.3/9 is normative text and, as quoted here, clearly states that stack unwinding may not be done before calling terminate().
So, OK, there is that slight difference, but why would you care?
I don't think that it is a slight difference. If you make an assumption that all unhandled exceptions will eventually trigger stack unwinding, but a specific implementation doesn't do that, it is huge difference.
Why would you want the non-deterministic behavior?
Such an implementation (legal implementation, btw) may break a bunch of RAII based designs.
Well, that may be a problem in the specification of the language that should be addressed. After all, RAII is generally the preferred solution for writing exception tolerant code. Perhaps Dave Abrahams can weigh in on this.
Why would this -- no stack unwinding for an unhandled exception -- matter in boost::fsm?
Because boost::fsm doesn't allow me to discriminate exception types and keep the stack from unwinding (for unhandled exceptions) at the same time.
Why would you want terminate() to be called without stack unwinding on those platforms that do it? What does that get you? I would think that boost::fsm's behavior, being more deterministic, would be preferable.
Put another way, what benefit does this provide and does it outweight the benefits of the approach being ^^^^^^^^^ Man I've got to stop my brain from adding that extra "t!"
used now?
Sorry, what outweighs what?
Does not unwinding the stack when terminate() is called, on whatever platforms do that, gain you something that outweighs the determinism and reuse of the boost::fsm approach? -- Rob Stewart stewart@sig.com Software Engineer http://www.sig.com Susquehanna International Group, LLP using std::disclaimer;

From: "Rob Stewart" <stewart@sig.com>
From: "E. Gladyshev" <eegg@comcast.net>
From: "Rob Stewart" <stewart@sig.com>
[...]
So, OK, there is that slight difference, but why would you care?
I don't think that it is a slight difference. If you make an assumption that all unhandled exceptions will eventually trigger stack unwinding, but a specific implementation doesn't do that, it is huge difference.
Why would you want the non-deterministic behavior?
Whether I want it or not doesn't change anything. All I am saying that it is implementation dependent and it is a big difference. [...]
Why would you want terminate() to be called without stack unwinding on those platforms that do it? What does that get you? I would think that boost::fsm's behavior, being more deterministic, would be preferable.
There could be many reasons, just to list few of them. 1. Some platforms pose non-C++ exceptions as normal C++ exception. You don't always want those platfrom specific exceptions trigger a whole bunch of stuff in your program. 2. Your design assumes that all possible exceptions are known and you handle them. So any unhandled exception is a bug. It is not safe to trigger stack unwinding in a buggy environment. These reasons could be of a particular importance in control applications where state machines are a must. But again whether I want it or not, the Standard allows implementation where the stack unwinding (for unhandled exceptions) never happens. [...]
Does not unwinding the stack when terminate() is called, on whatever platforms do that, gain you something that outweighs the determinism and reuse of the boost::fsm approach?
Please see above. Eugene

From: "E. Gladyshev" <eegg@comcast.net>
From: "Rob Stewart" <stewart@sig.com>
From: "E. Gladyshev" <eegg@comcast.net>
From: "Rob Stewart" <stewart@sig.com>
Why would you want terminate() to be called without stack unwinding on those platforms that do it? What does that get you? I would think that boost::fsm's behavior, being more deterministic, would be preferable.
There could be many reasons, just to list few of them. 1. Some platforms pose non-C++ exceptions as normal C++ exception. You don't always want those platfrom specific exceptions trigger a whole bunch of stuff in your program.
The only platform I know of that does that is Windows/MSVC, and IIRC, you can handle structured exceptions specially. Perhaps it means that boost::fsm needs to provide a means to install an exception handler for those exceptions not already recognized by the library: try { throw; } catch (...) { boost::fsm::detail::handle_exception(); } void boost::fsm::detail::handle_exception() { try { throw; } catch (type1 const &) { ... }; catch (type2 const &) { ... }; catch (type3 const &) { ... }; catch (...) { user_handler(); } } where user_handler is a pointer to function that's initialized to std::unexpected() and can be overridden by the library user. It may also be that SEH can be configured to intervene before a C++ exception is generated by the RTL so those never become C++ exceptions that can muck up the works.
2. Your design assumes that all possible exceptions are known and you handle them. So any unhandled exception is a bug.
Not quite. What I was showing would rethrow any unknown exception, leaving to clients to determine how to handle it.
It is not safe to trigger stack unwinding in a buggy environment.
If you say so. Every application and platform with which I work is buggy and yet I manage to use exceptions and stack unwinding works. This is just another case of things not being as pretty as one might like but that doesn't mean stack unwinding is faulty.
These reasons could be of a particular importance in control applications where state machines are a must.
But again whether I want it or not, the Standard allows implementation where the stack unwinding (for unhandled exceptions) never happens.
That doesn't mean that libraries must. -- Rob Stewart stewart@sig.com Software Engineer http://www.sig.com Susquehanna International Group, LLP using std::disclaimer;

I apologize for post this again. It is a copy of my previous post that somehow ended up in the wrong branch. Eugene --- Rob Stewart <stewart@sig.com> wrote:
From: "E. Gladyshev" <eegg@comcast.net>
From: "Rob Stewart" <stewart@sig.com>
From: "E. Gladyshev" <eegg@comcast.net>
From: "Rob Stewart" <stewart@sig.com>
[...]
1. Some platforms pose non-C++ exceptions as normal C++ exception. You don't always want those platfrom specific exceptions trigger a whole bunch of stuff in your program.
The only platform I know of that does that is Windows/MSVC
It makes it pretty common, doesn't it. :)
you can handle structured exceptions specially. Perhaps it means that boost::fsm needs to provide a means to install an exception handler for those exceptions not already recognized by the library:
try { throw; } catch (...) { boost::fsm::detail::handle_exception(); }
void boost::fsm::detail::handle_exception() { try { throw; } catch (type1 const &) { ... }; catch (type2 const &) { ... }; catch (type3 const &) { ... }; catch (...) { user_handler(); } } where user_handler is a pointer to function that's initialized to std::unexpected() and can be overridden by the library user.
Adding user_handler() doesn't stop stack unwinding. I think you are missing the point. The issue is not in how to process SEH exceptions (or any other unhandled exception) the issue is in how not to trigger stack unwinding (for all exceptions) and still be able discriminate user exception types generically in FSM. Your solution doesn't solve this problem.
It may also be that SEH can be configured to intervene before a C++ exception is generated by the RTL so those never become C++ exceptions that can muck up the works.
It is not trivial... but it is out of the FSM topic.
2. Your design assumes that all possible exceptions are known and you handle them. So any unhandled exception is a bug.
Not quite. What I was showing would rethrow any unknown exception, leaving to clients to determine how to handle it.
It is not safe to trigger stack unwinding in a buggy environment.
If you say so. Every application and platform with which I work is buggy and yet I manage to use exceptions and stack unwinding works.
What do you mean by "stack unwinding works". Do you mean that even if the exception is caused by a bug, stack unwinding didn't make things worse or less safer? Well, it's a long shot... [...]
This is just another case of things not being as pretty as one might like but that doesn't mean stack unwinding is faulty.
Sorry, who said that stack unwinding is faulty? Eugene

Rob Stewart <stewart@sig.com> writes:
Why would this -- no stack unwinding for an unhandled exception -- matter in boost::fsm?
Because boost::fsm doesn't allow me to discriminate exception types and keep the stack from unwinding (for unhandled exceptions) at the same time.
Why would you want terminate() to be called without stack unwinding on those platforms that do it? What does that get you?
It doesn't destroy program state information when the termination corresponds to a program bug. But then, you probably want abort() and not terminate(). You want a core dump for sure. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

E. Gladyshev wrote:
After thinking about your solution a bit more, I don't think that it is generic enough.
Your solution
try{...} catch(...) { try { throw; } catch( type1 ) { ... } }
is very different from
try{...} catch( type1 ) {...}
One of the differences is that in the first case, the stack unwinding will be triggered for any exception. In the second case, it is not necessarily the case.
This only applies to exceptions that will ultimately end up as unhandled and are never caught an rethrown inbetween, right? For all other exceptions it should not make a difference? Regards, Andreas

----- Original Message ----- From: "Andreas Huber" <ah2003@gmx.net>
E. Gladyshev wrote:
After thinking about your solution a bit more, I don't think that it is generic enough.
Your solution
try{...} catch(...) { try { throw; } catch( type1 ) { ... } }
is very different from
try{...} catch( type1 ) {...}
One of the differences is that in the first case, the stack unwinding will be triggered for any exception. In the second case, it is not necessarily the case.
This only applies to exceptions that will ultimately end up as unhandled and are never caught an rethrown inbetween, right? For all other exceptions it should not make a difference?
Right. So I was arguing that such a solution is not generic enough. I think that fsm should at the very least allow you to disable catch(...) and let unhandled exceptions go unhandled. Is ExceptionTranslator the best place to do that? Eugene

E. Gladyshev wrote:
I think that fsm should at the very least allow you to disable catch(...) and let unhandled exceptions go unhandled. Is ExceptionTranslator the best place to do that?
I think so. I believe the default ExceptionTranslator must not catch any exceptions. For the reasons you stated, only this solution is absolutely safe w.r.t unhandled exceptions. So the library will by default not provide any exception handling support (apart from terminating when an action throws). Regards, Andreas

----- Original Message ----- From: "Andreas Huber" <ah2003@gmx.net> [...]
So the library will by default not provide any exception handling support (apart from terminating when an action throws).
I am not sure I understand it. If there is no any exception handling, how would the library know when an action throws in order to terminate? Eugene

E. Gladyshev wrote:
----- Original Message ----- From: "Andreas Huber" <ah2003@gmx.net>
[...]
So the library will by default not provide any exception handling support (apart from terminating when an action throws).
I am not sure I understand it. If there is no any exception handling, how would the library know when an action throws in order to terminate?
I mean that the library will not attempt to catch any exceptions thrown from actions. Since the state machine could likely be in an unstable state when such an exception is thrown, it must be terminated before the exception is propagated to the client (I'm not doing this with try { ... } catch ( ... ) { terminate(); }but with a scope guard). Regards, Andreas

----- Original Message ----- From: "Andreas Huber" <ah2003@gmx.net>
E. Gladyshev wrote:
----- Original Message ----- From: "Andreas Huber" <ah2003@gmx.net>
[...]
So the library will by default not provide any exception handling support (apart from terminating when an action throws).
I am not sure I understand it. If there is no any exception handling, how would the library know when an action throws in order to terminate?
I mean that the library will not attempt to catch any exceptions thrown from actions. Since the state machine could likely be in an unstable state when such an exception is thrown, it must be terminated before the exception is propagated to the client (I'm not doing this with try { ... } catch ( ... ) { terminate(); }but with a scope guard).
Just to make that I got it right. There is no guarantee that for an unhandled exception, the scope guard will do it. Right? For the scope guard to work, the exception will have to be caught and the stack unwinding triggered. So you cannot just say that if an action throws, the state machine will be terminated. If the stack unwinding never happens, it is never terminated. Right? Eugene

E. Gladyshev wrote:
Just to make that I got it right. There is no guarantee that for an unhandled exception, the scope guard will do it. Right? For the scope guard to work, the exception will have to be caught and the stack unwinding triggered. So you cannot just say that if an action throws, the state machine will be terminated.
True.
If the stack unwinding never happens, it is never terminated. Right?
Exactly. Regards, Andreas

--- Andreas Huber <ah2003@gmx.net> wrote:
E. Gladyshev <eegg <at> comcast.net> writes:
If the entry or exit action throws, the state machine is in undefined state... unless you define the throw event as a legitimate event which causes a transition. However I don't see how this can be done generically.
boost::fsm does exactly this and in a generic fashion.
Does it mean that the state machine framework allows me to specify what event it will generate for a particular exception type? If so does it mean that all unspecified exceptions won't be caught by the framework?
In our project, we decided: 1. The state machine *framework* never catches user exceptions. 2. The framework doesn't have a concept of failed actions at all. Any action has to be completed.
This is the traditional way of dealing with failures in state machines. It works, but it is cumbersome.
Why is it cumbersome?
All fault situations during an action are *expected* and it is a responsibility of this action to generate an appropriate event that will cause a transition to a fault handling state or stop the process.
Why not let the exception slip out of the action and let the state machine framework: 1. catch the exception 2. generate an appropriate event 3. dispatch that event to the appropriate state (see docs for details) 4. check that the event has indeed been processed and that the machine is
back
in a stable state
If the framework knows all possible exception types and appropriate events associated with them, then this should work just fine. The only problem I have is #4. If all exceptions are expected, why do you need any special checks that the event has indeed been processed. In this case, any exception is just a normal event (just like any other event) and the state machine is never in an unstable state. What do you mean exactly by stable/unstable state? It sounds kind of disturbing when a generic state machine framework defines some sort of unstable states on its own. Eugene

Darryl Green <darryl.green@unitab.com.au> writes:
Maybe I'm just being thick, but the whole idea of a failing exit action seems rather odd to me. This has nothing to do with whether the exit action is a destructor or not. The state is trying to take a transition, it basically has done so, the transition action has run
If exit actions happen after transition actions, I agree that it's odd. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

David Abrahams <dave <at> boost-consulting.com> writes:
Darryl Green <darryl.green <at> unitab.com.au> writes:
Maybe I'm just being thick, but the whole idea of a failing exit action
seems
rather odd to me. This has nothing to do with whether the exit action is a destructor or not. The state is trying to take a transition, it basically has done so, the transition action has run
If exit actions happen after transition actions...
They don't. In any transition all exit actions are executed first, then the transition action and finally all entry actions. Regards, Andreas

Andreas Huber <ah2003 <at> gmx.net> writes:
Darryl Green <darryl.green <at> unitab.com.au> writes:
Maybe I'm just being thick If exit actions happen after transition actions... They don't. In any transition all exit actions are executed first, then the
David Abrahams <dave <at> boost-consulting.com> writes: transition action and finally all entry actions.
Ok - make that definitely being thick and failing to rtfm :o) In hindsight, this is clearly the order one would expect for action execution but the idea that a state would "exist" until a new state was entered was too strongly ingrained, and I failed to think this through fully. It seems that this whole business of having an FSM that has meta-states in which no state exists (ok, you call them unstable states and an outer state, or possibly the FSM itself, always exists, but the outer state isn't really part of the simple state machine) is a LEM violation that just makes things complicated. I do think there is utility in having memory and resources associated with/contained by the state. However I think the current state lifetime wrt actions is wrong. To quote Bruce Douglass in Doing Hard Time - 7.3.1 Basic State Semantics ".. a state is a condition of an object during which a set of events is accepted and some actions and activities are executed, and the object can reach some set of states based on the events it accepts." If the lifetime of the state object (afaik in UML a state isn't an object at all) is to model the duration of the state, this implies that the state must exist until the transition is complete (and a new state exists). Anything else is a LEM violation. So, I now see that mapping entry/exit actions to c'tor/d'tor of the state gets one or the other of action invocation order or state duration wrong. I think that with a separation and re-ordering made, the exception handling should be less controversial, though if the previous state is destroyed before the new state is constructed, a throwing constructor will still be interesting to handle. That said, one suspects the "can't acquire resources to enter state" exception is really one which dooms at least the inner fsm, and it isn't particularly likely that the previous state could have dealt with this in any useful way. This change would also allow an action's context be to be the source state - or would there still be some problem with that? Does the context really have to be an outer state of the destination as well as the source? Oh - and I should have said somewhere earlier in all this that it is a very nice library - it really does seem very powerful, and I'm very keen to put it to real use. Regards Darryl.

Darryl Green <darryl.green <at> unitab.com.au> writes:
This change would also allow an action's context be to be the source state - or would there still be some problem with that? Does the context really have to be an outer state of the destination as well as the source?
To answer my own question - If transitions actually occur before destructors run as per my proposal, then the source state itself, and all its outer state contexts exist so the restriction of only common outer contexts being available is removed.

Darryl Green wrote:
It seems that this whole business of having an FSM that has meta-states in which no state exists (ok, you call them unstable states and an outer state, or possibly the FSM itself, always exists, but the outer state isn't really part of the simple state machine) is a LEM violation that just makes things complicated.
What does LEM stand for?
I do think there is utility in having memory and resources associated with/contained by the state. However I think the current state lifetime wrt actions is wrong. To quote Bruce Douglass in Doing Hard Time - 7.3.1 Basic State Semantics ".. a state is a condition of an object during which a set of events is accepted and some actions and activities are executed, and the object can reach some set of states based on the events it accepts."
If the lifetime of the state object (afaik in UML a state isn't an object at all) is to model the duration of the state, this implies that the state must exist until the transition is complete (and a new state exists). Anything else is a LEM violation.
Sorry, I don't follow you here. The sequence of how states are left and entered during a transition is clearly defined. Do you suggest that a state that has been left must somehow still exist? If so, why?
So, I now see that mapping entry/exit actions to c'tor/d'tor of the state gets one or the other of action invocation order or state duration wrong. I think that with a separation and re-ordering made, the exception handling should be less controversial, though if the previous state is destroyed before the new state is constructed, a throwing constructor will still be interesting to handle.
I don't see what that would buy you. Whether you separate state construction/destruction from execution of entry/exit actions or not you will still face the same logical problems (e.g. a failing exit action still gets you in the same trouble, see http://tinyurl.com/32hhc, (Why exit actions must not fail)).
That said, one suspects the "can't acquire resources to enter state" exception is really one which dooms at least the inner fsm,
Right, it does doom the inner FSM, but why should that be a problem?
and it isn't particularly likely that the previous state could have dealt with this in any useful way.
I often find myself needing to handle a failure that happens in a "bunch of states", no matter where the failure actually occurs (failing entry action, failing transition action). In such a situation I put all these states into an outer state and implement an exception_thrown transition from the outer state. This might seem like an arbitrary decision, but more often than not I discover that the "bunch of states" already resides in an outer state for logical reasons, so I just have to implement the exception_thrown transition from there.
This change would also allow an action's context be to be the source state - or would there still be some problem with that? Does the context really have to be an outer state of the destination as well as the source?
Yes, I very much believe so. The UML standard and all the code generators I've seen define/implement it this way (i.e. all exit actions are executed before the transition action is executed). Even if you decouple entry/exit from state ctor/dtor, I don't think it is a good idea to have the transition action potentially read/modify a state whose exit action has already been called. Again, I cannot prove this, this is just my experience. Actually, the UML standard contradicts itself in this regard. In one place it defines that the transition action must be called before the exit actions, and in the other place it defines that the transition action is called after the exit actions. But I'm confident that the former is a mistake.
Oh - and I should have said somewhere earlier in all this that it is a very nice library - it really does seem very powerful, and I'm very keen to put it to real use.
Thanks :-) Regards, Andreas

Andreas Huber <ah2003 <at> gmx.net> writes:
Darryl Green wrote:
It seems that this whole business of having an FSM that has meta-states in which no state exists (ok, you call them unstable states and an outer state, or possibly the FSM itself, always exists, but the outer state isn't really part of the simple state machine) is a LEM violation that just makes things complicated. What does LEM stand for? Law of Excluded Middle. A proposition must be true or false. A FSM is always in exactly one state. A FSM has a finite number of states. If an FSM is not in some state A then it must be in some other state.
However, I've thought about this some more, and if we consider the simple, formal state machine alone, your model doesn't have a problem - because the transition is a transitory thing that makes no reference to the states themselves. The "gap" where the machine is in no state doesn't matter if it indivisible and unable to directly influence the fsm behaviour. The only influence possible on the fsm behaviour is when an exception is thrown during the transition, which destructs (if there is anything left to destruct) the inner fsm, making this behaviour outside what can be modeled directly as a fsm, and this reasoning irrelevant.
Do you suggest that a state that has been left must somehow still exist? If so, why?
I was trying to say that there must be no period during which the FSM is in no state. It depends what the definition of "left" is. Aside from my (misguided I suspect) attempts at a theoretical argument, this is because I would like to utilise the RAII aspects of your state model in a way that requires the transition action to have access to the context of the state "in the process of being left". I'm arguing for the following definition of left: A state is considered left (and hence is destructed) only after exit and transition actions have run. That is, the previous state is destructed immediately before the new state is constructed. For nested states, all exit actions would run, followed by the transition action, followed by destruction of the states being left, followed by construction of the new state(s).
I don't see what that would buy you.
Well it buys access to the state being left from the transition action for a start. In addition, if the exit action isn't the destructor, and it fails, you are still in the original state. A state specific recovery action is possible, using the state's own context. The state destructor still must not fail.
That said, one suspects the "can't acquire resources to enter state" exception is really one which dooms at least the inner fsm,
Right, it does doom the inner FSM, but why should that be a problem?
It isn't. It is just a supporting argument for destructing the state being left no later than immediately prior to destination state construction, and for allowing construction/entry to fail.
I often find myself needing to handle a failure that happens in a "bunch of states", no matter where the failure actually occurs (failing entry action, failing transition action). In such a situation I put all these states into an outer state and implement an exception_thrown transition from the outer state. This might seem like an arbitrary decision, but more often than not I discover that the "bunch of states" already resides in an outer state for logical reasons, so I just have to implement the exception_thrown transition from there.
Ok.
This change would also allow an action's context be to be the source state - or would there still be some problem with that? Does the context really have to be an outer state of the destination as well as the source?
I answered my own question in another post (thread got lost somehow though): If transitions actually occur before destructors run as per my proposal, then the source state itself, and all its outer state contexts exist so the restriction of only common outer contexts being available is removed.
Yes, I very much believe so. The UML standard and all the code generators I've seen define/implement it this way (i.e. all exit actions are executed before the transition action is executed).
I don't read the concepts of context and states with context/resources/state local storage into the UML standard. I think these things are pure extensions, and potentially quite valuable ones. They address practical implementation issues for fsms in C++. I think they are separate, but interact with, the UML concepts of exit, transition and entry actions, which I agree run in that order. My specific concern is the use of the extended functionality to produce states which have/build significant context that (should) only exist until a transition action deals with it. I don't see that using this state context after the exit action has run is a problem. The exit action may well have added to or modified it to make the context complete, not damaged (and in particular not destructed) it. I'm also concerned that the current fsm destruction/exit action implementation model results in an environment where I should avoid using exit actions that have any significant external effects because I don't want/expect them to be run when/if the fsm is destructed. Both the above concerns can be addressed by using custom reactions to implement "pre-exit" actions when/if this turns out to be necessary. I'll have to use the fsm library some more to see if this turns out to be the case regularly enough to make it a significant use case. So, I now believe that the library has the necessary flexibility, and only experience will tell how much and in what direction that flexibility gets used/stretched. I'll keep you posted... Regards Darryl.

Darryl wrote:
Andreas Huber <ah2003 <at> gmx.net> writes:
Darryl Green wrote:
It seems that this whole business of having an FSM that has meta-states in which no state exists (ok, you call them unstable states and an outer state, or possibly the FSM itself, always exists, but the outer state isn't really part of the simple state machine) is a LEM violation that just makes things complicated. What does LEM stand for? Law of Excluded Middle. A proposition must be true or false. A FSM is always in exactly one state. A FSM has a finite number of states. If an FSM is not in some state A then it must be in some other state.
Ok, I must defend Andreas a bit: you are obviously right about the LEM and the single-statedness of the FSM (I am talking about DFA.) BUT, it is crucial to realize that an implementation is, beside a proper abstraction of a single-stated, also contains its own state to emulate that FSM. This is necessarily so. Most of the time the FSM's actual state can be held by an equivalent (actually, identical...) state in the implementation, but there are times when the emulator (read "implementation") have to visit a meta state that is not directly isomorphic to the FSM state. I think that is what Andreas is talking about in his "unstable states" or "outer states." So, in fact, one should, via UML or other means (;-)), describe the (meta) states of the emulating implementation, and create a homomorphism from those states to the (object-level) FSM states. /David

Darryl Green wrote:
A state is considered left (and hence is destructed) only after exit and transition actions have run.
That is, the previous state is destructed immediately before the new state is constructed. For nested states, all exit actions would run, followed by the transition action, followed by destruction of the states being left, followed by construction of the new state(s).
I don't see what that would buy you.
Well it buys access to the state being left from the transition action for a start.
Right, but I don't think it is justified to complicate the interface and the implementation (we'd need to add entry()/exit()) for that. As I've explained in my answer to your other post you can always move such a variables into an outer context that exists during a transition.
In addition, if the exit action isn't the destructor, and it fails, you are still in the original state. A state specific recovery action is possible, using the state's own context. The state destructor still must not fail.
A state-specific recovery function would make error handling much more difficult, as you have no idea what you need to do to bring the state machine back into a stable state. If you e.g. make a transition from such a state, it is not guaranteed that the machine is stable afterwards (see Exception handling in the tutorial).
My specific concern is the use of the extended functionality to produce states which have/build significant context that (should) only exist until a transition action deals with it. I don't see that using this state context after the exit action has run is a problem. The exit action may well have added to or modified it to make the context complete, not damaged (and in particular not destructed) it.
That's true, see above.
I'm also concerned that the current fsm destruction/exit action implementation model results in an environment where I should avoid using exit actions that have any significant external effects because I don't want/expect them to be run when/if the fsm is destructed.
Concern noted. This is actually the central disagreement between me and Dave Abrahams. If you have any real-world examples, I'd be *very* interested to see them.
Both the above concerns can be addressed by using custom reactions to implement "pre-exit" actions when/if this turns out to be necessary.
Yes. It becomes a bit cumbersome when you have multiple transitions originating at the state though.
I'll have to use the fsm library some more to see if this turns out to be the case regularly enough to make it a significant use case.
So, I now believe that the library has the necessary flexibility, and only experience will tell how much and in what direction that flexibility gets used/stretched. I'll keep you posted...
Please do! Regards, Andreas

"Andreas Huber" <ah2003@gmx.net> writes:
I'm also concerned that the current fsm destruction/exit action implementation model results in an environment where I should avoid using exit actions that have any significant external effects because I don't want/expect them to be run when/if the fsm is destructed.
Concern noted. This is actually the central disagreement between me and Dave Abrahams. If you have any real-world examples, I'd be *very* interested to see them.
Here's one I'm pulling out of the air, since I don't have any real-world experience *with FSM exit actions*. I hope my extensive real-world experience with programs in general, state and invariant management, and error handling counts for something. I'm certain I could come up with five more of these given an hour, but if you demand an example that I know exists in some real FSM code, I can't help you. A state X has a local buffer of data to be written to disk upon making a successful transition from X. The presence of that data on disk indicates something important about what's happened in the state machine, and you *don't* want the data on disk unless you have made the transition from X successfully. It would be a bad idea to write that data out if the FSM's controlling process decided to simply discard the FSM when it was in state X. Also, putting the instructions to write the data in a state destructor could require the otherwise-unneccessary quashing of exceptions from the function used to write the data to disk. I said long ago that I was going to stop poking at this one, and didn't, because I had the sense that issues in this area, and the way you were reacting to my poking, might indicate that there were other problems. I don't feel as though much progress has been made, and you seem to feel that trying to convince me of anything by example is futile. So really, now I'm going to stop worrying this issue. May other peoples' posts be more helpful than mine have been! -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

David Abrahams wrote:
I don't feel as though much progress has been made, and you seem to feel that trying to convince me of anything by example is futile.
*No!* I don't think it is futile because of you or anything. I think it is futile because of the nature of the disagreement (and I think I very clearly stated that): <quote> I think it is futile to do so. No matter how many examples I provide in support for A1, you can still argue that in a sufficiently large minority it could be undesirable (and rightly so). Since I think that A1 is almost always (>99%) a good idea, it would be much more economical if you or other people provide examples where it is highly undesirable or downright wrong and workarounds make matters overly complex. It only takes two or three good examples to convince me. </quote> Regards, Andreas

David Abrahams wrote:
"Andreas Huber" <ah2003@gmx.net> writes:
I'm also concerned that the current fsm destruction/exit action implementation model results in an environment where I should avoid using exit actions that have any significant external effects because I don't want/expect them to be run when/if the fsm is destructed.
Concern noted. This is actually the central disagreement between me and Dave Abrahams. If you have any real-world examples, I'd be *very* interested to see them.
Here's one I'm pulling out of the air, since I don't have any real-world experience *with FSM exit actions*. I hope my extensive real-world experience with programs in general, state and invariant management, and error handling counts for something.
It certainly does.
A state X has a local buffer of data to be written to disk upon making a successful transition from X. The presence of that data on disk indicates something important about what's happened in the state machine, and you *don't* want the data on disk unless you have made the transition from X successfully.
Since most of my experience with FSMs comes from working with code generators that do not offer the state local storage feature, I would have written that buffer to the disk in a transition action, which is associated with all transitions leaving that state. With boost::fsm this means to move the buffer to transition context, which admittedly could sometimes look a bit arificial. Darryl Green had a much better idea: He would use a custom reaction to write the buffer to the disk and would afterwards initiate the transition (the whole sequence is therefore an in-state reaction immediately followed by a transition). This way the buffer does not have to be moved out of the state. Yes, this solution has the slight problem that one has to do this for all transitions originating at the state X. Although it can be done without duplicating any of the actual action code, one still has to explictly mention this for each transition in the reaction list of the state. However, and this is the main point here, well-designed real-world composite state machines tend to have very few transitions originating at each state. You often have one, less often two, rarely three and almost never four transitions originating at one particular state. Even if you have more than one reaction, IME it is unlikely that you want to save the buffer when any of them is triggered. My experience with FSMs mainly comes from controlling machinery and I have found the design patterns outlined above to be valid there. While I expect that they are also valid elsewhere, I cannot be entirely sure and that's also one of the reasons why I insisted on an example.
It would be a bad idea to write that data out if the FSM's controlling process decided to simply discard the FSM when it was in state X. Also, putting the instructions to write the data in a state destructor could require the otherwise-unneccessary quashing of exceptions from the function used to write the data to disk.
With the solution mentioned above this is no problem at all.
I said long ago that I was going to stop poking at this one, and didn't, because I had the sense that issues in this area, and the way you were reacting to my poking, might indicate that there were other problems.
I can only guess what exactly you mean with this, but it does sound like an accusation of me somehow evading your questions, see below.
I don't feel as though much progress has been made, and you seem to feel that trying to convince me of anything by example is futile.
I did provide one example many posts ago (the pump in the dishwasher). Also, let me quote what you had to say about this in another post: David Abrahams wrote:
I think a foundation for a decision is not flimsy when the behavior in question has proved to be useful in practice. So far nobody, who seems to have experience with non-trivial FSMs, has doubted that it is useful to terminate a state machine when it is destructed (my assumption A1).
I don't doubt that it's often useful. I also think it is surely sometimes highly undesirable. If you remove A1, the "useful" behavior is trivial to achieve without transforming the FSM, so it seems that a design without A1 is both more flexible and more orthogonal.
Since you don't doubt that it's often useful, why exactly should I then provide further examples in support for it? They will surely not convince that your "sometimes highly undesirable" assertion is invalid (that would seem highly illogical). BTW, I don't think that it is completely invalid either. What this all really boils down to is an interface that is easy to use and works well for the vast majority of the use-cases and requires workarounds for a minority of the use cases. I insisted on an example also to show how simple those workarounds can be and I doubt that they can typically even be called workarounds as they don't make machine design worse in any way (especially not with Darryl's trick).
So really, now I'm going to stop worrying this issue. May other peoples' posts be more helpful than mine have been!
Having slept over this and having spent pretty much the whole morning rereading our posts and reflecting on this whole discussion, I'm a bit at a loss what exactly went wrong. While I tried to always give accurate and to-the-point answers, you seem to have misunderstood them too often, what culminated in your accusation of me reasoning circularily. I don't want to imply that the problem necessarily lies on your side. In the beginning I definitely made the mistake that I assumed too often on what we would agree and what not. However, this alone couldn't possibly have led to the situation we now have. I don't have much of a clue what else I have done wrong. If other Boosters reading this have more of a clue then I would highly appreciate it if they'd let me know by private email. Regards, Andreas

"Andreas Huber" <ah2003@gmx.net> writes:
I said long ago that I was going to stop poking at this one, and didn't, because I had the sense that issues in this area, and the way you were reacting to my poking, might indicate that there were other problems.
I can only guess what exactly you mean with this, but it does sound like an accusation of me somehow evading your questions, see below.
That was not my intention. It did seem to me that when several people have said "this aspect of the design seems to complicate things for too little advantage", your reaction was to defend it ever more vigorously, declaring yourself to have "proven" things that seemed to me far from well-founded, and with not very much consideration given to the opposing arguments -- in fact IIRC you said that *no* technical reasoning against the design choice had been given, when I definitely made some techinical arguments. It made me concerned that similar self-justifying thought-processes had gone into other design decisions.
I don't feel as though much progress has been made, and you seem to feel that trying to convince me of anything by example is futile.
I did provide one example many posts ago (the pump in the dishwasher).
Ah, yes; I think that was before I was paying much attention.
Also, let me quote what you had to say about this in another post:
David Abrahams wrote:
I think a foundation for a decision is not flimsy when the behavior in question has proved to be useful in practice. So far nobody, who seems to have experience with non-trivial FSMs, has doubted that it is useful to terminate a state machine when it is destructed (my assumption A1).
I don't doubt that it's often useful. I also think it is surely sometimes highly undesirable. If you remove A1, the "useful" behavior is trivial to achieve without transforming the FSM, so it seems that a design without A1 is both more flexible and more orthogonal.
Since you don't doubt that it's often useful, why exactly should I then provide further examples in support for it? They will surely not convince that your "sometimes highly undesirable" assertion is invalid (that would seem highly illogical).
Good point.
BTW, I don't think that it is completely invalid either. What this all really boils down to is an interface that is easy to use and works well for the vast majority of the use-cases and requires workarounds for a minority of the use cases. I insisted on an example also to show how simple those workarounds can be and I doubt that they can typically even be called workarounds as they don't make machine design worse in any way ^^^^^^^^^^
This is the kind of argumentation I'm talking about. Having to repeat the would-be exit action in all outgoing transitions *is* worse in some ways. Duplication of code or logic is evil. The choice is between having to call the FSM's terminate once, in some controlling scope (e.g. derived class destructor) when you want it, or having to apply a workaround that affects _all_ transitions from _all_ states whose exit actions might fail, or shouldn't run from the FSM's destructor. It seems like a poor design trade-off to me... but didn't I say I was done trying to make points here? ;-)
(especially not with Darryl's trick).
I don't understand it, FWIW.
So really, now I'm going to stop worrying this issue. May other peoples' posts be more helpful than mine have been!
Having slept over this and having spent pretty much the whole morning rereading our posts and reflecting on this whole discussion, I'm a bit at a loss what exactly went wrong. While I tried to always give accurate and to-the-point answers, you seem to have misunderstood them too often, what culminated in your accusation of me reasoning circularily. I don't want to imply that the problem necessarily lies on your side. In the beginning I definitely made the mistake that I assumed too often on what we would agree and what not. However, this alone couldn't possibly have led to the situation we now have. I don't have much of a clue what else I have done wrong.
I'm sorry if my post caused you to agonize over this. You haven't done anything wrong; there's no crisis. Sometimes communication simply fails to be effective. It's not a big deal. I need to disengage from the argument, and thought I should give an explanation. Cheers, -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

David Abrahams wrote:
It did seem to me that when several people have said "this aspect of the design seems to complicate things for too little advantage", your reaction was to defend it ever more vigorously, declaring yourself to have "proven" things that seemed to me far from well-founded, and with not very much consideration given to the opposing arguments
Some of the opposing arguments didn't seem to be well-founded either.
-- in fact IIRC you said that *no* technical reasoning against the design choice had been given, when I definitely made some technical arguments.
I mainly agree. I guess this was probably one of the main points of misunderstanding. Most of your arguments were theoretical and I brushed them aside as not being relevant in practice (sometimes without clearly saying so). Instead of "technical arguments" I should definitely have said "arguments backed by real-world use-cases". I apologize for not spelling this out clearly.
BTW, I don't think that it is completely invalid either. What this all really boils down to is an interface that is easy to use and works well for the vast majority of the use-cases and requires workarounds for a minority of the use cases. I insisted on an example also to show how simple those workarounds can be and I doubt that they can typically even be called workarounds as they don't make machine design worse in any way ^^^^^^^^^^
This is the kind of argumentation I'm talking about. Having to repeat the would-be exit action in all outgoing transitions *is* worse in some ways. Duplication of code or logic is evil.
The word "typically" is important. I don't doubt that it *can* make the design worse in a *few* cases. But anyway, I see that a change on my part is in order. I will no longer attempt to judge whether something is relevant in practice or not. I did before because I feared (and still do a bit) that this could lead to a number of additions that might not be very useful in practice and would therefore needlessly bloat the interface and complicate the implementation. As long as a requested feature can be justified theoretically and the implementation is relatively easy, I will add it. Reviewers can then later vote whether to keep the feature or not. Regards, Andreas

Andreas Huber <ah2003 <at> gmx.net> writes:
Darryl Green wrote:
A state is considered left (and hence is destructed) only after exit and transition actions have run. Well it buys access to the state being left from the transition action for a start.
Right, but I don't think it is justified to complicate the interface and the implementation (we'd need to add entry()/exit()) for that. As I've explained in my answer to your other post you can always move such a variables into an outer context that exists during a transition.
You seem to be missing my point. The very feature that is (afaik) unique to your fsm library is made far less useable by this design decision. The feature in question is the "state local storage". Moving it up a level is all very well but it doesn't allow RAII based on state because it must be in the ICOS or higher. I want the state local storage gone in the new state. I can either write a "pre-exit" action in a react() function, or run it in the exit (destructor), which is only useful for those actions that do not depend on the event. I would like to be able to define transitions in the state that will make them, and refer to that state's context in the action. I really don't see what is so bizarre about that. Note that I'm quite happy to leave entry() == constructor (see below).
A state-specific recovery function would make error handling much more difficult, as you have no idea what you need to do to bring the state machine back into a stable state. If you e.g. make a transition from such a state, it is not guaranteed that the machine is stable afterwards (see Exception handling in the tutorial).
You keep saying this. I have seen the exception handling in the tutorial. As far as I know your arguments re exit actions not failing are: 1) All exit actions must run. This is because every subsequent action (be it an outer exit action, the transition action, or an entry action in going to the new state) may reasonably depend on the successful execution of all prior exit actions. 2) Exit actions must run because their side effects :-) can be important - don't forget to put those rods back in the reactor.... 3) Exit actions are often be logically paired with entry actions in a way similar to C++ ctor/dtor in order to implement RAII like idioms. The resource may be something physical like "the valve" and acquisition may mean "turn on" and release may mean "turn off". My view (fwiw) on these is: 1) This is very important. See below. 2) This is a usage decision best left to the user - any action, not just exit actions, can be critical - or not. 3) I think this is a red herring - you represent state activation by constructing an object, so you have c'tors and d'tors as ideal places to put those "actions" that do map precisely to these concepts, regardless of whether you also provide exit actions. Trying to stick rigidly to the UML spec and use a rigidly defined language mechanism to implement it is very inflexible. Note I haven't mentioned anything about c'tor/entry action mapping. This is because I don't think there is any real distinction between entry actions and c'tors. There are plenty of languages (java etc) with c'tors to perform initialisation, which is similar to the purpose of an entry action, but that don't provide object destruction/destructors (at least not in a RAII compatible way), so I don't see anything evil about the use of one and not the other. I propose the following handling of failing exit actions, which as far as I can see addresses the important item (1) above. Use a mechanism essentially the same as what you have now for failure handling. If exit() of a state fails the innermost outer state is checked for a handler for the failure. If there is one, the transition is made. If there isn't, it is an irrecoverable failure (can't run any more exit actions). At this point the fsm is simply destructed (without running exit actions) and the exception rethrown. Restrict the allowable transition to be to an inner state only (once again, avoids any further exit actions). When writing a reaction for exit handle failure, be aware that as an inner exit handler has failed, certain preconditions which would exist for other transitions, don't. This is not any different to any other action failing afaiks. I would envisage using such a mechanism by making the transition to a recovery state used only for that purpose. The recovery state, as a sibling of the failed state has access to all the context the failed state had (except for the now destructed failed states own). The recovery state can attempt to perform recovery (or wait for something external to performed it) before making a transition back to the state that previously failed exit. I would be inclined to decompose a state with a possibly failing exit into an outer state with the context (state local storage) that was previously in the orignal state, and a pair of inner states, one implementing the behaviour of the orignal state, and the other used for recovery. Most likely, it wouldn't be a single state anyway (It might be only one with an exit that can fail, but it would already be part of a larger outer state) so there isn't much overhead/difficulty in doing this.
My specific concern is the use of the extended functionality to produce states which have/build significant context that (should) only exist until a transition action deals with it. I don't see that using this state context after the exit action has run is a problem. The exit action may well have added to or modified it to make the context complete, not damaged (and in particular not destructed) it.
That's true, see above.
See what above? That I could move the context out a level? See above :-) for why this doesn't address the problem as I see it.
I'm also concerned that the current fsm destruction/exit action implementation model results in an environment where I should avoid using exit actions that have any significant external effects because I don't want/expect them to be run when/if the fsm is destructed.
Concern noted. This is actually the central disagreement between me and Dave Abrahams. If you have any real-world examples, I'd be *very* interested to see them.
Well, Dave's example pretty much mirrors a real world case. The case in question doesn't use disk, but it does use persistent storage of sorts. In this case, I have a state machine which doesn't consider shutdown to be something that can be reacted to, because by design the system in question is able to be shutdown in the following ways: 1) By having the power removed at any time. 2) A watchdog may kill (with varying degrees of prejudice) the process (or anything up to a full system restart). The reaction to this should be to do as little as possible before terminating. Similarly (this isn't implemented as part of the FSM) an orderly shutdown doesn't try to complete in progress "transactions" - it abandons them, leaving the system in a slightly old, but stable, state. 3) At a finer grained level, under various circumstances, enough context external to a particular FSM instance has (possibly physically) gone before the FSM knows about it, that trying to run exit actions to update external state is just silly (and hard to handle). When this happens the FSM just gets destructed. The destructor doesn't do anything externally observeable. The required reaction to each of these shutdown conditions is identical. The fsm doesn't "know" about the shutdown. It is never notified of them (obviously, in case 1 :-). It uses exit actions only to maintain the state exit invariants that make sense in the absence of shutdown. Obviously, to allow resource cleanup and orderly shutdown to occur in 2 (sometimes) and 3 (always) destructors do run. hth Darryl.

Darryl, Unfortunately I've run out of time for now. I'll try to answer as soon as I can. Regards, Andreas

Darryl Green wrote:
Right, but I don't think it is justified to complicate the interface and the implementation (we'd need to add entry()/exit()) for that. As I've explained in my answer to your other post you can always move such a variables into an outer context that exists during a transition.
You seem to be missing my point. The very feature that is (afaik) unique to your fsm library is made far less useable by this design decision. The feature in question is the "state local storage". Moving it up a level is all very well but it doesn't allow RAII based on state because it must be in the ICOS or higher. I want the state local storage gone in the new state. I can either write a "pre-exit" action in a react() function,
Right. A very nice way to solve the problem, BTW.
or run it in the exit (destructor), which is only useful for those actions that do not depend on the event. I would like to be able to define transitions in the state that will make them, and refer to that state's context in the action. I really don't see what is so bizarre about that.
It isn't bizare, it's only surprising because: 1. It allows that states are accessed when their exit() function has already run. People putting their exit code into exit() might not expect that the transition action might later again access the state. They could assume that it's the other way round. 2. It makes entry/exit asymmetrical. A programmer changing a state having only an entry action (i.e. a ctor) could very quickly come to the conclusion that he needs to add a destructor for an exit action. Both make the library a bit less intuitive to use. However, this is not the biggest problem. I'm much more concerned about the fact that your proposal breaks existing client code (currently, there are at least 2 independent projects using boost::fsm) to achieve something that can already be done. In FSM terms your "pre-exit" trick is an in-state reaction immediately followed by a transition and I think it is much more intuitive to do it this way than the way you propose. Plus, it doesn't even require more code. Don't get me wrong, I'm not strictly against breaking existing client code, it just doesn't seem justified in this case.
Note that I'm quite happy to leave entry() == constructor (see below).
A state-specific recovery function would make error handling much more difficult, as you have no idea what you need to do to bring the state machine back into a stable state. If you e.g. make a transition from such a state, it is not guaranteed that the machine is stable afterwards (see Exception handling in the tutorial).
You keep saying this. I have seen the exception handling in the tutorial.
Sorry, this has become a bit of a reflex lately.
As far as I know your arguments re exit actions not failing are:
1) All exit actions must run. This is because every subsequent action (be it an outer exit action, the transition action, or an entry action in going to the new state) may reasonably depend on the successful execution of all prior exit actions.
Right.
2) Exit actions must run because their side effects :-) can be important - don't forget to put those rods back in the reactor....
Right.
3) Exit actions are often be logically paired with entry actions in a way similar to C++ ctor/dtor in order to implement RAII like idioms. The resource may be something physical like "the valve" and acquisition may mean "turn on" and release may mean "turn off".
Right.
My view (fwiw) on these is:
1) This is very important. See below.
2) This is a usage decision best left to the user - any action, not just exit actions, can be critical - or not.
Exit actions are more critical than others in the current implementation because the state machine can be terminated pre-maturely at any time (state_machine<>::terminate()). UML clearly defines what termination means. Now, you could rightly argue that state_machine<> should not have a terminate() function as this can easily be implemented with a reaction in an outermost state, which is triggered by a user-defined event. I'm not saying this is a problem at all, I just want to show the implications of making exit actions equal citizens. One more thing that makes exit actions different is that an entry action failure or transition action failure often leads to states being exited before the problem can be handled.
3) I think this is a red herring
Do you mean a logical fallacy?
- you represent state activation by constructing an object, so you have c'tors and d'tors as ideal places to put those "actions" that do map precisely to these concepts, regardless of whether you also provide exit actions. Trying to stick rigidly to the UML spec
I think this *is* quite important. I believe not sticking to UML would lower the acceptance of the library a lot.
and use a rigidly defined language mechanism to implement it is very inflexible.
I agree that using destructors makes it slightly less flexible.
Note I haven't mentioned anything about c'tor/entry action mapping. This is because I don't think there is any real distinction between entry actions and c'tors. There are plenty of languages (java etc) with c'tors to perform initialisation, which is similar to the purpose of an entry action, but that don't provide object destruction/destructors (at least not in a RAII compatible way), so I don't see anything evil about the use of one and not the other.
In such languages RAII is achieved with a separate Dispose() function. If I was to port the library I'd have users provide Dispose() to implement an exit action.
I propose the following handling of failing exit actions, which as far as I can see addresses the important item (1) above.
Use a mechanism essentially the same as what you have now for failure handling.
If exit() of a state fails the innermost outer state is checked for a handler for the failure. If there is one, the transition is made. If there isn't, it is an irrecoverable failure (can't run any more exit actions). At this point the fsm is simply destructed (without running exit actions) and the exception rethrown.
Ok so far, this is certainly doable.
Restrict the allowable transition to be to an inner state only (once again, avoids any further exit actions).
I don't understand this. You said that the reaction should be searched in the ICOS. If you make a transition from the ICOS to a state that is an inner state of the one whose exit action failed then the ICOS and all its inner states must be left (again this behavior is required by UML), right?
When writing a reaction for exit handle failure, be aware that as an inner exit handler has failed, certain preconditions which would exist for other transitions, don't. This is not any different to any other action failing afaiks.
I think it *is* genuinely different. A failing exit action prevents the calling of the exit actions of outer states. Once an exit action has failed you can never leave the state again, *unless* you find a way to somehow recover and then retry exiting the state. If you cannot handle the problem, the only thing you can do is to enter inner states or abort everything and rethrow the exception. A failure of the other actions never brings you in a situation where you are *forced* to either retry the previously failed action or bail out completely.
I would envisage using such a mechanism by making the transition to a recovery state used only for that purpose. The recovery state, as a sibling of the failed state has access to all the context the failed state had (except for the now destructed failed states own).
We have never left the state whose exit action has thrown, right? How can we then enter a sibling (in StopWatch e.g. Running and Stopped are siblings)?
My specific concern is the use of the extended functionality to produce states which have/build significant context that (should) only exist until a transition action deals with it. I don't see that using this state context after the exit action has run is a problem. The exit action may well have added to or modified it to make the context complete, not damaged (and in particular not destructed) it.
That's true, see above.
See what above? That I could move the context out a level? See above :-) for why this doesn't address the problem as I see it.
Hmm, I don't remember why I wrote "see above". I guess it wasn't that important. Anyway, you are right with this observation. However as I've explained above ;-), I'd rather have this handled with your pre-exit trick. [example snipped] Ok, I see (finally ;-)) that there might be a use case for exit(), namely the one that you don't want to run certain actions on destruction, but you do want to run them on termination. exit() also doesn't break any existing code, as destructors keep acting as they do now. We have to define more thoroughly what happens when exit() throws. Regards, Andreas

Andreas Huber <ah2003 <at> gmx.net> writes:
Darryl Green wrote:
Right, but I don't think it is justified to complicate the interface and the implementation (we'd need to add entry()/exit()) for that. As I've explained in my answer to your other post you can always move such a variables into an outer context that exists during a transition.
You seem to be missing my point. The very feature that is (afaik) unique to your fsm library is made far less useable by this design decision. The feature in question is the "state local storage". Moving it up a level is all very well but it doesn't allow RAII based on state because it must be in the ICOS or higher. I want the state local storage gone in the new state. I can either write a "pre-exit" action in a react() function,
Right. A very nice way to solve the problem, BTW.
or run it in the exit (destructor), which is only useful for those actions that do not depend on the event. I would like to be able to define transitions in the state that will make them, and refer to that state's context in the action. I really don't see what is so bizarre about that.
It isn't bizare, it's only surprising because: 1. It allows that states are accessed when their exit() function has already run. People putting their exit code into exit() might not expect that the transition action might later again access the state. They could assume that it's the other way round.
I have been trying to formulate a reasonable model that is a pure extension to UML statechart semantics to deal with this - I think I have. It also shouldn't break any code that currently uses boost:fsm. My proposal is to allow a transition action's context to be *any* outer state of the source state and to change the state transition processing to: 1) Exit and destruct out to (but not including) the transition action's context or the ICOS, whichever comes first. 2) Execute transition action. 3) Exit and destruct any remaining states out to the ICOS. 4) Enter destination states. This doesn't allow access to the state being exited in the transition action, but it does allow the action of a transition that exits an outer state to access that state's context "on the way out". I am finding this to be more frequent/useful than access to an individual simple state's context anyway. Where the requirement is really to access an individual state the cost is a split into an inner and outer state. There is afaiks no impact on exception handling (I don't think it changes the docs, but it my change the implementation - I haven't checked). If the transition action fails, the exit actions up to the ICOS should run, then the failure reaction is searched for from ICOS out. Note that it doesn't seem to be hard to make this facility optional - a compiletime option could determine whether a context inside of the ICOS was a legal action context parameter or not.
2. It makes entry/exit asymmetrical. A programmer changing a state having only an entry action (i.e. a ctor) could very quickly come to the conclusion that he needs to add a destructor for an exit action.
True.
In FSM terms your "pre-exit" trick is an in-state reaction immediately followed by a transition and I think it is much more intuitive to do it this way than the way you propose.
Intuitive to you ;-) I don't think it is too bad in simple cases, but it gets ugly when what I really wanted was more like what is described in my proposal above (access to an outer state's context), which I've found my usage evolving into, more often than not.
Don't get me wrong, I'm not strictly against breaking existing client code, it just doesn't seem justified in this case.
Ok - I also want to avoid breaking existing code.
1) All exit actions must run. This is because every subsequent action (be it an outer exit action, the transition action, or an entry action in going to the new state) may reasonably depend on the successful execution of all prior exit actions.
Right.
2) Exit actions must run because their side effects can be important - don't forget to put those rods back in the reactor....
Right.
3) Exit actions are often be logically paired with entry actions in a way similar to C++ ctor/dtor in order to implement RAII like idioms. The resource may be something physical like "the valve" and acquisition may mean "turn on" and release may mean "turn off".
Right.
My view (fwiw) on these is:
1) This is very important. See below.
2) This is a usage decision best left to the user - any action, not just exit actions, can be critical - or not.
Exit actions are more critical than others in the current implementation because the state machine can be terminated pre-maturely at any time (state_machine<>::terminate()). UML clearly defines what termination means.
I agree with this. I overstated my case somewhat. I think you will see in my later description of exception handling that I do conceed that exit is critical in some ways. I'm having trouble clearly delineating the features of exit that need special handling, and felt that this case was one that didn't. I think this is just a very slight difference of opinion on how to decompose the features of exit, and not of any great importance overall.
Now, you could rightly argue that state_machine<> should not have a terminate() function as this can easily be implemented with a reaction in an outermost state, which is triggered by a user-defined event. I'm not saying this is a problem at all, I just want to show the implications of making exit actions equal citizens.
Ultimately, I don't make them equal.
3) I think this is a red herring
Do you mean a logical fallacy?
I think it is in the sense that you can't infer that because some aspect of A is the same as a corresponding aspect of B that A is B.
- you represent state activation by constructing an object, so you have c'tors and d'tors as ideal places to put those "actions" that do map precisely to these concepts, regardless of whether you also provide exit actions. Trying to stick rigidly to the UML spec
I think this *is* quite important. I believe not sticking to UML would lower the acceptance of the library a lot.
I didn't mean to say that the library shouldn't stick to the UML spec where it is applicable. I don't think that it is applicable in the area of object lifetime management because in UML states are not objects. In any case, UML statecharts (or Harel statecharts) aren't the only/ultimate/most rigourous definitions of FSM semantics. It isn't hard to find a plethora of possibly more rigorous FSM models (I'm not making any claims for them, I have just seen a lot of citations - not read all the papers). One thing that is not so easy to find is anything regarding integrating FSM as an aspect of a multi-paradigm programming language like C++ (yuk - reads like an add - lucky I didn't say "rich API" as well...). I think your work is very valuable here, and that dealing with object lifetime issues is an important part of that. However, it is relatively virgin teritory, afaik. It would be remiss of me to fail to invest at least some effort in contesting some of your claims for this new approach. Apprently a number of other people feel the same way ;-)
and use a rigidly defined language mechanism to implement it is very inflexible.
I agree that using destructors makes it slightly less flexible.
Note I haven't mentioned anything about c'tor/entry action mapping. This is because I don't think there is any real distinction between entry actions and c'tors. There are plenty of languages (java etc) with c'tors to perform initialisation, which is similar to the purpose of an entry action, but that don't provide object destruction/destructors (at least not in a RAII compatible way), so I don't see anything evil about the use of one and not the other.
In such languages RAII is achieved with a separate Dispose() function. If I was to port the library I'd have users provide Dispose() to implement an exit action.
Precisely. I don't mean that it can't be done, only that the C++ view that what a ctor does a dtor "undoes" doesn't always hold in other contexts, why in this one? I suggest that users have to provide an exit() to implement an exit action. If you think it will help Java programmers, I guess you could call it dispose() -)
I propose the following handling of failing exit actions, which as far as I can see addresses the important item (1) above.
Use a mechanism essentially the same as what you have now for failure handling.
If exit() of a state fails the innermost outer state is checked for a handler for the failure. If there is one, the transition is made. If there isn't, it is an irrecoverable failure (can't run any more exit actions). At this point the fsm is simply destructed (without running exit actions) and the exception rethrown.
Ok so far, this is certainly doable.
Restrict the allowable transition to be to an inner state only (once again, avoids any further exit actions).
I don't understand this. You said that the reaction should be searched in the ICOS.
No I didn't I said innermost outer state, (IOS) not ICOS. A better term might have been immediate outer state (of the state that had exit fail). I'm going to write IOS from now on, and hope that the definition is clear enough.
If you make a transition from the ICOS to a state that is an inner state of the one whose exit action failed then the ICOS and all its inner states must be left (again this behavior is required by UML), right?
Yes. That is why I propose that the reaction must be in the IOS, as that avoids exiting any more states before making the transition.
When writing a reaction for exit handle failure, be aware that as an inner exit handler has failed, certain preconditions which would exist for other transitions, don't. This is not any different to any other action failing afaiks.
I think it *is* genuinely different. A failing exit action prevents the calling of the exit actions of outer states.
As does my proposal.
Once an exit action has failed you can never leave the state again, *unless* you find a way to somehow recover and then retry exiting the state. If you cannot handle the problem, the only thing you can do is to enter inner states or abort everything and rethrow the exception.
I reached my proposed solution after discounting the approach of entering inner states to do recovery, because it won't work for an innermost state with an exit action that can fail (because the recovery state would then be the innermost state, making the original state unstable). I guess some ephemeral "recovery state" concept could deal with this and only construct/enter an inner recovery state when needed, otherwise treating the recovery state's IOS as the innermost state. I haven't checked to see how boost::fsm behaves if you don't specify the initial inner state (illegal in UML afaik).
I would envisage using such a mechanism by making the transition to a recovery state used only for that purpose. The recovery state, as a sibling of the failed state has access to all the context the failed state had (except for the now destructed failed states own).
We have never left the state whose exit action has thrown, right?
That was my first preference, but it created the problem of ephemeral inner recovery states. So instead, I proposed considering exit to be "done" but "failed". This seems reasonable enough - analogous to exception handling where you aren't in the block that threw any more, you are in the catch block. This means that after the exit action runs and fails, the state's destructor must run (and succeed - don't throw in the dtor or we are doomed obviously).
How can we then enter a sibling (in StopWatch e.g. Running and Stopped are siblings)?
I think the above explained it. If we were in Running, and its exit action failed, this would have to be reacted to by Active, which could then make a transition to stopped (or more likely, a 3rd sibling that dealt with recovery from failing to exit Running).
Ok, I see (finally ) that there might be a use case for exit(), namely the one that you don't want to run certain actions on destruction, but you do want to run them on termination. exit() also doesn't break any existing code, as destructors keep acting as they do now.
Yep.
We have to define more thoroughly what happens when exit() throws.
I hope I just did. Regards Darryl.

"Darryl Green" <darryl.green@unitab.com.au> wrote in message news:loom.20040602T031804-228@post.gmane.org...
Andreas Huber <ah2003 <at> gmx.net> writes:
[snip]
Don't get me wrong, I'm not strictly against breaking existing client
code,
it just doesn't seem justified in this case.
Ok - I also want to avoid breaking existing code.
Frankly, avoiding to break existing code for clients using a _prerelease_ library should not be overly considered (if considered at all). If clients need the exact current state (no pun intended) of the prerelease boost::fsm, they should stick the current code in their own SCM system (which they should do anyway IMHO) and maintain it themselves. There's nothing in the current Boost licence preventing them to do so, I hope. Sorry if that sounded a bit harsh, but the purpose of (pre)reviews is to get a good _starting_ point of a library isn't it? Leaving out bits of good (or even just slightly better) design to avoid breaking existing client code seems to be a bit malplaced at this stage in development. Just my 0.05EUR. // Johan

Johan Nilsson wrote:
Frankly, avoiding to break existing code for clients using a _prerelease_ library should not be overly considered (if considered at all).
I mostly agree. However, the change in question was a purely *cosmetical* one (IMHO the client code wouldn't be improved much) and it would also introduce other problems. So, even without considering existing client code I'm not convinced it would have been a good idea. But this isn't relevant anymore as we're now discussing a change that does not break existing code. Regards, Andreas

Darryl Green wrote:
I have been trying to formulate a reasonable model that is a pure extension to UML statechart semantics to deal with this - I think I have. It also shouldn't break any code that currently uses boost:fsm. My proposal is to allow a transition action's context to be *any* outer state of the source state and to change the state transition processing to:
1) Exit and destruct out to (but not including) the transition action's context or the ICOS, whichever comes first.
2) Execute transition action.
3) Exit and destruct any remaining states out to the ICOS.
4) Enter destination states.
Ok, this doesn't break any existing client code. But I think it is also surprising for FSM folks because UML clearly defines that all exit actions are called before the transition action is called. But I'm not strictly against such a change, I just have to think about this some more.
This doesn't allow access to the state being exited in the transition action, but it does allow the action of a transition that exits an outer state to access that state's context "on the way out".
I don't understand. With your proposal above the transition action can access any state that is exited as the result of the transition, even the innermost state. Simply make the action a member of the state you want to access and you're done, right?
I am finding this to be more frequent/useful than access to an individual simple state's context anyway. Where the requirement is really to access an individual state the cost is a split into an inner and outer state.
Still confused...
There is afaiks no impact on exception handling (I don't think it changes the docs, but it my change the implementation - I haven't checked). If the transition action fails, the exit actions up to the ICOS should run, then the failure reaction is searched for from ICOS out.
I'll have to change the implementation to run exit() and destructors when an exception is propagated from the transition action. Also when this happens we have a major problem if one of the called exit() functions throws as this would inevitably abort the program.
In FSM terms your "pre-exit" trick is an in-state reaction immediately followed by a transition and I think it is much more intuitive to do it this way than the way you propose.
Intuitive to you ;-)
I'm not sure. I'd expect that an FSM expert would immediately know what's going on when I say "in-state reaction immediately followed by a transition".
I don't think it is too bad in simple cases, but it gets ugly when what I really wanted was more like what is described in my proposal above (access to an outer state's context), which I've found my usage evolving into, more often than not.
If you only need to access the outer states' data members, why not give this outer state an in-state reaction, as I have written in my second followup to your message? I'll quote here for clarity: Andreas Huber wrote:
One more and probably the strongest argument pro your pre-exit trick is that it also works nicely with a transition originating at an outer state. E.g. in the StopWatch state chart you want to do something special when a EvReset event is received and the machine is currently in the Stopped state. You'd then add a Stopped in-state reaction, which does what it has to do with whatever Stopped data members and them simply calls forward_event(). The event will then trigger the transition originating at the Active state.
I'm also unsure why you think that your pre-exit trick leads to ugly code. I guess an example would make this a much clearer.
Exit actions are more critical than others in the current implementation because the state machine can be terminated pre-maturely at any time (state_machine<>::terminate()). UML clearly defines what termination means.
I agree with this. I overstated my case somewhat. I think you will see in my later description of exception handling that I do conceed that exit is critical in some ways. I'm having trouble clearly delineating the features of exit that need special handling, and felt that this case was one that didn't. I think this is just a very slight difference of opinion on how to decompose the features of exit, and not of any great importance overall.
Agreed.
3) I think this is a red herring
Do you mean a logical fallacy?
I think it is in the sense that you can't infer that because some aspect of A is the same as a corresponding aspect of B that A is B.
Ok.
I think this *is* quite important. I believe not sticking to UML would lower the acceptance of the library a lot.
I didn't mean to say that the library shouldn't stick to the UML spec where it is applicable. I don't think that it is applicable in the area of object lifetime management because in UML states are not objects.
True. Perhaps a better way to put it: One should be able to model UML state charts with boost::fsm, with as few detours/workarounds/surprises as possible. In any case, UML statecharts (or Harel statecharts) aren't
the only/ultimate/most rigourous definitions of FSM semantics. It isn't hard to find a plethora of possibly more rigorous FSM models (I'm not making any claims for them, I have just seen a lot of citations - not read all the papers). One thing that is not so easy to find is anything regarding integrating FSM as an aspect of a multi-paradigm programming language like C++
Agreed.
(yuk - reads like an add - lucky I didn't say "rich API" as well...).
:-)
In such languages RAII is achieved with a separate Dispose() function. If I was to port the library I'd have users provide Dispose() to implement an exit action.
Precisely. I don't mean that it can't be done, only that the C++ view that what a ctor does a dtor "undoes" doesn't always hold in other contexts, why in this one? I suggest that users have to provide an exit() to implement an exit action. If you think it will help Java programmers, I guess you could call it dispose() -)
LOL
Restrict the allowable transition to be to an inner state only (once again, avoids any further exit actions).
I don't understand this. You said that the reaction should be searched in the ICOS.
No I didn't I said innermost outer state, (IOS) not ICOS.
Sorry, I guess it was a little late.
A better term might have been immediate outer state (of the state that had exit fail). I'm going to write IOS from now on, and hope that the definition is clear enough.
It certainly is. BTW, in the docs I always use "direct outer state", I think that is even clearer (at least for my Swiss German-thinking brain).
I reached my proposed solution after discounting the approach of entering inner states to do recovery, because it won't work for an innermost state with an exit action that can fail (because the recovery state would then be the innermost state, making the original state unstable). I guess some ephemeral "recovery state" concept could deal with this and only construct/enter an inner recovery state when needed, otherwise treating the recovery state's IOS as the innermost state. I haven't checked to see how boost::fsm behaves if you don't specify the initial inner state (illegal in UML afaik).
In boost::fsm, if you don't specify an inner initial state for state A then A is automatically an innermost state and can therefore not have inner states. That is, trying to specify A as the Context of another state will lead to a compile time error.
I would envisage using such a mechanism by making the transition to a recovery state used only for that purpose. The recovery state, as a sibling of the failed state has access to all the context the failed state had (except for the now destructed failed states own).
We have never left the state whose exit action has thrown, right?
That was my first preference, but it created the problem of ephemeral inner recovery states.
It wouldn't if we required that exit() failures must always be handled in the state the caused the failure, see below.
So instead, I proposed considering exit to be "done" but "failed". This seems reasonable enough - analogous to exception handling where you aren't in the block that threw any more, you are in the catch block. This means that after the exit action runs and fails, the state's destructor must run (and succeed - don't throw in the dtor or we are doomed obviously).
How can we then enter a sibling (in StopWatch e.g. Running and Stopped are siblings)?
I think the above explained it. If we were in Running, and its exit action failed, this would have to be reacted to by Active, which could then make a transition to stopped (or more likely, a 3rd sibling that dealt with recovery from failing to exit Running).
Hmmm, all this is very problematic because in the transition to the recovery state the IOS state is exited although its inner state has never been successfully exited. I'd very much prefer to stay in the state that we just failed to exit, recover and then retry to exit the state. Everything else seems very questionable to me. Moreover, how many times do we retry? Does it even make sense to retry? The more I think about this the more I believe that pretty much the only sensible thing to do is to destruct the remaining state objects and propagate the exception to the state machine client... Regards, Andreas

Andreas Huber <ah2003 <at> gmx.net> writes:
Darryl Green wrote:
My proposal is to allow a transition action's context to be *any* outer state of the source state and to change the state transition processing to:
1) Exit and destruct out to (but not including) the transition action's context or the ICOS, whichever comes first.
2) Execute transition action.
3) Exit and destruct any remaining states out to the ICOS.
4) Enter destination states.
Ok, this doesn't break any existing client code. But I think it is also surprising for FSM folks because UML clearly defines that all exit actions are called before the transition action is called. But I'm not strictly against such a change, I just have to think about this some more.
The actual invariants implied by the use of composite states and entry/exit actions are not violated by this modification. Repositioning the transition action within the same overall sequence doesn't break anything, except possibly for the transition action itelf. I say this because the exit actions should only depend on all inner exits having run. A transition action can normally depend on all exit actions having been run. However, you only get what you ask for. fsm::transition< event, destination, action_context, action
clearly specifies the context as part of the transition action. As the action is a member of that context/state object, it can hardly expect (in fact it requires that this not be the case) that state's exit actions to have run when the action runs. It all seems logically consistent to me.
This doesn't allow access to the state being exited in the transition action, but it does allow the action of a transition that exits an outer state to access that state's context "on the way out".
I don't understand.
Sorry. Because I came up with this while trying to think of a model that was consistent with "exit before transition action" I didn't think about it allowing the case where nothing was exited before the action. You are right, and I was confused.
There is afaiks no impact on exception handling (I don't think it changes the docs, but it my change the implementation - I haven't checked). If the transition action fails, the exit actions up to the ICOS should run, then the failure reaction is searched for from ICOS out.
I'll have to change the implementation to run exit() and destructors when an exception is propagated from the transition action. Also when this happens we have a major problem if one of the called exit() functions throws as this would inevitably abort the program.
I think you could allow exit() failure, but no failures allowed in the "exit failure handler". This is probably a reasonable rule in any case (see below).
I'm not sure. I'd expect that an FSM expert would immediately know what's going on when I say "in-state reaction immediately followed by a transition".
Well, I'd wonder how you made an in-state reaction be "immediately" followed by another transition. But that might be my hardware background showing...
If you only need to access the outer states' data members, why not give this outer state an in-state reaction, as I have written in my second followup to your message?
Ok. That would work. It makes the statechart "look funny" (transforms it in a way that I would never do if not for this "feature").
I'm also unsure why you think that your pre-exit trick leads to ugly code. I guess an example would make this a much clearer.
It just leads to a state machine that is less readable than it would be if everything was in nice neat transition declarations. The custom reaction isn't ugly - just obfuscating because its semantics aren't visible.
A better term might have been immediate outer state (of the state that had exit fail). I'm going to write IOS from now on, and hope that the definition is clear enough.
It certainly is. BTW, in the docs I always use "direct outer state", I think that is even clearer (at least for my Swiss German-thinking brain).
Direct outer state it is then (but I'll keep using IOS in this thread).
I would envisage using such a mechanism by making the transition to a recovery state used only for that purpose. The recovery state, as a sibling of the failed state has access to all the context the failed state had (except for the now destructed failed states own).
We have never left the state whose exit action has thrown, right?
That was my first preference, but it created the problem of ephemeral inner recovery states.
It wouldn't if we required that exit() failures must always be handled in the state the caused the failure, see below.
So instead, I proposed considering exit to be "done" but "failed". This seems reasonable enough - analogous to exception handling where you aren't in the block that threw any more, you are in the catch block. This means that after the exit action runs and fails, the state's destructor must run (and succeed - don't throw in the dtor or we are doomed obviously).
How can we then enter a sibling (in StopWatch e.g. Running and Stopped are siblings)?
I think the above explained it. If we were in Running, and its exit action failed, this would have to be reacted to by Active, which could then make a transition to stopped (or more likely, a 3rd sibling that dealt with recovery from failing to exit Running).
Hmmm, all this is very problematic because in the transition to the recovery state the IOS state is exited
No it isn't. The failed_exit reaction needs to redirect a partially completed transition, not start a new one. I didn't explain this properly. Active is the IOS. We try to exit Running, it fails, so we destruct running, but not Active. Active handles the failure by entering Stopped. Active is never exited/destroyed.
although its inner state has never been successfully exited. I'd very much prefer to stay in the state that we just failed to exit, recover and then retry to exit the state. Everything else seems very questionable to me. Moreover, how many times do we retry? Does it even make sense to retry? The more I think about this the more I believe that pretty much the only sensible thing to do is to destruct the remaining state objects and propagate the exception to the state machine client...
Consider state S which has a direct outer state O. A accumulates some data to be written to disk. It stores it in a buffer owned by O. On exit from S, the exit action should write the buffer to disk. However, the disk is full, so the exit action fails. We make a transition to the recovery state R, which does something (prompts user, starts deleting stuff, whatever) and exits when some criteria is met (user clicks on try again, some timer expires?). At this point R, which has an exit action exactly the same as S, may itself fail exit. Potentially this could go on forever (R repeatedly handling its own failure). Perhaps it is at this point (when the failed_exit handler state itself fails exit) that the state machine should be destructed etc. Regards Darryl.

Darryl Green <darryl.green <at> unitab.com.au> writes:
Ok, this doesn't break any existing client code. But I think it is also surprising for FSM folks because UML clearly defines that all exit actions are called before the transition action is called. But I'm not strictly against such a change, I just have to think about this some more.
The actual invariants implied by the use of composite states and entry/exit actions are not violated by this modification. Repositioning the transition action within the same overall sequence doesn't break anything, except possibly for the transition action itelf. I say this because the exit actions should only depend on all inner exits having run. A transition action can normally depend on all exit actions having been run. However, you only get what you ask for. fsm::transition< event, destination, action_context, action
clearly specifies the context as part of the transition action. As the action is a member of that context/state object, it can hardly expect (in fact it requires that this not be the case) that state's exit actions to have run when the action runs. It all seems logically consistent to me.
I sort of agree, but I think that it's a bad idea to change the behavior of fsm::transition and simple_state<>::transit<>(). What do you say about the following: namespace boost { namespace fsm { template< class Event, class Destination, class InStateReactionContext, class void ( InStateReactionContext::*pInStateReaction )( const Event & ) class TransitionContext = unspecified, void ( TransitionContext::*pTransitionAction )( const Event & ) = unspecified > struct compound_transition { // implementation-defined }; } } This is slightly different from your proposal but I think it is clearer and therefore more easily understood. All exit actions are always called after the in-state reaction and before the transition action. IUC, then you wouldn't normally need the last two parameters, right?
There is afaiks no impact on exception handling (I don't think it changes the docs, but it my change the implementation - I haven't checked). If the transition action fails, the exit actions up to the ICOS should run, then the failure reaction is searched for from ICOS out.
I'll have to change the implementation to run exit() and destructors when an exception is propagated from the transition action. Also when this happens we have a major problem if one of the called exit() functions throws as this would inevitably abort the program.
I think you could allow exit() failure,
Definitely not in this particular situation. This would be the same as allowing a destructor to throw. I know I said this before and people rightly argued that I'm wrong. But here we really have the same situation because a second exception is thrown (from exit()) while we are *unwinding* from a first exception (thrown from the transition action). Now you suddenly have two exceptions. Which one are you going to handle? Since you can only catch one of them the other one is inevitably lost. I think throwing exit actions make sense only when you propagate *all* exceptions to the state machine client. As soon as you want to be able to handle exceptions in the state machine itself you very quickly run into logical problems similar to the one above, *if* you allow exit actions to throw. For example, suppose that in StopWatch there is an additional transition from Active to a new state Error. If the entry action of Stopped throws, then the resulting exception_thrown event leads to a transition to Error. So far so good. What if the exit action of Active throws? We have one exception pending (since Error has never been reached we never had the chance to actually handle the exception) and now an additional exception is thrown. What do we do now? We can bail out at this point an propagate the second exception to the state machine client but we still have lost the first exception. *Bad* *idea*! Note that we don't have this problem if we never attempt to handle the exception in the state machine but propagate it out to the client instead because we wouldn't call any exit actions in this case (the library currently does but it wouldn't if we had exit()). Since exit() actions are not called when the state machine is later destructed we won't run into the same problem. Thoughts?
I'm also unsure why you think that your pre-exit trick leads to ugly code. I guess an example would make this a much clearer.
It just leads to a state machine that is less readable than it would be if everything was in nice neat transition declarations. The custom reaction isn't ugly - just obfuscating because its semantics aren't visible.
Ok, I think compound_transition<> should solve this problem rather nicely. Regards, Andreas

Andreas Huber <ah2003 <at> gmx.net> writes:
Darryl Green <darryl.green <at> unitab.com.au> writes:
The actual invariants implied by the use of composite states and entry/exit actions are not violated by this modification. Repositioning the transition action within the same overall sequence doesn't break anything, except possibly for the transition action itelf. I say this because the exit actions should only depend on all inner exits having run. A transition action can normally depend on all exit actions having been run. However, you only get what you ask for. fsm::transition< event, destination, action_context, action
clearly specifies the context as part of the transition action. As the action is a member of that context/state object, it can hardly expect (in fact it requires that this not be the case) that state's exit actions to have
when the action runs. It all seems logically consistent to me.
I sort of agree, but I think that it's a bad idea to change the behavior of fsm::transition and simple_state<>::transit<>(). What do you say about the following:
template< class Event, class Destination, class InStateReactionContext, class void ( InStateReactionContext::*pInStateReaction )( const Event & ) class TransitionContext = unspecified, void ( TransitionContext::*pTransitionAction )( const Event & ) = unspecified > struct compound_transition;
This is slightly different from your proposal but I think it is clearer and therefore more easily understood. All exit actions are always called after
run the
in-state reaction and before the transition action. IUC, then you wouldn't normally need the last two parameters, right?
Right. This works too. I don't really understand your objection to the transition semantics I proposed. I personally find that proposal perfectly clear (well I would), and the complication of another type of transition unnecessary, but it is of course up to you. I'm done on this one, but I would like to hear other comments - I have to agree with Rob Stewart that it is a bit too quiet around here (though I'm not convinced that we need fsm "experts" - I'm sure there is plenty to be offered by those untainted by too much previous fsm experience).
I'll have to change the implementation to run exit() and destructors when an exception is propagated from the transition action. Also when this happens we have a major problem if one of the called exit() functions throws as this would inevitably abort the program.
I think you could allow exit() failure,
Definitely not in this particular situation. This would be the same as allowing a destructor to throw. I know I said this before and people rightly argued that I'm wrong. But here we really have the same situation because a second exception is thrown (from exit()) while we are *unwinding* from a first exception (thrown from the transition action). Now you suddenly have two exceptions. Which one are you going to handle? Since you can only catch one of them the other one is inevitably lost.
I don't think it is really the same as throwing while unwinding - or are you saying that the implementation would have to be such that it genuinely was exactly that? I envisioned that the transition exception would be caught and converted to an event. The event would be (must be) queued (in a queue of depth 1) while doing the exit action. If the exit threw, this would be handled immediately (in line with the exit exception proposal outlined previously), but the transition exception would *not* be lost. Only when/if the exit "recovery" completed sucessfully would the fsm be considered to be in the correct context/state to handle the original transition action failure event. Only if recovery failed would we be in real trouble - at that point the fsm would have to be "abandoned" (ie. terminated without exit).
I think throwing exit actions make sense only when you propagate *all* exceptions to the state machine client. As soon as you want to be able to handle exceptions in the state machine itself you very quickly run into logical problems similar to the one above, *if* you allow exit actions to throw. For example, suppose that in StopWatch there is an additional transition from Active to a new state Error. If the entry action of Stopped throws, then the resulting exception_thrown event leads to a transition to Error. So far so good. What if the exit action of Active throws? We have one exception pending (since Error has never been reached we never had the chance to actually handle the exception) and now an additional exception is thrown. What do we do now? We can bail out at this point an propagate the second exception to the state machine client but we still have lost the first exception. *Bad* *idea*!
Yes, exit failure is "special". However, I think it can be dealt with as I outlined above and in the previous post. That is, exit exception event handling must not fail.
Note that we don't have this problem if we never attempt to handle the exception in the state machine but propagate it out to the client instead because we wouldn't call any exit actions in this case (the library currently does but it wouldn't if we had exit()). Since exit() actions are not called when the state machine is later destructed we won't run into the same problem.
Thoughts?
I'm not claiming any particular expertise or experience here - my first reaction, which seems pretty common, is to be very keen to avoid mixing fsms and exceptions at all. I certainly want at least the option of the simple behaviour of just abandoning the fsm and propagating the exception. If anyone else is still interested in giving serious consideration to other options, I'd be interested in continuing to try to work something out, otherwise, I'm done on this one too. I don't have any immediate need for anything more elaborate, and I'd be pretty happy if I never did.
The custom reaction isn't ugly - just obfuscating because its semantics aren't visible.
Ok, I think compound_transition<> should solve this problem rather nicely.
It does. Regards Darryl.

Darryl Green <darryl.green <at> unitab.com.au> writes:
Right. This works too. I don't really understand your objection to the transition semantics I proposed.
Sorry, I didn't justify this enough. My main concern is that changes in one part of a state machine no longer lead to compile time errors in other parts, although they probably should. That is, the programmer is no longer forced to explicitly say what he means when he makes a certain type of change. For example, suppose there is an action associated with the transition between Running and Stopped. If you now move the Stopped state out of Active then said transition action is still executed before the Active exit action (i.e. the programmer is not forced to consider all the side effects of his change). I see your point that this would lead to problems only rarely, but it *can* happen. In fact, in the field where I used to employ state machines (controlling machinery) this could be a problem more than rarely because actions more often than not have global side effects (they change the state of hardware machine parts). Sure, the error very clearly lies with the programmer who failed to RTFM but he does have a point when he says "Ummm, UML mandates that all exit actions are always called before the transition action and I thought that boost::fsm claims to stick to UML, right?" Moreover, I think it is much more readable this way (although we should probably find a better name for compound_transition) and I hope that other people will agree. I do see some merit in your proposal, I just think it is much too early to make such a change with just you and me considering the implications (once the change is made we cannot go back without breaking existing code).
I'm done on this one, but I would like to hear other comments
- I have to agree with Rob Stewart that it is a bit too quiet around here (though I'm not convinced that we need fsm "experts" - I'm sure there is plenty to be offered by those untainted by too much
I'd also like to hear other comments. If more people with practical experience would speak up in favor of your proposal then I'd reconsider adding it. previous
fsm experience).
Agreed. People with a lot of experience with FSMs are often convinced that the way how they usually solve a particular problem is the best. As a result they could fail to use new features of boost::fsm in previously unknown ways. As you know, this happended to me as well. I never considered to use your "pre- exit" trick although I definitely could have benefited from it in the past too.
I think you could allow exit() failure,
Definitely not in this particular situation. This would be the same as allowing a destructor to throw. I know I said this before and people rightly argued that I'm wrong. But here we really have the same situation because a second exception is thrown (from exit()) while we are *unwinding* from a first exception (thrown from the transition action). Now you suddenly have two exceptions. Which one are you going to handle? Since you can only catch one of them the other one is inevitably lost.
I don't think it is really the same as throwing while unwinding - or are you saying that the implementation would have to be such that it genuinely was exactly that?
No. It could be implemented such that the first exception is always caught in a catch handler before the second exception is thrown. boost::fsm currently doesn't do so for performance reasons. But, even if it did you could still lose one exception because *non-error-handling* user code (the exit actions) must be executed before the problem can be handled. If this non-error-handling code fails we are in deep trouble.
I envisioned that the transition exception would be caught and converted to an event. The event would be (must be) queued (in a queue of depth 1) while doing the exit action.
boost::fsm can't queue events resulting from an exception as that could involve a dynamic allocation. I think it's not a good idea to do that while handling an exception.
If the exit threw, this would be handled immediately (in line with the exit exception proposal outlined previously), but the transition exception would *not* be lost.
What if we fail to handle the exception thrown from the exit action? I know you said that it must not fail, but what do we do when it fails anyway? We cannot force the user to handle the exception, right? We'd need to propagate the exit action exception to the state machine client, but the first exception is inevitably lost. Alternatively we could also lose the second exception and propagate the first one, but still one exception is lost. IIRC, one of the reasons why to never propagate exceptions from destructors is very similar (see Herb Sutter, Exceptional C++).
Thoughts?
I'm not claiming any particular expertise or experience here - my first reaction, which seems pretty common, is to be very keen to avoid mixing fsms and exceptions at all. I certainly want at least the option of the simple behaviour of just abandoning the fsm and propagating the exception. If anyone else is still interested in giving serious consideration to other options, I'd be interested in continuing to try to work something out, otherwise
I have reached sort of a conclusion: 1. boost::fsm users want to have the option to just let exceptions "fly" through the framework and let the state machine client handle the problem. Whenever this happens the state machine object is no longer usable. Before handing the exception to the user, the framework should probably destruct (but not exit) all state objects. Users can propagate exceptions from exit actions. 2. boost::fsm users might want to gracefully handle exceptions propagated from actions and continue to use the state machine object afterwards. If so, they must accept that exit actions must not propagate any exceptions. This is because it seems that, no matter how action failures are handled in a state machine, in general exit actions must be executed before an error-handling state can be reached. I think the only way to avoid the non-throwing exit actions in 2 is to use the ephemeral error state or the exit() error handling you proposed for *all* failures, not only for exit. It is worth to explore this some more. What do you think? Regards, Andreas

----- Original Message ----- From: "Andreas Huber" <ah2003@gmx.net> [...]
I'd also like to hear other comments. If more people with practical experience would speak up in favor of your proposal then I'd reconsider adding it.
It's been a long discussion. It'd be helpful if you could summarize the proposed changes on d'tor/exit() and exception handling in general. I was personally fine with the requirement that d'tor (or exit()) must not throw. Typically when I work on a state machine, I require that nothing in the state machine may throw. In other words, if something throws, the state machine doesn't care and it doesn't handle it in any way. To me all exceptions are out the state machine domain. I realize that it may be an old fashioned way of doing things but it is safe and deterministic. [...]
I have reached sort of a conclusion:
1. boost::fsm users want to have the option to just let exceptions "fly" through the framework and let the state machine client handle the problem. Whenever this happens the state machine object is no longer usable. Before handing the exception to the user, the framework should probably destruct (but not exit) all state objects.
2. boost::fsm users might want to gracefully handle exceptions propagated from actions and continue to use the state machine object afterwards. If so,
If "fly through" is what I think it is (exceptions are not caught by the framework), it is one or another. You cannot "let exceptions "fly" through the framework" and destroy all states at the same time. they
must accept that exit actions must not propagate any exceptions. This is because it seems that, no matter how action failures are handled in a state machine, in general exit actions must be executed before an error-handling state can be reached.
I believe that it is similar to how things work now (with the default ExceptionTranslator)?
I think the only way to avoid the non-throwing exit actions in 2 is to use
the
ephemeral error state or the exit() error handling you proposed for *all* failures, not only for exit. It is worth to explore this some more.
"ephemeral error state" makes me nervous. :) Eugene

E. Gladyshev wrote:
----- Original Message ----- From: "Andreas Huber" <ah2003@gmx.net> [...] It's been a long discussion. It'd be helpful if you could summarize the proposed changes on d'tor/exit() and exception handling in general.
Ok, I will.
I have reached sort of a conclusion:
1. boost::fsm users want to have the option to just let exceptions "fly" through the framework and let the state machine client handle the problem. Whenever this happens the state machine object is no longer usable. Before handing the exception to the user, the framework should probably destruct (but not exit) all state objects.
If "fly through" is what I think it is (exceptions are not caught by the framework), it is one or another. You cannot "let exceptions "fly" through the framework" and destroy all states at the same time.
What I meant was the default behavior we were talking about before. I rephrase, trying to stick more to accepted exception handling terminology: 1. By default, boost::fsm is completely neutral to exceptions. All exceptions thrown by user actions are propagated to the state machine client. If such an exception is caught by client code, all state objects are destructed (but not exited). This ensures that the state machine object is in a defined state if client code uses it after handling the exception. State objects are constructed on state entry. On state exit, the states exit() function is called (if present) and the state object is destructed afterwards. Exceptions can be propagated from all user code except state destructors.
2. boost::fsm users might want to gracefully handle exceptions propagated from actions and continue to use the state machine object afterwards. If so, they must accept that exit actions must not propagate any exceptions. This is because it seems that, no matter how action failures are handled in a state machine, in general exit actions must be executed before an error-handling state can be reached.
I believe that it is similar to how things work now (with the default ExceptionTranslator)?
Yes.
I think the only way to avoid the non-throwing exit actions in 2 is to use the ephemeral error state or the exit() error handling you proposed for *all* failures, not only for exit. It is worth to explore this some more.
"ephemeral error state" makes me nervous. :)
Me too. I haven't got very far yet on the precise semantics. Whenever I try to formulate how exit() failures are handled I find myself trapped in inconsistencies. Contrary to Darryls proposal my gut feeling is that we have not left a state when its exit action fails. As a result, the exit() action of a particular state object could be called more than once, which I find *very* strange. Maybe Darryls way to handle things is better nevertheless ... Regards, Andreas

"Andreas Huber" <ah2003@gmx.net> wrote in message news:c9qra1$2mq$1@sea.gmane.org...
E. Gladyshev wrote: [...]
What I meant was the default behavior we were talking about before. I rephrase, trying to stick more to accepted exception handling terminology: 1. By default, boost::fsm is completely neutral to exceptions. All exceptions thrown by user actions are propagated to the state machine client. If such an exception is caught by client code, all state objects are destructed (but not exited).
I know where the confusion is. I don't think that the last statement is technically correct. The fact that the client catches the exception doesn't guarantee that the state objects are destructed. fsm::state_machine<...> machine; try { machine.initiate(); } catch(...) { ... } OR try { fsm::state_machine<...> *m = new fsm::state_machine<...>(); m->initiate(); } catch(...) { ... } Do you mean when the state machine object goes out of scope or deleted? Best, Eugene

E. Gladyshev wrote:
"Andreas Huber" <ah2003@gmx.net> wrote in message news:c9qra1$2mq$1@sea.gmane.org...
E. Gladyshev wrote: [...]
What I meant was the default behavior we were talking about before. I rephrase, trying to stick more to accepted exception handling terminology: 1. By default, boost::fsm is completely neutral to exceptions. All exceptions thrown by user actions are propagated to the state machine client. If such an exception is caught by client code, all state objects are destructed (but not exited).
I know where the confusion is. I don't think that the last statement is technically correct. The fact that the client catches the exception doesn't guarantee that the state objects are destructed.
Hmmm, I guess I misinterpreted your earlier comments regarding unhandled exceptions then, see below.
fsm::state_machine<...> machine; try { machine.initiate(); } catch(...) { ... }
OR
try { fsm::state_machine<...> *m = new fsm::state_machine<...>(); m->initiate(); } catch(...) { ... }
Do you mean when the state machine object goes out of scope or deleted?
I thought that in both cases it is guaranteed that the stack is unwound (when we enter the catch ( ... ) handler). If so, then the scope guard inside initiate() will destruct all state objects. Inside the catch ( ... ) handler the machine object is therefore in a defined state (machine.terminated() returns true). I interpreted your earlier comments such that stack unwinding is not guaranteed *only* when we never catch the exception propagated out of the state_machine<>::initiate() function. As soon as the exception is caught somewhere the stack will unwind and the machine object is in a defined state when it is accessed. What am I missing? Regards, Andreas

----- Original Message ----- From: "Andreas Huber" <ah2003@gmx.net> To: <boost@lists.boost.org> Sent: Saturday, June 05, 2004 3:15 AM Subject: [boost] Re: Re: Re: [fsm] transition context and exit() [...]
I thought that in both cases it is guaranteed that the stack is unwound (when we enter the catch ( ... ) handler). If so, then the scope guard inside initiate() will destruct all state objects. Inside the catch (
... )
handler the machine object is therefore in a defined state (machine.terminated() returns true). I interpreted your earlier comments such that stack unwinding is not guaranteed *only* when we never catch the exception propagated out of the state_machine<>::initiate() function. As soon as the exception is caught somewhere the stack will unwind and the machine object is in a defined state when it is accessed.
What am I missing?
Nothing. I misunderstood you and thought that you meant the state_machine d'tor. If it is a scope guard in initiate() then you are right. Eugene

Andreas Huber <ah2003 <at> gmx.net> writes:
Darryl Green <darryl.green <at> unitab.com.au> writes:
Right. This works too. I don't really understand your objection to the transition semantics I proposed.
Sorry, I didn't justify this enough. My main concern is that changes in one part of a state machine no longer lead to compile time errors in other parts, although they probably should.
Ok. Valid criticism. The only thing that makes me uncomfortable about the compound_transition approach vs relaxed transition action is that for some very simple cases it rearranges the diagram into someting quite odd: An inner transition with action (the pre exit) that passes the event out (how do you represent *that* in UML?) and a normal transition and action. What I proposed is also unrepresentable but I would doesn't actually have a direct UML representation anyway. My reasons for favouring it are only because if Active held useful context, and hence various actions to work with this context, and if there were an added transition from Stopped out of Active into some state Foo, which needed to use the Active context in the transition action, I'd be inclined to try to write just that. The transformation to a compound_transition out of Active to Foo, That is, the programmer is no longer forced to
explicitly say what he means when he makes a certain type of change. For example, suppose there is an action associated with the transition between Running and Stopped. If you now move the Stopped state out of Active then said transition action is still executed before the Active exit action (i.e. the programmer is not forced to consider all the side effects of his change). I see your point that this would lead to problems only rarely, but it *can* happen. In fact, in the field where I used to employ state machines (controlling machinery) this could be a problem more than rarely because actions more often than not have global side effects (they change the state of hardware machine parts).
Point taken - but then I'm sure you take great care. Even without my modification, you still had to think carefully about the consequences of that small change - exiting Active might well have global side effects too. If you look at exit and any other Active actions/member functions that make up the transition you will get it right. If you don't...
Sure, the error very clearly lies with the programmer who failed to RTFM but he does have a point when he says "Ummm, UML mandates that all exit actions are always called before the transition action and I thought that boost::fsm claims to stick to UML, right?"
So you think the pprogrammer will consider the consequences of the exit, but will still expect the transition action to be called after its context is exited? The next question is - when it does fail to compile will the programmer find it easy/obvious to replace the Running->Stopped transition with an Active->Stopped compound_transition with the original action moved to be a the pre-exit action?
Moreover, I think it is much more readable this way (although we should probably find a better name for compound_transition) and I hope that other people will agree.
I do see some merit in your proposal, I just think it is much too early to make such a change with just you and me considering the implications (once
I've said my piece here already, and sort of re-said it in my last question above... the
change is made we cannot go back without breaking existing code).
Like I said - it is easy (I think?) to make it an option. It might be useful to acquire experience with how well these alternate approaches work in practice.
I'm done on this one, but I would like to hear other comments
Sorry for carrying this on when I said I was stopping :-) Regards Darryl.

Darryl Green <darryl.green <at> unitab.com.au> writes (a lot of gibberish):
What I proposed is also unrepresentable ... That is, the programmer is no longer forced to
Please ignore the above portion of the last post - I meant to cut it.

Darryl Green wrote:
Ok. Valid criticism. The only thing that makes me uncomfortable about the compound_transition approach vs relaxed transition action is that for some very simple cases it rearranges the diagram into someting quite odd:
An inner transition with action (the pre exit) that passes the event out (how do you represent *that* in UML?) and a normal transition and action.
You can't express it in UML, but it is a very natural thing to do. It is equivalent to overriding a virtual function in a derived class and have the implementation call the base class function before returning. Everybody uses this pattern in OO programming and I can't fathom why UML doesn't allow it. Probably an oversight.
My reasons for favouring it are only because if Active held useful context, and hence various actions to work with this context, and if there were an added transition from Stopped out of Active into some state Foo, which needed to use the Active context in the transition action, I'd be inclined to try to write just that. The transformation to a compound_transition out of Active to Foo,
I guess you wanted to add something more here but I think I got what you mean.
Sure, the error very clearly lies with the programmer who failed to RTFM but he does have a point when he says "Ummm, UML mandates that all exit actions are always called before the transition action and I thought that boost::fsm claims to stick to UML, right?"
So you think the pprogrammer will consider the consequences of the exit, but will still expect the transition action to be called after its context is exited?
I guess that he's more aware of the exit (it is directly visible in the diagram). But, yeah, that's just a lot of guessing.
The next question is - when it does fail to compile will the programmer find it easy/obvious to replace the Running->Stopped transition with an Active->Stopped compound_transition with the original action moved to be a the pre-exit action?
Good point.
I do see some merit in your proposal, I just think it is much too early to make such a change with just you and me considering the implications (once the change is made we cannot go back without breaking existing code).
Like I said - it is easy (I think?) to make it an option. It might be useful to acquire experience with how well these alternate approaches work in practice.
Ok, convinced. I need to check how much work this is and whether there are any side effects but I don't see much of a problem at the moment. What would you say about BOOST_FSM_RELAX_TRANSITION_CONTEXT? It's a little long, do you have a better idea? Hope you can live with a global switch instead of a per-machine policy. Regards, Andreas P.S. I was away over the weekend, I'll answer the other posts on Monday.

Andreas Huber <ah2003 <at> gmx.net> writes:
Ok, convinced. I need to check how much work this is and whether there are any side effects but I don't see much of a problem at the moment. What would you say about BOOST_FSM_RELAX_TRANSITION_CONTEXT? It's a little long, do you have a better idea?
No. It is a good descriptive name and I can't think of a shorter one that is sufficiently descriptive.
Hope you can live with a global switch instead of a per-machine policy.
Certainly. Regards Darryl.

Andreas Huber <ah2003 <at> gmx.net> writes:
you could still lose one exception because *non-error-handling* user code (the exit actions) must be executed before the problem can be handled. If this non-error-handling code fails we are in deep trouble.
Yes, exit is "special".
IIRC, one of the reasons why to never propagate exceptions from destructors is very similar (see Herb Sutter, Exceptional C++).
Yes. The similarity had occured to me also.
I have reached sort of a conclusion:
1. boost::fsm users want to have the option to just let exceptions "fly" through the framework and let the state machine client handle the problem. Whenever this happens the state machine object is no longer usable. Before handing the exception to the user, the framework should probably destruct (but not exit) all state objects. Users can propagate exceptions from exit actions.
A useful option.
2. boost::fsm users might want to gracefully handle exceptions propagated from actions and continue to use the state machine object afterwards. If so, they must accept that exit actions must not propagate any exceptions. This is because it seems that, no matter how action failures are handled in a state machine, in general exit actions must be executed before an error-handling state can be reached.
I think the only way to avoid the non-throwing exit actions in 2 is to use
Agreed. Specifically, if the action failure transition is not to an inner state of the ICOS of the failed transition, exit actions must not throw. Of course, if you were to accept my proposal to allow actions to occur at any level in the transition, that would become ".. an inner state of the transition action context or the ICOS, whichever is the innermost...". the
ephemeral error state or the exit() error handling you proposed for *all* failures, not only for exit.
Ok - that sounds like it would be the only sufficiently regular/comprehensive alternative to (1) to be worthy of further consideration.
It is worth to explore this some more. What do you think?
I am not sure if anything other than (1) is really useful. I think (2), if exception handling within the fsm framework is to be really useful, is too restrictive. I think (3) (meaning 2 with the modifications to allow exit to fail) might be too restrictive/hard to use to be really useful. It is the last point I'd really like to hear from others on. I find the idea interesting, but I wouldn't actually encourage you to invest effort in implementing it at this stage. Regards Darryl.

Darryl Green wrote:
I think the only way to avoid the non-throwing exit actions in 2 is to use the ephemeral error state or the exit() error handling you proposed for *all* failures, not only for exit.
Ok - that sounds like it would be the only sufficiently regular/comprehensive alternative to (1) to be worthy of further consideration.
It is worth to explore this some more. What do you think?
I am not sure if anything other than (1) is really useful. I think (2), if exception handling within the fsm framework is to be really useful, is too restrictive. I think (3) (meaning 2 with the modifications to allow exit to fail) might be too restrictive/hard to use to be really useful. It is the last point I'd really like to hear from others on. I find the idea interesting, but I wouldn't actually encourage you to invest effort in implementing it at this stage.
I've thought a little about (3) and will probably let it be until I have implemented the extensions (exit() and BOOST_FSM_RELAX_TRANSITION_CONTEXT). Moreover, I'll make (1) the default and describe (2) as an optional feature for the adventurous ;-). Regarding (2) I think I have found a model that, with limitations, allows for throwing exit(). The rules: a. All exit() functions are allowed to propagate exceptions at any time b. During normal operation, exit() will be called just before the state object is destructed c. exit() is not called when there is currently an exception "pending" (thrown from either a state constructor, a transition action or another exit() function). As soon as the exception has been handled (i.e. the state machine is stable), exit() is called again in the next transition or termination d. exit() is never called when a state machine is destructed These rules extend the ones already in place now. Admittedly, the whole rule set is rather complex and I'm not even sure whether it is water-tight in all situations. We'll see how that works out... Regards, Andreas P.S. I'll be off for my holidays by Friday at the latest and not be back before 1st of July. Unfortunately, I won't be able to implement anything beforehand and will be swamped with other stuff when I return. So, I'm afraid there won't be any progress before roughly mid-July. I hope you can wait until then.

One more and probably the strongest argument pro your pre-exit trick is that it also works nicely with a transition originating at an outer state. E.g. in the StopWatch state chart you want to do something special when a EvReset event is received and the machine is currently in the Stopped state. You'd then add a Stopped in-state reaction, which does what it has to do with whatever Stopped data members and them simply calls forward_event(). The event will then trigger the transition originating at the Active state. This is cannot be done with the change you proposed. Regards, Andreas

Darryl Green wrote:
On a vaguely related note - I have a question about in-state transitions. There is an example of a custom reaction to do this. However, there is no provision for an action associated with this transition. Obviously it is possible to write the action (or call it) as part of the custom reaction, but this seems irregular for a library that is trying to map directly from/to UML. My eyes glazed over a bit trying to follow the transition handling code to add some sort of an in_state_transition<> but the concept seems trivial enough. Having this facility means that the same action can be used for an in-state transition as for other transitions, which seems only reasonable, and the similar declaration should make developing/modifying a state's transitions easier.
I briefly considered this, but rejected it because in-state reactions can relatively nicely be implemented with custom_reactions. However, it does have advantages readability-wise. I'll work out the details and get back to you...
For that matter, I suppose the transition could actually specialise for the particular case of a transition back to the source state, and no special declaration would be needed?
No, that would be wrong. UML requires that in a self-transition the exit and the entry action is called (see StopWatch for an example where such a transition makes sense). This is not the case for an in-state reaction. Regards, Andreas

On Fri, May 28, 2004 at 12:29:01AM +0200, Andreas Huber wrote:
No, that would be wrong. UML requires that in a self-transition the exit and the entry action is called (see StopWatch for an example where such a transition makes sense). This is not the case for an in-state reaction.
What UML requires is not necessarily what is good for a generic FSM framework. The UML model is IMHO over complex and probably broken. An example is entry and exit actions for states. It is some what oxymoronic that a single state should actually consist of three sub-states because of entry and exit actions. The idea that they must be paired is particularly strange as one of the most common uses of the concept in the networl protocol work that I am familar with is prior to entering the idle stae some initialisation work is necessary and prior to re-entering an idle state from an exit event we want to do some clean-up. This whole concept is much better modeled *IMHO* by transitory states. These have the advantage that they do not have to be paired and they allow same/self-transition to be a no-op which is a very common requirement. Classical FSM's are a mapping from state/event pairs to behaviour and transitions. The Idea that a state should maintain its own data and thereby become stateful also seems to be bad modeling and against the KISS principle. My comments are based, so far, are based on a breif reading of the documentation and I plan to re-read it more carefully along with the implementation and provide further feedback. My gut feel is that this framework is too heavy weight and a one size fits all solution. Whereas I think that an FSM equivelent to STL collections would be a better approach. There are two other approaches to FSM's that do not apear to be discussed in the rationale; Herb Sutter, in an article hints at using boost::function for FSM's and the GOF state pattern tals of using singleton, flyweight states which are re-enterant and therefore offer exelent MT performance. /ikh

Iain K. Hanson wrote:
My comments are based, so far, are based on a breif reading of the documentation and I plan to re-read it more carefully along with the implementation and provide further feedback. My gut feel is that this framework is too heavy weight and a one size fits all solution. Whereas I think that an FSM equivelent to STL collections would be a better approach.
I've always been interested in crafting an STL-style approach to FSMs, with perhaps different implementations of state machines offering different FSM features (actions, conditions, composite states, history states, etc.) and different performance characteristics, similar to how the standard containers offer different features and performance characteristics. It seems like it might be a promising approach, but I've never had the time to flesh it out. Bob

Iain K. Hanson wrote:
What UML requires is not necessarily what is good for a generic FSM framework. The UML model is IMHO over complex and probably broken.
Yes, it is indeed broken (there is at least one contradiction in it). Whether it is overly complex or not depends on your application, i.e. how much abstraction you need/want to employ to implement the problem at hand. I have found that most of what is in there *can* be quite helpful.
An example is entry and exit actions for states. It is some what oxymoronic that a single state should actually consist of three sub-states because of entry and exit actions. The idea that they must be paired is particularly strange
Well, nobody forces you to pair them. My exit actions often don't do the exact opposite of my entry actions (e.g. see the StopWatch example). Sometimes I don't implement an exit action even if there is an entry action and vice versa.
as one of the most common uses of the concept in the networl protocol work that I am familar with is prior to entering the idle stae some initialisation work is necessary and prior to re-entering an idle state from an exit event we want to do some clean-up.
This whole concept is much better modeled *IMHO* by transitory states. These have the advantage that they do not have to be paired
You don't provide enough information for me to judge, but I agree that some stuff can only be implemented satisfactorily with transition actions (boost::fsm has them).
and they allow same/self-transition to be a no-op which is a very common requirement.
If you don't define entry/exit actions for a state without data members then a self-transition is essentially a no-op. If this doesn't convince you (e.g. because the state is still destructed and constructed once), you can implement this with an in-state reaction, which is a true no-op.
Classical FSM's are a mapping from state/event pairs to behaviour and transitions. The Idea that a state should maintain its own data and thereby become stateful also seems to be bad modeling and against the KISS principle.
Again, this depends on your application. You can find my arguments in the rationale (State-local storage, Speed versus Scalability tradeoffs). If this doesn't convince you then boost::fsm is not for you. I can't provide for everyone and I wouldn't be suprised to find more than one fsm framework in boost in say 5 years from now.
There are two other approaches to FSM's that do not apear to be discussed in the rationale; Herb Sutter, in an article hints at using boost::function for FSM's and the GOF state pattern tals of using singleton, flyweight states
Both of these approaches fail to meet so many of my requirements that I decided not to discuss them.
which are re-enterant and therefore offer exelent MT performance.
As I argue in the rationale, if you have FSMs residing in threads then the thread switching and locking overhead is typically much larger than the overhead introduced by boost::fsm. I measured on Windows and other OSes might be better in this regard but I still believe that the general rule applies. Regards, Andreas

Andreas Huber <ah2003@gmx.net> writes:
David Abrahams <dave <at> boost-consulting.com> writes:
I'm not only saying this because an active state happens to be an object in boost::fsm. State machines frequently acquire resources in entry actions and dispose of them in exit actions. Those resources would simply be leaked if there's no guarantee that exit actions are called under all non-fatal error situations.
It seems reasonable that well-designed state objects should also deallocate any resources they own in their destructors. It doesn't neccessarily seem reasonable as a consequence to force all exit actions into the state's destructor.
Are you proposing that an active state should still be represented by an object,
I don't have enough knowledge of the FSM domain to say whether that's a good design or not; I have to trust you that it makes sense to have a correspondence between states and distinct types whose instances' lifetimes correspond to the time spent in the state.
which acquires resources and executes entry actions in its constructor, releases resources in its destructor but executes exit actions in a separate function, which is called just before destructing the state object?
Yes, that's basically what I was saying. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

I don't know if this is helpful, but in my state machine implementations I move to the next state on success and on failure remain in the same state and throw an exception event event at that state, users are expected to configure the machine to handle exceptions at each state to transition into error handling behavior or ignore the error, or whatever. Events are propagated recursively beginning at the most granular state, so that parent state changes are only succeed when substate changes succeed. On May 24, 2004, at 1:49 AM, Andreas Huber wrote:
David Abrahams wrote:
"Andreas Huber" <ah2003@gmx.net> writes:
I don't think I have missed your point and I know that I'm talking to an exception handling expert. Have you read that whole paragraph?
Yep
Agreed, after posting I noticed that the first half isn't exactly the best explanation of why exit actions must not fail. However, I believe that the second half is a good argument why throwing exit actions are a bad idea: <quote> ... 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?
Stay at B, of course (?)
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.
I don't understand why. The user of the FSM could handle the exception on the outside if its state doesn't change.
Ok, I seem to finally see your point. You would want to leave the state machine in a possibly *invalid* state when propagating the exception to the state machine client, right? So what is the user then doing with the state machine object after handling that exception? Since the machine might be unstable he cannot process any more events. What's even more disturbing is the fact that he cannot even successfully destroy the state machine. Or are you proposing that the exit actions of the still active states should not be called upon destruction?
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. </quote>
A maybe more convincing argument: In the course of a transition a state entry action throws an exception. Note that the state machine is in an invalid state (unstable) when this happens. If the state machine does not successfully handle the exception, the exception must be propagated to the state machine client. Because the state machine is unstable, it *must* be terminated before propagating to the client.
OK so far...
Termination calls the exit actions of all currently active states.
that sounds like it could be the wrong design decision, to me.
Ok, but how do you propose should the state machine then be terminated? By simply not calling the exit actions of still active states?
Regards,
Andreas
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

John Fuller <jfuller <at> wernervas.com> writes:
I don't know if this is helpful, but in my state machine implementations I move to the next state on success and on failure remain in the same state and throw an exception event event at that state, users are expected to configure the machine to handle exceptions at each state to transition into error handling behavior or ignore the error, or whatever. Events are propagated recursively beginning at the most granular state, so that parent state changes are only succeed when substate changes succeed.
boost::fsm does exactly that and AFAICT Dave and I don't disagree on this behavior. What we disagree on is whether there should be a guarantee that exit actions are called under all circumstances or not. Regards, Andreas

"Andreas Huber" <ah2003@gmx.net> writes:
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).
OK, that's still dynamic allocation, but it may be good enough. Also, do I note that there's always function pointer indirection for event dispatch? While you clearly need something like that if there's an event queue, the MPL samples I posted earlier don't make you pay for that in the cases where you don't need it.
[snip]
Out of the box, state_machine<> does the following:
1. The exception is caught
By the state machine framework?
Yes.
Clarification in the text would be good.
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.
OK.
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.
OK.
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.
Thanks. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

David Abrahams wrote: [snip]
Also, do I note that there's always function pointer indirection for event dispatch?
I'm not sure I understand what you mean with "function pointer indirection". Event dispatch consists of a virtual function call followed by a linear search for a reaction matching the event. [snip]
By the state machine framework?
Yes.
Clarification in the text would be good.
Agreed. Regards, Andreas

"Andreas Huber" <ah2003@gmx.net> writes:
David Abrahams wrote:
[snip]
Also, do I note that there's always function pointer indirection for event dispatch?
I'm not sure I understand what you mean with "function pointer indirection". Event dispatch consists of a virtual function call followed by a linear search for a reaction matching the event.
That's what I mean. If you look at the FSM examples I posted, no virtual call is needed, and in fact the linear search could be optimized by the compiler into a hashed case statement or by metaprogramming into a log(n) search. The 2nd example uses a constant-time lookup, dispatching through a single function pointer. There are lots of dispatching tradeoffs available. I really like your state representation; it seems as though it should be possible to get all these different dispatching options with your state representation, rather than requiring a monolithic STT sequence to be assembled as my examples do. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

David Abrahams writes:
"Andreas Huber" <ah2003@gmx.net> writes:
David Abrahams wrote:
[snip]
Also, do I note that there's always function pointer indirection for event dispatch?
I'm not sure I understand what you mean with "function pointer indirection". Event dispatch consists of a virtual function call followed by a linear search for a reaction matching the event.
That's what I mean. If you look at the FSM examples I posted, no virtual call is needed, and in fact the linear search could be optimized by the compiler into a hashed case statement or by metaprogramming into a log(n) search. The 2nd example uses a constant-time lookup, dispatching through a single function pointer. There are lots of dispatching tradeoffs available.
I really like your state representation; it seems as though it should be possible to get all these different dispatching options with your state representation, rather than requiring a monolithic STT sequence to be assembled as my examples do.
A monolithic STT has many advantages, especially for small FSMs. -- Aleksey Gurtovoy MetaCommunications Engineering

Aleksey Gurtovoy <agurtovoy@meta-comm.com> writes:
I really like your state representation; it seems as though it should be possible to get all these different dispatching options with your state representation, rather than requiring a monolithic STT sequence to be assembled as my examples do.
A monolithic STT has many advantages, especially for small FSMs.
...and disadvantages for large ones. What are the advantages for small FSMs? -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

David Abrahams wrote:
"Andreas Huber" <ah2003@gmx.net> writes:
David Abrahams wrote:
[snip]
Also, do I note that there's always function pointer indirection for event dispatch?
I'm not sure I understand what you mean with "function pointer indirection". Event dispatch consists of a virtual function call followed by a linear search for a reaction matching the event.
That's what I mean. If you look at the FSM examples I posted, no virtual call is needed, and in fact the linear search could be optimized by the compiler into a hashed case statement or by metaprogramming into a log(n) search. The 2nd example uses a constant-time lookup, dispatching through a single function pointer. There are lots of dispatching tradeoffs available.
I finally found the time to have a closer look at your examples. I believe you can do away with one of the two double dispatch steps because you know at compile time exactly how the whole state machine looks like (i.e. your compile time algorithm "knows" all possible events and all possible states). This approach works well with small FSMs. It breaks down for large FSMs because either at some point the compiler will give up due to internal limits or the compilation times become unbearable. During state machine development, every little change (e.g. adding an event or a state) triggers a recompilation of the whole state machine. boost::fsm was designed to avoid these problems. You can spread a state machine over multiple translation units and even have multiple developers work on the same machine. Changes made by one developer (adding states, transitions, events to "his" part of the machine) will not trigger a recompilation of code implementing other parts of the machine. This virtually limitless scalability is possible exactly because there's no central algorithm that knows "everything". As a result, some stuff your examples can do at compile time has to be done at runtime by boost::fsm. More specifically, in your examples you know at compile time exactly which states have an outgoing transition for a given event. You therefore never need to runtime dispatch on an event that's known at compile time. boost::fsm does not have this information available at compile time. In fact, before a state becomes active at runtime my dispatch algorithm does not even know that such a state exists. Consequently, my dispatcher cannot know all the possible reactions for a given event and that's why I need to perform a linear search for the reaction after dispatching on the current state.
I really like your state representation; it seems as though it should be possible to get all these different dispatching options with your state representation, rather than requiring a monolithic STT sequence to be assembled as my examples do.
As long as no custom_reactions are used I think this should be possible, as all the necessary information is available at compile time in a single translation unit. However, I don't dare to explore what this would mean implementation-wise. It seems this pretty much requires a complete rewrite. I'd also need to change/extend the interface to provide replacements for some custom_reaction uses (guards and in-state reactions). Finally, I think custom_reactions are fundamentally incompatible with all dispatching options that implement one dispatch step at compile time. So, an interface-compatible boost::fsm variant offering the current as well as other dispatching options would somehow need to issue an error upon detecting a custom_reaction or somehow fall back to current dispatch algorithm... Regards, Andreas

"Andreas Huber" <ah2003@gmx.net> wrote in message news:c8ogtj$npe$1@sea.gmane.org...
Dear Boosters
I think boost::fsm has finally reached a state that is worthy of a formal review.
The .zip can be found here:
http://boost-sandbox.sf.net/fsm.zip
The docs are also available online:
http://boost-sandbox.sf.net/libs/fsm
As always: Feedback is highly welcome.
I've only read through the tutorial and glanced at the rationale, so bear with me if I've missed something out. I have not looked at the implementation. In brief it looks like some real good work has been invested into the development of the library! Some personal opinions though (of which I think some has been discussed otherwere in this thread): 1. No real support for runtime-defined fsm:s (yes, I've read the rationale). This is something that I personally _could_ live without, but would prefer not having to. I've definitely have had the need for (and implemented) a totally runtime-defined fsm. The problem would be how to implement this generically without the need for common interface classes ... hmmm. <speculation/>How about implementing two models within boost::fsm (dynamic + static model), or allow a hybrid approach?</speculation> 2. I'd prefer the states to be more loosely coupled (i.e. not having to know about other states at the same or higher levels, or the fsm itself). The fsm could take care of transitioning based on incoming events, current state and the abstract "result" of the event[*]. This way the state transitioning could be extended/changed without rewriting the states themselves. Exception: outer states should have some knowledge about inner states (they might be considered state-machines themselves). I guess by now my ignorance of the implementation shines through -> are enclosing states actually state machines already in boost::fsm? 3. The entering/exiting of a state is modeled by the construction/destruction of a state object (see details in subsections). 3.1 Entering/exiting a state should be decoupled from creating/destroying the state objects themselves. I can see the simplicity in creating the states when they are entered etc, but for me states live continuously within a fsm; they are just entered/leaved at certain points in time. And - if they happen to be active states (i.e. run by a thread or running a worker thread themselves) they might need to perform background activities even when they are inactive. 3.2. The initialization of a state could be potentially expensive; needing to initialize (construct) a state each time it is entered could result in intolerable performance (consider a state needing to setup a TCP connection). This could be handled in the current fsm implentation (I believe) by keeping such things in the fsm or enclosing state - but there's that coupling again. 3.3 I prefer (to the utmost extent) that initialization/construction is performed once, which should be during the application initialization. Once done, I'd like to know that my state objects (in this case) are constructed and that the will remain so for the rest of the execution; no memory allocation errors; no socket creation errors; ... etc .. during runtime [basically the same as 3.2 above]. (aka "do or die"). 4. Built-in support for active states would be great. Perhaps you've been reading Miro Samek's great articles in CUJ (if not, please do so). Providing at least some rudimentary (but expandable) support for active states would be ... great (am I reapeating myself or what). <speculation/>Provide an active_state base for states needing their own thread; provide an active_fsm that keeps a worker thread that is running the currently active state (which would require some cooperation from the states to implement transitions); or ... </speculation>. 5. If the dynamic stuff's never going to happen; perhaps provide an example on how to implement a runtime-defined fsm on top of boost::fsm (you briefly mention the possibility in the rationale) [related to 1]. [*] By abstract results I just mean some magical stuff telling the state machine what the result of the "reaction" was. It could possibly be allowed for a state to explicitly exit by using using the imaginary "return exit_state(succeeded_result())". Just my 0.05?. // Johan

Hi Johan,
I've only read through the tutorial and glanced at the rationale, so bear with me if I've missed something out. I have not looked at the implementation. In brief it looks like some real good work has been invested into the development of the library! Some personal opinions though (of which I think some has been discussed otherwere in this thread):
1. No real support for runtime-defined fsm:s (yes, I've read the rationale). This is something that I personally _could_ live without, but would prefer not having to. I've definitely have had the need for (and implemented) a totally runtime-defined fsm. The problem would be how to implement this generically without the need for common interface classes ... hmmm. <speculation/>How about implementing two models within boost::fsm (dynamic + static model), or allow a hybrid approach?</speculation>
Runtime-defined FSMs are fundamentally different implementation-wise from statically defined FSMs and IMHO the rationale clearly explains this. I believe a hybrid approach with a common implementation would inevitably throw away some of the advantages of statically defined FSMs. To retain these advantages, you'd need to have two different implementations. Even worse, the current interface is incompatible with dynamic FSMs, so a hybrid approach would basically need two different interfaces. So you end up with two mostly incompatible interfaces and two implementations and that's why I think that dynamically defined FSMs should be tackled in a separate library.
2. I'd prefer the states to be more loosely coupled (i.e. not having to know about other states at the same or higher levels, or the fsm itself). The fsm could take care of transitioning based on incoming events, current state and the abstract "result" of the event[*]. This way the state transitioning could be extended/changed without rewriting the states themselves.
Umm, I don't fully understand what you're getting at. Currently, a state only has to "know" its outer states and all the states it transitions to. With custom_reactions you can losen the latter relationships by shoving these dependencies into other translation units. So, in a way you can already change some aspects of a state without "rewriting" it.
Exception: outer states should have some knowledge about inner states (they might be considered state-machines themselves).
I don't see what that would buy you. I think it is a bad idea to have outer states know exactly what inner states they contain. This would make it close to impossible to spread machines over multiple translation units, which I consider a key feature of my library.
I guess by now my ignorance of the implementation shines through -> are enclosing states actually state machines already in boost::fsm?
Although states with inner states can be seen as separate state machines, they are not implemented this way, mainly for performance reasons.
3. The entering/exiting of a state is modeled by the construction/destruction of a state object (see details in subsections).
3.1 Entering/exiting a state should be decoupled from creating/destroying the state objects themselves. I can see the simplicity in creating the states when they are entered etc, but for me states live continuously within a fsm; they are just entered/leaved at certain points in time.
So you would still have objects for states but you'd create them when the state machine is created, right? This is contrary to the scalabilty requirements as a state machine would have to know exactly which states belong to it. I.e. you are forced to implement the whole state machine in a single translation unit.
And - if they happen to be active states (i.e. run by a thread or running a worker thread themselves) they might need to perform background activities even when they are inactive.
I've never heard about such a state machine feature. UML has do-activities that are associated with a certain state. When the state is left the do- activity is aborted.
3.2. The initialization of a state could be potentially expensive; needing to initialize (construct) a state each time it is entered could result in intolerable performance.
Yes, boost::fsm is definitly not for applications that absolutely need top- notch dispatch and transition performance. I've personally never seen an application where boost::fsm would be too slow (see Speed versus scalability tradeoffs in the rationale). But, I've seen applications with scalability problems during state machine development (huge amounts of generated code resulting in long compilation times, recompilation of the whole state machine every time a small detail is changed, etc.). That's why boost::fsm focuses on good scalability and trades some of the theoretically possible speed for it. I realize that this might be the wrong decision for a few applications but I expect that a wide range of applications will perform reasonably with boost::fsm. I have some feedback from an independent real world project that seems to confirm my view. Sure, this is only one project but it might nevertheless remove some doubts. I'll ask whether he is willing to share his experience with boosters. (consider a state needing to setup a TCP
connection). This could be handled in the current fsm implentation (I believe) by keeping such things in the fsm or enclosing state - but there's that coupling again.
You lost me here.
3.3 I prefer (to the utmost extent) that initialization/construction is performed once, which should be during the application initialization.
Are you suggesting that multiple state machine objects should use the same state objects?
Once done, I'd like to know that my state objects (in this case) are constructed and that the will remain so for the rest of the execution; no memory allocation errors;
If you want to avoid memory allocation errors that's no problem, just customize memory management and preallocate as much as will be needed by your application.
no socket creation errors; ... etc .. during runtime [basically the same as 3.2 above]. (aka "do or die").
No problem either, just allocate your socket first thing in an outermost state and never leave that state unless the state machine is terminated.
4. Built-in support for active states would be great. Perhaps you've been reading Miro Samek's great articles in CUJ (if not, please do so). Providing at least some rudimentary (but expandable) support for active states would be ... great (am I reapeating myself or what).
I'm not entirely sure what you mean with active state. Do you mean UML do- activities? If yes, then you can already implement these with a thread that is started in the entry action and cancelled (!) in the exit action of a particular state.
5. If the dynamic stuff's never going to happen; perhaps provide an example on how to implement a runtime-defined fsm on top of boost::fsm (you briefly mention the possibility in the rationale) [related to 1].
Huh? I guess I was dreaming when I wrote that. Where do I mention this? I don't see any way how a dynamic FSM could be implemented in terms of boost::fsm. Regards, Andreas

Hi Johan,
I've only read through the tutorial and glanced at the rationale, so bear with me if I've missed something out. I have not looked at the implementation. In brief it looks like some real good work has been invested into the development of the library! Some personal opinions though (of which I think some has been discussed otherwere in this thread):
1. No real support for runtime-defined fsm:s (yes, I've read the rationale). This is something that I personally _could_ live without, but would
not having to. I've definitely have had the need for (and implemented) a totally runtime-defined fsm. The problem would be how to implement this generically without the need for common interface classes ... hmmm. <speculation/>How about implementing two models within boost::fsm (dynamic + static model), or allow a hybrid approach?</speculation>
Runtime-defined FSMs are fundamentally different implementation-wise from statically defined FSMs and IMHO the rationale clearly explains this. I believe a hybrid approach with a common implementation would inevitably
away some of the advantages of statically defined FSMs. To retain these advantages, you'd need to have two different implementations. Even worse,
Andreas, "Andreas Huber" <ah2003@gmx.net> wrote in message news:loom.20040525T102414-987@post.gmane.org... prefer throw the
current interface is incompatible with dynamic FSMs, so a hybrid approach would basically need two different interfaces. So you end up with two mostly incompatible interfaces and two implementations and that's why I think that dynamically defined FSMs should be tackled in a separate library.
boost::fsm::static and boost::fsm::dynamic ? ;-)
2. I'd prefer the states to be more loosely coupled (i.e. not having to
know
about other states at the same or higher levels, or the fsm itself). The fsm could take care of transitioning based on incoming events, current state and the abstract "result" of the event[*]. This way the state transitioning could be extended/changed without rewriting the states themselves.
Umm, I don't fully understand what you're getting at. Currently, a state only has to "know" its outer states and all the states it transitions to.
That's mainly what I was getting at. With
custom_reactions you can losen the latter relationships by shoving these dependencies into other translation units. So, in a way you can already change some aspects of a state without "rewriting" it.
Sorry, "rewriting" was entirely incorrect; I referred to modifying the source code when a reaction (correct terminology?) should result in a transition to a newly implemented state (i.e. one not originally defined).
Exception: outer states should have some knowledge about inner states
(they
might be considered state-machines themselves).
I don't see what that would buy you. I think it is a bad idea to have outer states know exactly what inner states they contain. This would make it close to impossible to spread machines over multiple translation units, which I consider a key feature of my library.
I didn't intend to imply that states should _exactly_ know their inner states (i.e actual types). I was thinking more in terms of the implementation of a superstate as an fsm itself (and, more specifically I guess, dynamic fsm:s).
I guess by now my ignorance of the implementation shines through -> are enclosing states actually
machines already in boost::fsm?
Although states with inner states can be seen as separate state machines,
state they
are not implemented this way, mainly for performance reasons.
3. The entering/exiting of a state is modeled by the construction/destruction of a state object (see details in subsections).
3.1 Entering/exiting a state should be decoupled from creating/destroying the state objects themselves. I can see the simplicity in creating the states when they are entered etc, but for me states live continuously within a fsm; they are just entered/leaved at certain points in time.
So you would still have objects for states but you'd create them when the state machine is created, right?
No, before. This is contrary to the scalabilty
requirements as a state machine would have to know exactly which states belong to it. I.e. you are forced to implement the whole state machine in a single translation unit.
No, only if statically defined. I was thinking in runtime-terms again, perhaps: // // pseudo-code (no, I didn't think thoroughly before writing this) // from_state_id = fsm.add_state(<some-state>) to_state_id = fsm.add_state(<another-state>) fsm.add_transition(from_state_id, <on-event>, <event-result>, to_state_id) fsm.add_transition_to_self(from_state_id, <another-event>, <another-event-result>)
And - if they happen to be active states (i.e. run by a thread or running a worker
themselves) they might need to perform background activities even when
thread they
are inactive.
I've never heard about such a state machine feature. UML has do-activities that are associated with a certain state. When the state is left the do- activity is aborted.
I guess it's not in the formal definition of state machines. I'll try to give an example; suppose one state is responsible for updating the status of a remote entity via TCP when active; but the remote entity requires a periodic keep-alive so as not to close the connection (and - as I mention below - I'd obviously prefer not having to re-create the connection upon each state entry).
3.2. The initialization of a state could be potentially expensive;
to initialize (construct) a state each time it is entered could result in intolerable performance.
Yes, boost::fsm is definitly not for applications that absolutely need top- notch dispatch and transition performance. I've personally never seen an application where boost::fsm would be too slow (see Speed versus scalability tradeoffs in the rationale). But, I've seen applications with scalability problems during state machine development (huge amounts of generated code resulting in long compilation times, recompilation of the whole state machine every time a small detail is changed, etc.). That's why boost::fsm focuses on good scalability and trades some of the theoretically possible speed for it. I realize that this might be the wrong decision for a few applications but I expect that a wide range of applications will perform reasonably with boost::fsm. I have some feedback from an independent real world project that seems to confirm my view. Sure, this is only one project but it might nevertheless remove some doubts. I'll ask whether he is willing to share his experience with boosters.
(consider a state needing to setup a TCP
connection). This could be handled in the current fsm implentation (I believe) by keeping such things in the fsm or enclosing state - but
needing there's
that coupling again.
You lost me here.
I just meant that the state is dependent on its "container" to maintain non-volatile data.
3.3 I prefer (to the utmost extent) that initialization/construction is performed once, which should be during the application initialization.
Are you suggesting that multiple state machine objects should use the same state objects?
Once done, I'd like to know that my state objects (in this case) are
constructed
and that the will remain so for the rest of the execution; no memory allocation errors;
If you want to avoid memory allocation errors that's no problem, just customize memory management and preallocate as much as will be needed by your application.
Well, memory allocation problems are rarely a problem nowadays; it was just an example.
no socket creation errors; ... etc .. during runtime [basically the same as 3.2 above]. (aka "do or die").
No problem either, just allocate your socket first thing in an outermost
state
and never leave that state unless the state machine is terminated.
Isn't that quite a restriction? I do like the 'context' stuff, but in a complex statemachine the context could get bloated with unrelated functionality.
4. Built-in support for active states would be great. Perhaps you've
been
reading Miro Samek's great articles in CUJ (if not, please do so). Providing at least some rudimentary (but expandable) support for active states would be ... great (am I reapeating myself or what).
I'm not entirely sure what you mean with active state. Do you mean UML do- activities?
Oops, sorry. I realize the term 'active state' could easily be a source of confusion. I'm not referring to the currently active state in the fsm; I mean a state that performs its "own" activity when it is the fsm:s active state (i.e. even when not executing in the context of a call event or signal). I just looked up the term do-activity and it seems to model the concept rather nicely. If yes, then you can already implement these with a thread that is
started in the entry action and cancelled (!) in the exit action of a particular state.
Again, that's quite a heavy-weight solution. How about implementing the do-activity with a worker thread in the fsm itself (requires cooperation from within the active state, of course).
5. If the dynamic stuff's never going to happen; perhaps provide an
example
on how to implement a runtime-defined fsm on top of boost::fsm (you briefly mention the possibility in the rationale) [related to 1].
Huh? I guess I was dreaming when I wrote that. Where do I mention this? I don't see any way how a dynamic FSM could be implemented in terms of boost::fsm.
--- rationale excerpt --- Why not use a dynamically configurable FSM library for all state machines? ... It is for these reasons, that boost::fsm was built from ground up to not support dynamic configurability. However, this does not mean that it's impossible to dynamically shape a machine implemented with this library. ... --- end rationale excerpt --- Or do I misinterpret the meaning of the text? // Johan

From: "Johan Nilsson" <johan.nilsson@esrange.ssc.se>
"Andreas Huber" <ah2003@gmx.net> wrote in message
3. The entering/exiting of a state is modeled by the construction/destruction of a state object (see details in subsections).
3.1 Entering/exiting a state should be decoupled from
creating/destroying
the state objects themselves. I can see the simplicity in creating the states when they are entered etc, but for me states live continuously within a fsm; they are just entered/leaved at certain points in time.
So you would still have objects for states but you'd create them when the state machine is created, right?
No, before.
This is contrary to the scalabilty
requirements as a state machine would have to know exactly which states belong to it. I.e. you are forced to implement the whole state machine in a single translation unit.
No, only if statically defined. I was thinking in runtime-terms again, perhaps:
// // pseudo-code (no, I didn't think thoroughly before writing this) // from_state_id = fsm.add_state(<some-state>) to_state_id = fsm.add_state(<another-state>) fsm.add_transition(from_state_id, <on-event>, <event-result>, to_state_id) fsm.add_transition_to_self(from_state_id, <another-event>, <another-event-result>)
Could the fsm manage a container of (pointers to) state objects that it populates as it first creates each state object and from which it retrieves a state object when transitioning? IOW, the first time the fsm encounters the need to transition to a given state (from A to B, say), it creates B and tracks it in the container. Upon transition back to A, the fsm locates state A in the container and reuses it. With such an approach, the state objects are only created and destroyed once, and the fsm simply calls enter() and exit() member functions at the appropriate times. That means that states are added on demand, rather than all "up front," but once added, the fsm simply reuses them, avoiding free store machinations and overhead. BTW, not relying on a dtor as the "exit method" means that an exit transition can throw exceptions. -- Rob Stewart stewart@sig.com Software Engineer http://www.sig.com Susquehanna International Group, LLP using std::disclaimer;

Hi Rob,
Could the fsm manage a container of (pointers to) state objects that it populates as it first creates each state object and from which it retrieves a state object when transitioning? IOW, the first time the fsm encounters the need to transition to a given state (from A to B, say), it creates B and tracks it in the container. Upon transition back to A, the fsm locates state A in the container and reuses it.
This is certainly possible. I just have my doubts whether this approach is so much better speed-wise. If you customize state memory management, construction and destruction doesn't take an awful amount of cycles. Searching that state container might well take longer for large FSMs. Regards, Andreas

From: Andreas Huber <ah2003@gmx.net>
Could the fsm manage a container of (pointers to) state objects that it populates as it first creates each state object and from which it retrieves a state object when transitioning? IOW, the first time the fsm encounters the need to transition to a given state (from A to B, say), it creates B and tracks it in the container. Upon transition back to A, the fsm locates state A in the container and reuses it.
This is certainly possible. I just have my doubts whether this approach is so much better speed-wise. If you customize state memory management, construction and destruction doesn't take an awful amount of cycles. Searching that state container might well take longer for large FSMs.
That's certainly an important consideration, but I was thinking in terms of something built into the fsm framework, thus providing the benefit for all clients versus requiring clients to do work if they want the speed improvement. If your concern proves correct, then using the container approach isn't helpful. However, the states' base class, which I presume is part of your library, could provide a pool allocator for state objects. That would put the onus on the library, rather than the clients, and would still reduce the (perceived, not proven) overhead caused by creating and destroying state objects on each transition. Don't forget, though, that part of my proposal was that state objects would have enter() and exit() mfs rather than relying on the ctor and dtor for entry and exit, respectively. That can simplify matters when dealing with exceptions. -- Rob Stewart stewart@sig.com Software Engineer http://www.sig.com Susquehanna International Group, LLP using std::disclaimer;

Rob Stewart wrote:
From: Andreas Huber <ah2003@gmx.net>
Could the fsm manage a container of (pointers to) state objects that it populates as it first creates each state object and from which it retrieves a state object when transitioning? IOW, the first time the fsm encounters the need to transition to a given state (from A to B, say), it creates B and tracks it in the container. Upon transition back to A, the fsm locates state A in the container and reuses it.
This is certainly possible. I just have my doubts whether this approach is so much better speed-wise. If you customize state memory management, construction and destruction doesn't take an awful amount of cycles. Searching that state container might well take longer for large FSMs.
That's certainly an important consideration, but I was thinking in terms of something built into the fsm framework, thus providing the benefit for all clients versus requiring clients to do work if they want the speed improvement. If your concern proves correct, then using the container approach isn't helpful. However, the states' base class, which I presume is part of your library, could provide a pool allocator for state objects. That would put the onus on the library, rather than the clients, and would still reduce the (perceived, not proven) overhead caused by creating and destroying state objects on each transition.
<rationale quote> simple_state<> and state<> subclass objects are constructed and destructed only by the state machine. It would therefore be possible to use the state_machine<> allocator instead of forcing the user to overload operator new() and operator delete(). However, a lot of systems employ at most one instance of a particular state machine, which means that a) there is at most one object of a particular state and b) this object is always constructed, accessed and destructed by one and the same thread. We can exploit these facts in a much simpler (and faster) new/delete implementation (for example, see UniqueObject.hpp in the BitMachine example). However, this is only possible as long as we have the freedom to customize memory management for state classes separately. </rationale quote> Building a pool allocator into state_machine<> or even using the one that the user can pass would mean to needlessly throw away some opportunities for optimization. The user has to make that decision.
Don't forget, though, that part of my proposal was that state objects would have enter() and exit() mfs rather than relying on the ctor and dtor for entry and exit, respectively. That can simplify matters when dealing with exceptions.
The longer I think about it the more am I convinced that separate entry() and exit() functions don't buy you anything w.r.t to exception handling (see my discussion with Dave). Regards, Andreas

"Andreas Huber" <ah2003@gmx.net> wrote in message news:c90n7i$gbn$1@sea.gmane.org...
Rob Stewart wrote:
From: Andreas Huber <ah2003@gmx.net>
Could the fsm manage a container of (pointers to) state objects that it populates as it first creates each state object and from which it retrieves a state object when transitioning? IOW, the first time the fsm encounters the need to transition to a given state (from A to B, say), it creates B and tracks it in the container. Upon transition back to A, the fsm locates state A in the container and reuses it.
This is certainly possible. I just have my doubts whether this approach is so much better speed-wise. If you customize state memory management, construction and destruction doesn't take an awful amount of cycles. Searching that state container might well take longer for large FSMs.
That's certainly an important consideration, but I was thinking in terms of something built into the fsm framework, thus providing the benefit for all clients versus requiring clients to do work if they want the speed improvement. If your concern proves correct, then using the container approach isn't helpful. However, the states' base class, which I presume is part of your library, could provide a pool allocator for state objects. That would put the onus on the library, rather than the clients, and would still reduce the (perceived, not proven) overhead caused by creating and destroying state objects on each transition.
<rationale quote> simple_state<> and state<> subclass objects are constructed and destructed only by the state machine. It would therefore be possible to use the state_machine<> allocator instead of forcing the user to overload operator new() and operator delete(). However, a lot of systems employ at most one instance of a particular state machine, which means that a) there is at most one object of a particular state and b) this object is always constructed, accessed and destructed by one and the same thread. We can exploit these facts in a much simpler (and faster) new/delete implementation (for example, see UniqueObject.hpp in the BitMachine example). However, this is only possible as long as we have the freedom to customize memory management for state classes separately. </rationale quote>
Building a pool allocator into state_machine<> or even using the one that the user can pass would mean to needlessly throw away some opportunities for optimization. The user has to make that decision.
Don't forget, though, that part of my proposal was that state objects would have enter() and exit() mfs rather than relying on the ctor and dtor for entry and exit, respectively. That can simplify matters when dealing with exceptions.
The longer I think about it the more am I convinced that separate entry() and exit() functions don't buy you anything w.r.t to exception handling (see my discussion with Dave).
He's definitely the expert on the subject in question. Exception handling aside for a moment, I still would strongly prefer to have separate entry/exit methods in the state object interface; it more clearly relates to the conceptual view of FSMs. And, while we're at it: perhaps the entry()/exit() points should be user configurable (per-state-type) to correspond to the UML notation e.g. "entry() / init()", "exit() / cleanup()" IIRC. As I haven't been following your discussion with Dave; is this also a performance concern (function call overhead)? // Johan

Johan Nilsson <johan.nilsson <at> esrange.ssc.se> writes:
He's definitely the expert on the subject in question. Exception handling aside for a moment, I still would strongly prefer to have separate entry/exit methods in the state object interface; it more clearly relates to the conceptual view of FSMs. And, while we're at it: perhaps the entry()/exit() points should be user configurable (per-state-type) to correspond to the UML notation e.g. "entry() / init()", "exit() / cleanup()" IIRC.
Again, this is a matter of simplicity. Agreed, entry() and exit() are more understandable for someone who is proficient in the FSM domain. However, how long does it take to explain that the entry action is mapped to the state constructor and the exit action is mapped to the state destructor? Probably 10 seconds. If something can be explained that quickly, I'm very sceptical about the benefit of introducing a *new feature* just for the sake of understandability. Remember, even if we had entry() and exit(), we would still need state objects that are created on entry and destructed on exit. This is crucial to satisfy the scalability requirements.
As I haven't been following your discussion with Dave; is this also a performance concern (function call overhead)?
Yes, I think so. I currently don't see any way how entry()/exit() could be implemented without causing overhead for people not using them, although I vaguely recall that Dave once hinted in this direction. Regards, Andreas

Andreas Huber wrote:
Johan Nilsson <johan.nilsson <at> esrange.ssc.se> writes:
He's definitely the expert on the subject in question. Exception handling aside for a moment, I still would strongly prefer to have separate entry/exit methods in the state object interface; it more clearly relates to the conceptual view of FSMs. And, while we're at it: perhaps the entry()/exit() points should be user configurable (per-state-type) to correspond to the UML notation e.g. "entry() / init()", "exit() / cleanup()" IIRC.
Again, this is a matter of simplicity. Agreed, entry() and exit() are more understandable for someone who is proficient in the FSM domain. However, how long does it take to explain that the entry action is mapped to the state constructor and the exit action is mapped to the state destructor? Probably 10 seconds. If something can be explained that quickly, I'm very sceptical about the benefit of introducing a *new feature* just for the sake of understandability. Remember, even if we had entry() and exit(), we would still need state objects that are created on entry and destructed on exit. This is crucial to satisfy the scalability requirements.
Here's one argument in favor of entry()/exit() over constructor/destructor. If you use constructors/destructors, your hands will be tied if someday you want to change your mind about the implementation of state entry and exit constructing and destructing states (a couple of alternatives have been discussed here, such as constructing states when they're entered but not destroying them when they're exited). If instead you implement entry and exit with entry() and exit() member functions, it doesn't matter if the states are constructed and destructed as they are now, constructed at entry point and destroyed at machine destruction time, or constructed all at once at machine construction time. I don't have any idea if this flexibility is important or not, but if I was implementing fsm, I'd consider it. Bob

Robert Bell wrote:
Here's one argument in favor of entry()/exit() over constructor/destructor. If you use constructors/destructors, your hands will be tied if someday you want to change your mind about the implementation of state entry and exit constructing and destructing states (a couple of alternatives have been discussed here, such as constructing states when they're entered but not destroying them when they're exited). If instead you implement entry and exit with entry() and exit() member functions, it doesn't matter if the states are constructed and destructed as they are now, constructed at entry point and destroyed at machine destruction time, or constructed all at once at machine construction time.
I don't have any idea if this flexibility is important or not, but if I was implementing fsm, I'd consider it.
Believe me, I *am* considering it. I even started dreaming about it :-). However, no matter from what angle I start to think this through I always end up with the same bad feeling. If we introduce entry()/exit() for the reasons you give (more flexibility for optimization?) this essentially means to tell the users that they must not under any circumstances rely on when exactly constructors and destructors of state objects are called. Even worse, they must not even rely on how many times state ctors/dtors are called. Today they might be called exactly as often as entry()/exit() but tomorrow they might be called only exactly once. This means that most non-POD data members of states need to be created on the heap, as you otherwise wouldn't have a chance to create them inside entry() and destroy them inside exit(). And believe me, you definitely need to do that when you design non-trivial FSMs. Therefore, to me entry()/exit() means falling at least halfway back into medieval times where you always had to explictly call constructors and destructors yourself (Turbo Pascal). Please have a look at the StopWatch example and imagine the additional complexity introduced with entry()/exit() (and ctors/dtors called at unspecified times). I only want to go there if there are compelling reasons: - I believe that boost::fsm is already reasonably fast (and there is still some potential for optimization). Nobody who is actually using it has ever complained that it is too slow. Someone has even reported that it performs "very well". - Nobody has so far presented hard technical facts that support the view that mapping entry/exit to ctor/dtor is wrong. To the contrary, I think I have almost proved in my discussion with Dave that exit actions must not fail and that you *can* recover from failing entry actions. Regards, Andreas

"Andreas Huber" <ah2003@gmx.net> writes:
To the contrary, I think I have almost proved in my discussion with Dave that exit actions must not fail
IIUC your almost-proof that they must not fail is based on the assumption that they're done with destructors. That would be circular reasoning. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

On May 26, 2004, at 4:30 PM, David Abrahams wrote:
"Andreas Huber" <ah2003@gmx.net> writes:
To the contrary, I think I have almost proved in my discussion with Dave that exit actions must not fail
IIUC your almost-proof that they must not fail is based on the assumption that they're done with destructors. That would be circular reasoning.
As I understood it, the argument was that to make it possible for entry actions to fail it must be impossible for exit actions to fail. So to me this design looks like "resource acquisition is initialization", but I might be missing something.

Gregory Colvin <gregory.colvin@oracle.com> writes:
On May 26, 2004, at 4:30 PM, David Abrahams wrote:
"Andreas Huber" <ah2003@gmx.net> writes:
To the contrary, I think I have almost proved in my discussion with Dave that exit actions must not fail
IIUC your almost-proof that they must not fail is based on the assumption that they're done with destructors. That would be circular reasoning.
As I understood it, the argument was that to make it possible for entry actions to fail it must be impossible for exit actions to fail. So to me this design looks like "resource acquisition is initialization", but I might be missing something.
That argument was wrong AFAICT. Exits happen before entries, and you can't undo an exit; that's status quo. If you get arrive at the point where an entry is about to fail, then the preceding exit didn't fail, so the possibility of a failing exit has no impact. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

David Abrahams <dave <at> boost-consulting.com> writes:
As I understood it, the argument was that to make it possible for entry actions to fail it must be impossible for exit actions to fail. So to me this design looks like "resource acquisition is initialization", but I might be missing something.
That argument was wrong AFAICT.
I don't think so. Exactly the same arguments as with C++ ctors/dtors apply to entry and exit actions (and to make this very clear: I don't say this because the boost::fsm design maps entry/exit to ctor/dtor). Why must a C++ destructor not fail (I guess I don't have to spell this out)? Exactly the same reasons apply to exit actions! If C++ destructors could fail, that would make it impossible to correctly bail out of most error situations. Exactly the same argument applies to exit actions (even if you implement the exit action in a normal function).
Exits happen before entries, and you can't undo an exit; that's status quo. If you get arrive at the point where an entry is about to fail, then the preceding exit didn't fail, so the possibility of a failing exit has no impact.
As I have tried to explain before: The problem lies in the fact that you can have multiple exit actions called in a row before entry actions are called. If e.g. the second of those exit actions fails (the first one succeeded) then your state machine is in an invalid state from where it is impossible to recover. What exactly is the loophole/problem/circularity in this reasoning? Regards, Andreas

"Andreas Huber" <ah2003@gmx.net> wrote in message news:loom.20040527T171933-644@post.gmane.org...
[...] Why must a C++ destructor not fail (I guess I don't have to spell this out)?
Actually, it would have helped if you did. The reason d'tors must not fail is that a fundamental guarantee of C++ is that d'tors are always called on exit of the enclosing scope. Throwing d'tors break this guarantee.
Exactly the same reasons apply to exit actions!
Not quite. If there were a compelling reason that all exit actions should be called upon leaving state, then the situations would be analogous. But Dave is arguing that there is *not* such a compelling reason. That's where the disagreement comes in.
If C++ destructors could fail, that would make it impossible to correctly bail out of most error situations.
Yes, but that's not the first reason why we have non-throwing d'tors, even though it *is* the reason why you want to have non-throwing exit actions.
Exactly the same argument applies to exit actions (even if you implement the exit action in a normal function). [...]
So in reality, your claim is that throwing exit actions would would mask the original error, and thus make it difficult to impossible to handle the original exception (and leaving your FSM in an invalid state). But this is an artifact of your exception handling model, and nothing intrinsic to exit actions. (It seems that you could have the same problem with entry actions). I still think it's a good argument, but the association with d'tors distracts from your main point. Dave --- Outgoing mail is certified Virus Free. Checked by AVG anti-virus system (http://www.grisoft.com). Version: 6.0.691 / Virus Database: 452 - Release Date: 5/26/2004

David B. Held wrote:
"Andreas Huber" <ah2003@gmx.net> wrote in message news:loom.20040527T171933-644@post.gmane.org...
[...] Why must a C++ destructor not fail (I guess I don't have to spell this out)?
Actually, it would have helped if you did. The reason d'tors must not fail is that a fundamental guarantee of C++ is that d'tors are always called on exit of the enclosing scope. Throwing d'tors break this guarantee.
As I said, the same applies to exit actions, see below.
Exactly the same reasons apply to exit actions!
Not quite. If there were a compelling reason that all exit actions should be called upon leaving state, then the situations would be analogous.
But Dave is arguing that there is *not* such a compelling reason. That's where the disagreement comes in.
David Harel (the inventor of state charts), the UML standard and all other standards about state machines require that the exit action is called when a state is left. They also require that all states are left when the state machine is terminated. AFAICT, the only assumption I make is that a state machine must be terminated when it is destructed. To me, this alone is compelling enough. However, my assumption might well be wrong and/or the standards bodies might all have got it wrong. So lets have a look at how one typically uses entry and exit actions. Often, something that is done in an entry action of a particular state is undone in the exit action of the state. I'm not just talking about the acquisition of resources. A dishwasher for example, could turn on a Pump upon state entry and turn it off when the state is left. IOW, FSMs frequently do rely on that an entry action call is always matched with the correponding exit action call when the state is left (otherwise in certain situations our Pump might continue pumping until you pull the plug). I think everyone who has ever developed non-trivial FSMs will agree with me on this. Unfortunately, all FSM standards/publications I know are absolutely silent on error handling. Maybe I'm naive, but all I did is work out an error handling system that under all circumstances retains the guarantee that an entry action call is always matched with an exit action call. It is an inevitable consequence of the guarantee that exit actions must not fail. Regards, Andreas

From: "Andreas Huber" <ah2003@gmx.net> [...]
Unfortunately, all FSM standards/publications I know are absolutely silent on error handling.
I can understand why. If you are referring to "error" as something unexpected, then it is out of the state machine realm. The state machine mantra is complete behavioral determinism. If "error" is an expected condition, then it is just a normal event that is part of the state machine design. Best, Eugene

E. Gladyshev wrote:
From: "Andreas Huber" <ah2003@gmx.net> [...]
Unfortunately, all FSM standards/publications I know are absolutely silent on error handling.
I can understand why. If you are referring to "error" as something unexpected, then it is out of the state machine realm.
If the errors were unexpected it would make little sense to handle them in the state machine, right?
The state machine mantra is complete behavioral determinism. If "error" is an expected condition, then it is just a normal event that is part of the state machine design.
When an entry action fails, you have the problem that the state machine is unstable because it would be a bad idea to just continue to enter other (inner) states (see Rationale). Since it is non-trivial to define what to do in such a situation, I think it is unfortunate that all these standards are silent on error handling. They implicitly force you define all your entry and transition actions in a way so that they can never fail. Regards, Andreas

----- Original Message ----- From: "Andreas Huber" <ah2003@gmx.net>
E. Gladyshev wrote:
From: "Andreas Huber" <ah2003@gmx.net> [...]
Unfortunately, all FSM standards/publications I know are absolutely silent on error handling.
I can understand why. If you are referring to "error" as something unexpected, then it is out of the state machine realm.
If the errors were unexpected it would make little sense to handle them in the state machine, right?
Right.
The state machine mantra is complete behavioral determinism. If "error" is an expected condition, then it is just a normal event that is part of the state machine design.
When an entry action fails, you have the problem that the state machine is unstable because it would be a bad idea to just continue to enter other (inner) states (see Rationale). Since it is non-trivial to define what to do in such a situation, I think it is unfortunate that all these standards are silent on error handling. They implicitly force you define all your entry and transition actions in a way so that they can never fail.
Agree. All these standards imply that by definition nothing *unpredictable* can happen to the state machine. Eugene

"David B. Held" <dheld@codelogicconsulting.com> writes:
Exactly the same reasons apply to exit actions!
Not quite. If there were a compelling reason that all exit actions should be called upon leaving state, then the situations would be ^^^^^ analogous. But Dave is arguing that there is *not* such a compelling reason.
No I'm not. "called upon leaving state" is almost the definition of an exit action. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

Andreas Huber <ah2003@gmx.net> writes:
Exits happen before entries, and you can't undo an exit; that's status quo. If you get arrive at the point where an entry is about to fail, then the preceding exit didn't fail, so the possibility of a failing exit has no impact.
As I have tried to explain before: The problem lies in the fact that you can have multiple exit actions called in a row before entry actions are called. If e.g. the second of those exit actions fails (the first one succeeded) then your state machine is in an invalid state from where it is impossible to recover. What exactly is the loophole/problem/circularity in this reasoning?
It seems to me that part of the problem lies in how you define "recover". You seem to think that if an *entry* action fails after exiting a state there is a sensible meaning of "recover" that can always be achieved, while if a (2nd) exit action fails in the same circumstances no sensible recovery is possible. I don't understand how that can be, but as I've said many times, I'm probably missing something. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

David Abrahams wrote:
It seems to me that part of the problem lies in how you define "recover". You seem to think that if an *entry* action fails after exiting a state there is a sensible meaning of "recover" that can always be achieved, while if a (2nd) exit action fails in the same circumstances no sensible recovery is possible. I don't understand how that can be, but as I've said many times, I'm probably missing something.
I give up. I will never convince you this way. My reasoning obviously too often assumes that the reader agrees with things that I believe are well-established in the FSM domain. Please forget everything I've said so far and consider only what follows. As the subject says, I'm only trying to argue that exit actions in state machines must never fail (i.e. that it is a bad idea to add the possibility of failure of exit actions to the behavior defined in the UML standard). Neither am I trying to justify my exception handling system nor do I assume any kind of implementation (especially not that exit actions are implemented with destructors). I do assume that the hypothetical FSM library is written in standard C++. What follows somewhat repeats arguments in another post of mine but I think it is beneficial to do so for maximum clarity. Facts: F1. UML standard 2.12.4.2 (State entry and exit): "Whenever a state is entered, it executes its entry action before any other action is executed. Conversely, whenever a state is exited, it executes its exit action as the final step prior to leaving the state." F2. The UML standard defines that all currently active states are left when a state machine is terminated. I do not quote the text here as in the UML standard this is not a single definition but consists of what a final state is (2.12.2.5) and how states are exited when a transition is made (2.12.4.6). What is called "terminating the state machine" in this post (and in the boost::fsm documentation) is called "making a transition to the final state" in UML. F3. UML standard 2.12.4.3 (Exiting non-concurrent state): "When exiting from a composite state, the active substate is exited recursively. This means that the exit actions are executed in sequence starting with the innermost active state in the current state configuration." Assumptions: A1. When a state machine object is destructed, the modeled state machine must also be terminated (i.e. the destructor of the state machine unconditionally terminates the state machine before returning to the client). Actually, the UML standard in one place (2.12.4.4) hints in this direction but it is far from clear whether this assumption is covered by the UML standard (and could thus be put in the hard facts section). A2. If an exit action fails, the state associated with the exit action has not been left. If exit actions were allowed to fail, then the following problems/inconsistencies/loopholes would arise: P1. Since C++ destructors must not throw and cannot return a value, an exit action failure cannot be propagated up the call chain to the caller of the state machine destructor. P2. A state machine termination can involve the calling of multiple exit actions (F2). If one of these exit actions fails, the state associated with the action has not yet been left (A2) and the termination process cannot continue because F3 defines the order in which the states must be left. Neither can it abort the termination and return to the client normally as that would violate A1. Conclusion: Since P1 & P2 are very directly caused by failing exit actions, I conclude that state exit actions must not fail. I realize that I have only provided reasoning for when a state machine is destructed and not for its normal operation. So one could argue that only those exit actions that can possibly be called from the state machine destructor must not throw. However, sometimes state machines need to be destructed pre-maturely (e.g. a human initiated a system shutdown) and the caller of the destructor has no means to bring the state machine into the "right" state before destruction. So, pretty much no exit action should be allowed to fail. I realize that the above reasoning is far from water-tight, as it is based on two assumptions. However, I believe that most people proficient in the FSM domain will agree that it is safe to assume A1 & A2. Moreover, one could also question whether the UML standard should have any bearing for an FSM library that implements behavior that is not covered in the standard (i.e. exit action failures). I see failing exit actions as an addition or enhancement to the behavior defined in the standard. If such an enhancement breaks the behavior defined in the standard, I think the enhancement should be rejected. The above is the best reasoning I can currently give to show that failing exit actions do not make any sense. For me, this is more than enough to not ever consider them again (at least for an FSM library implemented in C++). I will answer questions arising from this post and I am interested in feedback whether this convinces people but I will not try to reason any further as I have run out of arguments and I think I have already invested too much time into this. Regards, Andreas

"Andreas Huber" <ah2003@gmx.net> writes:
Assumptions: A1. When a state machine object is destructed, the modeled state machine must also be terminated (i.e. the destructor of the state machine unconditionally terminates the state machine before returning to the client).
I've got a problem with that assumption. It seems to be an association made because of a conflation of C++'s notions of termination with the UML standard's use of the word "terminate" to denote something unrelated, at least as you described it in snipped text.
Actually, the UML standard in one place (2.12.4.4) hints in this direction but it is far from clear whether this assumption is covered by the UML standard (and could thus be put in the hard facts section).
A2. If an exit action fails, the state associated with the exit action has not been left.
I'm fine with that assumption. In fact, that seems provable. <snip>
I realize that the above reasoning is far from water-tight, as it is based qon two assumptions. However, I believe that most people proficient in the FSM domain will agree that it is safe to assume A1 & A2.
I can't speak for them, but as I said, A1 seems far from obvious to me.
Moreover, one could also question whether the UML standard should have any bearing for an FSM library that implements behavior that is not covered in the standard (i.e. exit action failures). I see failing exit actions as an addition or enhancement to the behavior defined in the standard. If such an enhancement breaks the behavior defined in the standard, I think the enhancement should be rejected.
Another thing I should mention: the finite state machine abstraction was around long before UML and will stay around long after UML has crumbled to dust because of its overbearing weight and its OO orthodoxy <wink>. Actually I think that UML _state charts_ seem like a pretty good representation, but I'm not sure that I would want to read the UML spec like a bible when approaching the project of building a C++ state machine framework.
The above is the best reasoning I can currently give to show that failing exit actions do not make any sense. For me, this is more than enough to not ever consider them again (at least for an FSM library implemented in C++).
Well, I guess we're done then.
I will answer questions arising from this post and I am interested in feedback whether this convinces people but I will not try to reason any further as I have run out of arguments and I think I have already invested too much time into this.
I know how you feel ;-) -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

David Abrahams wrote:
"Andreas Huber" <ah2003@gmx.net> writes:
Assumptions: A1. When a state machine object is destructed, the modeled state machine must also be terminated (i.e. the destructor of the state machine unconditionally terminates the state machine before returning to the client).
I've got a problem with that assumption. It seems to be an association made because of a conflation of C++'s notions of termination with the UML standard's use of the word "terminate" to denote something unrelated, at least as you described it in snipped text.
I suspected that we will disagree on this.
A2. If an exit action fails, the state associated with the exit action has not been left.
I'm fine with that assumption. In fact, that seems provable.
Yes, I realized that after posting. F1 provides strong arguments for A2.
Moreover, one could also question whether the UML standard should have any bearing for an FSM library that implements behavior that is not covered in the standard (i.e. exit action failures). I see failing exit actions as an addition or enhancement to the behavior defined in the standard. If such an enhancement breaks the behavior defined in the standard, I think the enhancement should be rejected.
Another thing I should mention: the finite state machine abstraction was around long before UML and will stay around long after UML has crumbled to dust because of its overbearing weight and its OO orthodoxy <wink>.
:-)
Actually I think that UML _state charts_ seem like a pretty good representation, but I'm not sure that I would want to read the UML spec like a bible when approaching the project of building a C++ state machine framework.
I'm reading it like a bible only when it helps to argue my case ;-). Otherwise boost::fsm takes many (hopefully undisputed) diversions from the UML standard (see http://tinyurl.com/33vht, UML to boost::fsm mapping summary). Regards, Andreas

"Andreas Huber" <ah2003@gmx.net> writes:
Actually I think that UML _state charts_ seem like a pretty good representation, but I'm not sure that I would want to read the UML spec like a bible when approaching the project of building a C++ state machine framework.
I'm reading it like a bible only when it helps to argue my case ;-).
I know there's a smiley, but you're not doing your credibility any favors with that statement. It makes me wonder how many other decisions in the design have such a flimsy foundation. If you want to convince _me_ (I'm not saying that's important or anything), you're going to have to make the case for why an FSM object should make transitions (or state changes, if you like) when it is destroyed. To me it seems like there are very obvious use cases supporting the argument that it should *not* happen. Furthermore, it seems as though, if you want that behavior, it's trivial to get it by wrapping the FSM in a derived class whose destructor finalizes the state machine. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

David Abrahams wrote:
I'm reading it like a bible only when it helps to argue my case ;-).
I know there's a smiley, but you're not doing your credibility any favors with that statement. It makes me wonder how many other decisions in the design have such a flimsy foundation.
I think a foundation for a decision is not flimsy when the behavior in question has proved to be useful in practice. So far nobody, who seems to have experience with non-trivial FSMs, has doubted that it is useful to terminate a state machine when it is destructed (my assumption A1). BTW, as pointed out by someone else the user *does* have a choice. If she happens to have an exit action that she'd rather not have executed upon destruction then she can always transform the machine part in question such that her action is executed as part of a transition action instead (I would consider it a design error not to do so). Every Moore machine (an FSM that has only entry and exit actions) can be transformed to a Mealy machine (an FSM that has only transition actions) and vice versa.
If you want to convince _me_ (I'm not saying that's important or anything), you're going to have to make the case for why an FSM object should make transitions (or state changes, if you like) when it is destroyed. To me it seems like there are very obvious use cases supporting the argument that it should *not* happen.
As pointed out above, you *can* implement your FSM in a way such that no action is ever executed upon destruction. Whether such a machine is typically well-designed is an altogether different question (I'm not saying that such a machine is always badly designed). State machines do have a theoretical/mathematical background. Despite this background, I need to make some assumptions when I model them in C++. I strongly believe that not making such assumptions would have lead to an interface that is more complex than necessary for most real-world applications. For one in X applications an assumption could well turn out to be wrong and the application needs to resort to work-arounds as a result (as outlined above). To me, said assumption is justified if X turns out to be sufficiently high. The assumptions are based on practical experience, which is of course not provable. Since you seem to accept only strictly provable arguments, I will never be able to convince you.
Furthermore, it seems as though, if you want that behavior, it's trivial to get it by wrapping the FSM in a derived class whose destructor finalizes the state machine.
Yes, but only at the cost of making the current interface (and implementation) more complex (we'd need to have at least a separate exit() function, right?). Regards, Andreas

Andreas Huber wrote:
BTW, as pointed out by someone else the user *does* have a choice. If she happens to have an exit action that she'd rather not have executed upon destruction then she can always transform the machine part in question such that her action is executed as part of a transition action instead (I would consider it a design error not to do so).
This obviously still links destruction with termination. So, a user is still stuck when she absolutely wants to have some actions to be run on termination but not on destruction. For this purpose, a destructing() function could be introduced. However, I think it is a good idea to add this only if users ask for it. Regards, Andreas

"Andreas Huber" <ah2003@gmx.net> writes:
The assumptions are based on practical experience, which is of course not provable. Since you seem to accept only strictly provable arguments,
No, examples carry a lot of weight with me. So far you've provided none in support of A1. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

David Abrahams wrote:
"Andreas Huber" <ah2003@gmx.net> writes:
The assumptions are based on practical experience, which is of course not provable. Since you seem to accept only strictly provable arguments,
No, examples carry a lot of weight with me. So far you've provided none in support of A1.
I think it is futile to do so. No matter how many examples I provide in support for A1, you can still argue that in a sufficiently large minority it could be undesirable (and rightly so). Since I think that A1 is almost always (>99%) a good idea, it would be much more economical if you or other people provide examples where it is highly undesirable or downright wrong and workarounds make matters overly complex. It only takes two or three good examples to convince me. Regards, Andreas

"Andreas Huber" <ah2003@gmx.net> writes:
Furthermore, it seems as though, if you want that behavior, it's trivial to get it by wrapping the FSM in a derived class whose destructor finalizes the state machine.
Yes, but only at the cost of making the current interface (and implementation) more complex (we'd need to have at least a separate exit() function, right?).
No. At the moment we're just talking about whether A1 is justified. Whether or not exit actions should use destructors is a separate question. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

David Abrahams wrote:
Yes, but only at the cost of making the current interface (and implementation) more complex (we'd need to have at least a separate exit() function, right?).
No. At the moment we're just talking about whether A1 is justified. Whether or not exit actions should use destructors is a separate question.
Is it? In the current design the state machine object owns the state objects (it does so for good reasons). How can you destruct the state machine object without destructing the state objects (and thus inevitably also exiting them and terminating the state machine as a result)? Regards, Andreas

"Andreas Huber" <ah2003@gmx.net> writes:
David Abrahams wrote:
Yes, but only at the cost of making the current interface (and implementation) more complex (we'd need to have at least a separate exit() function, right?).
No. At the moment we're just talking about whether A1 is justified. Whether or not exit actions should use destructors is a separate question.
Is it? In the current design the state machine object owns the state objects (it does so for good reasons). How can you destruct the state machine object without destructing the state objects (and thus inevitably also exiting them and terminating the state machine as a result)?
You're still collapsing separate concepts. According to the definition you posted, termination has nothing to do with destruction of anything; it's about making a transition to the FSM's "final" state. Conceptually, state (machine) destruction is independent of state machine termination. Your A1 says that they shouldn't be independent (and on that basis you conclude that state destructors and their exit actions must be linked). I'm challenging A1. I have no argument with the idea that any state objects should be destroyed when the FSM is destroyed. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

David Abrahams wrote:
"Andreas Huber" <ah2003@gmx.net> writes:
David Abrahams wrote:
Yes, but only at the cost of making the current interface (and implementation) more complex (we'd need to have at least a separate exit() function, right?).
No. At the moment we're just talking about whether A1 is justified. Whether or not exit actions should use destructors is a separate question.
Is it? In the current design the state machine object owns the state objects (it does so for good reasons). How can you destruct the state machine object without destructing the state objects (and thus inevitably also exiting them and terminating the state machine as a result)?
You're still collapsing separate concepts.
The current implementation does. I don't think I did with the above reasoning.
Conceptually, state (machine) destruction is independent of state machine termination.
Yes, conceptually. However, since in the current implementation the state machine destructor will inevitably destruct all the state objects, it is also inevitable that they are all exited (which is equivalent to terminating the state machine). That's why I said that I'd need to change the state interface to separate state machine termination (which calls exit() on all states and destructs them afterwards) from destruction (which only destructs the states). Regards, Andreas

From: "Andreas Huber" <ah2003@gmx.net>
David Abrahams wrote:
"Andreas Huber" <ah2003@gmx.net> writes:
David Abrahams wrote:
Having *very* limited occasions to make informal state machines, I've been reticent to speak up in this thread. However, I feel compelled to do so in the hopes of bringing clarity that seems, thus far, to elude the conversation. I may not be successful, but I will try nonetheless. Given my relative inexperience with FSMs, I can't speak much to theory. However, I can speak from the perspective of a potential user who might make more use of FSMs with a library that makes it easy to get things right. boost::fsm could be a flexible, powerful tool for experienced FSM authors or it could be a simple, relatively inflexible, and relatively modest tool for inexperienced FSM authors to just get things working. I doubt that it can be both at once. I'd expect boost::fsm to be the advanced tool upon which a simpler tool can be built. In that role, it is reasonable that it leave room for some things to be mishandled, just as C++ does. We add safety and simplicity to the core language and Standard Library. That means we have the power available when we need it. That's my view of boost::fsm.
Yes, but only at the cost of making the current interface (and implementation) more complex (we'd need to have at least a separate exit() function, right?).
No. At the moment we're just talking about whether A1 is justified. Whether or not exit actions should use destructors is a separate question.
Forget about the current design, forget about impact on the current interface. Let's focus on the questions being raised. As I see it, the salient points to discuss are: 1. Should state machine creation be tied to entry of the initial state? 2. Should state machine destruction be tied to exit of the final state? 3. Should state object creation be tied to entry of the corresponding FSM state? 4. Should state object destruction be tied to exit of the corresponding FSM state? (I've probably missed one or two others that arose in this thread, but these will do for the moment.) I read Dave's posts as trying to focus on these areas and I read Andreas' posts as still assuming these decisions are fixed because various good reasoning led to the answers codified in the current implementation. Based upon this thread, I think there is still plenty of room for debate on the answers to these questions.
Is it? In the current design the state machine object owns the state objects (it does so for good reasons). How can you destruct the state machine object without destructing the state objects (and thus inevitably also exiting them and terminating the state machine as a result)?
You're still collapsing separate concepts.
The current implementation does. I don't think I did with the above reasoning.
I read "in the current design...how can you destruct [sic] the state machine object without destructing [sic] the state objects (and thus...exiting them,..." as clearly linking the ideas. (Points 2 and 4, above.) If I may put words in his mouth, Dave is saying that neither points 2 nor 4, above, are given. Thus, regardless of impact it may have on the current interfaces and design, he's addressing those two points at this time. Let me address those two points. If destruction of a state machine involved no exit actions or transitions, but merely released memory and other resources, and similarly, if destruction of state objects involved no actions or transitions, but merely released resources, then management of those objects is simplified. The library can impose upon such objects that no transitions or actions are permitted. This could be handled by tracking a Boolean that indicates whether the FSM was terminated and disallowing any further activity or by simply saying, "I told you so," when things go wrong due to violating that design tenet. If termination of a state machine was a specific action -- either external by calling terminate() -- or internal via a state transition, the resulting FSM seems to me to be more like one would hand code. Is that better? I think so, but I'm no expert. I think that's what another poster was arguing. If exit of a state was separate from destruction, it could throw an exception. At that point, the state machine could be declared unstable and could be forcibly terminated. (Remember that Boolean I mentioned?) IOW, you could require that all exceptions be handled specifically via an exception handler installed by the client to turn execeptions into transitions -- I think that's essentially what you've described thus far. Any unhandled exception can be treated like C++ does it: it calls terminate(). (In this case, I mean terminate() for the FSM so that nothing more may occur.) Put another way, an unhandled exception in a FSM is a fatal design error and nothing more may be done by the FSM. In both your current approach and that outlined above, destroying state objects and the state machine object happens without propogating exceptions. Ultimately, they achieve the same thing. The approach above adds one area of flexibility: it allows for the possibility of unhandled exceptions to terminate the state machine. Actually, it offers more. The state machine could be a static object and it could be restarted with the approach above. With your approach, since construction implied entering the initial state, that isn't necessarily possible. (Yes, you could add a special, benign, initial state, but then you'd have to inject an event to trigger the transition to the "real" initial state.) Similarly, not tying state object construction with entering that state, you can keep state objects alive for reuse. (I know you already said you didn't think that would be much of a benefit, but it is one more degree of freedom.)
Conceptually, state (machine) destruction is independent of state machine termination.
Yes, conceptually. However, since in the current implementation the state ^^^^^^^^^^^^^^^^^^^^^^ machine destructor will inevitably destruct all the state objects, it is also inevitable that they are all exited (which is equivalent to terminating the state machine). That's why I said that I'd need to change the state interface to separate state machine termination (which calls exit() on all states and destructs them afterwards) from destruction (which only destructs the states).
Sure, you may need to make changes to accommodate the ideas being discussed, but let's focus on the ideas for now. If the ideas prove beneficial, even if you had good reasons to dismiss them previously, then shouldn't we seriously consider them? I realize you spent a long time creating a library that you thought was worthy of inclusion in Boost and that solves real problems for you and others. It may be, however, that there are some improvements that could be made to make it a superior library. Then again, in the end, you may just prove your case and convince those presently unconvinced that you're correct. Frankly, I hope you succeed in your arguments because I'd hate to see you have to redesign and reimplement your library to any great degree. (Either way, there's really good fodder for your rationale pages from all of this. ;-) Ideally, we need more FSM experts to participate in this discussion in order to bring their experience to bear on the discussion. It seems rather one sided right now (the experienced FSM person on the side of the current design, and the less experienced on the side that's questioning some design decisions). -- Rob Stewart stewart@sig.com Software Engineer http://www.sig.com Susquehanna International Group, LLP using std::disclaimer;

Rob Stewart wrote:
Is it? In the current design the state machine object owns the state objects (it does so for good reasons). How can you destruct the state machine object without destructing the state objects (and thus inevitably also exiting them and terminating the state machine as a result)?
You're still collapsing separate concepts.
The current implementation does. I don't think I did with the above reasoning.
I read "in the current design...how can you destruct [sic] the state machine object without destructing [sic] the state objects (and thus...exiting them,..." as clearly linking the ideas. (Points 2 and 4, above.)
I think you misunderstood why this particular discussion stared: Andreas Huber wrote:
Furthermore, it seems as though, if you want that behavior, it's trivial to get it by wrapping the FSM in a derived class whose destructor finalizes the state machine.
Yes, but only at the cost of making the current interface (and implementation) more complex (we'd need to have at least a separate exit() function, right?).
IIRC, with "that behavior" Dave meant that a state machine is terminated on destruction. The rest of the argument then is about whether an interface change is necessary or not (and I still don't see how it could possibly be done without introducing exit()). But this isn't relevant anymore anyway, see below.
If destruction of a state machine involved no exit actions or transitions, but merely released memory and other resources, and similarly, if destruction of state objects involved no actions or transitions, but merely released resources, then management of those objects is simplified. The library can impose upon such objects that no transitions or actions are permitted. This could be handled by tracking a Boolean that indicates whether the FSM was terminated and disallowing any further activity or by simply saying, "I told you so," when things go wrong due to violating that design tenet.
You've lost me here. Can you please rephrase what you mean with this?
If termination of a state machine was a specific action -- either external by calling terminate() -- or internal via a state transition, the resulting FSM seems to me to be more like one would hand code. Is that better? I think so, but I'm no expert. I think that's what another poster was arguing.
I'm actually unsure about this too. It could well be that state_machine<> will lose the terminate() function.
If exit of a state was separate from destruction, it could throw an exception. At that point, the state machine could be declared unstable and could be forcibly terminated. (Remember that Boolean I mentioned?) IOW, you could require that all exceptions be handled specifically via an exception handler installed by the client to turn execeptions into transitions -- I think that's essentially what you've described thus far. Any unhandled exception can be treated like C++ does it: it calls terminate(). (In this case, I mean terminate() for the FSM so that nothing more may occur.)
terminate() is the wrong word for that. In FSM terminology it is clearly defined what termination means.
With your approach, since construction implied entering the initial state, that isn't necessarily possible.
No. There's state_machine<>::initiate().
Sure, you may need to make changes to accommodate the ideas being discussed, but let's focus on the ideas for now. If the ideas prove beneficial, even if you had good reasons to dismiss them previously, then shouldn't we seriously consider them?
Darryl Green convinced me that exit() (or an equivalent feature that allows the user to separate termination from destruction) is sometimes necessary.
Ideally, we need more FSM experts to participate in this discussion in order to bring their experience to bear on the discussion. It seems rather one sided right now (the experienced FSM person on the side of the current design, and the less experienced on the side that's questioning some design decisions).
I think Darryl and Eugene are quite experienced FSM folks... Regards, Andreas

From: "Andreas Huber" <ah2003@gmx.net>
Rob Stewart wrote:
Is it? In the current design the state machine object owns the state objects (it does so for good reasons). How can you destruct the state machine object without destructing the state objects (and thus inevitably also exiting them and terminating the state machine as a result)?
You're still collapsing separate concepts.
The current implementation does. I don't think I did with the above reasoning.
I read "in the current design...how can you destruct [sic] the state machine object without destructing [sic] the state objects (and thus...exiting them,..." as clearly linking the ideas. (Points 2 and 4, above.)
I think you misunderstood why this particular discussion stared:
Andreas Huber wrote:
Furthermore, it seems as though, if you want that behavior, it's trivial to get it by wrapping the FSM in a derived class whose destructor finalizes the state machine.
Yes, but only at the cost of making the current interface (and implementation) more complex (we'd need to have at least a separate exit() function, right?).
IIRC, with "that behavior" Dave meant that a state machine is terminated on destruction. The rest of the argument then is about whether an interface change is necessary or not (and I still don't see how it could possibly be done without introducing exit()). But this isn't relevant anymore anyway, see below.
OK. I understood Dave to be troubled by using the dtor for the exit action and the issues that raised for managing exceptions. He then was saying that if, for ease of use, a client wanted a dtor to automatically invoke the exit() function, creating a derived class with a dtor that does so is quite easy. Thus, he was arguing that requiring that the exit funcionality to be invoked by the dtor was limiting and apparently made error handling more problematic. Anyway, you've been convinced for other reasons that exit() is needed, at least in some form, so this is probably moot.
If destruction of a state machine involved no exit actions or transitions, but merely released memory and other resources, and similarly, if destruction of state objects involved no actions or transitions, but merely released resources, then management of those objects is simplified. The library can impose upon such objects that no transitions or actions are permitted. This could be handled by tracking a Boolean that indicates whether the FSM was terminated and disallowing any further activity or by simply saying, "I told you so," when things go wrong due to violating that design tenet.
You've lost me here. Can you please rephrase what you mean with this?
I'll try. Assume that the state machine dtor only releases resources, that it doesn't do any FSM things like exit actions or transitions. Assume that the library states clearly and emphatically that state object dtors may only release resources, and that they may not do any FSM things like exit actions or transitions. Then, once the state machine has been halted (akin to std::terminate()) for any reason, the library can disallow further FSM behavior or it can just state that doing any such things once the state machine has been halted results in undefined behavior.
If exit of a state was separate from destruction, it could throw an exception. At that point, the state machine could be declared unstable and could be forcibly terminated. (Remember that Boolean I mentioned?) IOW, you could require that all exceptions be handled specifically via an exception handler installed by the client to turn execeptions into transitions -- I think that's essentially what you've described thus far. Any unhandled exception can be treated like C++ does it: it calls terminate(). (In this case, I mean terminate() for the FSM so that nothing more may occur.)
terminate() is the wrong word for that. In FSM terminology it is clearly defined what termination means.
I meant something akin to std::terminate(). I was drawing an analogy. Call it halt or anything else that works.
Ideally, we need more FSM experts to participate in this discussion in order to bring their experience to bear on the discussion. It seems rather one sided right now (the experienced FSM person on the side of the current design, and the less experienced on the side that's questioning some design decisions).
I think Darryl and Eugene are quite experienced FSM folks...
ISTR that they often qualified their own experience, but they definitely have more than I. Still, it would be nice to have more to get a broader range of opinions or a stronger base of consensus. -- Rob Stewart stewart@sig.com Software Engineer http://www.sig.com Susquehanna International Group, LLP using std::disclaimer;

Rob Stewart wrote:
Assume that the state machine dtor only releases resources, that it doesn't do any FSM things like exit actions or transitions. Assume that the library states clearly and emphatically that state object dtors may only release resources, and that they may not do any FSM things like exit actions or transitions. Then, once the state machine has been halted (akin to std::terminate()) for any reason, the library can disallow further FSM behavior or it can just state that doing any such things once the state machine has been halted results in undefined behavior.
Ok, I think I got it. I don't like the undefined behavior part but in general I agree that once an exception has left the state machine, the state machine is essentially no longer usable. boost::fsm currently terminates the state machine before the exception is propagated to the client. I agree that termination may be too much, as this inevitably exits all states but I think it is a good idea to put the state machine into a defined state whenever an exception is propagated. This way there's much less room for error when someone accidentially uses such a state machine object. Regards, Andreas

"Andreas Huber" <ah2003@gmx.net> writes:
David Abrahams wrote:
I'm reading it like a bible only when it helps to argue my case ;-).
I know there's a smiley, but you're not doing your credibility any favors with that statement. It makes me wonder how many other decisions in the design have such a flimsy foundation.
I think a foundation for a decision is not flimsy when the behavior in question has proved to be useful in practice. So far nobody, who seems to have experience with non-trivial FSMs, has doubted that it is useful to terminate a state machine when it is destructed (my assumption A1).
I don't doubt that it's often useful. I also think it is surely sometimes highly undesirable. If you remove A1, the "useful" behavior is trivial to achieve without transforming the FSM, so it seems that a design without A1 is both more flexible and more orthogonal.
BTW, as pointed out by someone else the user *does* have a choice. If she happens to have an exit action that she'd rather not have executed upon destruction then she can always transform the machine part in question such that her action is executed as part of a transition action instead (I would consider it a design error not to do so).
Why?
Every Moore machine (an FSM that has only entry and exit actions) can be transformed to a Mealy machine (an FSM that has only transition actions) and vice versa.
Why require a transformation when it's not neccessary? -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

David Abrahams wrote:
I think a foundation for a decision is not flimsy when the behavior in question has proved to be useful in practice. So far nobody, who seems to have experience with non-trivial FSMs, has doubted that it is useful to terminate a state machine when it is destructed (my assumption A1).
I don't doubt that it's often useful. I also think it is surely sometimes highly undesirable.
I'd be interested in examples.
If you remove A1, the "useful" behavior is trivial to achieve without transforming the FSM, so it seems that a design without A1 is both more flexible and more orthogonal.
Removing A1 means complicating the interface (see my other post regarding exit()).
BTW, as pointed out by someone else the user *does* have a choice. If she happens to have an exit action that she'd rather not have executed upon destruction then she can always transform the machine part in question such that her action is executed as part of a transition action instead (I would consider it a design error not to do so).
Why?
Every Moore machine (an FSM that has only entry and exit actions) can be transformed to a Mealy machine (an FSM that has only transition actions) and vice versa.
Why require a transformation when it's not neccessary?
Because I very much believe that such a transformation is necessary only in very few cases (I've never encountered one) and I don't want to complicate the interface and potentially also pessimize the implementation for all users just because of those few cases. Regards, Andreas

"Andreas Huber" <ah2003@gmx.net> writes:
If you remove A1, the "useful" behavior is trivial to achieve without transforming the FSM, so it seems that a design without A1 is both more flexible and more orthogonal.
Removing A1 means complicating the interface (see my other post regarding exit()).
No it does not; see my other posts. 1. A1 has no *direct* bearing on whether you put your exit actions in destructors or in exit(). You can pick any combination of A1, !A1, exit actions, and destructors. 2. I'm fairly confident that you can allow exit() actions *and* destructors to coexist without requiring every state to have exit() actions and without loss of efficiency... but that's really beside the point. I'm only interested in the question of whether the state machine must enter its final state when destroyed, here.
BTW, as pointed out by someone else the user *does* have a choice. If she happens to have an exit action that she'd rather not have executed upon destruction then she can always transform the machine part in question such that her action is executed as part of a transition action instead (I would consider it a design error not to do so).
Why?
Why? -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

Andreas Huber wrote:
F2. The UML standard defines that all currently active states are left when a state machine is terminated. I do not quote the text here as in the UML standard this is not a single definition but consists of what a final state is (2.12.2.5) and how states are exited when a transition is made (2.12.4.6). What is called "terminating the state machine" in this post (and in the boost::fsm documentation) is called "making a transition to the final state" in UML.
[and snip]
A1. When a state machine object is destructed, the modeled state machine must also be terminated (i.e. the destructor of the state machine unconditionally terminates the state machine before returning to the client). Actually, the UML standard in one place (2.12.4.4) hints in this direction but it is far from clear whether this assumption is covered by the UML standard (and could thus be put in the hard facts section).
F2 defines termination as "making a transition to the final state". It's far from clear that destroying a state machine should make a transition to the final state. Specifically, if the machine is in a state which has no transition to the final state, what does it mean to terminate the machine? If the machine is in such a state when it is destroyed, does it make sense to pretend it can transition to the final state when no such transition exists? I would think that when a state machine object is destroyed, no actions (exit or otherwise) should be executed; rather, it should just tear down the state machine and release its resources (state objects, transitions, whatever). What am I missing? Bob

Robert Bell wrote:
F2 defines termination as "making a transition to the final state". It's far from clear that destroying a state machine should make a transition to the final state. Specifically, if the machine is in a state which has no transition to the final state, what does it mean to terminate the machine? If the machine is in such a state when it is destroyed, does it make sense to pretend it can transition to the final state when no such transition exists?
You can see this as another assumption related to A1...
I would think that when a state machine object is destroyed, no actions (exit or otherwise) should be executed; rather, it should just tear down the state machine and release its resources (state objects, transitions, whatever). What am I missing?
You're not missing anything. If you disagree with the assumptions I make then I will not be able to convince you. The only hope I have is that you will see that my assumptions do make sense *in practice*. Regards, Andreas

----- Original Message ----- [...] From: "Andreas Huber" <ah2003@gmx.net>
The above is the best reasoning I can currently give to show that failing exit actions do not make any sense. For me, this is more than enough to not ever consider them again (at least for an FSM library implemented in C++). I will answer questions arising from this post and I am interested in feedback whether this convinces people but I will not try to reason any further as I have run out of arguments and I think I have already invested too much time into this.
Your default exception handling isn't very intuitive. What you've done may be the best way to go but I don't have a strong opinion on how to deploy it in practice or how to adopt it to existing methodologies of state machine design. I don't want to repeat my arguments but as I suggested in one of my previous posts, I would prefer if by default no action (entry or exit) may throw so that in terms of the user actions, fsm doesn't provide any exception safety guarantees (I realize that for most boost folks, it sounds controversial, to say the least :) ... but this will make fsm (by default!) act according to the well established state machine standards. If the user wants to enable the guarantees and play with the new stuff, she should do it explicitly. To conclude: Andreas, whatever you decide is fine with me. Even now, fsm gives you a lot of flexibility in how to handle faults. Great job overall and thanks for sharing it! Eugene

Andreas Huber <ah2003@gmx.net> writes:
Exits happen before entries, and you can't undo an exit; that's status quo. If you get arrive at the point where an entry is about to fail, then the preceding exit didn't fail, so the possibility of a failing exit has no impact.
As I have tried to explain before: The problem lies in the fact that you can have multiple exit actions called in a row before entry actions are called. If e.g. the second of those exit actions fails (the first one succeeded) then your state machine is in an invalid state from where it is impossible to recover.
Actually I don't understand why you say that. If exit actions are called from innermost to outermost, it seems to me that you can simply stop in the state whose exit action threw an exception without putting the state machine into an *intrinsically* invalid state. I am distinguishing *intrinsic* invalidity from a state that's inconsistent with whole-program invariants. This is just the same kind of guarantee std::vector gives: if an exception is thrown, the invariants _of the vector_ are preserved, but there's no guarantee that the state of the vector is consistent with the expectations of the rest of the program. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

David Abrahams wrote:
"Andreas Huber" <ah2003@gmx.net> writes:
To the contrary, I think I have almost proved in my discussion with Dave that exit actions must not fail
IIUC your almost-proof that they must not fail is based on the assumption that they're done with destructors.
At least I was never my intention to reason like that. The concept of failing exit actions is bankrupt in *any* case. If you have the time please reread my posts and every time I talk about exit actions please assume that I'm talking about ordinary functions. I think the reasoning is pretty water-tight, though not formal enough to be seen as a proof. Regards, Andreas

Andreas Huber wrote:
Robert Bell wrote:
Here's one argument in favor of entry()/exit() over constructor/destructor. If you use constructors/destructors, your hands will be tied if someday you want to change your mind about the implementation of state entry and exit constructing and destructing states (a couple of alternatives have been discussed here, such as constructing states when they're entered but not destroying them when they're exited). If instead you implement entry and exit with entry() and exit() member functions, it doesn't matter if the states are constructed and destructed as they are now, constructed at entry point and destroyed at machine destruction time, or constructed all at once at machine construction time.
I don't have any idea if this flexibility is important or not, but if I was implementing fsm, I'd consider it.
Believe me, I *am* considering it. I even started dreaming about it :-).
That happens to me sometimes too. Sometimes I think it's cool. Other times I think it means I need a vacation. ;-)
However, no matter from what angle I start to think this through I always end up with the same bad feeling. If we introduce entry()/exit() for the reasons you give (more flexibility for optimization?)
More flexibility to change the implementation, for any reason (not necessarily optimization). By using constructors and destructors for entry and exit, you're telling users that fsm guarantess that States will be constructed and destroyed at specific times, which means that for all time fsm must provide those guarantees.
this essentially means to tell the users that they must not under any circumstances rely on when exactly constructors and destructors of state objects are called.
Keep in mind that it's entirely possible that I'm missign something. That said, they can't rely on when exactly constructors and destructors are called today.
Even worse, they must not even rely on how many times state ctors/dtors are called.
They can't rely on the number of times they're called now.
Today they might be called exactly as often as entry()/exit() but tomorrow they might be called only exactly once.
None of these issues having to do with when and how many times constructors and destructors are called sound bad to me; why would anyone care?
This means that most non-POD data members of states need to be created on the heap, as you otherwise wouldn't have a chance to create them inside entry() and destroy them inside exit(). And believe me, you definitely need to do that when you design non-trivial FSMs. Therefore, to me entry()/exit() means falling at least halfway back into medieval times where you always had to explictly call constructors and destructors yourself (Turbo Pascal).
I don't quite see it this way myself, but in any event, this is a concern for the implementer of fsm, not a user of fsm.
Please have a look at the StopWatch example and imagine the additional complexity introduced with entry()/exit() (and ctors/dtors called at unspecified times). I only want to go there if there are compelling reasons: - I believe that boost::fsm is already reasonably fast (and there is still some potential for optimization). Nobody who is actually using it has ever complained that it is too slow. Someone has even reported that it performs "very well".
I'm not so concerned about performance; if the interfaces are reasonably clean, it can always be optimized later.
- Nobody has so far presented hard technical facts that support the view that mapping entry/exit to ctor/dtor is wrong. To the contrary, I think I have almost proved in my discussion with Dave that exit actions must not fail and that you *can* recover from failing entry actions.
I'm not so sure there _are_ hard technical facts; there are just two different interface choices, each with different tradeoffs. I'm sure you've thought this out more than I have. I'm just coming from a gut-level reaction I have to linking entry/exit to construction/destruction. It just feels weird. For example, if entry and exit actions are things a state "does", then I'd expect them to be overridable; constructors and destructors can't be overridden. (In the past, when I've implemented state machines I've made Action a separate class; that way, Actions are attached to States, which makes it easier to reuse Actions in different States.) Don't take this as me saying you're doing it wrong or that fsm is ill-designed or anything like that. I just wanted to raise a point I have not yet seen raised, and as I said, you've obviously spent a lot more time working on this than I. I don't quite understand the objection to entry() and exit() member functions, but then again I don't have to understand it for fsm to proceed. Bob

Robert Bell <belvis <at> imageworks.com> writes:
However, no matter from what angle I start to think this through I always end up with the same bad feeling. If we introduce entry()/exit() for the reasons you give (more flexibility for optimization?)
More flexibility to change the implementation, for any reason (not necessarily optimization).
Why else would you want to change the implementation (while leaving the interface as is)?
By using constructors and destructors for entry and exit, you're telling users that fsm guarantess that States will be constructed and destroyed at specific times, which means that for all time fsm must provide those guarantees.
Yes, what's so bad about that? We'd have to guarantee the same with entry() and exit().
this essentially means to tell the users that they must not under any circumstances rely on when exactly constructors and destructors of state objects are called.
Keep in mind that it's entirely possible that I'm missign something. That said, they can't rely on when exactly constructors and destructors are called today.
Yes they can. It is clearly defined when state ctors/dtors are called.
Even worse, they must not even rely on how many times state ctors/dtors are called.
They can't rely on the number of times they're called now.
Again, yes they can. The docs clearly define how often they are called.
Today they might be called exactly as often as entry()/exit() but tomorrow they might be called only exactly once.
None of these issues having to do with when and how many times constructors and destructors are called sound bad to me; why would anyone care?
This means that most non-POD data members of states need to be created on the heap, as you otherwise wouldn't have a chance to create them inside entry() and destroy them inside exit(). And believe me, you definitely need to do that when you design non-trivial FSMs. Therefore, to me entry()/exit() means falling at least halfway back into medieval times where you always had to explictly call constructors and destructors yourself (Turbo Pascal).
I don't quite see it this way myself, but in any event, this is a concern for the implementer of fsm, not a user of fsm.
No, users are directly affected. Assume for a moment that we have entry()/exit () and that you want to create a non-pod object of type A in the entry action and destroy it in the exit action. For the reasons given before you cannot do this in the ctor/dtor. So, your state object cannot have a data member of type A, instead you need to either have A * (or the smart equivalent) as member. In entry(), you then allocate a new A object on the heap and set the data member pointer to that object. In exit() you need to delete the object and set the pointer back to 0.
- Nobody has so far presented hard technical facts that support the view that mapping entry/exit to ctor/dtor is wrong. To the contrary, I think I have almost proved in my discussion with Dave that exit actions must not fail and that you *can* recover from failing entry actions.
I'm not so sure there _are_ hard technical facts; there are just two different interface choices, each with different tradeoffs.
So what exactly are the tradeoffs then, if it isn't performance?
I'm sure you've thought this out more than I have. I'm just coming from a gut-level reaction I have to linking entry/exit to construction/destruction. It just feels weird. For example, if entry and exit actions are things a state "does", then I'd expect them to be overridable; constructors and destructors can't be overridden.
How exactly would you want to override entry and exit actions? I mean, I personally haven't ever seen a framework allowing this, much less UML.
(In the past, when I've implemented state machines I've made Action a separate class; that way, Actions are attached to States, which makes it easier to reuse Actions in different States.)
While I do sometimes reuse transition actions, I have never felt the urge to do so for entry and exit actions. If you need to do that, no problem: Just call a function from ctor/dtor
I don't quite understand the objection to entry() and exit() member functions, but then again I don't have to understand it for fsm to proceed.
The objection is that it violates KISS. If there are hard *technical* arguments in favor of entry()/exit() then I'll happily add them. I hope we agree that so far no such arguments have been presented. Regards, Andreas

Andreas Huber wrote:
Robert Bell <belvis <at> imageworks.com> writes:
However, no matter from what angle I start to think this through I always end up with the same bad feeling. If we introduce entry()/exit() for the reasons you give (more flexibility for optimization?)
More flexibility to change the implementation, for any reason (not necessarily optimization).
Why else would you want to change the implementation (while leaving the interface as is)?
I don't know, I can't predict the future. Maybe you'll come up with an implementation strategy that you think is superior. Is it true that the only reason you've ever had for changing an implementation is optimization? That's certainly not been true for me.
By using constructors and destructors for entry and exit, you're telling users that fsm guarantess that States will be constructed and destroyed at specific times, which means that for all time fsm must provide those guarantees.
Yes, what's so bad about that? We'd have to guarantee the same with entry() and exit().
It strikes me that making that guarantee with entry() and exit() may be easier to maintain if the implementation changes.
this essentially means to tell the users that they must not under any circumstances rely on when exactly constructors and destructors of state objects are called.
Keep in mind that it's entirely possible that I'm missign something. That said, they can't rely on when exactly constructors and destructors are called today.
Yes they can. It is clearly defined when state ctors/dtors are called.
I guess I was interpreting the word "exactly" a little differently. Saying "the constructor is called when the state is entered" means that it won't be called until the right sequence of events occurs, so I can't know "exactly" when the constructor will be called.
Even worse, they must not even rely on how many times state ctors/dtors are called.
They can't rely on the number of times they're called now.
Again, yes they can. The docs clearly define how often they are called.
Same interpretation: I don't know how many times the state will be entered, so I don't know how many times the constructor will be run.
Today they might be called exactly as often as entry()/exit() but
tomorrow they might be called only exactly once.
None of these issues having to do with when and how many times constructors and destructors are called sound bad to me; why would anyone care?
Regardless of what was meant by "exactly" (it's clear I misinterpreted), I thought this was the more important point, so I'll ask again. Why would anyone care when and how many times the constructors and destructors are called?
This means that most
non-POD data members of states need to be created on the heap, as you otherwise wouldn't have a chance to create them inside entry() and destroy them inside exit(). And believe me, you definitely need to do that when you design non-trivial FSMs. Therefore, to me entry()/exit() means falling at least halfway back into medieval times where you always had to explictly call constructors and destructors yourself (Turbo Pascal).
I don't quite see it this way myself, but in any event, this is a concern for the implementer of fsm, not a user of fsm.
No, users are directly affected. Assume for a moment that we have entry()/exit () and that you want to create a non-pod object of type A in the entry action and destroy it in the exit action. For the reasons given before you cannot do this in the ctor/dtor. So, your state object cannot have a data member of type A, instead you need to either have A * (or the smart equivalent) as member. In entry(), you then allocate a new A object on the heap and set the data member pointer to that object. In exit() you need to delete the object and set the pointer back to 0.
I see what you're saying; I thought you meant something else by "explictly call constructors and destructors yourself". Even if it's common to want to create a non-POD in an entry action and then destroy it in the corresponding exit action, I don't see a problem with doing the allocation in entry() and deallocation in exit(). But I'm not even convinced that this usage is common anyway; do you have reason to think so?
- Nobody has so far presented hard technical facts that support the view that mapping entry/exit to ctor/dtor is wrong. To the contrary, I think I have almost proved in my discussion with Dave that exit actions must not fail and that you *can* recover from failing entry actions.
I'm not so sure there _are_ hard technical facts; there are just two different interface choices, each with different tradeoffs.
So what exactly are the tradeoffs then, if it isn't performance?
I've tried to give a sense of a maintenance tradeoff, but apparently I've failed. Here are more off the top of my head: constructor/destructor -- demands that the state be created when it's entered and destroyed when it's exited -- no possibility for State member data to persist after exit unless the user somehow provides for it -- simple implementation of the "allocate a non-POD at entry/deallocate at exit" use case -- only one state (plus parents) exists at a time; siblings cannot communicate with each other except through means such as global flags entry()/exit() -- only demands that a State be created once -- allows State data to persist after exit -- slightly more complicated implementation of the "allocate a non-POD at entry/deallocate at exit" use case -- allows siblings to exist at the same time
I'm sure you've thought this out more than I have. I'm just coming from a gut-level reaction I have to linking entry/exit to construction/destruction. It just feels weird. For example, if entry and exit actions are things a state "does", then I'd expect them to be overridable; constructors and destructors can't be overridden.
How exactly would you want to override entry and exit actions? I mean, I personally haven't ever seen a framework allowing this, much less UML.
I'm not sure what your question means. If entry and exit actions are implemented as member functions called entry() and exit(), how else would you use them except by making them virtual and overriding?
(In the past, when I've implemented state machines I've made Action a separate class; that way, Actions are attached to States, which makes it easier to reuse Actions in different States.)
While I do sometimes reuse transition actions, I have never felt the urge to do so for entry and exit actions. If you need to do that, no problem: Just call a function from ctor/dtor
If I remember correctly, the reason this design evolved was because of a need to reuse entry/exit actions (although I don't remember exactly what the actions were). As I went forward, it made more and more sense to separate States and Actions. States represent the state the machine can be in; Actions do things. Having this separation made it easy to do things like combine Actions in interesting ways, such as defining a composite Action that executed a list of other Actions. One example of a reuse of an entry Action was for debugging purposes. An Action that prints the ID of the State could be attached as an entry Action for each State to generate a trace of a machine's behavior. Another reason for this design was that it occured to me that by having actions be member functions of State, it requires the user to derive new State classes to get different actions. One implication this had that I didn't like was that since the actual classes of the States in a machine would be dynamic, constructing a state machine was more complicated. I wanted to be able to do things like read a state machine description from an XML file and build it; if there's just one State class that's used, this is pretty easy. Otherwise some kind of factory is needed. Since the only reason I ever derived from State was for actions, separating actions into their own classes meant that State wouldn't be needed as a base class. I'm not saying that this is the way boost.fsm should go, just trying to give a little more context about why I did it the way I did. Again, it's about tradeoffs; my goals were obviously different from those of boost.fsm.
I don't quite understand the objection to entry() and exit() member functions, but then again I don't have to understand it for fsm to proceed.
The objection is that it violates KISS. If there are hard *technical* arguments in favor of entry()/exit() then I'll happily add them. I hope we agree that so far no such arguments have been presented.
KISS is subjective; what's simple to one may not be simple to another. I'm not convinced that constructor/destructor is simpler than entry()/exit(), but I'm not convinced that entry()/exit() is simpler than constructor/destructor either (entry()/exit() "feels" simpler to me, but I can't make a much stronger statement than that). We can agree that there have been no hard *technical* reasons in favor of entry() and exit(). I'm certain that we disagree there have been no hard *technical* reasons in favor of constructor/destructor. Bob

Robert Bell wrote:
Why else would you want to change the implementation (while leaving the interface as is)?
I don't know, I can't predict the future. Maybe you'll come up with an implementation strategy that you think is superior.
Is it true that the only reason you've ever had for changing an implementation is optimization? That's certainly not been true for me.
No, refactoring is the other reason but I'm very skeptical whether it is a good idea to force an IMHO more difficult interface onto users just to be able to *maybe* refactor the implementation in the future. This sounds to me like "Ummm, I'm not sure how to implement this, so lets design the interface in a way that makes implementation changes easier".
By using constructors and destructors for entry and exit, you're telling users that fsm guarantess that States will be constructed and destroyed at specific times, which means that for all time fsm must provide those guarantees.
Yes, what's so bad about that? We'd have to guarantee the same with entry() and exit().
It strikes me that making that guarantee with entry() and exit() may be easier to maintain if the implementation changes.
Only if I would see the slightest chance that such an implementation change will ever become necessary. To the contrary, entry() / exit() would inevitably complicate the implementation and I'm not convinced that it has any advantages whatsoever (performance or otherwise).
this essentially means to tell the users that they must not under any circumstances rely on when exactly constructors and destructors of state objects are called.
Keep in mind that it's entirely possible that I'm missign something. That said, they can't rely on when exactly constructors and destructors are called today.
Yes they can. It is clearly defined when state ctors/dtors are called.
I guess I was interpreting the word "exactly" a little differently. Saying "the constructor is called when the state is entered" means that it won't be called until the right sequence of events occurs, so I can't know "exactly" when the constructor will be called.
Yes, but the same reasoning applies to entry() / exit(), doesn't it? entry() / exit() doesn't buy you anything here.
Regardless of what was meant by "exactly" (it's clear I misinterpreted), I thought this was the more important point, so I'll ask again. Why would anyone care when and how many times the constructors and destructors are called?
That's exactly my point, if we had entry/exit, nobody *must* ever care. If you don't read the docs thoroughly and/or are tired, etc, you easily stick stuff in the ctor/dtor that doesn't belong there. That's why I think that entry()/exit() complicates the interface instead of simplifying it. If you only have ctor/dtor such errors are not possible, are they?
No, users are directly affected. Assume for a moment that we have entry()/exit () and that you want to create a non-pod object of type A in the entry action and destroy it in the exit action. For the reasons given before you cannot do this in the ctor/dtor. So, your state object cannot have a data member of type A, instead you need to either have A * (or the smart equivalent) as member. In entry(), you then allocate a new A object on the heap and set the data member pointer to that object. In exit() you need to delete the object and set the pointer back to 0.
I see what you're saying; I thought you meant something else by "explictly call constructors and destructors yourself".
Even if it's common to want to create a non-POD in an entry action and then destroy it in the corresponding exit action, I don't see a problem with doing the allocation in entry() and deallocation in exit(). But
You can easily forget one or the other. With ctor/dtor the compiler takes care of this for you. Why do you think have the C++ language designers arrived at the ctor/dtor design we have today? Exactly because they saw that manually calling ctor/dtor has led to problems in other languages.
I'm not even convinced that this usage is common anyway; do you have reason to think so?
Well, it is not super-common, but it does happen. Locks, custom arithmetic types and monostates come to mind.
constructor/destructor
-- demands that the state be created when it's entered and destroyed when it's exited -- no possibility for State member data to persist after exit unless the user somehow provides for it
Didn't we agree that the user couldn't rely on the lifetime of the state objects anyway (the argument was that I could more easily change the implementation and thus the lifetime of the state objects), or am I missing something?
-- only one state (plus parents) exists at a time; siblings cannot communicate with each other except through means such as global flags
With siblings I assume you mean inner states (e.g. in the StopWatch example Running and Stopped are siblings). I can't recall a single case where such siblings needed to communicate (I assume you mean access common variables). Maybe it's just because that for me it is *much* more natural to put such variables into a common outer state (e.g. see the Active::elapsedTime_ in the StopWatch example). The inner states share pretty much everything of their outer state anyway why not also share common variables?
entry()/exit()
-- only demands that a State be created once -- allows State data to persist after exit
This assumes that the state object is created before first entry and then lives until the state machine is destructed. This essentially throws away all implementation flexibility, which IIUC was one of the reasons to introduce entry()/exit().
I'm sure you've thought this out more than I have. I'm just coming from a gut-level reaction I have to linking entry/exit to construction/destruction. It just feels weird. For example, if entry and exit actions are things a state "does", then I'd expect them to be overridable; constructors and destructors can't be overridden.
How exactly would you want to override entry and exit actions? I mean, I personally haven't ever seen a framework allowing this, much less UML.
I'm not sure what your question means. If entry and exit actions are implemented as member functions called entry() and exit(), how else would you use them except by making them virtual and overriding?
I don't see how constructors and destructors are any different here, except for the fact that with entry()/exit() the base class implementation is not automatically called.
If I remember correctly, the reason this design evolved was because of a need to reuse entry/exit actions (although I don't remember exactly what the actions were). As I went forward, it made more and more sense to separate States and Actions. States represent the state the machine can be in; Actions do things. Having this separation made it easy to do things like combine Actions in interesting ways, such as defining a composite Action that executed a list of other Actions.
I have not felt the urge to do so myself, except for what boost::fsm does automatically anyway (i.e. the calling of multiple exit actions in the right order).
One example of a reuse of an entry Action was for debugging purposes. An Action that prints the ID of the State could be attached as an entry Action for each State to generate a trace of a machine's behavior.
Another reason for this design was that it occured to me that by having actions be member functions of State, it requires the user to derive new State classes to get different actions. One implication this had that I didn't like was that since the actual classes of the States in a machine would be dynamic, constructing a state machine was more complicated. I wanted to be able to do things like read a state machine description from an XML file and build it;
You'll never be able to do that with boost::fsm, unless you somehow manage to compile C++ on the fly. I think much of what you describe above and below may make sense in a dynamic environment (I don't have enough domain knowledge to judge). In such an environment you can shape pretty much everything of an FSM in an external file, *except* for the actions as they are still written in normal C++. I'm not that surprised that you need/want to combine actions in new ways as that can be done relatively easy in the external file without recompilation. The boost::fsm equivalent would be to do so with normal function calls.
I don't quite understand the objection to entry() and exit() member functions, but then again I don't have to understand it for fsm to proceed.
The objection is that it violates KISS. If there are hard *technical* arguments in favor of entry()/exit() then I'll happily add them. I hope we agree that so far no such arguments have been presented.
KISS is subjective; what's simple to one may not be simple to another.
That's true. However, I do hope that we agree that ctor/dtor *is* simpler from a strictly technical POV. While it may strike you as odd/more complex when you make your first steps, I'm quite confident that with time ctor/dtor is as simple as it gets. As I've explained above entry/exit does have issues a newbie might not be aware of. Regards, Andreas

"Andreas Huber" <ah2003@gmx.net> wrote in message news:c9aid2$g3g$1@sea.gmane.org...
Robert Bell wrote:
Why else would you want to change the implementation (while leaving the interface as is)?
[...]
By using constructors and destructors for entry and exit, you're telling users that fsm guarantess that States will be constructed and destroyed at specific times, which means that for all time fsm must provide those guarantees.
Yes, what's so bad about that? We'd have to guarantee the same with entry() and exit().
It strikes me that making that guarantee with entry() and exit() may be easier to maintain if the implementation changes.
Only if I would see the slightest chance that such an implementation change will ever become necessary. To the contrary, entry() / exit() would inevitably complicate the implementation and I'm not convinced that it has any advantages whatsoever (performance or otherwise).
As you referred me to this posting in another part of this thread; I'll break in here: - You asked why calling virtual functions in the context of the constructor was necessary when the state must always be the most derived state: What about the case when the most derived state also derives from a protocol class? E.g. (rather contrived perhaps): struct x_proto { void do_x() { pre_x(); in_x(); post_x(); } private: virtual void pre_x() = 0; virtual void in_x() = 0; virtual void post_x() = 0; }; struct x : fsm::simple_state<x, ...>, x_proto { x() // aka 'entry' { x_proto::do_x(); // will probably work ok if we are fully constructed before this call, but doesn't 'feel right' } virtual void pre_x() { ...} virtual void in_x() { ... } virtual void post_x() { ... } }; - Is it possible to indicate that a state should exit right away in boost::fsm (I'm not sure what this would be referred to as in FSM terminology - a 'transitional state' perhaps)? E.g.: struct flush_buffers : fsm::simple_state<...> { flush_buffers() { ... flush ... ... if ok - goto xxx_state ... if fail - return to previous } }; This was just me trying to find some examples where the current design could be limiting. What might (or might not) be the problem is that constructors and destructors are special functions with special limitations and I'm not so sure that entry/exit of states should be limited by this. Perhaps this would change if the notion of a "state" in boost::fsm would change to a "state_activation" (ref Topher Cooper's post). Regards, // Johan

Johan Nilsson wrote: [snipped example of calling virtual functions from a state ctor] I'm too lazy to look in the standard what exactly is allowed w.r.t calling virtual functions from ctors, but I'd very much expect that this should work (since we're *not* calling a function that is potentially overridden in a derived class).
- Is it possible to indicate that a state should exit right away in boost::fsm (I'm not sure what this would be referred to as in FSM terminology - a 'transitional state' perhaps)? E.g.:
struct flush_buffers : fsm::simple_state<...> { flush_buffers() { ... flush ... ... if ok - goto xxx_state ... if fail - return to previous } };
This has nothing to do with ctors/dtors. You cannot just "goto" another state inside an action and therefore entry()/exit() wouldn't change anything here. FSMs usually do this by posting internal events, boost::fsm is no different here. Regards, Andreas

From: "Andreas Huber" <ah2003@gmx.net>
Johan Nilsson wrote: [snipped example of calling virtual functions from a state ctor]
I'm too lazy to look in the standard what exactly is allowed w.r.t calling virtual functions from ctors, but I'd very much expect that this should work (since we're *not* calling a function that is potentially overridden in a derived class).
The function as overridden in the class of the running ctor is called. (If that class doesn't override the function, then the base class' version is the current class' version.) -- Rob Stewart stewart@sig.com Software Engineer http://www.sig.com Susquehanna International Group, LLP using std::disclaimer;

"Andreas Huber" <ah2003@gmx.net> wrote in message news:c9l4es$tf7$1@sea.gmane.org...
Johan Nilsson wrote:
[snip]
- Is it possible to indicate that a state should exit right away in boost::fsm (I'm not sure what this would be referred to as in FSM terminology - a 'transitional state' perhaps)? E.g.:
struct flush_buffers : fsm::simple_state<...> { flush_buffers() { ... flush ... ... if ok - goto xxx_state ... if fail - return to previous } };
This has nothing to do with ctors/dtors. You cannot just "goto" another state inside an action and therefore entry()/exit() wouldn't change
anything
here. FSMs usually do this by posting internal events, boost::fsm is no different here.
This was just pseudo-code; I meant something corresponding to return "transit<other_state> / return transit<previous_state>". As I understand you this possible by posting an internal event from within the constructor? I'll have to bail out of this discussion anyway. Thanks for listening. // Johan

Johan Nilsson <johan.nilsson <at> esrange.ssc.se> writes:
This was just pseudo-code; I meant something corresponding to return "transit<other_state> / return transit<previous_state>". As I understand you this possible by posting an internal event from within the constructor?
Yes. However, for this to work you have to derive from fsm::state<> instead of fsm::simple_state<>. Regards, Andreas

"Andreas Huber" <ah2003@gmx.net> wrote in message news:c930is$p44$1@sea.gmane.org...
Robert Bell wrote:
Here's one argument in favor of entry()/exit() over constructor/destructor. If you use constructors/destructors, your hands will be tied if someday you want to change your mind about the implementation of state entry and exit constructing and destructing states (a couple of alternatives have been discussed here, such as constructing states when they're entered but not destroying them when they're exited). If instead you implement entry and exit with entry() and exit() member functions, it doesn't matter if the states are constructed and destructed as they are now, constructed at entry point and destroyed at machine destruction time, or constructed all at once at machine construction time.
I don't have any idea if this flexibility is important or not, but if I was implementing fsm, I'd consider it.
Believe me, I *am* considering it. I even started dreaming about it :-). However, no matter from what angle I start to think this through I always end up with the same bad feeling. If we introduce entry()/exit() for the reasons you give (more flexibility for optimization?) this essentially means to tell the users that they must not under any circumstances rely on when exactly constructors and destructors of state objects are called. Even worse, they must not even rely on how many times state ctors/dtors are called. Today they might be called exactly as often as entry()/exit() but tomorrow they might be called only exactly once. This means that most non-POD data members of states need to be created on the heap, as you otherwise wouldn't have a chance to create them inside entry() and destroy them inside exit().
What's stopping me from always create them in constructor/delete in destructor, then just init/deinit them (if necessary) in entry/exit?
And believe me, you definitely need to do that when you design non-trivial FSMs. Therefore, to me entry()/exit() means falling at least halfway back into medieval times where you always had to explictly call constructors and destructors yourself (Turbo Pascal).
I don't follow you here; what's the analogy?
Please have a look at the StopWatch example and imagine the additional complexity introduced with entry()/exit() (and ctors/dtors called at unspecified times). I only want to go there if there are compelling reasons:
By unspecified times I assume you mean that they are not created at the 'logical' state exit and entry. I'll look into the stopwatch example to see what you're getting at (but having you describe the problems would be even better).
- I believe that boost::fsm is already reasonably fast (and there is still some potential for optimization). Nobody who is actually using it has ever complained that it is too slow. Someone has even reported that it performs "very well".
In terms of speed, I've got no real opinions (so far). I guess it depends on whatever fits the particular application in question - another reason for policy-based state-lifetime management (if possible)?
- Nobody has so far presented hard technical facts that support the view that mapping entry/exit to ctor/dtor is wrong.
<philosophical entry/> For me, hard technical facts aren't everything. All software engineering is an art and a craft as well, IMHO. Should one always choose a particular design on hard technical facts only, or should you also trust your gut-level feelings (open question)? </philosophical entry> // Johan

Johan Nilsson <johan.nilsson <at> esrange.ssc.se> writes:
What's stopping me from always create them in constructor/delete in destructor, then just init/deinit them (if necessary) in entry/exit?
The fact that most classes (well-designed ones) don't have init/deinit.
And believe me, you definitely need to do that when you design non-trivial FSMs. Therefore, to me entry()/exit() means falling at least halfway back into medieval times where you always had to explictly call constructors and destructors yourself (Turbo Pascal).
I don't follow you here; what's the analogy?
A lot of the C++ ctors/dtors design is about automation and error-avoidance. If you have non-POD data members in your class, C++ gives you a guarantee that they are constructed/destructed at the right times. With entry()/exit() you'd need to do that manually again (just like you had to in Turbo Pascal). Ugly!
Please have a look at the StopWatch example and imagine the additional complexity introduced with entry()/exit() (and ctors/dtors called at unspecified times). I only want to go there if there are compelling reasons:
By unspecified times I assume you mean that they are not created at the 'logical' state exit and entry.
Yes.
- I believe that boost::fsm is already reasonably fast (and there is still some potential for optimization). Nobody who is actually using it has ever complained that it is too slow. Someone has even reported that it performs "very well".
In terms of speed, I've got no real opinions (so far). I guess it depends on whatever fits the particular application in question - another reason for policy-based state-lifetime management (if possible)?
Everything is possible. The question is: What exactly is the advantage? What are the use-cases?
- Nobody has so far presented hard technical facts that support the view that mapping entry/exit to ctor/dtor is wrong.
<philosophical entry/> For me, hard technical facts aren't everything. All software engineering is an art and a craft as well, IMHO. Should one always choose a particular design on hard technical facts only, or should you also trust your gut-level feelings (open question)? </philosophical entry>
To me good design is 10% ideas, imagination and, yes, gut-level feelings. The other 90% are technical facts. I believe the initial effort when crafting an interface is always gut-based, afterwards comes the long and tedious period of verifying (with hard facts) that your gut feelings are right. If there are no hard facts to justify a feature then I unceremoniously throw it out. Not doing so means overloading an interface and elevating the risk of overwhelming potential users with unnecessary baggage. Regards, Andreas

"Andreas Huber" <ah2003@gmx.net> wrote in message news:loom.20040527T133057-890@post.gmane.org...
Johan Nilsson <johan.nilsson <at> esrange.ssc.se> writes:
What's stopping me from always create them in constructor/delete in destructor, then just init/deinit them (if necessary) in entry/exit?
The fact that most classes (well-designed ones) don't have init/deinit.
I'm totally aware of the RAII idom and didn't literally mean that the classes had init/deinit methods which would totally initialize and clean up the state. I referred to actions (_manipulating_ already created members: classes/pods/built-ins) that _might_ be necessary to call depending on the state in question. Ideally a state should be state-less, right? Once created it should always be able to execute the same actions no matter when in time. IRL there's more likely something that needs to be done to initiate the state. Does it matter if this is a two-step procedure: Allocate in constructors + Initialize in entry or one-step: Allocate and initialize in constructor. Non-trivial states will doubtlessly need to perform at least some amount of actions that are not simply construct their members. If you by providing explicit entry()/exit() actions (which can be _guaranteed_ to be called using RAII AFAIK) you're also encouraging the users of the library to separate resource acquisition from resource init: this also enables policy-based state lifetimes. If users have no need for entry / exit actions - they could be made virtual so that users don't need to implement them. Yes - virtual calls are comparatively expensive; maybe this could be fixed using some template meta-programming magic, explicit policies or whatever. I don't know - I'm just a potential library user ;-). Thinking about it there's (at least) one technical argument agains mapping entry/exit to constructor/destructor calls - restrictions on calling virtual member functions. Is this a 'hard' technical argument of not ... depends on your point of view. For me personally the semantics of explicit entry/exit methods is (at least) equally important. [... snip ...]
In terms of speed, I've got no real opinions (so far). I guess it depends on whatever fits the particular application in question - another reason for policy-based state-lifetime management (if possible)?
Everything is possible. The question is: What exactly is the advantage? What are the use-cases?
Library user wants to keep his state alive during the entire fsm lifetime ;-)
- Nobody has so far presented hard technical facts that support the
view
that mapping entry/exit to ctor/dtor is wrong.
See at least the restriction I mention above.
<philosophical entry/> For me, hard technical facts aren't everything. All software engineering
is
an art and a craft as well, IMHO. Should one always choose a particular design on hard technical facts only, or should you also trust your gut-level feelings (open question)? </philosophical entry>
To me good design is 10% ideas, imagination and, yes, gut-level feelings.
The other 90% are technical facts. I believe the initial effort when crafting an interface is always gut-based, afterwards comes the long and tedious
Gut-level feelings are there all the time during development; refactoring; recognizing "code smells"; ... I think it amounts to much more than 10% of the time you spend implementing (aka low-level designing) your library. period of
verifying (with hard facts) that your gut feelings are right. If there are no hard facts to justify a feature then I unceremoniously throw it out. Not doing so means overloading an interface and elevating the risk of overwhelming potential users with unnecessary baggage.
A bit off-topic perhaps, but Ruby (the language) frequently "overloads" methods with multiple names - makes more users think that the interface is intuitive. Not saying that we should do it in C++ (bloated interfaces are ge nerally bad), but it's worth thinking about. Regards // Johan

"Johan Nilsson" <johan.nilsson@esrange.ssc.se> wrote in message news:c96p5d$qlg$1@sea.gmane.org...
"Andreas Huber" <ah2003@gmx.net> wrote in message news:loom.20040527T133057-890@post.gmane.org...
Johan Nilsson <johan.nilsson <at> esrange.ssc.se> writes:
What's stopping me from always create them in constructor/delete in destructor, then just init/deinit them (if necessary) in entry/exit?
The fact that most classes (well-designed ones) don't have init/deinit.
I'm totally aware of the RAII idom and didn't literally mean that the classes had init/deinit methods which would totally initialize and clean up the state. I referred to actions (_manipulating_ already created members: classes/pods/built-ins) that _might_ be necessary to call depending on the state in question.
Ideally a state should be state-less, right? Once created it should always be able to execute the same actions no matter when in time. IRL there's
more
likely something that needs to be done to initiate the state. Does it matter if this is a two-step procedure:
Allocate in constructors + Initialize in entry
or one-step:
Allocate and initialize in constructor.
class astate : non_copyable { T& mRef; astate(); public: astate( T& aRef ):mRef(aRef){ aRef.somefunction(); } void init( T& aRef ){ mRef = aRef; mRef.somefunction(); } // not the same }; Jeff Flinn

"Jeff Flinn" <TriumphSprint2000@hotmail.com> wrote in message news:c97afd$60h$1@sea.gmane.org...
"Johan Nilsson" <johan.nilsson@esrange.ssc.se> wrote in message news:c96p5d$qlg$1@sea.gmane.org...
[...]
matter
if this is a two-step procedure: Allocate in constructors + Initialize in entry or one-step: Allocate and initialize in constructor.
class astate : non_copyable { T& mRef;
astate();
public:
astate( T& aRef ):mRef(aRef){ aRef.somefunction(); }
void init( T& aRef ){ mRef = aRef; mRef.somefunction(); } // not the same
};
No, of course not. I meant: // one-step class astate : non_copyable { ... astate( T& aRef ):mRef(aRef){ aRef.somefunction(); } ... }; versus: // two-step class astate : non_copyable { ... astate( T& aRef ):mRef(aRef){} void entry(){ mRef.somefunction(); } ... }; Regards, // Johan

"Johan Nilsson" <johan.nilsson@esrange.ssc.se> wrote in message news:c97cum$cn2$1@sea.gmane.org...
"Jeff Flinn" <TriumphSprint2000@hotmail.com> wrote in message news:c97afd$60h$1@sea.gmane.org...
"Johan Nilsson" <johan.nilsson@esrange.ssc.se> wrote in message news:c96p5d$qlg$1@sea.gmane.org...
[...]
matter
if this is a two-step procedure: Allocate in constructors + Initialize in entry or one-step: Allocate and initialize in constructor.
class astate : non_copyable { T& mRef;
astate();
public:
astate( T& aRef ):mRef(aRef){ aRef.somefunction(); }
void init( T& aRef ){ mRef = aRef; mRef.somefunction(); } // not the same
};
No, of course not. I meant:
// one-step class astate : non_copyable { ... astate( T& aRef ):mRef(aRef){ aRef.somefunction(); } ... };
versus:
// two-step class astate : non_copyable { ... astate( T& aRef ):mRef(aRef){} void entry(){ mRef.somefunction(); } ... };
This assumes that you have access to the same information at construction time as at the call to astate::entry (you previously were discussing init/deinit). I was pointing out that separating out init() does in fact come with some cost to flexibility. Jeff Flinn

Johan Nilsson wrote:
Ideally a state should be state-less, right? Once created it should always be able to execute the same actions no matter when in time.
No, even a state in its simplest form is not state-less (what an insight ;-)). Seriously, you for example could implement a single state with one bit (there are better ways to do this but lets leave them aside for the moment). The bit is set when the state is entered and reset when it is exited. What boost::fsm does is give you a way to augment this one bit with further information (again, see the StopWatch example). This design evolved because it is almost always uneconomical and often downright stupid to implement everything with states. E.g. you might need to produce a reaction when the nth event arrives and just ignore the n-1 events before it. You can implement this without any counters or other data members just by chaining n states in a row and connecting them with a transition triggered by the said event. I think nobody in their right mind does this for n>2. So you use an in-state reaction that increments a counter, which you will need to store somewhere. I think it is best to store said counter in the state itself, augmenting the state information. For further arguments in favor of state local storage please see the rationale (State local storage). For answers concerning entry()/exit(), please see my other followup to Robert Bells post here: http://tinyurl.com/3583x.
Thinking about it there's (at least) one technical argument agains mapping entry/exit to constructor/destructor calls - restrictions on calling virtual member functions. Is this a 'hard' technical argument of not ... depends on your point of view. For me personally the semantics of explicit entry/exit methods is (at least) equally important.
Adding virtual functions to a state class does not make a lot of sense because you cannot derive from it (you'd be violating the requirement that you must pass the most derived type to the simple_state<> base class). Regards, Andreas

"Andreas Huber" <ah2003@gmx.net> wrote in message news:c9anfn$q40$1@sea.gmane.org...
Johan Nilsson wrote:
Ideally a state should be state-less, right? Once created it should always be able to execute the same actions no matter when in time.
No, even a state in its simplest form is not state-less (what an insight ;-)). Seriously, you for example could implement a single state with one bit (there are better ways to do this but lets leave them aside for the moment). The bit is set when the state is entered and reset when it is exited. What boost::fsm does is give you a way to augment this one bit with further information (again, see the StopWatch example). This design evolved because it is almost always uneconomical and often downright stupid to implement everything with states. E.g. you might need to produce a reaction when the nth event arrives and just ignore the n-1 events before it. You can implement this without any counters or other data members just by chaining n states in a row and connecting them with a transition triggered by the said event. I think nobody in their right mind does this for n>2. So you use an in-state reaction that increments a counter, which you will need to store somewhere. I think it is best to store said counter in the state itself, augmenting the state information. For further arguments in favor of state local storage please see the rationale (State local storage).
I was not seriously considering this for a real-world implementation. If so; why would I be in favour of long-lived state objects? // Johan

Hi: I recently joined Boost, and have been getting up to speed by following the various discussions. I realize that this, my first contribution, is a bit abstract, but I hope that it may provide some helpful perspective on the constructor/destructor vs entry/exit discussion. A common temptation in coding is to "take advantage" of some detail of the way some mechanism happens to work, while completely ignoring the intended meaning. The result is clever, frequently efficient, but obscure, difficult to understand (except for the original implementor) and inflexible. Occasionally their use with sufficient commenting is justified, but more often it is a mistake. (Back in prehistoric times, when I first joined the field, such tricks were called "hacks" and programmers with a propensity to use them were called, pejoratively, "hackers" -- but the meaning of that term has mutated three or four times since then). My first reaction (do note, before any hackles are raised, that I did say my *first* reaction) when reading this thread (without having read the material that it refers to) was that this appeared to be a classic example of this programming error. The argument certainly seemed to show all the ear-marks. A particular mechanism (C++ constructor/destructor behavior) having been identified as being *analogous* to a desired behavior (entry/exit actions in an fsm), was then strictly identified with the same, and used to implement it. Questions as to the details of the way this worked, appeared to be defended as necessary: the tail wagging the horse, i.e., the implementation technique justifying the design rather than vice versa. A number of postings seem to indicate to me that others have had a similar reaction. On further thought, though, I have come to realize that this is not really justified. The design error appears to be (remember, this is based solely on the thread not the design itself) a much less serious one -- an error in identification of the abstractions. The problem, as I see it, is that what is being referred to as a finite state machine "state" is not that at all. A "state" is a rather static, declarative entity. Creating the "state" during execution and then associating entry and exit actions with its construction and destruction seems arbitrary and less than obvious -- especially when that starts to drive the design rather than being simply a mostly hidden detail of the implementation. In actuality, though, the objects being referred to as "states" are, in fact a kind of process-object corresponding to (the process of) *state activation*. What would more traditionally be thought of as a "state" of the finite state machine actually appears to correspond most closely to the class of the corresponding "state activation" object. With this in mind, identifying entry and exit of the state (beginning and ending of the state activation) with the constructor and destructor of the object is quite natural, and having entry/exit conform to the specific semantics/restriction of C++ constructors/destructors is then, quite reasonably, making the library C++ like in behavior. Furthermore, it clarifies (to me) the static/dynamic fsm library distinction. In a static library (like this one), the basically declarative "states" are represented declaratively as classes. In the absence of reflection mechanisms, the states are not actually represented at run time at all. On the other hand, in a dynamic library, the states are represented by some appropriate data structure and the state activation would most likely correspond to execution of appropriate methods specialized by delegation. This doesn't mean, of course, that this was the best design (I still haven't read the design, so I couldn't say), but it is certainly -- except for the terminological confusion -- a reasonable approach. Topher Cooper

"Topher Cooper" <topher@topherc.net> wrote in message news:EB688081-B031-11D8-BA4A-003065841348@topherc.net...
Hi:
[snip]
A particular mechanism (C++ constructor/destructor behavior) having been identified as being *analogous* to a desired behavior (entry/exit actions in an fsm), was then strictly identified with the same, and used to implement it.
Well stated. I agree with many things you mention in your posting, and in particular to this. No offense meant to Andreas though; no matter what the outcome, he still has made an impressive work. // Johan

Johan Nilsson wrote:
"Topher Cooper" <topher@topherc.net> wrote in message news:EB688081-B031-11D8-BA4A-003065841348@topherc.net...
Hi:
[snip]
A particular mechanism (C++ constructor/destructor behavior) having been identified as being *analogous* to a desired behavior (entry/exit actions in an fsm), was then strictly identified with the same, and used to implement it.
Well stated. I agree with many things you mention in your posting, and in particular to this.
Please finish reading Topher Cooper's post. In it, you'll find the answer to:
Everything is possible. The question is: What exactly is the advantage? What are the use-cases?
Library user wants to keep his state alive during the entire fsm lifetime ;-)
The argument is that the "state" is not a FSM state. It is an object that represents the condition of an FSM state being active. This condition is regarded as a resource, and RAII is applied to manage it. You can't "want to keep the object alive", because that would mean that the FSM state is always active. It's similar to your wanting to keep a mutex lock alive. Simon Meiklejohn wrote:
On the subject of Constructors/destructors for state entry/exit-
A consideration is that the state object also gets deleted on shutdown (or deletion of the FSM), which is not a state-transition event as such.
The argument is that the FSM being in a particular state is a resource, and if the FSM is destroyed, this resource must be released (because the non-existent FSM is no longer in this particular state). It corresponds to real world cases where a particular state being active may be tied to physical resources being held, for example the pump being switched on. That's how I understand it, anyway, all based on cursory reading of the thread. ;-)

"Peter Dimov" <pdimov@mmltd.net> wrote in message news:009a01c444ac$a8587690$0600a8c0@pdimov...
Johan Nilsson wrote:
"Topher Cooper" <topher@topherc.net> wrote in message news:EB688081-B031-11D8-BA4A-003065841348@topherc.net...
Hi:
[snip]
A particular mechanism (C++ constructor/destructor behavior) having been identified as being *analogous* to a desired behavior (entry/exit actions in an fsm), was then strictly identified with the same, and used to implement it.
Well stated. I agree with many things you mention in your posting, and in particular to this.
Please finish reading Topher Cooper's post. In it, you'll find the answer to:
Everything is possible. The question is: What exactly is the advantage? What are the use-cases?
Library user wants to keep his state alive during the entire fsm lifetime ;-)
The argument is that the "state" is not a FSM state. It is an object that represents the condition of an FSM state being active. This condition is regarded as a resource, and RAII is applied to manage it. You can't "want to keep the object alive", because that would mean that the FSM state is always active. It's similar to your wanting to keep a mutex lock alive.
Assuming I had the same mental definition of a "state" as the library in its current incarnation, yes. I'm expressing myself too ambiguosly, with the only excuse getting a bit tired of the discussion. If it's not a state, as in FSM state, why call it a state? It's already been a source of confusion (and not only for me). I did actually read Topher Cooper's post, but obviously skimmed the important parts. Now I've given it the time it well deserved. Perhaps renaming "state" to "state_activation" would clear things up. However I'm still not convinced that states (state objects corresponding to FSM states) should be non-existing in the library. I'm probably too tired to mention this but: how about creating state_activations with state objects as ctor parameters? Regards // Johan

"Andreas Huber" <ah2003@gmx.net> wrote in message news:loom.20040526T173111-721@post.gmane.org...
Johan Nilsson <johan.nilsson <at> esrange.ssc.se> writes:
He's definitely the expert on the subject in question. Exception handling aside for a moment, I still would strongly prefer to have separate entry/exit methods in the state object interface; it more clearly relates to the conceptual view of FSMs. And, while we're at it: perhaps the entry()/exit() points should be user configurable (per-state-type) to correspond to the UML notation e.g. "entry() / init()", "exit() / cleanup()" IIRC.
Again, this is a matter of simplicity. Agreed, entry() and exit() are more understandable for someone who is proficient in the FSM domain. However, how long does it take to explain that the entry action is mapped to the state constructor and the exit action is mapped to the state destructor? Probably 10 seconds. If something can be explained that quickly, I'm very sceptical about the benefit of introducing a *new feature* just for the sake of understandability.
Isn't good understandability in the public interfaces what libraries are all about (well, perhaps not _all_, but you get my point)?
Remember, even if we had entry() and exit(), we would still need state objects that are created on entry and destructed on exit. This is crucial to satisfy the scalability requirements.
I got the impression otherwere in this thread that destruction on exit was unrelated to 'scalability', does that not hold?
As I haven't been following your discussion with Dave; is this also a performance concern (function call overhead)?
Yes, I think so. I currently don't see any way how entry()/exit() could be implemented without causing overhead for people not using them, although I vaguely recall that Dave once hinted in this direction.
Policies? // Johan

Johan Nilsson <johan.nilsson <at> esrange.ssc.se> writes:
Isn't good understandability in the public interfaces what libraries are all about (well, perhaps not _all_, but you get my point)?
Yes, but adding entry()/exit() is non-trivial and complicates the interface. I don't think this is justified when one can explain it in 10 seconds.
Remember, even if we had entry() and exit(), we would still need state objects that are created on entry and destructed on exit. This is crucial to satisfy the scalability requirements.
I got the impression otherwere in this thread that destruction on exit was unrelated to 'scalability', does that not hold?
You're right, sorry for the confusion. Scalability is retained, even if state objects are constructed long before entry and destroyed long after exit. Important is only that every FSM object has its own state objects.
As I haven't been following your discussion with Dave; is this also a performance concern (function call overhead)?
Yes, I think so. I currently don't see any way how entry()/exit() could be implemented without causing overhead for people not using them, although I vaguely recall that Dave once hinted in this direction.
Policies?
Maybe. Regards, Andreas

"Rob Stewart" <stewart@sig.com> wrote in message news:200405251832.i4PIW7G18701@lawrencewelk.systems.susq.com...
From: Andreas Huber <ah2003@gmx.net>
Could the fsm manage a container of (pointers to) state objects that it populates as it first creates each state object and from which it retrieves a state object when transitioning? IOW, the first time the fsm encounters the need to transition to a given state (from A to B, say), it creates B and tracks it in the container. Upon transition back to A, the fsm locates state A in the container and reuses it.
This is certainly possible. I just have my doubts whether this approach
is so
much better speed-wise. If you customize state memory management, construction and destruction doesn't take an awful amount of cycles. Searching that state container might well take longer for large FSMs.
That's certainly an important consideration, but I was thinking in terms of something built into the fsm framework, thus providing the benefit for all clients versus requiring clients to do work if they want the speed improvement. If your concern proves correct, then using the container approach isn't helpful. However, the states' base class, which I presume is part of your library, could provide a pool allocator for state objects. That would put the onus on the library, rather than the clients, and would still reduce the (perceived, not proven) overhead caused by creating and destroying state objects on each transition.
If you refer to my posts, I'd just like to make clear that I was never concerned about overhead originating from the creation/destruction (to be perfectly clear; memory allocation/freeing) of the state objects themselves. I was more concerned about the execution of the code needed to initalize the state objects, which is the same regardless of whatever allocators being used. // Johan

From: "Johan Nilsson" <johan.nilsson@esrange.ssc.se>
"Rob Stewart" <stewart@sig.com> wrote in message
From: Andreas Huber <ah2003@gmx.net>
much better speed-wise. If you customize state memory management,
construction
and destruction doesn't take an awful amount of cycles. Searching that state container might well take longer for large FSMs.
That's certainly an important consideration, but I was thinking in terms of something built into the fsm framework, thus providing the benefit for all clients versus requiring clients to do work if they want the speed improvement. If your concern proves correct, then using the container approach isn't helpful. However, the states' base class, which I presume is part of your library, could provide a pool allocator for state objects. That would put the onus on the library, rather than the clients, and would still reduce the (perceived, not proven) overhead caused by creating and destroying state objects on each transition.
If you refer to my posts, I'd just like to make clear that I was never concerned about overhead originating from the creation/destruction (to be perfectly clear; memory allocation/freeing) of the state objects themselves. I was more concerned about the execution of the code needed to initalize the state objects, which is the same regardless of whatever allocators being used.
OK, I guess I launched off on a tangent. You make a good argument though that *if* state objects were only ever created once, then the initialization would only be needed once. The question arises, then, whether there are compelling cases where that's the case. For example, it would be possible to create a state class that referred to an external object for the reusable data, so Andreas' current design would only preclude you from putting that data in the state object itself. Thus, his current design can be made to fit your desires pretty easily, so it may argue against keeping the state objects alive. Andreas made the remark that tying construction to entry and destruction to exit helps with scalability. I imagine that means the he's worried about a potentially large number of states in some fsm's and that keeping all the state objects alive simultaneously could be a bad idea. If that's accurate, my question is whether this concern is valid. How many state machines have so many states that are so large that they would cause appreciable memory problems were the state objects not destroyed upon exit? I have no idea, and I won't speculate. -- Rob Stewart stewart@sig.com Software Engineer http://www.sig.com Susquehanna International Group, LLP using std::disclaimer;

Rob Stewart wrote:
Andreas made the remark that tying construction to entry and destruction to exit helps with scalability. I imagine that means the he's worried about a potentially large number of states in some fsm's and that keeping all the state objects alive simultaneously could be a bad idea.
Ok, I guess I didn't express this clearly enough. When I mention scalability I always refer to the fact that you can spread a state machine over multiple translation units and that a change in one of these TUs will not trigger a recompilation of the whole state machine. The two corner stones that enable this are: - State local storage. Every FSM object owns separate state objects. I.e. state objects are *not* shared between multiple FSM objects (as they are in some FSM implementations) - custom_reactions Whether or not states are also destructed when they are exited does not make a difference here.
If that's accurate, my question is whether this concern is valid. How many state machines have so many states that are so large that they would cause appreciable memory problems were the state objects not destroyed upon exit? I have no idea, and I won't speculate.
In some very constrained environments this might rarely be a valid concern but I'd be surprised if someone chose to use boost::fsm in such a situation. Don't get me wrong, I think it should still perform reasonably even on quite constrained systems but for a few applications it will inevitably be too slow/heavyweight. Regards, Andreas

"Andreas Huber" <ah2003@gmx.net> wrote in message news:c9321i$njh$1@sea.gmane.org...
Rob Stewart wrote:
Andreas made the remark that tying construction to entry and destruction to exit helps with scalability. I imagine that means the he's worried about a potentially large number of states in some fsm's and that keeping all the state objects alive simultaneously could be a bad idea.
This was exactly the same impression I've had all the time - somewhat analogous to COM+ where objects have no own 'state' and are constantly recreated or even reused, IIRC.
Ok, I guess I didn't express this clearly enough. When I mention scalability I always refer to the fact that you can spread a state machine over multiple translation units and that a change in one of these TUs will not trigger a recompilation of the whole state machine.
This should really be more clearly stated in the docs - perhaps even the wording 'scalability' should be changed. You're referring to minimizing compile-time dependencies, right? The two corner stones that enable
this are: - State local storage. Every FSM object owns separate state objects. I.e. state objects are *not* shared between multiple FSM objects (as they are in some FSM implementations)
The term "state local storage" feels a bit misleading to me - is that a well-known term? I relate to thread local storage, which makes me think that each state has some private storage accessible to only itself (well ok, maybe it has - 'this'). Is another wording possible?
- custom_reactions
Whether or not states are also destructed when they are exited does not make a difference here.
If that is the case; would it be possible to address the question by adding a state_lifetime policy to e.g. the state_machine class? // Johan

Johan Nilsson wrote:
This should really be more clearly stated in the docs - perhaps even the wording 'scalability' should be changed.
Agreed.
You're referring to minimizing compile-time dependencies, right?
Right.
The term "state local storage" feels a bit misleading to me - is that a well-known term?
No, it isn't. I have coined that term. I hoped it is readily understood by people proficient in the FSM domain.
I relate to thread local storage, which makes me think that each state has some private storage accessible to only itself (well ok, maybe it has - 'this'). Is another wording possible?
Sure, do you have any suggestions?
If that is the case; would it be possible to address the question by adding a state_lifetime policy to e.g. the state_machine class?
It is certainly possible but I'm not convinced that such a policy solves a real problem. See my other posts... Regards, Andreas

Johan Nilsson <johan.nilsson <at> esrange.ssc.se> writes:
boost::fsm::static and boost::fsm::dynamic ?
Or boost::static_fsm and boost::dynamic_fsm... [snip]
Sorry, "rewriting" was entirely incorrect; I referred to modifying the source code when a reaction (correct terminology?) should result in a transition to a newly implemented state (i.e. one not originally defined).
I guess we agree that you have to modify some code somewhere to achieve this behavior change. What's so bad about changing the code of the state where the new transition originates? I think this is superior to having a global transition table. Such a global table inevitably becomes a change hotspot in large FSMs and leads to frequent recompilations. [snip]
Exception: outer states should have some knowledge about inner states (they might be considered state-machines themselves).
I don't see what that would buy you. I think it is a bad idea to have outer states know exactly what inner states they contain. This would make it close to impossible to spread machines over multiple translation units, which I consider a key feature of my library.
I didn't intend to imply that states should _exactly_ know their inner states (i.e actual types). I was thinking more in terms of the implementation of a superstate as an fsm itself (and, more specifically I guess, dynamic fsm:s).
Ok, but I still don't understand your question/suggestion/proposal. Could you please expand? [snip]
So you would still have objects for states but you'd create them when the state machine is created, right?
No, before.
When before? At program startup? [snip]
No, only if statically defined. I was thinking in runtime-terms again, perhaps:
// // pseudo-code (no, I didn't think thoroughly before writing this) // from_state_id = fsm.add_state(<some-state>) to_state_id = fsm.add_state(<another-state>) fsm.add_transition(from_state_id, <on-event>, <event-result>, to_state_id) fsm.add_transition_to_self(from_state_id, <another-event>, <another-event-result>)
Yes, this is incompatible with static FSMs... [snip]
I've never heard about such a state machine feature. UML has do-activities that are associated with a certain state. When the state is left the do- activity is aborted.
I guess it's not in the formal definition of state machines. I'll try to give an example; suppose one state is responsible for updating the status of a remote entity via TCP when active; but the remote entity requires a periodic keep-alive so as not to close the connection
Why not create the thread in an outer state or even in the state machine constructor and keep it alive for the whole lifetime of the state machine? If the thread needs to be alive longer than a certain state does I would consider it a design error to make it a member of the state. [snip]
connection). This could be handled in the current fsm implentation (I believe) by keeping such things in the fsm or enclosing state - but
(consider a state needing to setup a TCP there's
that coupling again.
You lost me here.
I just meant that the state is dependent on its "container" to maintain non-volatile data.
Yes, what's so bad about that? An inner state always depends on the behavior implemented by its outer state. Just like a derived class always depends on the implementation of its base class.
no socket creation errors; ... etc .. during runtime [basically the same as 3.2 above]. (aka "do or die").
No problem either, just allocate your socket first thing in an outermost state and never leave that state unless the state machine is terminated.
Isn't that quite a restriction? I do like the 'context' stuff, but in a complex statemachine the context could get bloated with unrelated functionality.
No, quite to the contrary: It gives you the opportunity to give every object exactly the lifetime it needs to have. If your object needs to live longer than a particular state does, make it a member of one of its outer states. I still fail to see what the problem with this approach is. [snip]
I just looked up the term do-activity and it seems to model the concept rather nicely.
If yes, then you can already implement these with a thread that is
started in the entry action and cancelled (!) in the exit action of a particular state.
Again, that's quite a heavy-weight solution. How about implementing the do-activity with a worker thread in the fsm itself (requires cooperation from within the active state, of course).
Still, that functionality is orthogonal to what boost::fsm does. You can easily implement this yourself without changing one line in boost fsm (provided you have a threading lib supporting cancellation).
Huh? I guess I was dreaming when I wrote that. Where do I mention this? I don't see any way how a dynamic FSM could be implemented in terms of boost::fsm.
--- rationale excerpt --- Why not use a dynamically configurable FSM library for all state machines? ... It is for these reasons, that boost::fsm was built from ground up to not support dynamic configurability. However, this does not mean that it's impossible to dynamically shape a machine implemented with this library. ... --- end rationale excerpt ---
Or do I misinterpret the meaning of the text?
Yes, you should have quoted the following sentences also: <quote> For example, guards can be used to make different transitions depending on input only available at runtime. However, such layout changes will always be limited to what can be foreseen before compilation. <\quote> I think the last sentence very clearly explains the difference between a dynamically shaped static fsm and a fully dynamic fsm, does it not? Regards, Andreas

"Andreas Huber" <ah2003@gmx.net> wrote in message news:loom.20040525T161541-653@post.gmane.org...
Johan Nilsson <johan.nilsson <at> esrange.ssc.se> writes:
boost::fsm::static and boost::fsm::dynamic ?
Or boost::static_fsm and boost::dynamic_fsm...
[snip]
Sorry, "rewriting" was entirely incorrect; I referred to modifying the source code when a reaction (correct terminology?) should result in a transition to a newly implemented state (i.e. one not originally defined).
I guess we agree that you have to modify some code somewhere to achieve
behavior change. What's so bad about changing the code of the state where
Even better. this the
new transition originates? I think this is superior to having a global transition table. Such a global table inevitably becomes a change hotspot in large FSMs and leads to frequent recompilations.
Are we convinced that the only thing we're discussing now is static FSMs? If so, you are correct and I believe you've made a correct design decision in this particular respect.
[snip]
Exception: outer states should have some knowledge about inner states (they might be considered state-machines themselves).
I don't see what that would buy you. I think it is a bad idea to have outer states know exactly what inner states they contain. This would make it close to impossible to spread machines over multiple translation units, which I consider a key feature of my library.
I didn't intend to imply that states should _exactly_ know their inner states (i.e actual types). I was thinking more in terms of the implementation of a superstate as an fsm itself (and, more specifically I guess, dynamic fsm:s).
Ok, but I still don't understand your question/suggestion/proposal. Could you please expand?
It's neither; we're comparing apples and pears (well, that might be a swedish idiom only ;-). If a static FSM would have complete control over all transitions, analogous to the dynamic example I provided below, the static FSM would need to know all inner states. If a dynamic FSM would have complete control over all transition it doesn't need to know the exact type of all inner states. Am I correct?
[snip]
So you would still have objects for states but you'd create them when
the
state machine is created, right?
No, before.
When before? At program startup?
Perhaps - perhaps not. I've got my mind set up for dynamic FSMs so I guess this is rather irrelevant for the discussion.
[snip]
No, only if statically defined. I was thinking in runtime-terms again, perhaps:
// // pseudo-code (no, I didn't think thoroughly before writing this) // from_state_id = fsm.add_state(<some-state>) to_state_id = fsm.add_state(<another-state>) fsm.add_transition(from_state_id, <on-event>, <event-result>,
to_state_id)
fsm.add_transition_to_self(from_state_id, <another-event>, <another-event-result>)
Yes, this is incompatible with static FSMs...
Obviously ;-)
[snip]
I've never heard about such a state machine feature. UML has do-activities that are associated with a certain state. When the state is left the do- activity is aborted.
I guess it's not in the formal definition of state machines. I'll try to give an example; suppose one state is responsible for updating the status of a remote entity via TCP when active; but the remote entity requires a periodic keep-alive so as not to close the connection
Why not create the thread in an outer state or even in the state machine constructor and keep it alive for the whole lifetime of the state machine? If the thread needs to be alive longer than a certain state does I would consider it a design error to make it a member of the state.
See comments below.
[snip]
connection). This could be handled in the current fsm implentation (I believe) by keeping such things in the fsm or enclosing state - but
(consider a state needing to setup a TCP there's
that coupling again.
You lost me here.
I just meant that the state is dependent on its "container" to maintain non-volatile data.
Yes, what's so bad about that? An inner state always depends on the behavior implemented by its outer state. Just like a derived class always depends on the implementation of its base class.
For me, an inner state "is-not-a" outer state (regardless of the name 'superstate' in the UML docs). The outer state <-> inner state relation feels more analogous to an association (or perhaps an aggregation). So the comparison to base and derived feels a bit out of hand. That's why I don't like the dependency on the "container". Two way associations generally bring with them too much coupling, IMHO. Let me make an alternate suggestion: The states needing 'persistent' data (or functionality, such in the admittedly convoluted TCP example above) would tell the state machine to maintain a special context object for them - for the entire lifetime of the FSM. This context object could be of any class -> but should _not_ be the fsm itself or an outer state. The context object could inherit from a specific base just so that the fsm could store them conveniently. The type of the context object could be specified as a template parameter to the e.g. simple_state<> template. I'll try to outline an example, just to give you an idea (I won't consider the exact steps needed to actually implement this): namespace fsm { struct no_context : fsm::context<no_context>{}; } struct my_state_context : public fsm::context<my_state_context, ...> { }; struct my_state : public fsm::simple_state< my_state, ..., my_state_context /* defaults to fsm::no_context */
{ }; Access to the context should be made easy by providing the 'context()' method in simple state, now returning a reference to the FSMs only instance of the context -> implemented in simple_state. The 'context' method should be unavailable if the context template argument equals no_context; perhaps doable through PTS. What would also be needed is the ability for several (closely related) states to share the same context, but _still_ without the need for the context to be the outer state or fsm - needs some consideration on how to implement this. As you see I really haven't concrete examples on how to implement this - but given this or similar implementation I could leave out the requirements for the actual state objects to remain live for the lifetime of the FSM itself. Now I could even make the context object an "active" object (i.e. run by a private thread). We'd still have the benefits of your basic FSM design; we'd have looser coupling between outer, inner states and the FSM. I'll leave the rest of the discussion for now, as most of it relates to dynamic FSMs (perhaps with the exception for out-of-the-box support for do-activities, which I'd like to have included). What do you say? // Johan

Johan Nilsson <johan.nilsson <at> esrange.ssc.se> writes:
I guess we agree that you have to modify some code somewhere to achieve this behavior change. What's so bad about changing the code of the state where the new transition originates? I think this is superior to having a global transition table. Such a global table inevitably becomes a change hotspot in large FSMs and leads to frequent recompilations.
Are we convinced that the only thing we're discussing now is static FSMs? If so, you are correct and I believe you've made a correct design decision in this particular respect.
Yes, I am ;-). Seriously, I hope I have convinced you that we need two libraries: One for static FSMs and one for dynamic FSMs. I don't have enough domain-knowledge to implement a dynamic FSM, so I can't really discuss this with you without making assumptions that might well be false.
Ok, but I still don't understand your question/suggestion/proposal. Could you please expand?
It's neither; we're comparing apples and pears (well, that might be a swedish idiom only .
If a static FSM would have complete control over all transitions, analogous to the dynamic example I provided below, the static FSM would need to know all inner states. If a dynamic FSM would have complete control over all transition it doesn't need to know the exact type of all inner states. Am I correct?
Yes, I believe that is correct. [snip]
I just meant that the state is dependent on its "container" to maintain non-volatile data.
Yes, what's so bad about that? An inner state always depends on the behavior implemented by its outer state. Just like a derived class always depends on the implementation of its base class.
For me, an inner state "is-not-a" outer state (regardless of the name 'superstate' in the UML docs).
Well, I think you have the wrong model of inner and outer states then. The correspondence between subclasses and inner states is really quite remarkable: - The sequence of base and derived class constructor calls is the same as the outer and inner state entry sequence - The sequence of destructor calls is the same as the exit sequence - Inner states sometimes define an in-state reaction for an event that an outer state also has a reaction for and delegate to the outer state reaction after doing some stuff internally. Derived and base classes do exactly the same through virtual functions. - The LSP must hold for an outer state and all its inner states (see Miro Samek's articles in CUJ last summer).
The outer state <-> inner state relation feels more analogous to an association (or perhaps an aggregation). So the comparison to base and derived feels a bit out of hand. That's why I don't like the dependency on the "container". Two way associations generally bring with them too much coupling, IMHO.
The only sort of two-way association exists between an outer state and its inner *initial* state. Even there it is not exactly two-way because the inner initial state is an incomplete type when the outer state is defined. The outer state does not have any knowledge of any of its other inner states.
Let me make an alternate suggestion: The states needing 'persistent' data (or functionality, such in the admittedly convoluted TCP example above) would tell the state machine to maintain a special context object for them - for the entire lifetime of the FSM. This context object could be of any class -> but should _not_ be the fsm itself or an outer state.
Why not?
The context object could inherit from a specific base just so that the fsm could store them conveniently. The type of the context object could be specified as a template parameter to the e.g. simple_state<> template. I'll try to outline an example, just to give you an idea (I won't consider the exact steps needed to actually implement this):
namespace fsm { struct no_context : fsm::context<no_context>{}; }
struct my_state_context : public fsm::context<my_state_context, ...> { };
struct my_state : public fsm::simple_state< my_state, ..., my_state_context /* defaults to fsm::no_context */
{ };
Access to the context should be made easy by providing the 'context()' method in simple state, now returning a reference to the FSMs only instance of the context -> implemented in simple_state. The 'context' method should be unavailable if the context template argument equals no_context; perhaps doable through PTS.
What would also be needed is the ability for several (closely related) states to share the same context, but _still_ without the need for the context to be the outer state or fsm - needs some consideration on how to implement this.
That is all very interesting but I still don't get why putting your objects into such a context container is better than putting your object in outer states or the state_machine subclass itself?
As you see I really haven't concrete examples on how to implement this - but given this or similar implementation I could leave out the requirements for the actual state objects to remain live for the lifetime of the FSM itself. Now I could even make the context object an "active" object (i.e. run by a private thread).
As I have already pointed out: I don't see why this should not be possible with boost::fsm as it is now. If your really need that thread to outlive all your states then make the thread object a member of the state_machine subclass.
We'd still have the benefits of your basic FSM design; we'd have looser coupling between outer, inner states and the FSM.
No, we wouldn't. Inner states will always need to directly know their outer states, there's no way around that (e.g. see the StopWatch example, where an inner state accesses its outer state). Regards, Andreas

Johan Nilsson <johan.nilsson <at> esrange.ssc.se> writes:
I guess we agree that you have to modify some code somewhere to achieve this behavior change. What's so bad about changing the code of the state where the new transition originates? I think this is superior to having a global transition table. Such a global table inevitably becomes a change hotspot in large FSMs and leads to frequent recompilations.
Are we convinced that the only thing we're discussing now is static FSMs? If so, you are correct and I believe you've made a correct design decision in this particular respect.
Yes, I am ;-). Seriously, I hope I have convinced you that we need two libraries: One for static FSMs and one for dynamic FSMs. I don't have enough domain-knowledge to implement a dynamic FSM, so I can't really discuss
"Andreas Huber" <ah2003@gmx.net> wrote in message news:loom.20040526T100736-389@post.gmane.org... this
with you without making assumptions that might well be false.
Ok, but I still don't understand your question/suggestion/proposal. Could you please expand?
It's neither; we're comparing apples and pears (well, that might be a swedish idiom only .
If a static FSM would have complete control over all transitions, analogous to the dynamic example I provided below, the static FSM would need to know all inner states. If a dynamic FSM would have complete control over all transition it doesn't need to know the exact type of all inner states. Am I correct?
Yes, I believe that is correct.
[snip]
I just meant that the state is dependent on its "container" to
To be honest, when it comes to details I probably haven't either. maintain
non-volatile data.
Yes, what's so bad about that? An inner state always depends on the behavior implemented by its outer state. Just like a derived class always depends on the implementation of its base class.
For me, an inner state "is-not-a" outer state (regardless of the name 'superstate' in the UML docs).
Well, I think you have the wrong model of inner and outer states then.
The correspondence between subclasses and inner states is really quite remarkable: - The sequence of base and derived class constructor calls is the same as
Actually, it's more of a gut feeling. the
outer and inner state entry sequence
For the inital inner state, yes.
- The sequence of destructor calls is the same as the exit sequence
In a way, yes. But the exit for the inital inner state is unlikely to be immediately followed by the exit for the outer state (unless the initial state's also the last inner state).
- Inner states sometimes define an in-state reaction for an event that an outer state also has a reaction for and delegate to the outer state reaction after doing some stuff internally. Derived and base classes do exactly the same through virtual functions.
True.
- The LSP must hold for an outer state and all its inner states (see Miro Samek's articles in CUJ last summer).
I did read them; I'll have to re-read and get back on that. That implies that all inner states must implement the (event handling) interface in the same way as the outer state?
The outer state <-> inner state relation feels more analogous to an association (or perhaps an aggregation). So
comparison to base and derived feels a bit out of hand. That's why I don't like the dependency on the "container". Two way associations generally bring with them too much coupling, IMHO.
The only sort of two-way association exists between an outer state and its inner *initial* state. Even there it is not exactly two-way because the inner initial state is an incomplete type when the outer state is defined. The outer state does not have any knowledge of any of its other inner states.
Let me make an alternate suggestion: The states needing 'persistent' data (or functionality, such in the admittedly convoluted TCP example above) would tell the state machine to maintain a special context object for
the them -
for the entire lifetime of the FSM. This context object could be of any class -> but should _not_ be the fsm itself or an outer state.
Why not?
That should read "... should _not_ _have_ to be the fsm ..."
The context object could inherit from a specific base just so that the fsm could
them conveniently. The type of the context object could be specified as a template parameter to the e.g. simple_state<> template. I'll try to outline an example, just to give you an idea (I won't consider the exact steps needed to actually implement this):
namespace fsm { struct no_context : fsm::context<no_context>{}; }
struct my_state_context : public fsm::context<my_state_context, ...> { };
struct my_state : public fsm::simple_state< my_state, ..., my_state_context /* defaults to fsm::no_context */
{ };
Access to the context should be made easy by providing the 'context()' method in simple state, now returning a reference to the FSMs only instance of the context -> implemented in simple_state. The 'context' method should be unavailable if the context template argument equals no_context;
store perhaps
doable through PTS.
What would also be needed is the ability for several (closely related) states to share the same context, but _still_ without the need for the context to be the outer state or fsm - needs some consideration on how to implement this.
That is all very interesting but I still don't get why putting your objects into such a context container is better than putting your object in outer states or the state_machine subclass itself?
"That is all very interesting" sounds like you're fed up with this discussion ... for me it boils down to a cleaner separation of the concepts and looser restrictions on how 'context' is implemented; but let me put it this way: what's so wrong about it?
As you see I really haven't concrete examples on how to implement this - but given this or similar implementation I could leave out the requirements for the actual state objects to remain live for the lifetime of the FSM itself. Now I could even make the context object an "active" object (i.e. run by a private thread).
As I have already pointed out: I don't see why this should not be possible with boost::fsm as it is now. If your really need that thread to outlive all your states then make the thread object a member of the state_machine subclass.
True. Same as above holds here.
We'd still have the benefits of your basic FSM design; we'd have looser coupling between outer, inner states and the FSM.
No, we wouldn't. Inner states will always need to directly know their outer states, there's no way around that (e.g. see the StopWatch example, where an inner state accesses its outer state).
Got me there. // Johan

Johan Nilsson <johan.nilsson <at> esrange.ssc.se> writes:
- The sequence of destructor calls is the same as the exit sequence
In a way, yes. But the exit for the inital inner state is unlikely to be immediately followed by the exit for the outer state (unless the initial state's also the last inner state).
Yes, that is the main difference between derived classes and inner states. Making a transition between two inner states is the equivalent of destructing the derived class portion of the object but leaving the base class portion intact and then constructing a different derived class object on top of the base class portion. This is of course not possible in C++ and presumably most other OO languages and is probably of little use outside the FSM domain. Apart from that there are not many more differences.
- Inner states sometimes define an in-state reaction for an event that an outer state also has a reaction for and delegate to the outer state reaction after doing some stuff internally. Derived and base classes do exactly the same through virtual functions.
True.
- The LSP must hold for an outer state and all its inner states (see Miro Samek's articles in CUJ last summer).
I did read them; I'll have to re-read and get back on that. That implies that all inner states must implement the (event handling) interface in the same way as the outer state?
Yes, just like derived classes they inherit that interface anyway (one more similarity), so an inner state that does not define any reactions behaves exactly like its outer state. The additional reactions an inner state defines can *refine* the outer states behavior, but it must not fundamentally change the behavior as that would violate the LSP.
Let me make an alternate suggestion: The states needing 'persistent' data (or functionality, such in the admittedly convoluted TCP example above) would tell the state machine to maintain a special context object for them - for the entire lifetime of the FSM. This context object could be of any class -> but should _not_ be the fsm itself or an outer state.
Why not?
That should read "... should _not_ _have_ to be the fsm ..."
Still, why should it not have to?
That is all very interesting but I still don't get why putting your objects into such a context container is better than putting your object in outer states or the state_machine subclass itself?
"That is all very interesting" sounds like you're fed up with this discussion
No, that's not the case (English isn't my mother tongue either so I'm not familiar with such subtleties). It *is* genuinely interesting, because I've never thought about this.
... for me it boils down to a cleaner separation of the concepts and looser restrictions on how 'context' is implemented; but let me put it this way: what's so wrong about it?
It violates the KISS principle. It introduces a facility for something that can already be done in boost::fsm and quite nicely too. Before I'm going to even think about such a thing I really need to see compelling real-world use cases. I hope you agree that simplicity in usage is a key factor in all boost libraries. Make it as complex as necessary but no more. Regards, Andreas

Making a transition between two inner states is the equivalent of destructing the derived class portion of the object but leaving the base class portion intact and then constructing a different derived class object on top of
"Andreas Huber" <ah2003@gmx.net> wrote: the
base class portion. This is of course not possible in C++ ....
It is sort of possible using placement constructors and destructors: - allocate enough of memory to hold largest derived object - construct derived object 1 - destruct derived object 1 but keep its base class data - construct derived object 2 on the same place and 'reuse' base class data Its rather dubious trick and probably relies on undefined behaviour but may work for typical compilers/situations. /Pavel

Hi Pavel,
Making a transition between two inner states is the equivalent of destructing the derived class portion of the object but leaving the base class portion intact and then constructing a different derived class object on top of the base class portion. This is of course not possible in C++ ....
It is sort of possible using placement constructors and destructors: - allocate enough of memory to hold largest derived object - construct derived object 1 - destruct derived object 1 but keep its base class data - construct derived object 2 on the same place and 'reuse' base class data
Hmm, how do you avoid that the base class constructor is called when you construct the derived object 2? Not that I have ever used placement new, but I can't recall having seen the possibility of not calling the base class constructor when you call a derived class constructor. Regards, Andreas

"Andreas Huber" <ah2003@gmx.net> wrote:
Making a transition between two inner states is the equivalent of destructing the derived class portion of the object but leaving the base class portion intact and then constructing a different derived class object on top of the base class portion. This is of course not possible in C++ ....
It is sort of possible using placement constructors and destructors: ....
Hmm, how do you avoid that the base class constructor is called when you construct the derived object 2? Not that I have ever used placement new, but I can't recall having seen the possibility of not calling the base class constructor when you call a derived class constructor.
The base class constructor needs to be aware of the trick. There should be flag indicating whether or not the data are already 'initialized' and constructor would act on this info. /Pavel

Hi, I would like to get some clarification, so please correct me if I'm wrong. The first feature listed in the boost::fsm documentation is "straightforward transformation from UML state chart to executable C++ code". Since straightforward is in the eye of the beholder, I think the following would convey more information : "Direct mapping of UML state elements to boost::fsm elements for simple but manual transformation from UML state chart to executable C++ code." This would seem to be in agreement with your addition comment that no code generator is required. I am confused why having no code generator is considered a feature. It would seem the reverse is a feature, because a code generator would save coding time and eliminate human translation errors. Thanks, "Andreas Huber" <ah2003@gmx.net> wrote in message news:c8ogtj$npe$1@sea.gmane.org...
Dear Boosters
I think boost::fsm has finally reached a state that is worthy of a formal review.

Kwee Heong Tan <tan.k.h <at> juno.com> writes:
I would like to get some clarification, so please correct me if I'm wrong.
The first feature listed in the boost::fsm documentation is "straightforward transformation from UML state chart to executable C++ code".
Since straightforward is in the eye of the beholder, I think the following would convey more information : "Direct mapping of UML state elements to boost::fsm elements for simple but manual transformation from UML state chart to executable C++ code."
Agreed, that is probably a better description. I'll change that sentence.
I am confused why having no code generator is considered a feature.
Code generation is useful, especially when both of the following conditions hold: - Humans don't need to read and/or modify the generated code. - There is no library that allows you solve the problem with similar ease as the code generator approach. If, for a given problem, both of the above conditions do not hold then I believe you're almost always better off with a library. We could now engage in a discussion about the pros and cons, but I believe this is rather off-topic for the boost list (recently there was a discussion about code generation on c.l.cpp.m and I have expressed my opinion there). The most important argument is that with boost::fsm you have a choice: - You can easily manually implement FSMs - You can write a code generator that generates boost::fsm client code For FSM frameworks that are geared towards a generator front-end you almost never have that choice as the generated code is much harder to read and understand (tiny bits of user code are embedded in large chuncks of generated code).
It would seem the reverse is a feature, because a code generator would save coding time and eliminate human translation errors.
Sorry, but that sounds like the usual marketing stuff you hear from companies selling code generators. I did work with FSM code generators and initially I thought that they are a good idea. However, with time and experience I got more and more convinced that in the long run they typically introduce more complexity than they purport to remove, especially if there is a library that allows you to very directly and easily convert UML to code. Regards, Andreas

Andreas Huber <ah2003 <at> gmx.net> writes:
The most important argument is that with boost::fsm you have a choice: - You can easily manually implement FSMs - You can write a code generator that generates boost::fsm client code
Or maybe a code "viewer" would be even better - I wonder if synopsis could be extended in that direction? I tend to find diagrams nice to read and awful to write (typing text may not be very trendy, but it is pretty quick when not interrupted by much pointing, clicking, dialog opening, form filling.....).

Darryl Green wrote:
Andreas Huber <ah2003 <at> gmx.net> writes:
The most important argument is that with boost::fsm you have a choice: - You can easily manually implement FSMs - You can write a code generator that generates boost::fsm client code
Or maybe a code "viewer" would be even better -
Before a generator I would definitely implement a viewer. However, I don't think I will have the time... Regards, Andreas
participants (20)
-
Aleksey Gurtovoy
-
Andreas Huber
-
Darryl Green
-
David Abrahams
-
David B. Held
-
David Bergman
-
E. Gladyshev
-
E. Gladyshev
-
Gregory Colvin
-
Iain K. Hanson
-
Jeff Flinn
-
Johan Nilsson
-
John Fuller
-
Kwee Heong Tan
-
Marshall Clow
-
Pavel Vozenilek
-
Peter Dimov
-
Rob Stewart
-
Robert Bell
-
Topher Cooper