1 // Copyright 2008 Christophe Henry
2 // henry UNDERSCORE christophe AT hotmail DOT com
3 // This is an extended version of the state machine available in the boost::mpl library
4 // Distributed under the same license as the original.
5 // Copyright for the original version:
6 // Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed
7 // under the Boost Software License, Version 1.0. (See accompanying
8 // file LICENSE_1_0.txt or copy at
9 // http://www.boost.org/LICENSE_1_0.txt)
10 
11 #ifndef BOOST_MSM_BACK_FAVOR_COMPILE_TIME_H
12 #define BOOST_MSM_BACK_FAVOR_COMPILE_TIME_H
13 
14 #include <utility>
15 #include <deque>
16 
17 #include <boost/mpl/filter_view.hpp>
18 #include <boost/mpl/for_each.hpp>
19 #include <boost/mpl/bool.hpp>
20 
21 #include <boost/msm/common.hpp>
22 #include <boost/msm/back/metafunctions.hpp>
23 #include <boost/msm/back/common_types.hpp>
24 #include <boost/msm/back/dispatch_table.hpp>
25 #include <boost/msm/back/any_event.hpp>
26 
27 namespace boost { namespace msm { namespace back
28 {
29 
30 #define BOOST_MSM_BACK_GENERATE_PROCESS_EVENT(fsmname)                                              \
31     namespace boost { namespace msm { namespace back{                                               \
32     template<class EventType>                                                                       \
33     class holder<EventType,fsmname> : public placeholder                                            \
34     {                                                                                               \
35     public:                                                                                         \
36         holder(EventType const& evt, fsmname& fsm): event_(evt),fsm_(fsm){}                         \
37         virtual ::boost::msm::back::HandledEnum process_event() const                               \
38         {return fsm_.process_event(event_);}                                                        \
39     private:                                                                                        \
40         EventType const& event_;                                                                    \
41         fsmname& fsm_;                                                                              \
42     };                                                                                              \
43     template<>                                                                                      \
44     ::boost::msm::back::HandledEnum fsmname::process_any_event( any_event const& evt)const          \
45     {                                                                                               \
46         return evt.process_event();                                                                 \
47     }                                                                                               \
48     }}}
49 
50 struct favor_compile_time
51 {
52     typedef int compile_policy;
53     typedef ::boost::mpl::false_ add_forwarding_rows;
54 };
55 
56 // Generates a singleton runtime lookup table that maps current state
57 // to a function that makes the SM take its transition on the given
58 // Event type.
59 template <class Fsm,class Stt, class Event>
60 struct dispatch_table < Fsm, Stt, Event, ::boost::msm::back::favor_compile_time>
61 {
62  private:
63     // This is a table of these function pointers.
64     typedef HandledEnum (*cell)(Fsm&, int,int,Event const&);
65     typedef bool (*guard)(Fsm&, Event const&);
66 
67     // Compute the maximum state value in the sm so we know how big
68     // to make the table
69     typedef typename generate_state_set<Stt>::type state_list;
70     BOOST_STATIC_CONSTANT(int, max_state = ( ::boost::mpl::size<state_list>::value));
71 
72     struct chain_row
73     {
operator ()boost::msm::back::dispatch_table::chain_row74         HandledEnum operator()(Fsm& fsm, int region,int state,Event const& evt) const
75         {
76             HandledEnum res = HANDLED_FALSE;
77             typename std::deque<cell>::const_iterator it = one_state.begin();
78             while (it != one_state.end() && res != HANDLED_TRUE)
79             {
80                 HandledEnum handled = (*it)(fsm,region,state,evt);
81                 // reject is considered as erasing an error (HANDLED_FALSE)
82                 if ((HANDLED_FALSE==handled) && (HANDLED_GUARD_REJECT==res) )
83                     res = HANDLED_GUARD_REJECT;
84                 else
85                     res = handled;
86                 ++it;
87             }
88             return res;
89         }
90         std::deque<cell> one_state;
91     };
92     template <class TransitionState>
call_submachineboost::msm::back::dispatch_table93     static HandledEnum call_submachine(Fsm& fsm, int region, int state, Event const& evt)
94     {
95         return (fsm.template get_state<TransitionState&>()).process_any_event
96             ( any_event(evt,fsm.template get_state<TransitionState&>()) );
97     }
98     // A function object for use with mpl::for_each that stuffs
99     // transitions into cells.
100     struct init_cell
101     {
init_cellboost::msm::back::dispatch_table::init_cell102         init_cell(dispatch_table* self_)
103           : self(self_)
104         {}
105         // version for transition event not base of our event
106         template <class Transition>
init_event_base_caseboost::msm::back::dispatch_table::init_cell107         void init_event_base_case(Transition const&, ::boost::mpl::true_ const &) const
108         {
109             typedef typename create_stt<Fsm>::type stt;
110             BOOST_STATIC_CONSTANT(int, state_id =
111                 (get_state_id<stt,typename Transition::current_state_type>::value));
112             self->entries[state_id].one_state.push_front(reinterpret_cast<cell>(&Transition::execute));
113         }
114         // version for transition event base of our event
115         template <class Transition>
init_event_base_caseboost::msm::back::dispatch_table::init_cell116         void init_event_base_case(Transition const&, ::boost::mpl::false_ const &) const
117         {
118             typedef typename create_stt<Fsm>::type stt;
119             BOOST_STATIC_CONSTANT(int, state_id =
120                 (get_state_id<stt,typename Transition::current_state_type>::value));
121             self->entries[state_id].one_state.push_front(&Transition::execute);
122         }
123         // Cell initializer function object, used with mpl::for_each
124         template <class Transition>
125         typename ::boost::enable_if<typename has_not_real_row_tag<Transition>::type,void >::type
operator ()boost::msm::back::dispatch_table::init_cell126             operator()(Transition const&,boost::msm::back::dummy<0> = 0) const
127         {
128             // version for not real rows. No problem because irrelevant for process_event
129         }
130         template <class Transition>
131         typename ::boost::disable_if<typename has_not_real_row_tag<Transition>::type,void >::type
operator ()boost::msm::back::dispatch_table::init_cell132         operator()(Transition const& tr,boost::msm::back::dummy<1> = 0) const
133         {
134             //only if the transition event is a base of our event is the reinterpret_case safe
135             init_event_base_case(tr,
136                 ::boost::mpl::bool_<
137                     ::boost::is_base_of<typename Transition::transition_event,Event>::type::value>() );
138         }
139 
140         dispatch_table* self;
141     };
142 
143     // Cell default-initializer function object, used with mpl::for_each
144     // initializes with call_no_transition, defer_transition or default_eventless_transition
145     // variant for non-anonymous transitions
146     template <class EventType,class Enable=void>
147     struct default_init_cell
148     {
default_init_cellboost::msm::back::dispatch_table::default_init_cell149         default_init_cell(dispatch_table* self_,chain_row* tofill_entries_)
150             : self(self_),tofill_entries(tofill_entries_)
151         {}
152         template <bool deferred,bool composite, int some_dummy=0>
153         struct helper
154         {};
155         template <int some_dummy> struct helper<true,false,some_dummy>
156         {
157             template <class State>
executeboost::msm::back::dispatch_table::default_init_cell::helper158             static void execute(boost::msm::wrap<State> const&,chain_row* tofill)
159             {
160                 typedef typename create_stt<Fsm>::type stt;
161                 BOOST_STATIC_CONSTANT(int, state_id = (get_state_id<stt,State>::value));
162                 cell call_no_transition = &Fsm::defer_transition;
163                 tofill[state_id].one_state.push_back(call_no_transition);
164             }
165         };
166         template <int some_dummy> struct helper<true,true,some_dummy>
167         {
168             template <class State>
executeboost::msm::back::dispatch_table::default_init_cell::helper169             static void execute(boost::msm::wrap<State> const&,chain_row* tofill)
170             {
171                 typedef typename create_stt<Fsm>::type stt;
172                 BOOST_STATIC_CONSTANT(int, state_id = (get_state_id<stt,State>::value));
173                 cell call_no_transition = &Fsm::defer_transition;
174                 tofill[state_id].one_state.push_back(call_no_transition);
175             }
176         };
177         template <int some_dummy> struct helper<false,true,some_dummy>
178         {
179             template <class State>
executeboost::msm::back::dispatch_table::default_init_cell::helper180             static void execute(boost::msm::wrap<State> const&,chain_row* tofill)
181             {
182                 typedef typename create_stt<Fsm>::type stt;
183                 BOOST_STATIC_CONSTANT(int, state_id = (get_state_id<stt,State>::value));
184                 cell call_no_transition = &call_submachine< State >;
185                 tofill[state_id].one_state.push_front(call_no_transition);
186             }
187         };
188         template <int some_dummy> struct helper<false,false,some_dummy>
189         {
190             template <class State>
executeboost::msm::back::dispatch_table::default_init_cell::helper191             static void execute(boost::msm::wrap<State> const&,chain_row* tofill)
192             {
193                 typedef typename create_stt<Fsm>::type stt;
194                 BOOST_STATIC_CONSTANT(int, state_id = (get_state_id<stt,State>::value));
195                 cell call_no_transition = &Fsm::call_no_transition;
196                 tofill[state_id].one_state.push_back(call_no_transition);
197             }
198         };
199         template <class State>
operator ()boost::msm::back::dispatch_table::default_init_cell200         void operator()(boost::msm::wrap<State> const& s)
201         {
202             helper<has_state_delayed_event<State,Event>::type::value,
203                    is_composite_state<State>::type::value>::execute(s,tofill_entries);
204         }
205         dispatch_table* self;
206         chain_row* tofill_entries;
207     };
208 
209     // variant for anonymous transitions
210     template <class EventType>
211     struct default_init_cell<EventType,
212                              typename ::boost::enable_if<
213                                 typename is_completion_event<EventType>::type>::type>
214     {
default_init_cellboost::msm::back::dispatch_table::default_init_cell215         default_init_cell(dispatch_table* self_,chain_row* tofill_entries_)
216             : self(self_),tofill_entries(tofill_entries_)
217         {}
218 
219         // this event is a compound one (not a real one, just one for use in event-less transitions)
220         // Note this event cannot be used as deferred!
221         template <class State>
operator ()boost::msm::back::dispatch_table::default_init_cell222         void operator()(boost::msm::wrap<State> const&)
223         {
224             typedef typename create_stt<Fsm>::type stt;
225             BOOST_STATIC_CONSTANT(int, state_id = (get_state_id<stt,State>::value));
226             cell call_no_transition = &Fsm::default_eventless_transition;
227             tofill_entries[state_id].one_state.push_back(call_no_transition);
228         }
229 
230         dispatch_table* self;
231         chain_row* tofill_entries;
232     };
233 
234  public:
235     // initialize the dispatch table for a given Event and Fsm
dispatch_tableboost::msm::back::dispatch_table236     dispatch_table()
237     {
238         // Initialize cells for no transition
239         ::boost::mpl::for_each<
240             ::boost::mpl::filter_view<
241                     Stt, ::boost::is_base_of<transition_event< ::boost::mpl::placeholders::_>, Event> > >
242         (init_cell(this));
243 
244         ::boost::mpl::for_each<
245             typename generate_state_set<Stt>::type,
246             boost::msm::wrap< ::boost::mpl::placeholders::_1> >
247          (default_init_cell<Event>(this,entries));
248 
249     }
250 
251     // The singleton instance.
252     static const dispatch_table instance;
253 
254  public: // data members
255      chain_row entries[max_state];
256 };
257 
258 template <class Fsm,class Stt, class Event>
259 const boost::msm::back::dispatch_table<Fsm,Stt, Event,favor_compile_time>
260 dispatch_table<Fsm,Stt, Event,favor_compile_time>::instance;
261 
262 }}} // boost::msm::back
263 
264 #endif //BOOST_MSM_BACK_FAVOR_COMPILE_TIME_H
265