Fw: State machine framework with dynamic configurability

Forwarded from comp.lang.c++.moderated for being OT there.
Original thread: http://tinyurl.com/cmvud
--
Andreas Huber
When replying by private email, please remove the words spam and trap
from the address shown in the header.
"Dave"
the FAQ illustrates how to modify the existing layout of an FSM, but I doubt whether it would be possible to implement the main FSM in the main exe and then dynamically load dlls to add inner/sub-states to existing states; since the state transition/reactions have to be specified as template parameters.
Out of curiosity: Would you care to describe what kind of application you need such a library for?
This is probably off-topic for this ng but I'll provide my reasons so you can decide if they're reasonable or not.
The application I mentioned original post is a call control application, in fact several of them. (Naturally each application is maintained by a group of developers) Each of the applications has a flat Fsm with many states. My goal is to design a framework that has a main Fsm that is common to all the call control applications (with points of customization pre-defined) and then dynamically load DLLs to customize the behavior (by adding inner/sub-states) of the Fsm. The obvious advantage of doing so would be code reuse. Another reason to do so is that if a customization for a specific customer is needed (say add a few states to the fsm), only a few DLLs need to be modified/added, whereas in the existing design the main application has to be modified and recompiled. In this way the customization may be done by a developer who does not have full access to the source code/statechart, all he/she needs to know is the interface of the "point of customization".
Dynamic configurabliity aside, the application I'm working on is a call control application, so I only need one instance of the state machine, one instance of each state and use context objects to store state information for each call. When I skimmed through the statechart tutorial I see that the "Entry" and "Exit" functions are implemented using the constructor and destructor of the simple_state class, so I suppose that each state object is constructed when entered and destructed when exited. I'm not sure.. if a state is entered and exited repeatedly, say in the stopwatch example, whether the object construction/destruction incurs an unecessary cost. If I'm wrong, please correct me.
If there's a free Fsm libary (not licensed under GPL) that satisfies my requirements that would be great, but I would also consider commercial products. Yet I'm under the impression that commerial Fsm libaries usually come with graphical UML editors... right now I'm stuck with Rational Rose 98 (old version), so the new tool would have to be able to import UML diagrams drawn using Rational Rose 98. One good thing about Rational Rose is that the saved files are pretty human readable so I can just write a script to parse the state chart specifications and generate code that uses some Fsm libarary (say boost statechart). Of course another alternative would be rely on the UML editor to generate frameowrk code for me but I would still have to make some modifications to archive dynamic configurability.
thanks
Dave - I'm a novice please be nice :-)
[ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ]

Dave wrote:
the FAQ illustrates how to modify the existing layout of an FSM, but I doubt whether it would be possible to implement the main FSM in the main exe and then dynamically load dlls to add inner/sub-states to existing states; since the state transition/reactions have to be specified as template parameters.
Why do you think that the transition/reaction specification interface is a problem? The FAQ item shows that you can have a base FSM that uses a virtual function to delay the decision where to transit from a given state (Idle) when a specific event occurs (EvStart). The base FSM (PumpBase) would be compiled into the main exe. If you want to modify the behavior in a dll, all you need to do is to derive from the base FSM and inside the virtual function override you transit to the desired state. Of course, for all this to work the main exe would need to have a means of constructing an object of the derived class (MyPump) that was loaded from the dll. This is easy enough to do with a factory.
Out of curiosity: Would you care to describe what kind of application you need such a library for?
This is probably off-topic for this ng but I'll provide my reasons so you can decide if they're reasonable or not.
The application I mentioned original post is a call control application, in fact several of them. (Naturally each application is maintained by a group of developers) Each of the applications has a flat Fsm with many states. My goal is to design a framework that has a main Fsm that is common to all the call control applications (with points of customization pre-defined) and then dynamically load DLLs to customize the behavior (by adding inner/sub-states) of the Fsm. The obvious advantage of doing so would be code reuse.
All this should be possible with Boost.Statechart, see above.
Another reason to do so is that if a customization for a specific customer is needed (say add a few states to the fsm), only a few DLLs need to be modified/added, whereas in the existing design the main application has to be modified and recompiled. In this way the customization may be done by a developer who does not have full access to the source code/statechart, all he/she needs to know is the interface of the "point of customization".
Exactly.
Dynamic configurabliity aside, the application I'm working on is a call control application, so I only need one instance of the state machine, one instance of each state and use context objects to store state information for each call.
Maybe there's just a terminology misunderstanding but I don't see the benefit of the context object. In fact, the use of a context object here looks like a common antipattern, a "winnebago" object (stolen from Jeff Garland). You'll find more on this here (search for the first occurrence of "heuristic" and read what follows): http://lists.boost.org/MailArchives/boost/msg79540.php
When I skimmed through the statechart tutorial I see that the "Entry" and "Exit" functions are implemented using the constructor and destructor of the simple_state class, so I suppose that each state object is constructed when entered and destructed when exited.
Exactly. This mechanism allows multiple developers to work on the same state machine without even knowing of each other. Very often you need additional variables (counters, etc.) that are associated with a state or a group of states. A developer working on a part of the machine can then simply add such a variable to an appropriate state class and never needs to modify a global context class or the state machine class.
I'm not sure.. if a state is entered and exited repeatedly, say in the stopwatch example, whether the object construction/destruction incurs an unecessary cost. If I'm wrong, please correct me.
That really depends on your application. If you have small flat fsms with no or little additional context associated with states then this cost can be prohibitive indeed. From what you have described so far this is not the case in your application. I don't think the construction/destruction overhead will be noticeable in your app, given that there is a lot of network communication involved (I assume). BTW, you can easily customize memory management to cut down the new/delete overhead. HTH & Regards, -- Andreas Huber When replying by private email, please remove the words spam and trap from the address shown in the header.

