|
Boost : |
From: Rob Stewart (stewart_at_[hidden])
Date: 2004-06-01 09:49:57
From: "Andreas Huber" <ah2003_at_[hidden]>
> David Abrahams wrote:
> > "Andreas Huber" <ah2003_at_[hidden]> 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_at_[hidden] Software Engineer http://www.sig.com Susquehanna International Group, LLP using std::disclaimer;
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk