
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