Why do you think that the transition/reaction specification interface is a problem? The FAQ item shows that you can have a base FSM that uses a virtual function to delay the decision where to transit from a given state (Idle) when a specific event occurs (EvStart). The base FSM (PumpBase) would be compiled into the main exe. If you want to modify the behavior in a dll, all you need to do is to derive from the base FSM and inside the virtual function override you transit to the desired state. Of course, for all this to work the main exe would need to have a means of constructing an object of the derived class (MyPump) that was loaded from the dll. This is easy enough to do with a factory.
You're right, it's possible to do so. I have yet to spend more time on writing test programs but right now I see some limitations. Say for the PumpBase example, the designer of the main Fsm decides to allow customization for the Idle state and Running state. In the example illustrated in the FAQ a MyRunning state (and inner states added to it) is defined and the MyPump Fsm derives from PumpBase to specify a state transition to the new MyRunning state. Suppose now I want to add some inner states for the Idle state. I can define a new MyIdle state but I cannot make it the initial state of MyPump since Idle was specified as a template parameter in PumpBase and MyPump derives from PumpBase. Let's put that aside and now let's suppose there's a third state in PumpBase Fsm: Stopped, and a state transition from Running to Stopped is triggered by event EvStop. and the following "point of customization" is defined in PumpBase virtual sc::result react(Running& running, const EvStop &) const; To add inner states to Stopped I can define MyStopped and have MyPump transition from Running to MyStopped. However in MyPump Running is replaced by MyRunning, so I need to have my own react method to transit to MyStopped, and I'm not even using the "point of customization" in PumpBase because it has a different signature. Up till now it looks like I'm defining a new Fsm to replace PumpBase rather than customizing it. All this is fine if PumpBase has few states. Yet consider the case when 2 developers work on PumpBase, one responsible for customizing the Running state, the other for customizing the Stopped state. The developers should be able to work on the state machine without knowing each other. Yet as I've illustrated above, there can only be one MyPump class and whoever writes that class has to be aware of the MyRunning and MyStopped class and write the react functions accordingly. btw, I think there's a typo in the FAQ code snippet. "sc::custom_reaction< EvStart >" should not be the third template argument for struct Idle.
Maybe there's just a terminology misunderstanding but I don't see the benefit of the context object. In fact, the use of a context object here looks like a common antipattern, a "winnebago" object (stolen from Jeff Garland). You'll find more on this here (search for the first occurrence of "heuristic" and read what follows):
Yes it is an antipattern if the object requires modification everytime a new field is needed, but with a property map the new field can simply be inserted into the map. Moreover, since a state class is destroyed after a state is exited, any state information stored within that state is lost. Information that needs to persist throughout the execution of the state machine has to be stored in the state machine class, which in effect becomes the winnebago object. thanks Dave p.s. thanks for showing me how to post on this list, I kept looking in the gmane.org site for instructions...

Say for the PumpBase example, the designer of the main Fsm decides to allow customization for the Idle state and Running state. In the example illustrated in the FAQ a MyRunning state (and inner states added to it) is defined and
[snip] the
MyPump Fsm derives from PumpBase to specify a state transition to the new MyRunning state.
Suppose now I want to add some inner states for the Idle state. I can define a new MyIdle state but I cannot make it the initial state of MyPump since Idle was specified as a template parameter in PumpBase and MyPump derives from PumpBase.
Let's put that aside and now let's suppose there's a third state in PumpBase Fsm: Stopped, and a state transition from Running to Stopped is triggered by event EvStop. and the following "point of customization" is defined in PumpBase
virtual sc::result react(Running& running, const EvStop &) const;
To add inner states to Stopped I can define MyStopped and have MyPump
Right. However, there's an easy workaround for this limitation: You leave everything as in the FAQ item but you rename Idle, MyRunning and Running to PreIdle, MyIdle and Idle respectively. You also *post* EvStart inside PreIdle's constructor. When you initiate this machine, there is an *immediate* transition to either Idle or MyIdle (depending on the actual type of the machine). I'll soon update this FAQ item accordingly. transition
from Running to MyStopped. However in MyPump Running is replaced by MyRunning, so I need to have my own react method to transit to MyStopped, and I'm not even using the "point of customization" in PumpBase because it has a different signature. Up till now it looks like I'm defining a new Fsm to replace PumpBase rather than customizing it.
Correct. In order to use this point of customization you will have to transition back to a state defined in the base FSM, for example PreStopped (can't think of a better name right now). PreStopped then uses the technique I outlined above to transition to either Stopped or MyStopped. A little awkward, I know, but it works quite well in practice if there aren't many such points of customization.
All this is fine if PumpBase has few states. Yet consider the case when 2 developers work on PumpBase, one responsible for customizing the Running state, the other for customizing the Stopped state. The developers should be able to work on the state machine without knowing each other. Yet as I've illustrated above, there can only be one MyPump class and whoever writes that class has to be aware of the MyRunning and MyStopped class and write the react functions accordingly.
I'm not sure whether I understand 100% but it seems if you use the technique above this shouldn't be a problem.
btw, I think there's a typo in the FAQ code snippet. "sc::custom_reaction< EvStart >" should not be the third template argument for struct Idle.
Maybe there's just a terminology misunderstanding but I don't see the benefit of the context object. In fact, the use of a context object here looks
Right. This is the old syntax. I forgot to transform the FAQ page to the new syntax that is documented in the tutorial and the reference. like a
common antipattern, a "winnebago" object (stolen from Jeff Garland). You'll find more on this here (search for the first occurrence of "heuristic" and read what follows):
Yes it is an antipattern if the object requires modification everytime a new field is needed, but with a property map the new field can simply be inserted into the map.
That's one way to get around that problem, but it has a price: You lose type-safety.
Moreover, since a state class is destroyed after a state is exited, any state information stored within that state is lost.
Not if you group states inside outer states according to their need to share variables. In the StopWatch example, the elapsedTime_ member resides in the Active state exactly because it needs to persist over Running <--> Stopped transitions. The Active state is only destroyed (and with it the elapsedTime_ member) when you terminate the whole machine or send an EvReset event to it.
Information that needs to persist throughout the execution of the state machine has to be stored in the state machine class,
Right. If you have variables that really need to live throughout the lifetime of the whole FSM (including through initiate() and terminate() operations) you definitely must put them into the FSM class itself.
which in effect becomes the winnebago object.
That really depends on the design of the FSM. For flat FSMs you're right, but one point of hierarchical FSM is that you logically group states that share common "properties" into outer states. In traditional FSMs these "properties" were reactions only, Boost.Statechart enables you to also group states accoring to their need to share variables. The good thing is: States that share variables almost always also share reactions and vice versa. E.g. in the StopWatch example, Stopped and Running share elapsedTime_ as well as the transition triggered by EvReset.
p.s. thanks for showing me how to post on this list, I kept looking in the gmane.org site for instructions...
I was under the impression that unregistered people get email that tells them how to register. This is obviously not the case so I really should have told you that right in the beginning. Sorry. HTH & Regards, -- Andreas Huber When replying by private email, please remove the words spam and trap from the address shown in the header.

All this is fine if PumpBase has few states. Yet consider the case when 2 developers work on PumpBase, one responsible for customizing the Running state, the other for customizing the Stopped state. The developers should be able to work on the state machine without knowing each other. Yet as I've illustrated above, there can only be one MyPump class and whoever writes that class has to be aware of the MyRunning and MyStopped class and write the react functions accordingly.
I'm not sure whether I understand 100% but it seems if you use the technique above this shouldn't be a problem.
I followed your example/suggestions and wrote the following test program.
Conceptually speaking, the PumpBase FSM has 2 states, Idle and Running, the
designer of this FSM want to allow customization of these 2 states, which is
limited to adding inner states. The customization for the Idle state (MyIdle)
could be developed by Bob, while the customization for the Running state
(MyRunning) could be developed by Mary and since Bob and Mary are adding inner
states to Idle and Running respectively, their code should not depend on each
other. However, as my test program shows, MyPump class needs to have knowledge
of both MyIdle and MyRunning and provide the implementation for the react
functions.
This means the 2 points of customization cannot be viewed as separate and
independent. If MyPump only customized the Idle state but not the Running state,
and a third developer with no access to MyPump's source code will have to
customize MyPump to add inner states for the Running state.
PumpBase
+--> MyPump
+--> CustomMyPump
I don't mean to say that this will prevent anything from working but I just want
to clarify what I was trying to point out. Please correct me if I'm wrong
thanks
Dave
struct EvStart : sc::event< EvStart > {};
struct EvPreIdleStart : sc::event< EvPreIdleStart > {};
struct EvPreRunningStart : sc::event< EvPreRunningStart > {};
class EntryExitDisplayer { ... }
////////////////////////////////////////////////////////////////////////////////
// Base FSM
////////////////////////////////////////////////////////////////////////////////
struct PreIdle;
struct PreRunning;
struct PumpBase : sc::state_machine< PumpBase, PreIdle >
{
virtual sc::result react(PreIdle & preIdle, const EvPreIdleStart & ) const;
virtual sc::result react(PreRunning & preRunning, const EvPreRunningStart &
) const;
};
struct PreIdle : sc::state

Dave
I'm not sure whether I understand 100% but it seems if you use the technique above this shouldn't be a problem.
I followed your example/suggestions and wrote the following test program. Conceptually speaking, the PumpBase FSM has 2 states, Idle and Running, the designer of this FSM want to allow customization of these 2 states, which is limited to adding inner states. The customization for the Idle state (MyIdle) could be developed by Bob, while the customization for the Running state (MyRunning) could be developed by Mary and since Bob and Mary are adding inner states to Idle and Running respectively, their code should not depend on each other. However, as my test program shows, MyPump class needs to have knowledge of both MyIdle and MyRunning and provide the implementation for the react functions.
Ahh, now I understand. I first thought you want to have one person working on
one state machine each (i.e. one on PumpBase and one on MyPump). By
reorganizing the source code a bit you can do even what you describe:
*** PumpBase.hpp ***
#ifndef PUMP_BASE_HPP_INCLUDED
#define PUMP_BASE_HPP_INCLUDED
#include
This means the 2 points of customization cannot be viewed as separate and independent. If MyPump only customized the Idle state but not the Running state, and a third developer with no access to MyPump's source code will have to customize MyPump to add inner states for the Running state.
PumpBase +--> MyPump +--> CustomMyPump
Ok, if you want to have both (i.e. independent development of additional derived state machines *and* independent customization on a per-state basis) the reorganization above won't help you. It pretty much trades one for the other. I.e. now it is no longer possible to develop additional derived FSMs without changing Idle.cpp and Running.cpp. However, I'm confident that even that is possible if you introduce an additional layer of indirection. That is, in your original code instead of: sc::result MyPump::react( PreRunning & preRunning, const EvPreRunningStart & ) const { return preRunning.transit< MyRunning >(); } you write something like that: sc::result MyPump::react( PreRunning & preRunning, const EvPreRunningStart & ) const { return StateFactory::MakeTransition( MyPump, preRunning ); } I haven't thought much about the implementation of StateFactory, but it seems this should allow you to register arbitrary transitions depending on the current state and the actual derived FSM class, and give you the freedom you want? HTH & Regards, Andreas

PreIdle( my_context ctx ) : my_base( ctx ), EntryExitDisplayer( "PreIdle" ) { // Posting is now also possible with stack-allocated events post_event( EvPreIdleStart() ); }
Is this in the latest version of boost::statechart? My compiler seems to
disagree with this statement:
test.cpp:65: error: no matching function for call to
'PreIdle::post_event(EvPreIdleStart)'
/usr/local/include/boost-1_32/boost/statechart/simple_state.hpp:271: note:
candidates are: void boost::statechart::simple_state
sc::result react( const EvPreIdleStart & evt ) { // Instead of mentioning the state machine directly with context< .. >, // the following is often more convenient return outermost_context().react( *this, evt ); }
I agree, but my impression is that the react function could be implemented in any "outer context" which may not be the outmost one, right?
Ok, if you want to have both (i.e. independent development of additional derived state machines *and* independent customization on a per-state basis) the reorganization above won't help you. It pretty much trades one for the other. I.e. now it is no longer possible to develop additional derived FSMs without changing Idle.cpp and Running.cpp. However, I'm confident that even that is possible if you introduce an additional layer of indirection. That is, in your original code instead of:
sc::result MyPump::react( PreRunning & preRunning, const EvPreRunningStart & ) const { return preRunning.transit< MyRunning >(); }
you write something like that:
sc::result MyPump::react( PreRunning & preRunning, const EvPreRunningStart & ) const { return StateFactory::MakeTransition( MyPump, preRunning ); }
I haven't thought much about the implementation of StateFactory, but it seems this should allow you to register arbitrary transitions depending on the current state and the actual derived FSM class, and give you the freedom you want?
Sounds plausible to me, I should spend some free time thinking about how to implement this. But right now I'm running into some problems, maybe it's specific to my platform/compiler/code... but if I'll be grateful if you could provide some help or point me in the right direction. Back to the organization of the PumpBase I posted, I split the code and organize them into the following directory structure ./core/Main.cpp ./core/PumpBase.cpp ./dll/MyPump.cpp ./include/PumpBase.hpp ./include/EntryExitDisplayer.hpp ./include/PumpBaseFactory.hpp I think the file names are pretty self-explanatory. The main program in core directory defines and uses the PumpBase FSM, and I want to implement a DLL (in the dll) directory and define MyPump to customize the PumpBase FSM. The main program would use dlopen/dlsym to load the DLL and get the factory that'll create MyPump. The include directory contains the header files shared by the main program and the dll. Everything compiles file, but when I run the program I get a runtime error from dlopen: Error loading MyPump.so MyPump.so: undefined symbol: _ZTIN5boost10statechart12simple_stateI7PreIdle8PumpBaseNS_3mpl4listIN4mpl_ \ 2naES7_S7_S7_S7_S7_S7_S7_S7_S7_S7_S7_S7_S7_S7_S7_S7_S7_S7_S7_EELNS0_ \ 12history_modeE0EEE (<-- line is too long to post so I split it up) it looks like some template class inside the boost::statechart code, so I'm not quite sure what the problem is. thanks and thanks for the quick response Dave

Dave wrote:
PreIdle( my_context ctx ) : my_base( ctx ), EntryExitDisplayer( "PreIdle" ) { // Posting is now also possible with stack-allocated events post_event( EvPreIdleStart() ); }
Is this in the latest version of boost::statechart? My compiler seems to disagree with this statement: [error snipped]
It should be possible with the version that I have released June 19th, which can be found here: http://boost-sandbox.sf.net/Statechart.zip
sc::result react( const EvPreIdleStart & evt ) { // Instead of mentioning the state machine directly with context< .. >, // the following is often more convenient return outermost_context().react( *this, evt ); }
I agree, but my impression is that the react function could be implemented in any "outer context" which may not be the outmost one, right?
Hmmm, this never occurred to me. It would mean that you need to have something like the following struct RunningBase : sc::simple_state< RunningBase, ... > { virtual sc::result react( const EvX & ) { ... } }; struct MyRunning : RunningBase { virtual sc::result react( const EvX & ) { ... } }; and then inside some inner state of RunningBase: struct Inner : sc::simple_state< Inner, RunningBase > { typedef sc::custom_reaction< EvX > reactions; sc::result react( const EvX & evt ) { // depending on the requirements for the transition // (is it ok to exit and maybe reenter RunningBase), // we'd maybe also need to pass *this to react context< RunningBase >().react( evt ); } }; I've never tried but I don't see anything that would keep you from doing this. I can't currently think of a good example where this technique would make sense, please let me know if you find one! [snip]
I haven't thought much about the implementation of StateFactory, but it seems this should allow you to register arbitrary transitions depending on the current state and the actual derived FSM class, and give you the freedom you want?
Sounds plausible to me, I should spend some free time thinking about how to implement this.
AFAICT, you'd be the first one to do something like this. I'd be interested to hear with what approach you settled in the end. [snip code organization]
I think the file names are pretty self-explanatory. The main program in core directory defines and uses the PumpBase FSM, and I want to implement a DLL (in the dll) directory and define MyPump to customize the PumpBase FSM. The main program would use dlopen/dlsym to load the DLL and get the factory that'll create MyPump. The include directory contains the header files shared by the main program and the dll. Everything compiles file, but when I run the program I get a runtime error from dlopen:
Error loading MyPump.so MyPump.so: undefined symbol: _ZTIN5boost10statechart12simple_stateI7PreIdle8PumpBaseNS_3mpl4listIN4mpl_ \ 2naES7_S7_S7_S7_S7_S7_S7_S7_S7_S7_S7_S7_S7_S7_S7_S7_S7_S7_S7_EELNS0_ \ 12history_modeE0EEE
Hmm, from the mangled name it looks like the *base* class of PreIdle cannot be found for some reason. I assume that PreIdle is declared and implemented in PumpBase.hpp, that is, everything is fully inlined? If so, I don't currently see why this happens. Unfortunately I'm not familiar with the details of .so file loading but I assume it should be roughly similar to Windows dlls. I guess it's probably easiest if you send me the full source by private email and I'll try to reproduce the problem with my compiler. BTW, are you using gcc? Regards, -- Andreas Huber When replying by private email, please remove the words spam and trap from the address shown in the header.

Andreas Huber
I agree, but my impression is that the react function could be implemented in any "outer context" which may not be the outmost one, right?
Hmmm, this never occurred to me. It would mean that you need to have something like the following
struct RunningBase : sc::simple_state< RunningBase, ... > { virtual sc::result react( const EvX & ) { ... } };
struct MyRunning : RunningBase { virtual sc::result react( const EvX & ) { ... } };
and then inside some inner state of RunningBase:
struct Inner : sc::simple_state< Inner, RunningBase > { typedef sc::custom_reaction< EvX > reactions;
sc::result react( const EvX & evt ) { // depending on the requirements for the transition // (is it ok to exit and maybe reenter RunningBase), // we'd maybe also need to pass *this to react context< RunningBase >().react( evt ); } };
I've never tried but I don't see anything that would keep you from doing this. I can't currently think of a good example where this technique would make sense, please let me know if you find one!
I just realized that the above only works in a very limited way: 1. History transitions *never* go back to the MyRunning state, they always go to the RunningBase state. 2. When calling state_base::dynamic_type() on a const state_base & referencing a MyRunning state object, a value identifying MyRunning is returned *only* *if* BOOST_STATECHART_USE_NATIVE_RTTI is defined. Otherwise, a value identifying RunningBase is returned. Even worse, MyRunning::static_type() *always* returns a value identifying RunningBase. 3. MyRunning::custom_static_type_ptr() sets custom type information for RunningBase, there's no way to define it for MyRunning. 4. There is no way for MyRunning to define additional reactions or replace some of those defined in RunningBase. 5. There is no way for MyRunning to define or replace inner initial states defined by RunningBase. There are probably more drawbacks looming, but IMO the list above is more than enough to stay away from this technique. It is therefore a good idea to always pass the "the most-derived subtype" of the simple_state class template as its first template parameter (as currently documented in the reference). However, something similar to the above is still possible without any of the drawbacks by making RunningBase a template: struct Inner; // There possibly more template parameters... template< class MostDerived > struct RunningBase : sc::simple_state< MostDerived, Inner > { typedef MostDerived RunningState; }; struct Running : RunningBase< Running > { sc::result react( const EvX & ) { // ... } }; struct MyRunning : RunningBase< MyRunning > { sc::result react( const EvX & ) { // ... } }; struct Inner : sc::simple_state< Inner, RunningBase > { typedef sc::custom_reaction< EvX > reactions; sc::result react( const EvX & evt ) { context< RunningState >().react( evt ); } }; This has the added benefit of no longer requiring a virtual function... -- Andreas Huber When replying by private email, please remove the words spam and trap from the address shown in the header.
participants (2)
-
Andreas Huber
-
Dave