
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