Re: Design for deriving one state-machine / double-dispatch class
from another
Hi Christophe!
Thank you very much for your reply and comments.
On Apr 10, 12:19 am, Christophe Henry
<christophe.j.he...@googlemail.com> wrote:
On 6 Apr., 22:03, Dave Abrahams <d...@boostpro.com> wrote:
on Tue Apr 03 2012, "K. Frank" <kfrank29.c-AT-gmail.com> wrote:
Is the ability to derive onestate machinefrom another naturally
supported by boost/msm?
You know, I'm not sure. I've pointed the Boost.MSM author at your post
and am hoping he'll answer.
Sorry for the delay, Easter time...
And to you a belated Happy Easter!
The documentation is not talking about this subject because it has
never been requested, but MSM does allow quite a bit.
...
The easiest way, which most people do, is to inherit a front-end state
machine class and provide a new table and initial state. States being
not part of the fsm, rewriting the table is sufficient and only means
a few lines of copying.
If I understand you correctly in this approach one
completely rewrites the transition table.
If I take the perspective that the guts of the state
machine, i.e., its core "business logic" is its
topology (i.e., the transition table), then this
approach doesn't achieve my goal of letting a
derived state machine reuse the bulk of its base
state machine.
The second way, provided as example inside libs\msm\doc\HTML\examples
is in the directory distributed_table. In this case, the table is
distributed in the different states, and the fsm itself has none. A
state then looks like:
struct Empty : public msm::front::state<>
{
template <class Event,class FSM>
void on_entry(Event const&,FSM& ) {std::cout << "entering: Empty"
<< std::endl;}
template <class Event,class FSM>
void on_exit(Event const&,FSM& ) {std::cout << "leaving: Empty" <<
std::endl;}
void open_drawer(open_close const&);
struct internal_transition_table : mpl::vector<
// Start Event Next
Action Guard
//+-------------+---------+-------------+---------
+---------------------------+----------------------+
msm::front::a_row2 < Empty , open_close , Open ,
Empty,&Empty::open_drawer >
//+-------------+---------+-------------+---------
+---------------------------+----------------------+
> {};
};
See here the distributed table. The drawback of this method is that it
forces strong coupling between states (like in your example) as they
need to know each other. MSM can do it, but it goes against its
philosophy of reuse and I think very few have really been interested
in this capability. But it allows a very good reuse while inheriting.
Okay, if I use the terminology that that rows of a
transition table are labelled by states and the
columns by events, am I right that in this scheme
each state contains its corresponding row in the
transition table? (To avoid any confusion, I am
using "row" here to denote a row in a two-dimensional
table. I believe what you are calling a "row" is what
I would call a "cell" or a "single entry" in the
transition table.)
In this case I have two questions:
First, in order to change the topology of my "derived"
state machine by changing one entry in the transition
table, would I then take the state that corresponds
to the row containing that entry and derive a new state
from it (or perhaps just replace the state with a new
state?), and rewrite the entire row of the transition
table in the derived state?
This would correspond in my example to overriding a
state-handler function in the derived state machine
and rewriting the entire switch statement that performs
the second level (discriminating on the event) of the
double dispatch. (This I can already do without incurring
the hierarchy-depth dependent run-time cost.)
So am I right that that in this scheme one can reuse
unchanged rows of the transition table, but not individual
unchanged cells?
Second, if an unchanged state references a modified (i.e.,
derived) state, does the msm framework know somehow to
use the modified state?
By way of explicit example, in my mod-2 modular-arithmetic
example when in state stateZero, event eOne triggers a
transition to state stateOne. In the mod-2 state machine,
eOne sends stateOne back to stateZero, but in the derived
mod-3 state machine, eOne sends stateOne to the new
stateTwo. So I have to modify the stateOne row of the
transition table. So in your scheme I would have a derived
(or new) version of the stateOne state class that contains
its modified row of the transition table. But now I go
back to the original stateZero class. Presumably it has
the following entry in its row of the transition table:
_row< stateZero, eOne, stateOne >
Do I now have to modify this part of the code for the
stateZero class to something like this:
_row< stateZero, eOne, derivedStateOne > // ?
If this is necessary, it looks like I still have to
rewrite the entire transition table, even though it
is distributed across the state classes.
Am I looking at this correctly?
The third way requires a bit of metaprogramming: inherit a fsm, and
add/delete/change the transitions you need. Adding is easy, the other
2 harder. If we take as basis the example provided in the functor
front-end (http://svn.boost.org/svn/boost/trunk/libs/msm/doc/HTML/
examples/SimpleWithFunctors.cpp), let's say we want to add a
transition Stopped => Playing on an event called new_event. We can use
boost::mpl::push_back:
// inherit from player_ fsm
struct derived_player : public player_
{
// new transition Stopped => Playing with event new_event
typedef Row<Stopped, new_event, Playing> added_row;
// new transition table based on player_ with an added transition
typedef
boost::mpl::push_back<player_::transition_table,added_row>::type
transition_table;
};
Okay, I think I see what is going on here. (I confess
to not being familiar with boost::mpl, but the intent
seems pretty clear.)
Can you clarify how I would rewire an existing transition
(i.e., entry in the transition table)?
For example, in my modular-arithmetic example, how would
I rewire something in my mod-2 state machine like
_row< stateOne, eOne, stateZero >
to become
_row< stateOne, eOne, stateTwo >
in my mod-3 state machine?
If this scheme does let me modify individual cells of
the transition table (as well as add to the transition
table), then it looks like it would accomplish my design
goal. (And would do so without me needing to pre-define
all of the "no-op" virtual functions for each state-event
combination.)
HTH,
Christophe
Thank you. I appreciate your insights.
K. Frank
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]