]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | <html><head> |
2 | <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> | |
3 | <title>Back-end</title><link rel="stylesheet" href="boostbook.css" type="text/css"><meta name="generator" content="DocBook XSL-NS Stylesheets V1.75.2"><link rel="home" href="index.html" title="Meta State Machine (MSM)"><link rel="up" href="ch03.html" title="Chapter 3. Tutorial"><link rel="prev" href="ch03s04.html" title="eUML"><link rel="next" href="ch04.html" title="Chapter 4. Performance / Compilers"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">Back-end</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="ch03s04.html">Prev</a> </td><th width="60%" align="center">Chapter 3. Tutorial</th><td width="20%" align="right"> <a accesskey="n" href="ch04.html">Next</a></td></tr></table><hr></div><div class="sect1" title="Back-end"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="d0e2298"></a>Back-end</h2></div></div></div><p>There is, at the moment, one back-end. This back-end contains the library | |
4 | engine and defines the performance and functionality trade-offs. The currently | |
5 | available back-end implements most of the functionality defined by the UML 2.0 | |
6 | standard at very high runtime speed, in exchange for longer compile-time. The | |
7 | runtime speed is due to a constant-time double-dispatch and self-adapting | |
8 | capabilities allowing the framework to adapt itself to the features used by a | |
9 | given concrete state machine. All unneeded features either disable themselves or | |
10 | can be manually disabled. See section 5.1 for a complete description of the | |
11 | run-to-completion algorithm.</p><div class="sect2" title="Creation"><div class="titlepage"><div><div><h3 class="title"><a name="d0e2303"></a>Creation </h3></div></div></div><p>MSM being divided between front and back-end, one needs to first define a | |
12 | front-end. Then, to create a real state machine, the back-end must be | |
13 | declared: | |
14 | </p><pre class="programlisting">typedef msm::back::state_machine<my_front_end> my_fsm;</pre><p>We now have a fully functional state machine type. The next sections will | |
15 | describe what can be done with it.</p></div><div class="sect2" title="Starting and stopping a state machine"><div class="titlepage"><div><div><h3 class="title"><a name="d0e2312"></a><span class="command"><strong><a name="backend-start"></a></strong></span>Starting and stopping a state | |
16 | machine</h3></div></div></div><p>The <code class="code">start()</code> method starts the state machine, meaning it will | |
17 | activate the initial state, which means in turn that the initial state's | |
18 | entry behavior will be called. We need the start method because you do not | |
19 | always want the entry behavior of the initial state to be called immediately | |
20 | but only when your state machine is ready to process events. A good example | |
21 | of this is when you use a state machine to write an algorithm and each loop | |
22 | back to the initial state is an algorithm call. Each call to start will make | |
23 | the algorithm run once. The <a class="link" href="examples/iPodSearch.cpp" target="_top">iPodSearch</a> example uses this possibility.</p><p>The <code class="code">stop()</code> method works the same way. It will cause the exit | |
24 | actions of the currently active states(s) to be called.</p><p>Both methods are actually not an absolute need. Not calling them will | |
25 | simply cause your first entry or your last exit action not to be | |
26 | called.</p></div><div class="sect2" title="Event dispatching"><div class="titlepage"><div><div><h3 class="title"><a name="d0e2331"></a>Event dispatching</h3></div></div></div><p>The main reason to exist for a state machine is to dispatch events. For | |
27 | MSM, events are objects of a given event type. The object itself can contain | |
28 | data, but the event type is what decides of the transition to be taken. For | |
29 | MSM, if some_event is a given type (a simple struct for example) and e1 and | |
30 | e2 concrete instances of some_event, e1 and e2 are equivalent, from a | |
31 | transition perspective. Of course, e1 and e2 can have different values and | |
32 | you can use them inside actions. Events are dispatched as const reference, | |
33 | so actions cannot modify events for obvious side-effect reasons. To dispatch | |
34 | an event of type some_event, you can simply create one on the fly or | |
35 | instantiate if before processing: </p><pre class="programlisting">my_fsm fsm; fsm.process_event(some_event()); | |
36 | some_event e1; fsm.process_event(e1)</pre><p>Creating an event on the fly will be optimized by the compiler so the | |
37 | performance will not degrade.</p></div><div class="sect2" title="Active state(s)"><div class="titlepage"><div><div><h3 class="title"><a name="d0e2340"></a>Active state(s)</h3></div></div></div><p>The backend also offers a way to know which state is active, though you | |
38 | will normally only need this for debugging purposes. If what you need simply | |
39 | is doing something with the active state, <span class="command"><strong><a class="command" href="ch02s02.html#UML-internal-transition">internal transitions</a></strong></span> or | |
40 | <span class="command"><strong><a class="command" href="ch03s05.html#backend-visitor">visitors</a></strong></span> are a better | |
41 | alternative. If you need to know what state is active, const int* | |
42 | current_state() will return an array of state ids. Please refer to the | |
43 | <span class="command"><strong><a class="command" href="ch06s03.html#internals-state-id">internals section</a></strong></span> to | |
44 | know how state ids are generated.</p></div><div class="sect2" title="Serialization"><div class="titlepage"><div><div><h3 class="title"><a name="d0e2354"></a><span class="command"><strong><a name="back-end-serialization"></a></strong></span>Serialization</h3></div></div></div><p>A common need is the ability to save a state machine and restore it at a | |
45 | different time. MSM supports this feature for the basic and functor | |
46 | front-ends, and in a more limited manner for eUML. MSM supports | |
47 | boost::serialization out of the box (by offering a <code class="code">serialize</code> | |
48 | function). Actually, for basic serialization, you need not do much, a MSM | |
49 | state machine is serializable almost like any other type. Without any | |
50 | special work, you can make a state machine remember its state, for | |
51 | example:</p><p> | |
52 | </p><pre class="programlisting">MyFsm fsm; | |
53 | // write to archive | |
54 | std::ofstream ofs("fsm.txt"); | |
55 | // save fsm to archive | |
56 | { | |
57 | boost::archive::text_oarchive oa(ofs); | |
58 | // write class instance to archive | |
59 | oa << fsm; | |
60 | } </pre><p> | |
61 | </p><p>Loading back is very similar:</p><p> | |
62 | </p><pre class="programlisting">MyFsm fsm; | |
63 | { | |
64 | // create and open an archive for input | |
65 | std::ifstream ifs("fsm.txt"); | |
66 | boost::archive::text_iarchive ia(ifs); | |
67 | // read class state from archive | |
68 | ia >> fsm; | |
69 | } </pre><p> | |
70 | </p><p>This will (de)serialize the state machine itself but not the concrete | |
71 | states' data. This can be done on a per-state basis to reduce the amount of | |
72 | typing necessary. To allow serialization of a concrete state, provide a | |
73 | do_serialize typedef and implement the serialize function:</p><p> | |
74 | </p><pre class="programlisting">struct Empty : public msm::front::state<> | |
75 | { | |
76 | // we want Empty to be serialized. First provide the typedef | |
77 | typedef int do_serialize; | |
78 | // then implement serialize | |
79 | template<class Archive> | |
80 | void serialize(Archive & ar, const unsigned int /* version */) | |
81 | { | |
82 | ar & some_dummy_data; | |
83 | } | |
84 | Empty():some_dummy_data(0){} | |
85 | int some_dummy_data; | |
86 | }; </pre><p> | |
87 | </p><p>You can also serialize data contained in the front-end class. Again, you | |
88 | need to provide the typedef and implement serialize:</p><p> | |
89 | </p><pre class="programlisting">struct player_ : public msm::front::state_machine_def<player_> | |
90 | { | |
91 | //we might want to serialize some data contained by the front-end | |
92 | int front_end_data; | |
93 | player_():front_end_data(0){} | |
94 | // to achieve this, provide the typedef | |
95 | typedef int do_serialize; | |
96 | // and implement serialize | |
97 | template<class Archive> | |
98 | void serialize(Archive & ar, const unsigned int ) | |
99 | { | |
100 | ar & front_end_data; | |
101 | } | |
102 | ... | |
103 | }; </pre><p> | |
104 | </p><p>The saving of the back-end data (the current state(s)) is valid for all | |
105 | front-ends, so a front-end written using eUML can be serialized. However, to | |
106 | serialize a concrete state, the macros like | |
107 | <code class="code">BOOST_MSM_EUML_STATE</code> cannot be used, so the state will have | |
108 | to be implemented by directly inheriting from | |
109 | <code class="code">front::euml::euml_state</code>.</p><p>The only limitiation is that the event queues cannot be serialized so | |
110 | serializing must be done in a stable state, when no event is being | |
111 | processed. You can serialize during event processing only if using no queue | |
112 | (deferred or event queue).</p><p>This <a class="link" href="examples/Serialize.cpp" target="_top">example</a> shows a state machine which we serialize after processing an | |
113 | event. The <code class="code">Empty</code> state also has some data to serialize.</p></div><div class="sect2" title="Base state type"><div class="titlepage"><div><div><h3 class="title"><a name="d0e2407"></a><span class="command"><strong><a name="backend-base-state"></a></strong></span>Base state type </h3></div></div></div><p>Sometimes, one needs to customize states to avoid repetition and provide a | |
114 | common functionality, for example in the form of a virtual method. You might | |
115 | also want to make your states polymorphic so that you can call typeid on | |
116 | them for logging or debugging. It is also useful if you need a visitor, like | |
117 | the next section will show. You will notice that all front-ends offer the | |
118 | possibility of adding a base type. Note that all states and state machines | |
119 | must have the same base state, so this could reduce reuse. For example, | |
120 | using the basic front end, you need to:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Add the non-default base state in your msm::front::state<> | |
121 | definition, as first template argument (except for | |
122 | interrupt_states for which it is the second argument, the first | |
123 | one being the event ending the interrupt), for example, | |
124 | my_base_state being your new base state for all states in a | |
125 | given state machine: | |
126 | </p><pre class="programlisting">struct Empty : public msm::front::state<my_base_state></pre><p> | |
127 | Now, my_base_state is your new base state. If it has a virtual | |
128 | function, your states become polymorphic. MSM also provides a | |
129 | default polymorphic base type, | |
130 | <code class="code">msm::front::polymorphic_state</code> | |
131 | </p></li><li class="listitem"><p>Add the user-defined base state in the state machine frontend | |
132 | definition, as a second template argument, for example: | |
133 | </p><pre class="programlisting">struct player_ : public msm::front::state_machine<player_,my_base_state> </pre></li></ul></div><p>You can also ask for a state with a given id (which you might have gotten | |
134 | from current_state()) using <code class="code">const base_state* get_state_by_id(int id) | |
135 | const</code> where base_state is the one you just defined. You can now | |
136 | do something polymorphically.</p></div><div class="sect2" title="Visitor"><div class="titlepage"><div><div><h3 class="title"><a name="d0e2433"></a><span class="command"><strong><a name="backend-visitor"></a></strong></span>Visitor</h3></div></div></div><p>In some cases, having a pointer-to-base of the currently active states is | |
137 | not enough. You might want to call non-virtually a method of the currently | |
138 | active states. It will not be said that MSM forces the virtual keyword down | |
139 | your throat!</p><p>To achieve this goal, MSM provides its own variation of a visitor pattern | |
140 | using the previously described user-defined state technique. If you add to | |
141 | your user-defined base state an <code class="code">accept_sig</code> typedef giving the | |
142 | return value (unused for the moment) and parameters and provide an accept | |
143 | method with this signature, calling visit_current_states will cause accept | |
144 | to be called on the currently active states. Typically, you will also want | |
145 | to provide an empty default accept in your base state in order in order not | |
146 | to force all your states to implement accept. For example your base state | |
147 | could be:</p><pre class="programlisting">struct my_visitable_state | |
148 | { | |
149 | // signature of the accept function | |
150 | typedef args<void> accept_sig; | |
151 | // we also want polymorphic states | |
152 | virtual ~my_visitable_state() {} | |
153 | // default implementation for states who do not need to be visited | |
154 | void accept() const {} | |
155 | };</pre><p>This makes your states polymorphic and visitable. In this case, accept is | |
156 | made const and takes no argument. It could also be:</p><pre class="programlisting">struct SomeVisitor {…}; | |
157 | struct my_visitable_state | |
158 | { | |
159 | // signature of the accept function | |
160 | typedef args<void,SomeVisitor&> accept_sig; | |
161 | // we also want polymorphic states | |
162 | virtual ~my_visitable_state() {} | |
163 | // default implementation for states who do not need to be visited | |
164 | void accept(SomeVisitor&) const {} | |
165 | };</pre><p>And now, <code class="code">accept</code> will take one argument (it could also be | |
166 | non-const). By default, <code class="code">accept</code> takes up to 2 arguments. To get | |
167 | more, set #define BOOST_MSM_VISITOR_ARG_SIZE to another value before | |
168 | including state_machine.hpp. For example:</p><pre class="programlisting">#define BOOST_MSM_VISITOR_ARG_SIZE 3 | |
169 | #include <boost/msm/back/state_machine.hpp></pre><p>Note that accept will be called on ALL active states <span class="underline">and also automatically on sub-states of a | |
170 | submachine</span>.</p><p><span class="underline">Important warning</span>: The method | |
171 | visit_current_states takes its parameter by value, so if the signature of | |
172 | the accept function is to contain a parameter passed by reference, pass this | |
173 | parameter with a boost:ref/cref to avoid undesired copies or slicing. So, | |
174 | for example, in the above case, call:</p><pre class="programlisting">SomeVisitor vis; sm.visit_current_states(boost::ref(vis));</pre><p>This <a class="link" href="examples/SM-2Arg.cpp" target="_top">example</a> uses a | |
175 | visiting function with 2 arguments.</p></div><div class="sect2" title="Flags"><div class="titlepage"><div><div><h3 class="title"><a name="d0e2476"></a>Flags</h3></div></div></div><p>Flags is a MSM-only concept, supported by all front-ends, which base | |
176 | themselves on the functions: </p><pre class="programlisting">template <class Flag> bool is_flag_active() | |
177 | template <class Flag,class BinaryOp> bool is_flag_active()</pre><p>These functions return true if the currently active state(s) support the | |
178 | Flag property. The first variant ORs the result if there are several | |
179 | orthogonal regions, the second one expects OR or AND, for example:</p><pre class="programlisting">my_fsm.is_flag_active<MyFlag>() | |
180 | my_fsm.is_flag_active<MyFlag,my_fsm_type::Flag_OR>()</pre><p>Please refer to the front-ends sections for usage examples.</p></div><div class="sect2" title="Getting a state"><div class="titlepage"><div><div><h3 class="title"><a name="d0e2489"></a>Getting a state</h3></div></div></div><p>It is sometimes necessary to have the client code get access to the | |
181 | states' data. After all, the states are created once for good and hang | |
182 | around as long as the state machine does so why not use it? You simply just | |
183 | need sometimes to get information about any state, even inactive ones. An | |
184 | example is if you want to write a coverage tool and know how many times a | |
185 | state was visited. To get a state, use the get_state method giving the state | |
186 | name, for example: </p><pre class="programlisting">player::Stopped* tempstate = p.get_state<player::Stopped*>();</pre><p> or </p><pre class="programlisting">player::Stopped& tempstate2 = p.get_state<player::Stopped&>();</pre><p>depending on your personal taste. </p></div><div class="sect2" title="State machine constructor with arguments"><div class="titlepage"><div><div><h3 class="title"><a name="d0e2502"></a><span class="command"><strong><a name="backend-fsm-constructor-args"></a></strong></span> State machine constructor with arguments </h3></div></div></div><p>You might want to define a state machine with a non-default constructor. | |
187 | For example, you might want to write: </p><pre class="programlisting">struct player_ : public msm::front::state_machine_def<player_> | |
188 | { | |
189 | player_(int some_value){…} | |
190 | }; </pre><p>This is possible, using the back-end as forwarding object: </p><pre class="programlisting">typedef msm::back::state_machine<player_ > player; player p(3);</pre><p>The back-end will call the corresponding front-end constructor upon | |
191 | creation.</p><p>You can pass arguments up to the value of the | |
192 | BOOST_MSM_CONSTRUCTOR_ARG_SIZE macro (currently 5) arguments. Change this | |
193 | value before including any header if you need to overwrite the default. </p><p>You can also pass arguments by reference (or const-reference) using | |
194 | boost::ref (or boost::cref):</p><pre class="programlisting">struct player_ : public msm::front::state_machine_def<player_> | |
195 | { | |
196 | player_(SomeType& t, int some_value){…} | |
197 | }; | |
198 | ||
199 | typedef msm::back::state_machine<player_ > player; | |
200 | SomeType data; | |
201 | player p(boost::ref(data),3); | |
202 | </pre><p>Normally, MSM default-constructs all its states or submachines. There are | |
203 | however cases where you might not want this. An example is when you use a | |
204 | state machine as submachine, and this submachine used the above defined | |
205 | constructors. You can add as first argument of the state machine constructor | |
206 | an expression where existing states are passed and copied:</p><pre class="programlisting">player p( back::states_ << state_1 << ... << state_n , boost::ref(data),3);</pre><p>Where state_1..n are instances of some or all of the states of the state | |
207 | machine. Submachines being state machines, this can recurse, for example, if | |
208 | Playing is a submachine containing a state Song1 having itself a constructor | |
209 | where some data is passed:</p><pre class="programlisting">player p( back::states_ << Playing(back::states_ << Song1(some_Song1_data)) , | |
210 | boost::ref(data),3);</pre><p>It is also possible to replace a given state by a new instance at any time | |
211 | using <code class="code">set_states()</code> and the same syntax, for example: | |
212 | </p><pre class="programlisting">p.set_states( back::states_ << state_1 << ... << state_n );</pre><p>An <a class="link" href="examples/Constructor.cpp" target="_top">example</a> making intensive use of this capability is provided.</p></div><div class="sect2" title="Trading run-time speed for better compile-time / multi-TU compilation"><div class="titlepage"><div><div><h3 class="title"><a name="d0e2542"></a><span class="command"><strong><a name="backend-tradeof-rt-ct"></a></strong></span>Trading run-time speed for | |
213 | better compile-time / multi-TU compilation</h3></div></div></div><p>MSM is optimized for run-time speed at the cost of longer compile-time. | |
214 | This can become a problem with older compilers and big state machines, | |
215 | especially if you don't really care about run-time speed that much and would | |
216 | be satisfied by a performance roughly the same as most state machine | |
217 | libraries. MSM offers a back-end policy to help there. But before you try | |
218 | it, if you are using a VC compiler, deactivate the /Gm compiler option | |
219 | (default for debug builds). This option can cause builds to be 3 times | |
220 | longer... If the compile-time still is a problem, read further. MSM offers a | |
221 | policy which will speed up compiling in two main cases:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>many transition conflicts</p></li><li class="listitem"><p>submachines</p></li></ul></div><p>The back-end <code class="code">msm::back::state_machine</code> has a policy argument | |
222 | (first is the front-end, then the history policy) defaulting to | |
223 | <code class="code">favor_runtime_speed</code>. To switch to | |
224 | <code class="code">favor_compile_time</code>, which is declared in | |
225 | <code class="code"><msm/back/favor_compile_time.hpp></code>, you need to:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>switch the policy to <code class="code">favor_compile_time</code> for the | |
226 | main state machine (and possibly submachines)</p></li><li class="listitem"><p>move the submachine declarations into their own header which | |
227 | includes | |
228 | <code class="code"><msm/back/favor_compile_time.hpp></code></p></li><li class="listitem"><p>add for each submachine a cpp file including your header and | |
229 | calling a macro, which generates helper code, for | |
230 | example:</p><pre class="programlisting">#include "mysubmachine.hpp" | |
231 | BOOST_MSM_BACK_GENERATE_PROCESS_EVENT(mysubmachine)</pre></li><li class="listitem"><p>configure your compiler for multi-core compilation</p></li></ul></div><p>You will now compile your state machine on as many cores as you have | |
232 | submachines, which will greatly speed up the compilation if you factor your | |
233 | state machine into smaller submachines.</p><p>Independently, transition conflicts resolution will also be much | |
234 | faster.</p><p>This policy uses boost.any behind the hood, which means that we will lose | |
235 | a feature which MSM offers with the default policy, <a class="link" href="ch03s02.html#event-hierarchy">event hierarchy</a>. The following | |
236 | example takes our iPod example and speeds up compile-time by using this | |
237 | technique. We have:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p><a class="link" href="examples/iPod_distributed/iPod.cpp" target="_top">our main | |
238 | state machine and main function</a></p></li><li class="listitem"><p><a class="link" href="examples/iPod_distributed/PlayingMode.hpp" target="_top">PlayingMode moved to a separate header</a></p></li><li class="listitem"><p><a class="link" href="examples/iPod_distributed/PlayingMode.cpp" target="_top">a | |
239 | cpp for PlayingMode</a></p></li><li class="listitem"><p><a class="link" href="examples/iPod_distributed/MenuMode.hpp" target="_top">MenuMode moved to a separate header</a></p></li><li class="listitem"><p><a class="link" href="examples/iPod_distributed/MenuMode.cpp" target="_top">a | |
240 | cpp for MenuMode</a></p></li><li class="listitem"><p><a class="link" href="examples/iPod_distributed/Events.hpp" target="_top">events | |
241 | move to a separate header as all machines use | |
242 | it</a></p></li></ul></div><p> | |
243 | </p></div><div class="sect2" title="Compile-time state machine analysis"><div class="titlepage"><div><div><h3 class="title"><a name="d0e2624"></a><span class="command"><strong><a name="backend-compile-time-analysis"></a></strong></span>Compile-time state machine analysis </h3></div></div></div><p>A MSM state machine being a metaprogram, it is only logical that cheking | |
244 | for the validity of a concrete state machine happens compile-time. To this | |
245 | aim, using the compile-time graph library <a class="link" href="http://www.dynagraph.org/mpl_graph/" target="_top">mpl_graph</a> (delivered at the moment | |
246 | with MSM) from Gordon Woodhull, MSM provides several compile-time checks:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Check that orthogonal regions ar truly orthogonal.</p></li><li class="listitem"><p>Check that all states are either reachable from the initial | |
247 | states or are explicit entries / pseudo-entry states.</p></li></ul></div><p>To make use of this feature, the back-end provides a policy (default is no | |
248 | analysis), <code class="code">msm::back::mpl_graph_fsm_check</code>. For example:</p><pre class="programlisting"> typedef msm::back::state_machine< player_,msm::back::mpl_graph_fsm_check> player; </pre><p>As MSM is now using Boost.Parameter to declare policies, the policy choice | |
249 | can be made at any position after the front-end type (in this case | |
250 | <code class="code">player_</code>).</p><p>In case an error is detected, a compile-time assertion is provoked.</p><p>This feature is not enabled by default because it has a non-neglectable | |
251 | compile-time cost. The algorithm is linear if no explicit or pseudo entry | |
252 | states are found in the state machine, unfortunately still O(number of | |
253 | states * number of entry states) otherwise. This will be improved in future | |
254 | versions of MSM.</p><p>The same algorithm is also used in case you want to omit providing the | |
255 | region index in the <span class="command"><strong><a class="command" href="ch03s02.html#explicit-entry-no-region-id">explicit entry / pseudo entry state</a></strong></span> declaration.</p><p>The author's advice is to enable the checks after any state machine | |
256 | structure change and disable it again after sucessful analysis.</p><p>The <a class="link" href="examples/TestErrorOrthogonality.cpp" target="_top">following example</a> provokes an assertion if one of the first two lines | |
257 | of the transition table is used.</p></div><div class="sect2" title="Enqueueing events for later processing"><div class="titlepage"><div><div><h3 class="title"><a name="d0e2668"></a><span class="command"><strong><a name="backend-enqueueing"></a></strong></span> Enqueueing events for later | |
258 | processing </h3></div></div></div><p>Calling <code class="code">process_event(Event const&)</code> will immediately | |
259 | process the event with run-to-completion semantics. You can also enqueue the | |
260 | events and delay their processing by calling <code class="code">enqueue_event(Event | |
261 | const&)</code> instead. Calling <code class="code">execute_queued_events()</code> | |
262 | will then process all enqueued events (in FIFO order). Calling | |
263 | <code class="code">execute_single_queued_event()</code> will execute the oldest | |
264 | enqueued event.</p><p>You can query the queue size by calling <code class="code">get_message_queue_size()</code>.</p></div><div class="sect2" title="Customizing the message queues"><div class="titlepage"><div><div><h3 class="title"><a name="d0e2691"></a><span class="command"><strong><a name="backend-queues"></a></strong></span> Customizing the message queues </h3></div></div></div><p>MSM uses by default a std::deque for its queues (one message queue for | |
265 | events generated during run-to-completion or with | |
266 | <code class="code">enqueue_event</code>, one for deferred events). Unfortunately, on some | |
267 | STL implementations, it is a very expensive container in size and copying | |
268 | time. Should this be a problem, MSM offers an alternative based on | |
269 | boost::circular_buffer. The policy is msm::back::queue_container_circular. | |
270 | To use it, you need to provide it to the back-end definition:</p><pre class="programlisting"> typedef msm::back::state_machine< player_,msm::back::queue_container_circular> player; </pre><p>You can access the queues with get_message_queue and get_deferred_queue, | |
271 | both returning a reference or a const reference to the queues themselves. | |
272 | Boost::circular_buffer is outside of the scope of this documentation. What | |
273 | you will however need to define is the queue capacity (initially is 0) to | |
274 | what you think your queue will at most grow, for example (size 1 is | |
275 | common):</p><pre class="programlisting"> fsm.get_message_queue().set_capacity(1); </pre></div><div class="sect2" title="Policy definition with Boost.Parameter"><div class="titlepage"><div><div><h3 class="title"><a name="d0e2706"></a><span class="command"><strong><a name="backend-boost-parameter"></a></strong></span>Policy definition with Boost.Parameter </h3></div></div></div><p>MSM uses Boost.Parameter to allow easier definition of | |
276 | back::state_machine<> policy arguments (all except the front-end). This | |
277 | allows you to define policy arguments (history, compile-time / run-time, | |
278 | state machine analysis, container for the queues) at any position, in any | |
279 | number. For example: </p><pre class="programlisting"> typedef msm::back::state_machine< player_,msm::back::mpl_graph_fsm_check> player; | |
280 | typedef msm::back::state_machine< player_,msm::back::AlwaysHistory> player; | |
281 | typedef msm::back::state_machine< player_,msm::back::mpl_graph_fsm_check,msm::back::AlwaysHistory> player; | |
282 | typedef msm::back::state_machine< player_,msm::back::AlwaysHistory,msm::back::mpl_graph_fsm_check> player; </pre></div><div class="sect2" title="Choosing when to switch active states"><div class="titlepage"><div><div><h3 class="title"><a name="d0e2714"></a><span class="command"><strong><a name="backend-state-switch"></a></strong></span>Choosing when to switch active | |
283 | states </h3></div></div></div><p>The UML Standard is silent about a very important question: when a | |
284 | transition fires, at which exact point is the target state the new active | |
285 | state of a state machine? At the end of the transition? After the source | |
286 | state has been left? What if an exception is thrown? The Standard considers | |
287 | that run-to-completion means a transition completes in almost no time. But | |
288 | even this can be in some conditions a very very long time. Consider the | |
289 | following example. We have a state machine representing a network | |
290 | connection. We can be <code class="code">Connected</code> and <code class="code">Disconnected</code>. When we move from one | |
291 | state to another, we send a (Boost) Signal to another entity. By default, | |
292 | MSM makes the target state as the new state after the transition is | |
293 | completed. We want to send a signal based on a flag is_connected which is | |
294 | true when in state Connected.</p><p>We are in state <code class="code">Disconnected</code> and receive an event <code class="code">connect</code>. The transition | |
295 | action will ask the state machine <code class="code">is_flag_active<is_connected></code> and will | |
296 | get... false because we are still in <code class="code">Disconnected</code>. Hmm, what to do? We could | |
297 | queue the action and execute it later, but it means an extra queue, more | |
298 | work and higher run-time.</p><p>MSM provides the possibility (in form of a policy) for a front-end to | |
299 | decide when the target state becomes active. It can be:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>before the transition fires, if the guard will allow the | |
300 | transition to fire: | |
301 | <code class="code">active_state_switch_before_transition</code></p></li><li class="listitem"><p>after calling the exit action of the source state: | |
302 | <code class="code">active_state_switch_after_exit</code></p></li><li class="listitem"><p>after the transition action is executed: | |
303 | <code class="code">active_state_switch_after_transition_action</code></p></li><li class="listitem"><p>after the entry action of the target state is executed | |
304 | (default): <code class="code">active_state_switch_after_entry</code></p></li></ul></div><p>The problem and the solution is shown for the | |
305 | <a class="link" href="examples/ActiveStateSetBeforeTransition.cpp" target="_top">functor-front-end</a> | |
306 | and <a class="link" href="examples/ActivateStateBeforeTransitionEuml.cpp" target="_top">eUML</a>. Removing <code class="code">active_state_switch_before_transition</code> | |
307 | will show the default state. </p></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="ch03s04.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="ch03.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="ch04.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">eUML </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> Chapter 4. Performance / Compilers</td></tr></table></div></body></html> |