|
Boost : |
From: Johan Nilsson (johan.nilsson_at_[hidden])
Date: 2004-05-26 02:30:47
"Andreas Huber" <ah2003_at_[hidden]> wrote in message
news:loom.20040525T161541-653_at_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...
>
Even better.
> [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.
>
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]
> > > (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.
> >
> > 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
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk