Boost logo

Boost :

From: Andreas Huber (ah2003_at_[hidden])
Date: 2004-05-26 03:57:52


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


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk