[msm] how to write a generic class for all state machine back-end.

In our project, we need a generic interface to refer to different state machine (back-end). For example, in the following codes, we want to use SMBase* to refer to any concrete state machine. SMBase should have similar interface as state machine. struct User { A(SMBase* s) : sm(s) (); SMBase* sm; ... // User can use sm->start(), sm->process_event() etc } struct SMBase { virtual void start() = 0; ... // how to design the rest ??? } // a wrapper implement SMBase template < typename SM > class SMWrapper : public SMBase{ SMWrapper(SM & s): sm(s){}; virtual void start() { sm.start(); } ... SM sm; } main() { typedef boost::msm::back::state_machine SM1; typedef boost::msm::back::state_machine SM2; User a1(new SMWrapper(SM1()) ); // Wrap the concrete state machine and pass it to User. User a2(new SMWrapper(SM2()) ); // Pass a different state machine. ... } The problem of designing SMBase is the function "process_event". We want it to to be virtual function so SMWrapper can implement it. But it also needs to be a template according to state machine interface. But we cannot have a virtual template function. How can we achieve/design this generic state machine interface? Many thanks. Jerry

In our project, we need a generic interface to refer to different state machine (back-end). For example, in the following codes, we want
to use SMBase* to refer to any concrete state machine. SMBase should have similar interface as state machine.
struct User { A(SMBase* s) : sm(s) (); SMBase* sm; ... // User can use sm->start(), sm->process_event() etc }
struct SMBase { virtual void start() = 0; ... // how to design the rest ??? }
The problem of designing SMBase is the function "process_event". We want it to to be virtual function so SMWrapper can implement it. >But it also needs to be a template according to state machine interface. But we cannot have a virtual template function.
How can we achieve/design this generic state machine interface?
Hi Jerry, Yes, virtual template methods are not possible in C++ :( (Note to C++ Standard Comittee: I'd consider donating a kidney for this one :) ) This leaves you with few possibilies. I see 2 at a first glance (suggestions welcome): - virtual void process_event_event1(); virtual void process_event_event2(); ...etc (not fun but quite in the OO spirit) - virtual void process_event(boost::any) followed by any_casts until you find the correct one (not fun either) All considered, solution 1 looks better. If you can define a common interface for state machines, then they are bound to expect common events. HTH, Christophe

On 23.6.2011. 22:04, Christophe Henry wrote:
In our project, we need a generic interface to refer to different state machine (back-end). For example, in the following codes, we want
to use SMBase* to refer to any concrete state machine. SMBase should have similar interface as state machine.
struct User { A(SMBase* s) : sm(s) (); SMBase* sm; ... // User can use sm->start(), sm->process_event() etc }
struct SMBase { virtual void start() = 0; ... // how to design the rest ??? }
The problem of designing SMBase is the function "process_event". We want it to to be virtual function so SMWrapper can implement it. >But it also needs to be a template according to state machine interface. But we cannot have a virtual template function.
How can we achieve/design this generic state machine interface?
Hi Jerry,
Yes, virtual template methods are not possible in C++ :(
(Note to C++ Standard Comittee: I'd consider donating a kidney for this one :) )
This leaves you with few possibilies. I see 2 at a first glance (suggestions welcome):
- virtual void process_event_event1(); virtual void process_event_event2(); ...etc (not fun but quite in the OO spirit)
- virtual void process_event(boost::any) followed by any_casts until you find the correct one (not fun either)
All considered, solution 1 looks better. If you can define a common interface for state machines, then they are bound to expect common events.
I have a combination of the above two which is quite fun :)
Use boost.variant on all event types:
typedef boost::variant

On 23.6.2011. 22:04, Christophe Henry wrote:
In our project, we need a generic interface to refer to different state machine (back-end). For example, in the following codes, we want
to use SMBase* to refer to any concrete state machine. SMBase should have similar interface as state machine.
struct User { A(SMBase* s) : sm(s) (); SMBase* sm; ... // User can use sm->start(), sm->process_event() etc }
struct SMBase { virtual void start() = 0; ... // how to design the rest ??? }
The problem of designing SMBase is the function "process_event". We want it to to be virtual function so SMWrapper can implement it. >But it also needs to be a template according to state machine interface. But we cannot have a virtual template function.
How can we achieve/design this generic state machine interface?
Hi Jerry,
Yes, virtual template methods are not possible in C++ :(
(Note to C++ Standard Comittee: I'd consider donating a kidney for this one :) )
This leaves you with few possibilies. I see 2 at a first glance (suggestions welcome):
- virtual void process_event_event1(); virtual void process_event_event2(); ...etc (not fun but quite in the OO spirit)
- virtual void process_event(boost::any) followed by any_casts until you find the correct one (not fun either)
All considered, solution 1 looks better. If you can define a common interface for state machines, then they are bound to expect common events.
I have a combination of the above two which is quite fun :) Use boost.variant on all event types:
typedef boost::variant
event_variant virtual void process_event( event_variant e ) = 0
and to use its apply_visitor function in derived class to force MSM to instantiate all needed process_event templates (untested):
template<typename SM> struct ProcessEvent { ProcessEvent( SM & sm ) : sm_( sm ) {} template<typename event> void operator( event & e ) { sm_.process_event( e ); }
SM & sm_; };
template <typename SM> class SMWrapper : public SMBase { virtual void process_event( event_variant ev ) { boost::variant::apply_visitor( ev, ProcessEvent<SM>( sm_ ) ); } };
Ah yes I didn't think about this solution, it's clearly more elegant. I need to give it a try. Good one :) Thanks, Christophe

On 06/27/2011 12:06 PM, Christophe Henry wrote:
On 23.6.2011. 22:04, Christophe Henry wrote:
In our project, we need a generic interface to refer to different state machine (back-end). For example, in the following codes, we want
to use SMBase* to refer to any concrete state machine. SMBase should have similar interface as state machine.
struct User { A(SMBase* s) : sm(s) (); SMBase* sm; ... // User can use sm->start(), sm->process_event() etc }
struct SMBase { virtual void start() = 0; ... // how to design the rest ??? }
The problem of designing SMBase is the function "process_event". We want it to to be virtual function so SMWrapper can implement it. But it also needs to be a template according to state machine interface. But we cannot have a virtual template function.
How can we achieve/design this generic state machine interface?
Hi Jerry,
Yes, virtual template methods are not possible in C++ :(
(Note to C++ Standard Comittee: I'd consider donating a kidney for this one :) )
This leaves you with few possibilies. I see 2 at a first glance (suggestions welcome):
- virtual void process_event_event1(); virtual void process_event_event2(); ...etc (not fun but quite in the OO spirit)
- virtual void process_event(boost::any) followed by any_casts until you find the correct one (not fun either)
All considered, solution 1 looks better. If you can define a common interface for state machines, then they are bound to expect common events.
I have a combination of the above two which is quite fun :) Use boost.variant on all event types:
typedef boost::variant
event_variant virtual void process_event( event_variant e ) = 0
and to use its apply_visitor function in derived class to force MSM to instantiate all needed process_event templates (untested):
template<typename SM> struct ProcessEvent { ProcessEvent( SM & sm ) : sm_( sm ) {} template<typename event> void operator( event & e ) { sm_.process_event( e ); }
SM & sm_; };
template <typename SM> class SMWrapper : public SMBase { virtual void process_event( event_variant ev ) { boost::variant::apply_visitor( ev, ProcessEvent<SM>( sm_ ) ); } };
Ah yes I didn't think about this solution, it's clearly more elegant. I need to give it a try. Good one :)
Thanks,
Christophe
FWIW ... this is basically what I do also. It works nicely. -- Michael Caisse Object Modeling Designs www.objectmodelingdesigns.com
participants (4)
-
Christophe Henry
-
Jerry Chen
-
Juraj Ivančić
-
Michael Caisse