]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> |
2 | ||
3 | <html> | |
4 | <head> | |
5 | <meta http-equiv="Content-Language" content="en-us"> | |
6 | <meta http-equiv="Content-Type" content="text/html; charset=us-ascii"> | |
7 | <meta name="GENERATOR" content="Microsoft FrontPage 6.0"> | |
8 | <meta name="ProgId" content="FrontPage.Editor.Document"> | |
9 | <link rel="stylesheet" type="text/css" href="../../../boost.css"> | |
10 | ||
11 | <title>The Boost Statechart Library - Tutorial</title> | |
12 | </head> | |
13 | ||
14 | <body link="#0000FF" vlink="#800080"> | |
15 | <table border="0" cellpadding="7" cellspacing="0" width="100%" summary= | |
16 | "header"> | |
17 | <tr> | |
18 | <td valign="top" width="300"> | |
19 | <h3><a href="../../../index.htm"><img alt="C++ Boost" src= | |
20 | "../../../boost.png" border="0" width="277" height="86"></a></h3> | |
21 | </td> | |
22 | ||
23 | <td valign="top"> | |
24 | <h1 align="center">The Boost Statechart Library</h1> | |
25 | ||
26 | <h2 align="center">Tutorial</h2> | |
27 | </td> | |
28 | </tr> | |
29 | </table> | |
30 | <hr> | |
31 | ||
32 | <p>A Japanese translation of an earlier version of this tutorial can be | |
33 | found at <a href= | |
34 | "http://prdownloads.sourceforge.jp/jyugem/7127/fsm-tutorial-jp.pdf">http://prdownloads.sourceforge.jp/jyugem/7127/fsm-tutorial-jp.pdf</a>. | |
35 | Kindly contributed by Mitsuo Fukasawa.</p> | |
36 | ||
37 | <h2>Contents</h2> | |
38 | ||
39 | <dl class="page-index"> | |
40 | <dt><a href="#Introduction">Introduction</a></dt> | |
41 | ||
42 | <dd><a href="#HowToReadThisTutorial">How to read this tutorial</a></dd> | |
43 | ||
44 | <dt><a href="#HelloWorld">Hello World!</a></dt> | |
45 | ||
46 | <dt><a href="#BasicTopicsAStopWatch">Basic topics: A stop watch</a></dt> | |
47 | ||
48 | <dd><a href="#DefiningStatesAndEvents">Defining states and | |
49 | events</a></dd> | |
50 | ||
51 | <dd><a href="#AddingReactions">Adding reactions</a></dd> | |
52 | ||
53 | <dd><a href="#StateLocalStorage">State-local storage</a></dd> | |
54 | ||
55 | <dd><a href="#GettingStateInformationOutOfTheMachine">Getting state | |
56 | information out of the machine</a></dd> | |
57 | ||
58 | <dt><a href="#IntermediateTopicsADigitalCamera">Intermediate topics: A | |
59 | digital camera</a></dt> | |
60 | ||
61 | <dd><a href= | |
62 | "#SpreadingAStateMachineOverMultipleTranslationUnits">Spreading a state | |
63 | machine over multiple translation units</a></dd> | |
64 | ||
65 | <dd><a href="#DeferringEvents">Deferring events</a></dd> | |
66 | ||
67 | <dd><a href="#Guards">Guards</a></dd> | |
68 | ||
69 | <dd><a href="#InStateReactions">In-state reactions</a></dd> | |
70 | ||
71 | <dd><a href="#TransitionActions">Transition actions</a></dd> | |
72 | ||
73 | <dt><a href="#AdvancedTopics">Advanced topics</a></dt> | |
74 | ||
75 | <dd><a href="#SpecifyingMultipleReactionsForAState">Specifying multiple | |
76 | reactions for a state</a></dd> | |
77 | ||
78 | <dd><a href="#PostingEvents">Posting events</a></dd> | |
79 | ||
80 | <dd><a href="#History">History</a></dd> | |
81 | ||
82 | <dd><a href="#OrthogonalStates">Orthogonal states</a></dd> | |
83 | ||
84 | <dd><a href="#StateQueries">State queries</a></dd> | |
85 | ||
86 | <dd><a href="#StateTypeInformation">State type information</a></dd> | |
87 | ||
88 | <dd><a href="#ExceptionHandling">Exception handling</a></dd> | |
89 | ||
90 | <dd><a href="#SubmachinesAndParameterizedStates">Submachines & | |
91 | Parametrized States</a></dd> | |
92 | ||
93 | <dd><a href="#AsynchronousStateMachines">Asynchronous state | |
94 | machines</a></dd> | |
95 | </dl> | |
96 | <hr> | |
97 | ||
98 | <h2><a name="Introduction" id="Introduction">Introduction</a></h2> | |
99 | ||
100 | <p>The Boost Statechart library is a framework that allows you to quickly | |
101 | transform a UML statechart into executable C++ code, <b>without</b> needing | |
102 | to use a code generator. Thanks to support for almost all UML features the | |
103 | transformation is straight-forward and the resulting C++ code is a nearly | |
104 | redundancy-free textual description of the statechart.</p> | |
105 | ||
106 | <h3><a name="HowToReadThisTutorial" id="HowToReadThisTutorial">How to read | |
107 | this tutorial</a></h3> | |
108 | ||
109 | <p>This tutorial was designed to be read linearly. First time users should | |
110 | start reading right at the beginning and stop as soon as they know enough | |
111 | for the task at hand. Specifically:</p> | |
112 | ||
113 | <ul> | |
114 | <li>Small and simple machines with just a handful of states can be | |
115 | implemented reasonably well by using the features described under | |
116 | <a href="#BasicTopicsAStopWatch">Basic topics: A stop watch</a></li> | |
117 | ||
118 | <li>For larger machines with up to roughly a dozen states the features | |
119 | described under <a href="#IntermediateTopicsADigitalCamera">Intermediate | |
120 | topics: A digital camera</a> are often helpful</li> | |
121 | ||
122 | <li>Finally, users wanting to create even more complex machines and | |
123 | project architects evaluating Boost.Statechart should also read the | |
124 | <a href="#AdvancedTopics">Advanced topics</a> section at the end. | |
125 | Moreover, reading the <a href= | |
126 | "rationale.html#Limitations">Limitations</a> section in the Rationale is | |
127 | strongly suggested</li> | |
128 | </ul> | |
129 | ||
130 | <h2><a name="HelloWorld" id="HelloWorld">Hello World!</a></h2> | |
131 | ||
132 | <p>We will use the simplest possible program to make our first steps. The | |
133 | statechart ...</p> | |
134 | ||
135 | <p><img alt="HelloWorld" src="HelloWorld.gif" border="0" width="379" | |
136 | height="94"></p> | |
137 | ||
138 | <p>... is implemented with the following code:</p> | |
139 | <pre> | |
140 | #include <boost/statechart/state_machine.hpp> | |
141 | #include <boost/statechart/simple_state.hpp> | |
142 | #include <iostream> | |
143 | ||
144 | namespace sc = boost::statechart; | |
145 | ||
146 | // We are declaring all types as <code>struct</code>s only to avoid having to | |
147 | // type <code>public</code>. If you don't mind doing so, you can just as well | |
148 | // use <code>class.</code> | |
149 | ||
150 | // We need to forward-declare the initial state because it can | |
151 | // only be defined at a point where the state machine is | |
152 | // defined. | |
153 | struct Greeting; | |
154 | ||
155 | // Boost.Statechart makes heavy use of the curiously recurring | |
156 | // template pattern. The deriving class must always be passed as | |
157 | // the first parameter to all base class templates. | |
158 | // | |
159 | // The state machine must be informed which state it has to | |
160 | // enter when the machine is initiated. That's why Greeting is | |
161 | // passed as the second template parameter. | |
162 | struct Machine : sc::state_machine< Machine, Greeting > {}; | |
163 | ||
164 | // For each state we need to define which state machine it | |
165 | // belongs to and where it is located in the statechart. Both is | |
166 | // specified with Context argument that is passed to | |
167 | // simple_state<>. For a flat state machine as we have it here, | |
168 | // the context is always the state machine. Consequently, | |
169 | // Machine must be passed as the second template parameter to | |
170 | // Greeting's base (the Context parameter is explained in more | |
171 | // detail in the next example). | |
172 | struct Greeting : sc::simple_state< Greeting, Machine > | |
173 | { | |
174 | // Whenever the state machine enters a state, it creates an | |
175 | // object of the corresponding state class. The object is then | |
176 | // kept alive as long as the machine remains in the state. | |
177 | // Finally, the object is destroyed when the state machine | |
178 | // exits the state. Therefore, a state entry action can be | |
179 | // defined by adding a constructor and a state exit action can | |
180 | // be defined by adding a destructor. | |
181 | Greeting() { std::cout << "Hello World!\n"; } // entry | |
182 | ~Greeting() { std::cout << "Bye Bye World!\n"; } // exit | |
183 | }; | |
184 | ||
185 | int main() | |
186 | { | |
187 | Machine myMachine; | |
188 | // The machine is not yet running after construction. We start | |
189 | // it by calling initiate(). This triggers the construction of | |
190 | // the initial state Greeting | |
191 | myMachine.initiate(); | |
192 | // When we leave main(), myMachine is destructed what leads to | |
193 | // the destruction of all currently active states. | |
194 | return 0; | |
195 | } | |
196 | </pre> | |
197 | ||
198 | <p>This prints <code>Hello World!</code> and <code>Bye Bye World!</code> | |
199 | before exiting.</p> | |
200 | ||
201 | <h2><a name="BasicTopicsAStopWatch" id="BasicTopicsAStopWatch">Basic | |
202 | topics: A stop watch</a></h2> | |
203 | ||
204 | <p>Next we will model a simple mechanical stop watch with a state machine. | |
205 | Such watches typically have two buttons:</p> | |
206 | ||
207 | <ul> | |
208 | <li>Start/Stop</li> | |
209 | ||
210 | <li>Reset</li> | |
211 | </ul> | |
212 | ||
213 | <p>And two states:</p> | |
214 | ||
215 | <ul> | |
216 | <li>Stopped: The hands reside in the position where they were last | |
217 | stopped: | |
218 | ||
219 | <ul> | |
220 | <li>Pressing the reset button moves the hands back to the 0 position. | |
221 | The watch remains in the Stopped state</li> | |
222 | ||
223 | <li>Pressing the start/stop button leads to a transition to the | |
224 | Running state</li> | |
225 | </ul> | |
226 | </li> | |
227 | ||
228 | <li>Running: The hands of the watch are in motion and continually show | |
229 | the elapsed time | |
230 | ||
231 | <ul> | |
232 | <li>Pressing the reset button moves the hands back to the 0 position | |
233 | and leads to a transition to the Stopped state</li> | |
234 | ||
235 | <li>Pressing the start/stop button leads to a transition to the | |
236 | Stopped state</li> | |
237 | </ul> | |
238 | </li> | |
239 | </ul> | |
240 | ||
241 | <p>Here is one way to specify this in UML:</p> | |
242 | ||
243 | <p><img alt="StopWatch" src="StopWatch.gif" border="0" width="560" height= | |
244 | "184"></p> | |
245 | ||
246 | <h3><a name="DefiningStatesAndEvents" id="DefiningStatesAndEvents">Defining | |
247 | states and events</a></h3> | |
248 | ||
249 | <p>The two buttons are modeled by two events. Moreover, we also define the | |
250 | necessary states and the initial state. <b>The following code is our | |
251 | starting point, subsequent code snippets must be inserted</b>:</p> | |
252 | <pre> | |
253 | #include <boost/statechart/event.hpp> | |
254 | #include <boost/statechart/state_machine.hpp> | |
255 | #include <boost/statechart/simple_state.hpp> | |
256 | ||
257 | namespace sc = boost::statechart; | |
258 | ||
259 | struct EvStartStop : sc::event< EvStartStop > {}; | |
260 | struct EvReset : sc::event< EvReset > {}; | |
261 | ||
262 | struct Active; | |
263 | struct StopWatch : sc::state_machine< StopWatch, Active > {}; | |
264 | ||
265 | struct Stopped; | |
266 | ||
267 | // The simple_state class template accepts up to four parameters: | |
268 | // - The third parameter specifies the inner initial state, if | |
269 | // there is one. Here, only Active has inner states, which is | |
270 | // why it needs to pass its inner initial state Stopped to its | |
271 | // base | |
272 | // - The fourth parameter specifies whether and what kind of | |
273 | // history is kept | |
274 | ||
275 | // Active is the outermost state and therefore needs to pass the | |
276 | // state machine class it belongs to | |
277 | struct Active : sc::simple_state< | |
278 | Active, StopWatch, Stopped > {}; | |
279 | ||
280 | // Stopped and Running both specify Active as their Context, | |
281 | // which makes them nested inside Active | |
282 | struct Running : sc::simple_state< Running, Active > {}; | |
283 | struct Stopped : sc::simple_state< Stopped, Active > {}; | |
284 | ||
285 | // Because the context of a state must be a complete type (i.e. | |
286 | // not forward declared), a machine must be defined from | |
287 | // "outside to inside". That is, we always start with the state | |
288 | // machine, followed by outermost states, followed by the direct | |
289 | // inner states of outermost states and so on. We can do so in a | |
290 | // breadth-first or depth-first way or employ a mixture of the | |
291 | // two. | |
292 | ||
293 | int main() | |
294 | { | |
295 | StopWatch myWatch; | |
296 | myWatch.initiate(); | |
297 | return 0; | |
298 | } | |
299 | </pre> | |
300 | ||
301 | <p>This compiles but doesn't do anything observable yet.</p> | |
302 | ||
303 | <h3><a name="AddingReactions" id="AddingReactions">Adding | |
304 | reactions</a></h3> | |
305 | ||
306 | <p>For the moment we will use only one type of reaction: transitions. We | |
307 | <b>insert</b> the bold parts of the following code:</p> | |
308 | <pre> | |
309 | <b>#include <boost/statechart/transition.hpp> | |
310 | </b> | |
311 | // ... | |
312 | ||
313 | struct Stopped; | |
314 | struct Active : sc::simple_state< Active, StopWatch, Stopped > | |
315 | { | |
316 | <b>typedef sc::transition< EvReset, Active > reactions;</b> | |
317 | }; | |
318 | ||
319 | struct Running : sc::simple_state< Running, Active > | |
320 | { | |
321 | <b>typedef sc::transition< EvStartStop, Stopped > reactions;</b> | |
322 | }; | |
323 | ||
324 | struct Stopped : sc::simple_state< Stopped, Active > | |
325 | { | |
326 | <b>typedef sc::transition< EvStartStop, Running > reactions;</b> | |
327 | }; | |
328 | ||
329 | // A state can define an arbitrary number of reactions. That's | |
330 | // why we have to put them into an mpl::list<> as soon as there | |
331 | // is more than one of them | |
332 | // (see <a href= | |
333 | "#SpecifyingMultipleReactionsForAState">Specifying multiple reactions for a state</a>). | |
334 | ||
335 | int main() | |
336 | { | |
337 | StopWatch myWatch; | |
338 | myWatch.initiate(); | |
339 | <b>myWatch.process_event( EvStartStop() ); | |
340 | </b> <b>myWatch.process_event( EvStartStop() ); | |
341 | </b> <b>myWatch.process_event( EvStartStop() ); | |
342 | </b> <b>myWatch.process_event( EvReset() ); | |
343 | </b> return 0; | |
344 | } | |
345 | </pre> | |
346 | ||
347 | <p>Now we have all the states and all the transitions in place and a number | |
348 | of events are also sent to the stop watch. The machine dutifully makes the | |
349 | transitions we would expect, but no actions are executed yet.</p> | |
350 | ||
351 | <h3><a name="StateLocalStorage" id="StateLocalStorage">State-local | |
352 | storage</a></h3> | |
353 | ||
354 | <p>Next we'll make the stop watch actually measure time. Depending on the | |
355 | state the stop watch is in, we need different variables:</p> | |
356 | ||
357 | <ul> | |
358 | <li>Stopped: One variable holding the elapsed time</li> | |
359 | ||
360 | <li>Running: One variable holding the elapsed time <b>and</b> one | |
361 | variable storing the point in time at which the watch was last | |
362 | started.</li> | |
363 | </ul> | |
364 | ||
365 | <p>We observe that the elapsed time variable is needed no matter what state | |
366 | the machine is in. Moreover, this variable should be reset to 0 when we | |
367 | send an <code>EvReset</code> event to the machine. The other variable is | |
368 | only needed while the machine is in the Running state. It should be set to | |
369 | the current time of the system clock whenever we enter the Running state. | |
370 | Upon exit we simply subtract the start time from the current system clock | |
371 | time and add the result to the elapsed time.</p> | |
372 | <pre> | |
373 | <b>#include <ctime> | |
374 | </b> | |
375 | // ... | |
376 | ||
377 | struct Stopped; | |
378 | struct Active : sc::simple_state< Active, StopWatch, Stopped > | |
379 | { | |
380 | <b>public:</b> | |
381 | typedef sc::transition< EvReset, Active > reactions; | |
382 | ||
383 | <b>Active() : elapsedTime_( 0.0 ) {} | |
384 | </b> <b>double ElapsedTime() const { return elapsedTime_; } | |
385 | </b> <b>double & ElapsedTime() { return elapsedTime_; } | |
386 | </b> <b>private: | |
387 | </b> <b>double elapsedTime_; | |
388 | </b>}; | |
389 | ||
390 | struct Running : sc::simple_state< Running, Active > | |
391 | { | |
392 | <b>public:</b> | |
393 | typedef sc::transition< EvStartStop, Stopped > reactions; | |
394 | ||
395 | <b>Running() : startTime_( std::time( 0 ) ) {} | |
396 | </b> <b>~Running() | |
397 | </b> <b>{</b> | |
398 | // Similar to when a derived class object accesses its | |
399 | // base class portion, context<>() is used to gain | |
400 | // access to the direct or indirect context of a state. | |
401 | // This can either be a direct or indirect outer state | |
402 | // or the state machine itself | |
403 | // (e.g. here: context< StopWatch >()). | |
404 | <b>context< Active >().ElapsedTime() += | |
405 | </b> <b>std::difftime( std::time( 0 ), startTime_ ); | |
406 | </b> <b>} | |
407 | </b> <b>private: | |
408 | </b> <b>std::time_t startTime_; | |
409 | </b>}; | |
410 | ||
411 | // ... | |
412 | </pre> | |
413 | ||
414 | <p>The machine now measures the time, but we cannot yet retrieve it from | |
415 | the main program.</p> | |
416 | ||
417 | <p>At this point, the advantages of state-local storage (which is still a | |
418 | relatively little-known feature) may not yet have become apparent. The FAQ | |
419 | item "<a href="faq.html#StateLocalStorage">What's so cool about state-local | |
420 | storage?</a>" tries to explain them in more detail by comparing this | |
421 | StopWatch with one that does not make use of state-local storage.</p> | |
422 | ||
423 | <h3><a name="GettingStateInformationOutOfTheMachine" id= | |
424 | "GettingStateInformationOutOfTheMachine">Getting state information out of | |
425 | the machine</a></h3> | |
426 | ||
427 | <p>To retrieve the measured time, we need a mechanism to get state | |
428 | information out of the machine. With our current machine design there are | |
429 | two ways to do that. For the sake of simplicity we use the less efficient | |
430 | one: <code>state_cast<>()</code> (StopWatch2.cpp shows the slightly | |
431 | more complex alternative). As the name suggests, the semantics are very | |
432 | similar to the ones of <code>dynamic_cast</code>. For example, when we call | |
433 | <code>myWatch.state_cast< const Stopped & >()</code> <b>and</b> | |
434 | the machine is currently in the Stopped state, we get a reference to the | |
435 | <code>Stopped</code> state. Otherwise <code>std::bad_cast</code> is thrown. | |
436 | We can use this functionality to implement a <code>StopWatch</code> member | |
437 | function that returns the elapsed time. However, rather than ask the | |
438 | machine in which state it is and then switch to different calculations for | |
439 | the elapsed time, we put the calculation into the Stopped and Running | |
440 | states and use an interface to retrieve the elapsed time:</p> | |
441 | <pre> | |
442 | <b>#include <iostream> | |
443 | ||
444 | </b>// ... | |
445 | ||
446 | <b>struct IElapsedTime | |
447 | { | |
448 | </b> <b>virtual double ElapsedTime() const = 0; | |
449 | }; | |
450 | ||
451 | </b>struct Active; | |
452 | struct StopWatch : sc::state_machine< StopWatch, Active > | |
453 | { | |
454 | <b>double ElapsedTime() const | |
455 | </b> <b>{ | |
456 | </b> <b>return state_cast< const IElapsedTime & >().ElapsedTime(); | |
457 | </b> <b>} | |
458 | </b>}; | |
459 | <b> | |
460 | </b>// ... | |
461 | ||
462 | struct Running : <b>IElapsedTime,</b> | |
463 | sc::simple_state< Running, Active > | |
464 | { | |
465 | public: | |
466 | typedef sc::transition< EvStartStop, Stopped > reactions; | |
467 | ||
468 | Running() : startTime_( std::time( 0 ) ) {} | |
469 | ~Running() | |
470 | { | |
471 | <b>context< Active >().ElapsedTime() = ElapsedTime(); | |
472 | </b> } | |
473 | <b> | |
474 | </b> <b>virtual double ElapsedTime() const | |
475 | </b> <b>{ | |
476 | </b> <b>return context< Active >().ElapsedTime() + | |
477 | </b> <b>std::difftime( std::time( 0 ), startTime_ ); | |
478 | </b> <b>} | |
479 | </b> private: | |
480 | std::time_t startTime_; | |
481 | }; | |
482 | ||
483 | struct Stopped : <b>IElapsedTime,</b> | |
484 | sc::simple_state< Stopped, Active > | |
485 | { | |
486 | typedef sc::transition< EvStartStop, Running > reactions; | |
487 | ||
488 | <b>virtual double ElapsedTime() const | |
489 | </b> <b>{ | |
490 | </b> <b>return context< Active >().ElapsedTime(); | |
491 | </b> <b>} | |
492 | </b>}; | |
493 | ||
494 | int main() | |
495 | { | |
496 | StopWatch myWatch; | |
497 | myWatch.initiate(); | |
498 | <b>std::cout << myWatch.ElapsedTime() << "\n"; | |
499 | </b> myWatch.process_event( EvStartStop() ); | |
500 | <b>std::cout << myWatch.ElapsedTime() << "\n"; | |
501 | </b> myWatch.process_event( EvStartStop() ); | |
502 | <b>std::cout << myWatch.ElapsedTime() << "\n"; | |
503 | </b> myWatch.process_event( EvStartStop() ); | |
504 | <b>std::cout << myWatch.ElapsedTime() << "\n"; | |
505 | </b> myWatch.process_event( EvReset() ); | |
506 | <b>std::cout << myWatch.ElapsedTime() << "\n"; | |
507 | </b> return 0; | |
508 | } | |
509 | </pre> | |
510 | ||
511 | <p>To actually see time being measured, you might want to single-step | |
512 | through the statements in <code>main()</code>. The StopWatch example | |
513 | extends this program to an interactive console application.</p> | |
514 | ||
515 | <h2><a name="IntermediateTopicsADigitalCamera" id= | |
516 | "IntermediateTopicsADigitalCamera">Intermediate topics: A digital | |
517 | camera</a></h2> | |
518 | ||
519 | <p>So far so good. However, the approach presented above has a few | |
520 | limitations:</p> | |
521 | ||
522 | <ul> | |
523 | <li>Bad scalability: As soon as the compiler reaches the point where | |
524 | <code>state_machine::initiate()</code> is called, a number of template | |
525 | instantiations take place, which can only succeed if the full declaration | |
526 | of each and every state of the machine is known. That is, the whole | |
527 | layout of a state machine must be implemented in one single translation | |
528 | unit (actions can be compiled separately, but this is of no importance | |
529 | here). For bigger (and more real-world) state machines, this leads to the | |
530 | following limitations: | |
531 | ||
532 | <ul> | |
533 | <li>At some point compilers reach their internal template | |
534 | instantiation limits and give up. This can happen even for | |
535 | moderately-sized machines. For example, in debug mode one popular | |
536 | compiler refused to compile earlier versions of the BitMachine | |
537 | example for anything above 3 bits. This means that the compiler | |
538 | reached its limits somewhere between 8 states, 24 transitions and 16 | |
539 | states, 64 transitions</li> | |
540 | ||
541 | <li>Multiple programmers can hardly work on the same state machine | |
542 | simultaneously because every layout change will inevitably lead to a | |
543 | recompilation of the whole state machine</li> | |
544 | </ul> | |
545 | </li> | |
546 | ||
547 | <li>Maximum one reaction per event: According to UML a state can have | |
548 | multiple reactions triggered by the same event. This makes sense when all | |
549 | reactions have mutually exclusive guards. The interface we used above | |
550 | only allows for at most one unguarded reaction for each event. Moreover, | |
551 | the UML concepts junction and choice point are not directly | |
552 | supported</li> | |
553 | </ul> | |
554 | ||
555 | <p>All these limitations can be overcome with custom reactions. <b>Warning: | |
556 | It is easy to abuse custom reactions up to the point of invoking undefined | |
557 | behavior. Please study the documentation before employing them!</b></p> | |
558 | ||
559 | <h3><a name="SpreadingAStateMachineOverMultipleTranslationUnits" id= | |
560 | "SpreadingAStateMachineOverMultipleTranslationUnits">Spreading a state | |
561 | machine over multiple translation units</a></h3> | |
562 | ||
563 | <p>Let's say your company would like to develop a digital camera. The | |
564 | camera has the following controls:</p> | |
565 | ||
566 | <ul> | |
567 | <li>Shutter button, which can be half-pressed and fully-pressed. The | |
568 | associated events are <code>EvShutterHalf</code>, | |
569 | <code>EvShutterFull</code> and <code>EvShutterReleased</code></li> | |
570 | ||
571 | <li>Config button, represented by the <code>EvConfig</code> event</li> | |
572 | ||
573 | <li>A number of other buttons that are not of interest here</li> | |
574 | </ul> | |
575 | ||
576 | <p>One use case for the camera says that the photographer can half-press | |
577 | the shutter <b>anywhere</b> in the configuration mode and the camera will | |
578 | immediately go into shooting mode. The following statechart is one way to | |
579 | achieve this behavior:</p> | |
580 | ||
581 | <p><img alt="Camera" src="Camera.gif" border="0" width="544" height= | |
582 | "317"></p> | |
583 | ||
584 | <p>The Configuring and Shooting states will contain numerous nested states | |
585 | while the Idle state is relatively simple. It was therefore decided to | |
586 | build two teams. One will implement the shooting mode while the other will | |
587 | implement the configuration mode. The two teams have already agreed on the | |
588 | interface that the shooting team will use to retrieve the configuration | |
589 | settings. We would like to ensure that the two teams can work with the | |
590 | least possible interference. So, we put the two states in their own | |
591 | translation units so that machine layout changes within the Configuring | |
592 | state will never lead to a recompilation of the inner workings of the | |
593 | Shooting state and vice versa.</p> | |
594 | ||
595 | <p><b>Unlike in the previous example, the excerpts presented here often | |
596 | outline different options to achieve the same effect. That's why the code | |
597 | is often not equal to the Camera example code.</b> Comments mark the parts | |
598 | where this is the case.</p> | |
599 | ||
600 | <p>Camera.hpp:</p> | |
601 | <pre> | |
602 | #ifndef CAMERA_HPP_INCLUDED | |
603 | #define CAMERA_HPP_INCLUDED | |
604 | ||
605 | #include <boost/statechart/event.hpp> | |
606 | #include <boost/statechart/state_machine.hpp> | |
607 | #include <boost/statechart/simple_state.hpp> | |
608 | #include <boost/statechart/custom_reaction.hpp> | |
609 | ||
610 | namespace sc = boost::statechart; | |
611 | ||
612 | struct EvShutterHalf : sc::event< EvShutterHalf > {}; | |
613 | struct EvShutterFull : sc::event< EvShutterFull > {}; | |
614 | struct EvShutterRelease : sc::event< EvShutterRelease > {}; | |
615 | struct EvConfig : sc::event< EvConfig > {}; | |
616 | ||
617 | struct NotShooting; | |
618 | struct Camera : sc::state_machine< Camera, NotShooting > | |
619 | { | |
620 | bool IsMemoryAvailable() const { return true; } | |
621 | bool IsBatteryLow() const { return false; } | |
622 | }; | |
623 | ||
624 | struct Idle; | |
625 | struct NotShooting : sc::simple_state< | |
626 | NotShooting, Camera, Idle > | |
627 | { | |
628 | // With a custom reaction we only specify that we <b>might</b> do | |
629 | // something with a particular event, but the actual reaction | |
630 | // is defined in the react member function, which can be | |
631 | // implemented in the .cpp file. | |
632 | <b>typedef sc::custom_reaction< EvShutterHalf > reactions;</b> | |
633 | ||
634 | // ... | |
635 | <b>sc::result react( const EvShutterHalf & );</b> | |
636 | }; | |
637 | ||
638 | struct Idle : sc::simple_state< Idle, NotShooting > | |
639 | { | |
640 | <b>typedef sc::custom_reaction< EvConfig > reactions;</b> | |
641 | ||
642 | // ... | |
643 | <b>sc::result react( const EvConfig & );</b> | |
644 | }; | |
645 | ||
646 | #endif | |
647 | </pre> | |
648 | ||
649 | <p>Camera.cpp:</p> | |
650 | <pre> | |
651 | #include "Camera.hpp" | |
652 | ||
653 | // The following includes are only made here but not in | |
654 | // Camera.hpp | |
655 | // The Shooting and Configuring states can themselves apply the | |
656 | // same pattern to hide their inner implementation, which | |
657 | // ensures that the two teams working on the Camera state | |
658 | // machine will never need to disturb each other. | |
659 | #include "Configuring.hpp" | |
660 | #include "Shooting.hpp" | |
661 | ||
662 | // ... | |
663 | ||
664 | // not part of the Camera example | |
665 | sc::result NotShooting::react( const EvShutterHalf & ) | |
666 | { | |
667 | return transit< Shooting >(); | |
668 | } | |
669 | ||
670 | sc::result Idle::react( const EvConfig & ) | |
671 | { | |
672 | return transit< Configuring >(); | |
673 | } | |
674 | </pre> | |
675 | ||
676 | <p><b><font color="#FF0000">Caution: Any call to | |
677 | <code>simple_state<>::transit<>()</code> or | |
678 | <code>simple_state<>::terminate()</code> (see <a href= | |
679 | "reference.html#transit1">reference</a>) will inevitably destruct the state | |
680 | object (similar to <code>delete this;</code>)! That is, code executed after | |
681 | any of these calls may invoke undefined behavior!</font></b> That's why | |
682 | these functions should only be called as part of a return statement.</p> | |
683 | ||
684 | <h3><a name="DeferringEvents" id="DeferringEvents">Deferring | |
685 | events</a></h3> | |
686 | ||
687 | <p>The inner workings of the Shooting state could look as follows:</p> | |
688 | ||
689 | <p><img alt="Camera2" src="Camera2.gif" border="0" width="427" height= | |
690 | "427"></p> | |
691 | ||
692 | <p>When the user half-presses the shutter, Shooting and its inner initial | |
693 | state Focusing are entered. In the Focusing entry action the camera | |
694 | instructs the focusing circuit to bring the subject into focus. The | |
695 | focusing circuit then moves the lenses accordingly and sends the EvInFocus | |
696 | event as soon as it is done. Of course, the user can fully-press the | |
697 | shutter while the lenses are still in motion. Without any precautions, the | |
698 | resulting EvShutterFull event would simply be lost because the Focusing | |
699 | state does not define a reaction for this event. As a result, the user | |
700 | would have to fully-press the shutter again after the camera has finished | |
701 | focusing. To prevent this, the EvShutterFull event is deferred inside the | |
702 | Focusing state. This means that all events of this type are stored in a | |
703 | separate queue, which is emptied into the main queue when the Focusing | |
704 | state is exited.</p> | |
705 | <pre> | |
706 | struct Focusing : sc::state< Focusing, Shooting > | |
707 | { | |
708 | typedef mpl::list< | |
709 | sc::custom_reaction< EvInFocus >, | |
710 | <b>sc::deferral< EvShutterFull ></b> | |
711 | > reactions; | |
712 | ||
713 | Focusing( my_context ctx ); | |
714 | sc::result react( const EvInFocus & ); | |
715 | }; | |
716 | </pre> | |
717 | ||
718 | <h3><a name="Guards" id="Guards">Guards</a></h3> | |
719 | ||
720 | <p>Both transitions originating at the Focused state are triggered by the | |
721 | same event but they have mutually exclusive guards. Here is an appropriate | |
722 | custom reaction:</p> | |
723 | <pre> | |
724 | // not part of the Camera example | |
725 | sc::result Focused::react( const EvShutterFull & ) | |
726 | { | |
727 | if ( context< Camera >().IsMemoryAvailable() ) | |
728 | { | |
729 | return transit< Storing >(); | |
730 | } | |
731 | else | |
732 | { | |
733 | // The following is actually a mixture between an in-state | |
734 | // reaction and a transition. See later on how to implement | |
735 | // proper transition actions. | |
736 | std::cout << "Cache memory full. Please wait...\n"; | |
737 | return transit< Focused >(); | |
738 | } | |
739 | } | |
740 | </pre> | |
741 | ||
742 | <p>Custom reactions can of course also be implemented directly in the state | |
743 | declaration, which is often preferable for easier browsing.</p> | |
744 | ||
745 | <p>Next we will use a guard to prevent a transition and let outer states | |
746 | react to the event if the battery is low:</p> | |
747 | ||
748 | <p>Camera.cpp:</p> | |
749 | <pre> | |
750 | // ... | |
751 | sc::result NotShooting::react( const EvShutterHalf & ) | |
752 | { | |
753 | if ( context< Camera >().IsBatteryLow() ) | |
754 | { | |
755 | // We cannot react to the event ourselves, so we forward it | |
756 | // to our outer state (this is also the default if a state | |
757 | // defines no reaction for a given event). | |
758 | <b>return forward_event();</b> | |
759 | } | |
760 | else | |
761 | { | |
762 | return transit< Shooting >(); | |
763 | } | |
764 | } | |
765 | // ... | |
766 | </pre> | |
767 | ||
768 | <h3><a name="InStateReactions" id="InStateReactions">In-state | |
769 | reactions</a></h3> | |
770 | ||
771 | <p>The self-transition of the Focused state could also be implemented as an | |
772 | <a href="definitions.html#InStateReaction">in-state reaction</a>, which has | |
773 | the same effect as long as Focused does not have any entry or exit | |
774 | actions:</p> | |
775 | ||
776 | <p>Shooting.cpp:</p> | |
777 | <pre> | |
778 | // ... | |
779 | sc::result Focused::react( const EvShutterFull & ) | |
780 | { | |
781 | if ( context< Camera >().IsMemoryAvailable() ) | |
782 | { | |
783 | return transit< Storing >(); | |
784 | } | |
785 | else | |
786 | { | |
787 | std::cout << "Cache memory full. Please wait...\n"; | |
788 | // Indicate that the event can be discarded. So, the | |
789 | // dispatch algorithm will stop looking for a reaction | |
790 | // and the machine remains in the Focused state. | |
791 | <b>return discard_event();</b> | |
792 | } | |
793 | } | |
794 | // ... | |
795 | </pre> | |
796 | ||
797 | <p>Because the in-state reaction is guarded, we need to employ a | |
798 | <code>custom_reaction<></code> here. For unguarded in-state reactions | |
799 | <code><a href= | |
800 | "reference.html#ClassTemplatein_state_reaction">in_state_reaction</a><></code> | |
801 | should be used for better code-readability.</p> | |
802 | ||
803 | <h3><a name="TransitionActions" id="TransitionActions">Transition | |
804 | actions</a></h3> | |
805 | ||
806 | <p>As an effect of every transition, actions are executed in the following | |
807 | order:</p> | |
808 | ||
809 | <ol> | |
810 | <li>Starting from the innermost active state, all exit actions up to but | |
811 | excluding the <a href="definitions.html#InnermostCommonContext">innermost | |
812 | common context</a></li> | |
813 | ||
814 | <li>The transition action (if present)</li> | |
815 | ||
816 | <li>Starting from the innermost common context, all entry actions down to | |
817 | the target state followed by the entry actions of the initial states</li> | |
818 | </ol> | |
819 | ||
820 | <p>Example:</p> | |
821 | ||
822 | <p><img alt="LCA" src="LCA.gif" border="0" width="604" height="304"></p> | |
823 | ||
824 | <p>Here the order is as follows: ~D(), ~C(), ~B(), ~A(), t(), X(), Y(), | |
825 | Z(). The transition action t() is therefore executed in the context of the | |
826 | InnermostCommonOuter state because the source state has already been left | |
827 | (destructed) and the target state has not yet been entered | |
828 | (constructed).</p> | |
829 | ||
830 | <p>With Boost.Statechart, a transition action can be a member of <b>any</b> | |
831 | common outer context. That is, the transition between Focusing and Focused | |
832 | could be implemented as follows:</p> | |
833 | ||
834 | <p>Shooting.hpp:</p> | |
835 | <pre> | |
836 | // ... | |
837 | struct Focusing; | |
838 | struct Shooting : sc::simple_state< Shooting, Camera, Focusing > | |
839 | { | |
840 | typedef sc::transition< | |
841 | EvShutterRelease, NotShooting > reactions; | |
842 | ||
843 | // ... | |
844 | <b>void DisplayFocused( const EvInFocus & );</b> | |
845 | }; | |
846 | ||
847 | // ... | |
848 | ||
849 | // not part of the Camera example | |
850 | struct Focusing : sc::simple_state< Focusing, Shooting > | |
851 | { | |
852 | typedef sc::transition< EvInFocus, Focused<b>,</b> | |
853 | <b>Shooting, &Shooting::DisplayFocused</b> > reactions; | |
854 | }; | |
855 | </pre> | |
856 | ||
857 | <p><b>Or</b>, the following is also possible (here the state machine itself | |
858 | serves as the outermost context):</p> | |
859 | <pre> | |
860 | // not part of the Camera example | |
861 | struct Camera : sc::state_machine< Camera, NotShooting > | |
862 | { | |
863 | <b>void DisplayFocused( const EvInFocus & );</b> | |
864 | }; | |
865 | </pre> | |
866 | <pre> | |
867 | // not part of the Camera example | |
868 | struct Focusing : sc::simple_state< Focusing, Shooting > | |
869 | { | |
870 | typedef sc::transition< EvInFocus, Focused<b>,</b> | |
871 | <b>Camera, &Camera::DisplayFocused</b> > reactions; | |
872 | }; | |
873 | </pre> | |
874 | ||
875 | <p>Naturally, transition actions can also be invoked from custom | |
876 | reactions:</p> | |
877 | ||
878 | <p>Shooting.cpp:</p> | |
879 | <pre> | |
880 | // ... | |
881 | sc::result Focusing::react( const EvInFocus & evt ) | |
882 | { | |
883 | // We have to manually forward evt | |
884 | return transit< Focused >( <b>&Shooting::DisplayFocused</b>, evt ); | |
885 | } | |
886 | </pre> | |
887 | ||
888 | <h2><a name="AdvancedTopics" id="AdvancedTopics">Advanced topics</a></h2> | |
889 | ||
890 | <h3><a name="SpecifyingMultipleReactionsForAState" id= | |
891 | "SpecifyingMultipleReactionsForAState">Specifying multiple reactions for a | |
892 | state</a></h3> | |
893 | ||
894 | <p>Often a state must define reactions for more than one event. In this | |
895 | case, an <code>mpl::list<></code> must be used as outlined below:</p> | |
896 | <pre> | |
897 | // ... | |
898 | ||
899 | <b>#include <boost/mpl/list.hpp> | |
900 | </b> | |
901 | <b>namespace mpl = boost::mpl; | |
902 | </b> | |
903 | // ... | |
904 | ||
905 | struct Playing : sc::simple_state< Playing, Mp3Player > | |
906 | { | |
907 | typdef <b>mpl::list<</b> | |
908 | sc::custom_reaction< EvFastForward >, | |
909 | sc::transition< EvStop, Stopped > <b>></b> reactions; | |
910 | ||
911 | /* ... */ | |
912 | }; | |
913 | </pre> | |
914 | ||
915 | <h3><a name="PostingEvents" id="PostingEvents">Posting events</a></h3> | |
916 | ||
917 | <p>Non-trivial state machines often need to post internal events. Here's an | |
918 | example of how to do this:</p> | |
919 | <pre> | |
920 | Pumping::~Pumping() | |
921 | { | |
922 | post_event( EvPumpingFinished() ); | |
923 | } | |
924 | </pre> | |
925 | ||
926 | <p>The event is pushed into the main queue. The events in the queue are | |
927 | processed as soon as the current reaction is completed. Events can be | |
928 | posted from inside <code>react</code> functions, entry-, exit- and | |
929 | transition actions. However, posting from inside entry actions is a bit | |
930 | more complicated (see e.g. <code>Focusing::Focusing()</code> in | |
931 | <code>Shooting.cpp</code> in the Camera example):</p> | |
932 | <pre> | |
933 | struct Pumping : <b>sc::state</b>< Pumping, Purifier > | |
934 | { | |
935 | <b>Pumping( my_context ctx ) : my_base( ctx )</b> | |
936 | { | |
937 | post_event( EvPumpingStarted() ); | |
938 | } | |
939 | // ... | |
940 | }; | |
941 | </pre> | |
942 | ||
943 | <p>As soon as an entry action of a state needs to contact the "outside | |
944 | world" (here: the event queue in the state machine), the state must derive | |
945 | from <code>state<></code> rather than from | |
946 | <code>simple_state<></code> and must implement a forwarding | |
947 | constructor as outlined above (apart from the constructor, | |
948 | <code>state<></code> offers the same interface as | |
949 | <code>simple_state<></code>). Hence, this must be done whenever an | |
950 | entry action makes one or more calls to the following functions:</p> | |
951 | ||
952 | <ul> | |
953 | <li><code>simple_state<>::post_event()</code></li> | |
954 | ||
955 | <li> | |
956 | <code>simple_state<>::clear_shallow_history<>()</code></li> | |
957 | ||
958 | <li><code>simple_state<>::clear_deep_history<>()</code></li> | |
959 | ||
960 | <li><code>simple_state<>::outermost_context()</code></li> | |
961 | ||
962 | <li><code>simple_state<>::context<>()</code></li> | |
963 | ||
964 | <li><code>simple_state<>::state_cast<>()</code></li> | |
965 | ||
966 | <li><code>simple_state<>::state_downcast<>()</code></li> | |
967 | ||
968 | <li><code>simple_state<>::state_begin()</code></li> | |
969 | ||
970 | <li><code>simple_state<>::state_end()</code></li> | |
971 | </ul> | |
972 | ||
973 | <p>In my experience, these functions are needed only rarely in entry | |
974 | actions so this workaround should not uglify user code too much.</p> | |
975 | ||
976 | <h3><a name="History" id="History">History</a></h3> | |
977 | ||
978 | <p>Photographers testing beta versions of our <a href= | |
979 | "#SpreadingAStateMachineOverMultipleTranslationUnits">digital camera</a> | |
980 | said that they really liked that half-pressing the shutter anytime (even | |
981 | while the camera is being configured) immediately readies the camera for | |
982 | picture-taking. However, most of them found it unintuitive that the camera | |
983 | always goes into the idle mode after releasing the shutter. They would | |
984 | rather see the camera go back into the state it had before half-pressing | |
985 | the shutter. This way they can easily test the influence of a configuration | |
986 | setting by modifying it, half- and then fully-pressing the shutter to take | |
987 | a picture. Finally, releasing the shutter will bring them back to the | |
988 | screen where they have modified the setting. To implement this behavior | |
989 | we'd change the state chart as follows:</p> | |
990 | ||
991 | <p><img alt="CameraWithHistory1" src="CameraWithHistory1.gif" border="0" | |
992 | width="542" height="378"></p> | |
993 | ||
994 | <p>As mentioned earlier, the Configuring state contains a fairly complex | |
995 | and deeply nested inner machine. Naturally, we'd like to restore the | |
996 | previous state down to the <a href= | |
997 | "definitions.html#InnermostState">innermost state</a>(s) in Configuring, | |
998 | that's why we use a deep history pseudo state. The associated code looks as | |
999 | follows:</p> | |
1000 | <pre> | |
1001 | // not part of the Camera example | |
1002 | struct NotShooting : sc::simple_state< | |
1003 | NotShooting, Camera, Idle, <b>sc::has_deep_history</b> > | |
1004 | { | |
1005 | // ... | |
1006 | }; | |
1007 | ||
1008 | // ... | |
1009 | ||
1010 | struct Shooting : sc::simple_state< Shooting, Camera, Focusing > | |
1011 | { | |
1012 | typedef sc::transition< | |
1013 | EvShutterRelease, <b>sc::deep_history< Idle ></b> > reactions; | |
1014 | ||
1015 | // ... | |
1016 | }; | |
1017 | </pre> | |
1018 | ||
1019 | <p>History has two phases: Firstly, when the state containing the history | |
1020 | pseudo state is exited, information about the previously active inner state | |
1021 | hierarchy must be saved. Secondly, when a transition to the history pseudo | |
1022 | state is made later, the saved state hierarchy information must be | |
1023 | retrieved and the appropriate states entered. The former is expressed by | |
1024 | passing either <code>has_shallow_history</code>, | |
1025 | <code>has_deep_history</code> or <code>has_full_history</code> (which | |
1026 | combines shallow and deep history) as the last parameter to the | |
1027 | <code>simple_state</code> and <code>state</code> class templates. The | |
1028 | latter is expressed by specifying either | |
1029 | <code>shallow_history<></code> or <code>deep_history<></code> | |
1030 | as a transition destination or, as we'll see in an instant, as an inner | |
1031 | initial state. Because it is possible that a state containing a history | |
1032 | pseudo state has never been entered before a transition to history is made, | |
1033 | both class templates demand a parameter specifying the default state to | |
1034 | enter in such situations.</p> | |
1035 | ||
1036 | <p>The redundancy necessary for using history is checked for consistency at | |
1037 | compile time. That is, the state machine wouldn't have compiled had we | |
1038 | forgotten to pass <code>has_deep_history</code> to the base of | |
1039 | <code>NotShooting</code>.</p> | |
1040 | ||
1041 | <p>Another change request filed by a few beta testers says that they would | |
1042 | like to see the camera go back into the state it had before turning it off | |
1043 | when they turn it back on. Here's the implementation:</p> | |
1044 | ||
1045 | <p><img alt="CameraWithHistory2" src="CameraWithHistory2.gif" border="0" | |
1046 | width="468" height="483"></p> | |
1047 | <pre> | |
1048 | // ... | |
1049 | ||
1050 | // not part of the Camera example | |
1051 | struct NotShooting : sc::simple_state< NotShooting, Camera, | |
1052 | <b>mpl::list< sc::deep_history< Idle > ></b>, | |
1053 | <b>sc::has_deep_history</b> > | |
1054 | { | |
1055 | // ... | |
1056 | }; | |
1057 | ||
1058 | // ... | |
1059 | </pre> | |
1060 | ||
1061 | <p>Unfortunately, there is a small inconvenience due to some | |
1062 | template-related implementation details. When the inner initial state is a | |
1063 | class template instantiation we always have to put it into an | |
1064 | <code>mpl::list<></code>, although there is only one inner initial | |
1065 | state. Moreover, the current deep history implementation has some <a href= | |
1066 | "rationale.html#Limitations">limitations</a>.</p> | |
1067 | ||
1068 | <h3><a name="OrthogonalStates" id="OrthogonalStates">Orthogonal | |
1069 | states</a></h3> | |
1070 | ||
1071 | <p><img alt="OrthogonalStates" src="OrthogonalStates.gif" border="0" width= | |
1072 | "633" height="393"></p> | |
1073 | ||
1074 | <p>To implement this statechart you simply specify more than one inner | |
1075 | initial state (see the Keyboard example):</p> | |
1076 | <pre> | |
1077 | struct Active; | |
1078 | struct Keyboard : sc::state_machine< Keyboard, Active > {}; | |
1079 | ||
1080 | struct NumLockOff; | |
1081 | struct CapsLockOff; | |
1082 | struct ScrollLockOff; | |
1083 | struct Active: sc::simple_state< Active, Keyboard, | |
1084 | <b>mpl::list< NumLockOff, CapsLockOff, ScrollLockOff ></b> > {}; | |
1085 | </pre> | |
1086 | ||
1087 | <p>Active's inner states must declare which orthogonal region they belong | |
1088 | to:</p> | |
1089 | <pre> | |
1090 | struct EvNumLockPressed : sc::event< EvNumLockPressed > {}; | |
1091 | struct EvCapsLockPressed : sc::event< EvCapsLockPressed > {}; | |
1092 | struct EvScrollLockPressed : | |
1093 | sc::event< EvScrollLockPressed > {}; | |
1094 | ||
1095 | struct NumLockOn : sc::simple_state< | |
1096 | NumLockOn, Active<b>::orthogonal< 0 ></b> > | |
1097 | { | |
1098 | typedef sc::transition< | |
1099 | EvNumLockPressed, NumLockOff > reactions; | |
1100 | }; | |
1101 | ||
1102 | struct NumLockOff : sc::simple_state< | |
1103 | NumLockOff, Active<b>::orthogonal< 0 ></b> > | |
1104 | { | |
1105 | typedef sc::transition< | |
1106 | EvNumLockPressed, NumLockOn > reactions; | |
1107 | }; | |
1108 | ||
1109 | struct CapsLockOn : sc::simple_state< | |
1110 | CapsLockOn, Active<b>::orthogonal< 1 ></b> > | |
1111 | { | |
1112 | typedef sc::transition< | |
1113 | EvCapsLockPressed, CapsLockOff > reactions; | |
1114 | }; | |
1115 | ||
1116 | struct CapsLockOff : sc::simple_state< | |
1117 | CapsLockOff, Active<b>::orthogonal< 1 ></b> > | |
1118 | { | |
1119 | typedef sc::transition< | |
1120 | EvCapsLockPressed, CapsLockOn > reactions; | |
1121 | }; | |
1122 | ||
1123 | struct ScrollLockOn : sc::simple_state< | |
1124 | ScrollLockOn, Active<b>::orthogonal< 2 ></b> > | |
1125 | { | |
1126 | typedef sc::transition< | |
1127 | EvScrollLockPressed, ScrollLockOff > reactions; | |
1128 | }; | |
1129 | ||
1130 | struct ScrollLockOff : sc::simple_state< | |
1131 | ScrollLockOff, Active<b>::orthogonal< 2 ></b> > | |
1132 | { | |
1133 | typedef sc::transition< | |
1134 | EvScrollLockPressed, ScrollLockOn > reactions; | |
1135 | }; | |
1136 | </pre> | |
1137 | ||
1138 | <p><code>orthogonal< 0 ></code> is the default, so | |
1139 | <code>NumLockOn</code> and <code>NumLockOff</code> could just as well pass | |
1140 | <code>Active</code> instead of <code>Active::orthogonal< 0 ></code> | |
1141 | to specify their context. The numbers passed to the <code>orthogonal</code> | |
1142 | member template must correspond to the list position in the outer state. | |
1143 | Moreover, the orthogonal position of the source state of a transition must | |
1144 | correspond to the orthogonal position of the target state. Any violations | |
1145 | of these rules lead to compile time errors. Examples:</p> | |
1146 | <pre> | |
1147 | // Example 1: does not compile because Active specifies | |
1148 | // only 3 orthogonal regions | |
1149 | struct WhateverLockOn: sc::simple_state< | |
1150 | WhateverLockOn, Active<b>::</b>orthogonal< <b>3</b> > > {}; | |
1151 | ||
1152 | // Example 2: does not compile because Active specifies | |
1153 | // that NumLockOff is part of the "0th" orthogonal region | |
1154 | struct NumLockOff : sc::simple_state< | |
1155 | NumLockOff, Active<b>::</b>orthogonal< <b>1</b> > > {}; | |
1156 | ||
1157 | // Example 3: does not compile because a transition between | |
1158 | // different orthogonal regions is not permitted | |
1159 | struct CapsLockOn : sc::simple_state< | |
1160 | CapsLockOn, Active<b>::</b>orthogonal< <b>1</b> > > | |
1161 | { | |
1162 | typedef sc::transition< | |
1163 | EvCapsLockPressed, CapsLockOff > reactions; | |
1164 | }; | |
1165 | ||
1166 | struct CapsLockOff : sc::simple_state< | |
1167 | CapsLockOff, Active<b>::</b>orthogonal< <b>2</b> > > | |
1168 | { | |
1169 | typedef sc::transition< | |
1170 | EvCapsLockPressed, CapsLockOn > reactions; | |
1171 | }; | |
1172 | </pre> | |
1173 | ||
1174 | <h3><a name="StateQueries" id="StateQueries">State queries</a></h3> | |
1175 | ||
1176 | <p>Often reactions in a state machine depend on the active state in one or | |
1177 | more orthogonal regions. This is because orthogonal regions are not | |
1178 | completely orthogonal or a certain reaction in an outer state can only take | |
1179 | place if the inner orthogonal regions are in particular states. For this | |
1180 | purpose, the <code>state_cast<></code> function introduced under | |
1181 | <a href="#GettingStateInformationOutOfTheMachine">Getting state information | |
1182 | out of the machine</a> is also available within states.</p> | |
1183 | ||
1184 | <p>As a somewhat far-fetched example, let's assume that our <a href= | |
1185 | "#OrthogonalStates">keyboard</a> also accepts | |
1186 | <code>EvRequestShutdown</code> events, the reception of which makes the | |
1187 | keyboard terminate only if all lock keys are in the off state. We would | |
1188 | then modify the Keyboard state machine as follows:</p> | |
1189 | <pre> | |
1190 | struct EvRequestShutdown : sc::event< EvRequestShutdown > {}; | |
1191 | ||
1192 | struct NumLockOff; | |
1193 | struct CapsLockOff; | |
1194 | struct ScrollLockOff; | |
1195 | struct Active: sc::simple_state< Active, Keyboard, | |
1196 | mpl::list< NumLockOff, CapsLockOff, ScrollLockOff > > | |
1197 | { | |
1198 | typedef sc::custom_reaction< EvRequestShutdown > reactions; | |
1199 | ||
1200 | sc::result react( const EvRequestShutdown & ) | |
1201 | { | |
1202 | if ( ( state_downcast< const NumLockOff * >() != 0 ) && | |
1203 | ( state_downcast< const CapsLockOff * >() != 0 ) && | |
1204 | ( state_downcast< const ScrollLockOff * >() != 0 ) ) | |
1205 | { | |
1206 | return terminate(); | |
1207 | } | |
1208 | else | |
1209 | { | |
1210 | return discard_event(); | |
1211 | } | |
1212 | } | |
1213 | }; | |
1214 | </pre> | |
1215 | ||
1216 | <p>Passing a pointer type instead of reference type results in 0 pointers | |
1217 | being returned instead of <code>std::bad_cast</code> being thrown when the | |
1218 | cast fails. Note also the use of <code>state_downcast<>()</code> | |
1219 | instead of <code>state_cast<>()</code>. Similar to the differences | |
1220 | between <code>boost::polymorphic_downcast<>()</code> and | |
1221 | <code>dynamic_cast</code>, <code>state_downcast<>()</code> is a much | |
1222 | faster variant of <code>state_cast<>()</code> and can only be used | |
1223 | when the passed type is a most-derived type. | |
1224 | <code>state_cast<>()</code> should only be used if you want to query | |
1225 | an additional base.</p> | |
1226 | ||
1227 | <h4>Custom state queries</h4> | |
1228 | ||
1229 | <p>It is often desirable to find out exactly which state(s) a machine | |
1230 | currently resides in. To some extent this is already possible with | |
1231 | <code>state_cast<>()</code> and <code>state_downcast<>()</code> | |
1232 | but their utility is rather limited because both only return a yes/no | |
1233 | answer to the question "Are you in state X?". It is possible to ask more | |
1234 | sophisticated questions when you pass an additional base class rather than | |
1235 | a state class to <code>state_cast<>()</code> but this involves more | |
1236 | work (all states need to derive from and implement the additional base), is | |
1237 | slow (under the hood <code>state_cast<>()</code> uses | |
1238 | <code>dynamic_cast</code>), forces projects to compile with C++ RTTI turned | |
1239 | on and has a negative impact on state entry/exit speed.</p> | |
1240 | ||
1241 | <p>Especially for debugging it would be so much more useful being able to | |
1242 | ask "In which state(s) are you?". For this purpose it is possible to | |
1243 | iterate over all active <b>innermost</b> states with | |
1244 | <code>state_machine<>::state_begin()</code> and | |
1245 | <code>state_machine<>::state_end()</code>. Dereferencing the returned | |
1246 | iterator returns a reference to <code>const | |
1247 | state_machine<>::state_base_type</code>, the common base of all | |
1248 | states. We can thus print the currently active state configuration as | |
1249 | follows (see the Keyboard example for the complete code):</p> | |
1250 | <pre> | |
1251 | void DisplayStateConfiguration( const Keyboard & kbd ) | |
1252 | { | |
1253 | char region = 'a'; | |
1254 | ||
1255 | for ( | |
1256 | Keyboard::state_iterator pLeafState = kbd.state_begin(); | |
1257 | pLeafState != kbd.state_end(); ++pLeafState ) | |
1258 | { | |
1259 | std::cout << "Orthogonal region " << region << ": "; | |
1260 | // The following use of typeid assumes that | |
1261 | // BOOST_STATECHART_USE_NATIVE_RTTI is defined | |
1262 | std::cout << typeid( *pLeafState ).name() << "\n"; | |
1263 | ++region; | |
1264 | } | |
1265 | } | |
1266 | </pre> | |
1267 | ||
1268 | <p>If necessary, the outer states can be accessed with | |
1269 | <code>state_machine<>::state_base_type::outer_state_ptr()</code>, | |
1270 | which returns a pointer to <code>const | |
1271 | state_machine<>::state_base_type</code>. When called on an outermost | |
1272 | state this function simply returns 0.</p> | |
1273 | ||
1274 | <h3><a name="StateTypeInformation" id="StateTypeInformation">State type | |
1275 | information</a></h3> | |
1276 | ||
1277 | <p>To cut down on executable size some applications must be compiled with | |
1278 | C++ RTTI turned off. This would render the ability to iterate over all | |
1279 | active states pretty much useless if it weren't for the following two | |
1280 | functions:</p> | |
1281 | ||
1282 | <ul> | |
1283 | <li><code>static <i>unspecified_type</i> | |
1284 | simple_state<>::static_type()</code></li> | |
1285 | ||
1286 | <li><code><i>unspecified_type<br></i> | |
1287 | state_machine<>::state_base_type::dynamic_type() const</code></li> | |
1288 | </ul> | |
1289 | ||
1290 | <p>Both return a value that is comparable via <code>operator==()</code> and | |
1291 | <code>std::less<></code>. This alone would be enough to implement the | |
1292 | <code>DisplayStateConfiguration</code> function above without the help of | |
1293 | <code>typeid</code> but it is still somewhat cumbersome as a map must be | |
1294 | used to associate the type information values with the state names.</p> | |
1295 | ||
1296 | <h4><a name="CustomStateTypeInformation" id= | |
1297 | "CustomStateTypeInformation">Custom state type information</a></h4> | |
1298 | ||
1299 | <p>That's why the following functions are also provided (only available | |
1300 | when <a href= | |
1301 | "configuration.html#ApplicationDefinedMacros">BOOST_STATECHART_USE_NATIVE_RTTI</a> | |
1302 | is <b>not</b> defined):</p> | |
1303 | ||
1304 | <ul> | |
1305 | <li><code>template< class T ><br> | |
1306 | static void simple_state<>::custom_static_type_ptr( const T * | |
1307 | );</code></li> | |
1308 | ||
1309 | <li><code>template< class T ><br> | |
1310 | static const T * | |
1311 | simple_state<>::custom_static_type_ptr();</code></li> | |
1312 | ||
1313 | <li><code>template< class T ><br> | |
1314 | const T * state_machine<>::<br> | |
1315 | state_base_type::custom_dynamic_type_ptr() const;</code></li> | |
1316 | </ul> | |
1317 | ||
1318 | <p>These allow us to directly associate arbitrary state type information | |
1319 | with each state ...</p> | |
1320 | <pre> | |
1321 | // ... | |
1322 | ||
1323 | int main() | |
1324 | { | |
1325 | NumLockOn::custom_static_type_ptr( "NumLockOn" ); | |
1326 | NumLockOff::custom_static_type_ptr( "NumLockOff" ); | |
1327 | CapsLockOn::custom_static_type_ptr( "CapsLockOn" ); | |
1328 | CapsLockOff::custom_static_type_ptr( "CapsLockOff" ); | |
1329 | ScrollLockOn::custom_static_type_ptr( "ScrollLockOn" ); | |
1330 | ScrollLockOff::custom_static_type_ptr( "ScrollLockOff" ); | |
1331 | ||
1332 | // ... | |
1333 | } | |
1334 | </pre> | |
1335 | ||
1336 | <p>... and rewrite the display function as follows:</p> | |
1337 | <pre> | |
1338 | void DisplayStateConfiguration( const Keyboard & kbd ) | |
1339 | { | |
1340 | char region = 'a'; | |
1341 | ||
1342 | for ( | |
1343 | Keyboard::state_iterator pLeafState = kbd.state_begin(); | |
1344 | pLeafState != kbd.state_end(); ++pLeafState ) | |
1345 | { | |
1346 | std::cout << "Orthogonal region " << region << ": "; | |
1347 | std::cout << | |
1348 | pLeafState->custom_dynamic_type_ptr< char >() << "\n"; | |
1349 | ++region; | |
1350 | } | |
1351 | } | |
1352 | </pre> | |
1353 | ||
1354 | <h3><a name="ExceptionHandling" id="ExceptionHandling">Exception | |
1355 | handling</a></h3> | |
1356 | ||
1357 | <p>Exceptions can be propagated from all user code except from state | |
1358 | destructors. Out of the box, the state machine framework is configured for | |
1359 | simple exception handling and does not catch any of these exceptions, so | |
1360 | they are immediately propagated to the state machine client. A scope guard | |
1361 | inside the <code>state_machine<></code> ensures that all state | |
1362 | objects are destructed before the exception is caught by the client. The | |
1363 | scope guard does not attempt to call any <code>exit</code> functions (see | |
1364 | <a href="#TwoStageExit">Two stage exit</a> below) that states might define | |
1365 | as these could themselves throw other exceptions which would mask the | |
1366 | original exception. Consequently, if a state machine should do something | |
1367 | more sensible when exceptions are thrown, it has to catch them before they | |
1368 | are propagated into the Boost.Statechart framework. This exception handling | |
1369 | scheme is often appropriate but it can lead to considerable code | |
1370 | duplication in state machines where many actions can trigger exceptions | |
1371 | that need to be handled inside the state machine (see <a href= | |
1372 | "rationale.html#ErrorHandling">Error handling</a> in the Rationale).<br> | |
1373 | That's why exception handling can be customized through the | |
1374 | <code>ExceptionTranslator</code> parameter of the | |
1375 | <code>state_machine</code> class template. Since the out-of-the box | |
1376 | behavior is to <b>not</b> translate any exceptions, the default argument | |
1377 | for this parameter is <code>null_exception_translator</code>. A | |
1378 | <code>state_machine<></code> subtype can be configured for advanced | |
1379 | exception handling by specifying the library-supplied | |
1380 | <code>exception_translator<></code> instead. This way, the following | |
1381 | happens when an exception is propagated from user code:</p> | |
1382 | ||
1383 | <ol> | |
1384 | <li>The exception is caught inside the framework</li> | |
1385 | ||
1386 | <li>In the catch block, an <code>exception_thrown</code> event is | |
1387 | allocated on the stack</li> | |
1388 | ||
1389 | <li>Also in the catch block, an <b>immediate</b> dispatch of the | |
1390 | <code>exception_thrown</code> event is attempted. That is, possibly | |
1391 | remaining events in the queue are dispatched only after the exception has | |
1392 | been handled successfully</li> | |
1393 | ||
1394 | <li>If the exception was handled successfully, the state machine returns | |
1395 | to the client normally. If the exception could not be handled | |
1396 | successfully, the original exception is rethrown so that the client of | |
1397 | the state machine can handle the exception</li> | |
1398 | </ol> | |
1399 | ||
1400 | <p>On platforms with buggy exception handling implementations users would | |
1401 | probably want to implement their own model of the <a href= | |
1402 | "reference.html#ExceptionTranslator">ExceptionTranslator concept</a> (see | |
1403 | also <a href="#DiscriminatingExceptions">Discriminating | |
1404 | exceptions</a>).</p> | |
1405 | ||
1406 | <h4>Successful exception handling</h4> | |
1407 | ||
1408 | <p>An exception is considered handled successfully, if:</p> | |
1409 | ||
1410 | <ul> | |
1411 | <li>an appropriate reaction for the <code>exception_thrown</code> event | |
1412 | has been found, <b>and</b></li> | |
1413 | ||
1414 | <li>the state machine is in a stable state after the reaction has | |
1415 | completed.</li> | |
1416 | </ul> | |
1417 | ||
1418 | <p>The second condition is important for scenarios 2 and 3 in the next | |
1419 | section. In these scenarios, the state machine is in the middle of a | |
1420 | transition when the exception is handled. The machine would be left in an | |
1421 | invalid state, should the reaction simply discard the event without doing | |
1422 | anything else. <code>exception_translator<></code> simply rethrows | |
1423 | the original exception if the exception handling was unsuccessful. Just as | |
1424 | with simple exception handling, in this case a scope guard inside the | |
1425 | <code>state_machine<></code> ensures that all state objects are | |
1426 | destructed before the exception is caught by the client.</p> | |
1427 | ||
1428 | <h4>Which states can react to an <code>exception_thrown</code> event?</h4> | |
1429 | ||
1430 | <p>Short answer: If the state machine is stable when the exception is | |
1431 | thrown, the state that caused the exception is first tried for a reaction. | |
1432 | Otherwise the outermost <a href="definitions.html#UnstableState">unstable | |
1433 | state</a> is first tried for a reaction.</p> | |
1434 | ||
1435 | <p>Longer answer: There are three scenarios:</p> | |
1436 | ||
1437 | <ol> | |
1438 | <li>A <code>react</code> member function propagates an exception | |
1439 | <b>before</b> calling any of the reaction functions or the action | |
1440 | executed during an in-state reaction propagates an exception. The state | |
1441 | that caused the exception is first tried for a reaction, so the following | |
1442 | machine will transit to Defective after receiving an EvStart event:<br> | |
1443 | <br> | |
1444 | <img alt="ThrowingInStateReaction" src="ThrowingInStateReaction.gif" | |
1445 | border="0" width="362" height="182"><br> | |
1446 | <br></li> | |
1447 | ||
1448 | <li>A state entry action (constructor) propagates an exception:<br> | |
1449 | ||
1450 | <ul> | |
1451 | <li>If there are no orthogonal regions, the direct outer state of the | |
1452 | state that caused the exception is first tried for a reaction, so the | |
1453 | following machine will transit to Defective after trying to enter | |
1454 | Stopped:<br> | |
1455 | <br> | |
1456 | <img alt="ThrowingEntryAction" src="ThrowingEntryAction.gif" border= | |
1457 | "0" width="438" height="241"><br></li> | |
1458 | ||
1459 | <li>If there are orthogonal regions, the outermost <a href= | |
1460 | "definitions.html#UnstableState">unstable state</a> is first tried | |
1461 | for a reaction. The outermost unstable state is found by first | |
1462 | selecting the direct outer state of the state that caused the | |
1463 | exception and then moving outward until a state is found that is | |
1464 | unstable but has no direct or indirect outer states that are | |
1465 | unstable. This more complex rule is necessary because only reactions | |
1466 | associated with the outermost unstable state (or any of its direct or | |
1467 | indirect outer states) are able to bring the machine back into a | |
1468 | stable state. Consider the following statechart:<br> | |
1469 | <br> | |
1470 | <img alt="OutermostUnstableState" src="OutermostUnstableState.gif" | |
1471 | border="0" width="467" height="572"><br> | |
1472 | <br> | |
1473 | Whether this state machine will ultimately transition to E or F after | |
1474 | initiation depends on which of the two orthogonal regions is | |
1475 | initiated first. If the upper orthogonal region is initiated first, | |
1476 | the entry sequence is as follows: A, D, B, (exception is thrown). | |
1477 | Both D and B were successfully entered, so B is the outermost | |
1478 | unstable state when the exception is thrown and the machine will | |
1479 | therefore transition to F. However, if the lower orthogonal region is | |
1480 | initiated first, the sequence is as follows: A, B, (exception is | |
1481 | thrown). D was never entered so A is the outermost unstable state | |
1482 | when the exception is thrown and the machine will therefore | |
1483 | transition to E.<br> | |
1484 | In practice these differences rarely matter as top-level error | |
1485 | recovery is adequate for most state machines. However, since the | |
1486 | sequence of initiation is clearly defined (orthogonal region 0 is | |
1487 | always initiated first, then region 1 and so forth), users <b>can</b> | |
1488 | accurately control when and where they want to handle | |
1489 | exceptions<br></li> | |
1490 | </ul> | |
1491 | </li> | |
1492 | ||
1493 | <li>A transition action propagates an exception: The innermost common | |
1494 | outer state of the source and the target state is first tried for a | |
1495 | reaction, so the following machine will transit to Defective after | |
1496 | receiving an EvStartStop event:<br> | |
1497 | <br> | |
1498 | <img alt="ThrowingTransitionAction" src="ThrowingTransitionAction.gif" | |
1499 | border="0" width="422" height="362"></li> | |
1500 | </ol> | |
1501 | ||
1502 | <p>As with a normal event, the dispatch algorithm will move outward to find | |
1503 | a reaction if the first tried state does not provide one (or if the | |
1504 | reaction explicitly returned <code>forward_event();</code>). However, <b>in | |
1505 | contrast to normal events, it will give up once it has unsuccessfully tried | |
1506 | an outermost state</b>, so the following machine will <b>not</b> transit to | |
1507 | Defective after receiving an EvNumLockPressed event:</p> | |
1508 | ||
1509 | <p><img alt="ExceptionsAndOrthStates" src="ExceptionsAndOrthStates.gif" | |
1510 | border="0" width="571" height="331"></p> | |
1511 | ||
1512 | <p>Instead, the machine is terminated and the original exception | |
1513 | rethrown.</p> | |
1514 | ||
1515 | <h4><a name="DiscriminatingExceptions" id= | |
1516 | "DiscriminatingExceptions">Discriminating exceptions</a></h4> | |
1517 | ||
1518 | <p>Because the <code>exception_thrown</code> event is dispatched from | |
1519 | within the catch block, we can rethrow and catch the exception in a custom | |
1520 | reaction:</p> | |
1521 | <pre> | |
1522 | struct Defective : sc::simple_state< | |
1523 | Defective, Purifier > {}; | |
1524 | ||
1525 | // Pretend this is a state deeply nested in the Purifier | |
1526 | // state machine | |
1527 | struct Idle : sc::simple_state< Idle, Purifier > | |
1528 | { | |
1529 | typedef mpl::list< | |
1530 | sc::custom_reaction< EvStart >, | |
1531 | sc::custom_reaction< sc::exception_thrown > | |
1532 | > reactions; | |
1533 | ||
1534 | sc::result react( const EvStart & ) | |
1535 | { | |
1536 | throw std::runtime_error( "" ); | |
1537 | } | |
1538 | ||
1539 | sc::result react( const sc::exception_thrown & ) | |
1540 | { | |
1541 | try | |
1542 | { | |
1543 | <b>throw;</b> | |
1544 | } | |
1545 | catch ( const std::runtime_error & ) | |
1546 | { | |
1547 | // only std::runtime_errors will lead to a transition | |
1548 | // to Defective ... | |
1549 | return transit< Defective >(); | |
1550 | } | |
1551 | catch ( ... ) | |
1552 | { | |
1553 | // ... all other exceptions are forwarded to our outer | |
1554 | // state(s). The state machine is terminated and the | |
1555 | // exception rethrown if the outer state(s) can't | |
1556 | // handle it either... | |
1557 | return forward_event(); | |
1558 | } | |
1559 | ||
1560 | // Alternatively, if we want to terminate the machine | |
1561 | // immediately, we can also either rethrow or throw | |
1562 | // a different exception. | |
1563 | } | |
1564 | }; | |
1565 | </pre> | |
1566 | ||
1567 | <p><b>Unfortunately, this idiom (using <code>throw;</code> inside a | |
1568 | <code>try</code> block nested inside a <code>catch</code> block) does not | |
1569 | work on at least one very popular compiler.</b> If you have to use one of | |
1570 | these platforms, you can pass a customized exception translator class to | |
1571 | the <code>state_machine</code> class template. This will allow you to | |
1572 | generate different events depending on the type of the exception.</p> | |
1573 | ||
1574 | <h4><a name="TwoStageExit" id="TwoStageExit">Two stage exit</a></h4> | |
1575 | ||
1576 | <p>If a <code>simple_state<></code> or <code>state<></code> | |
1577 | subtype declares a public member function with the signature <code>void | |
1578 | exit()</code> then this function is called just before the state object is | |
1579 | destructed. As explained under <a href="rationale.html#ErrorHandling">Error | |
1580 | handling</a> in the Rationale, this is useful for two things that would | |
1581 | otherwise be difficult or cumbersome to achieve with destructors only:</p> | |
1582 | ||
1583 | <ol> | |
1584 | <li>To signal a failure in an exit action</li> | |
1585 | ||
1586 | <li>To execute certain exit actions <b>only</b> during a transition or a | |
1587 | termination but not when the state machine object is destructed</li> | |
1588 | </ol> | |
1589 | ||
1590 | <p>A few points to consider before employing <code>exit()</code>:</p> | |
1591 | ||
1592 | <ul> | |
1593 | <li>There is no guarantee that <code>exit()</code> will be called: | |
1594 | ||
1595 | <ul> | |
1596 | <li>If the client destructs the state machine object without calling | |
1597 | <code>terminate()</code> beforehand then the currently active states | |
1598 | are destructed without calling <code>exit()</code>. This is necessary | |
1599 | because an exception that is possibly thrown from <code>exit()</code> | |
1600 | could not be propagated on to the state machine client</li> | |
1601 | ||
1602 | <li><code>exit()</code> is not called when a previously executed | |
1603 | action propagated an exception and that exception has not (yet) been | |
1604 | handled successfully. This is because a new exception that could | |
1605 | possibly be thrown from <code>exit()</code> would mask the original | |
1606 | exception</li> | |
1607 | </ul> | |
1608 | </li> | |
1609 | ||
1610 | <li>A state is considered exited, even if its <code>exit</code> function | |
1611 | propagated an exception. That is, the state object is inevitably | |
1612 | destructed right after calling <code>exit()</code>, regardless of whether | |
1613 | <code>exit()</code> propagated an exception or not. A state machine | |
1614 | configured for advanced exception handling is therefore always unstable | |
1615 | while handling an exception propagated from an <code>exit</code> | |
1616 | function</li> | |
1617 | ||
1618 | <li>In a state machine configured for advanced exception handling the | |
1619 | processing rules for an exception event resulting from an exception | |
1620 | propagated from <code>exit()</code> are analogous to the ones defined for | |
1621 | exceptions propagated from state constructors. That is, the outermost | |
1622 | unstable state is first tried for a reaction and the dispatcher then | |
1623 | moves outward until an appropriate reaction is found</li> | |
1624 | </ul> | |
1625 | ||
1626 | <h3><a name="SubmachinesAndParameterizedStates" id= | |
1627 | "SubmachinesAndParameterizedStates">Submachines & parameterized | |
1628 | states</a></h3> | |
1629 | ||
1630 | <p>Submachines are to event-driven programming what functions are to | |
1631 | procedural programming, reusable building blocks implementing often needed | |
1632 | functionality. The associated UML notation is not entirely clear to me. It | |
1633 | seems to be severely limited (e.g. the same submachine cannot appear in | |
1634 | different orthogonal regions) and does not seem to account for obvious | |
1635 | stuff like e.g. parameters.</p> | |
1636 | ||
1637 | <p>Boost.Statechart is completely unaware of submachines but they can be | |
1638 | implemented quite nicely with templates. Here, a submachine is used to | |
1639 | improve the copy-paste implementation of the keyboard machine discussed | |
1640 | under <a href="#OrthogonalStates">Orthogonal states</a>:</p> | |
1641 | <pre> | |
1642 | enum LockType | |
1643 | { | |
1644 | NUM_LOCK, | |
1645 | CAPS_LOCK, | |
1646 | SCROLL_LOCK | |
1647 | }; | |
1648 | ||
1649 | template< LockType lockType > | |
1650 | struct Off; | |
1651 | struct Active : sc::simple_state< | |
1652 | Active, Keyboard, mpl::list< | |
1653 | Off< NUM_LOCK >, Off< CAPS_LOCK >, Off< SCROLL_LOCK > > > {}; | |
1654 | ||
1655 | template< LockType lockType > | |
1656 | struct EvPressed : sc::event< EvPressed< lockType > > {}; | |
1657 | ||
1658 | template< LockType lockType > | |
1659 | struct On : sc::simple_state< | |
1660 | On< lockType >, Active::orthogonal< lockType > > | |
1661 | { | |
1662 | typedef sc::transition< | |
1663 | EvPressed< lockType >, Off< lockType > > reactions; | |
1664 | }; | |
1665 | ||
1666 | template< LockType lockType > | |
1667 | struct Off : sc::simple_state< | |
1668 | Off< lockType >, Active::orthogonal< lockType > > | |
1669 | { | |
1670 | typedef sc::transition< | |
1671 | EvPressed< lockType >, On< lockType > > reactions; | |
1672 | }; | |
1673 | </pre> | |
1674 | ||
1675 | <h3><a name="AsynchronousStateMachines" id= | |
1676 | "AsynchronousStateMachines">Asynchronous state machines</a></h3> | |
1677 | ||
1678 | <h4>Why asynchronous state machines are necessary</h4> | |
1679 | ||
1680 | <p>As the name suggests, a synchronous state machine processes each event | |
1681 | synchronously. This behavior is implemented by the | |
1682 | <code>state_machine</code> class template, whose <code>process_event</code> | |
1683 | function only returns after having executed all reactions (including the | |
1684 | ones provoked by internal events that actions might have posted). This | |
1685 | function is strictly non-reentrant (just like all other member functions, | |
1686 | so <code>state_machine<></code> is not thread-safe). This makes it | |
1687 | difficult for two <code>state_machine<></code> subtype objects to | |
1688 | communicate via events in a bi-directional fashion correctly, <b>even in a | |
1689 | single-threaded program</b>. For example, state machine <code>A</code> is | |
1690 | in the middle of processing an external event. Inside an action, it decides | |
1691 | to send a new event to state machine <code>B</code> (by calling | |
1692 | <code>B::process_event()</code>). It then "waits" for B to send back an | |
1693 | answer via a <code>boost::function<></code>-like call-back, which | |
1694 | references <code>A::process_event()</code> and was passed as a data member | |
1695 | of the event. However, while <code>A</code> is "waiting" for <code>B</code> | |
1696 | to send back an event, <code>A::process_event()</code> has not yet returned | |
1697 | from processing the external event and as soon as <code>B</code> answers | |
1698 | via the call-back, <code>A::process_event()</code> is <b>unavoidably</b> | |
1699 | reentered. This all really happens in a single thread, that's why "wait" is | |
1700 | in quotes.</p> | |
1701 | ||
1702 | <h4>How it works</h4> | |
1703 | ||
1704 | <p>The <code>asynchronous_state_machine</code> class template has none of | |
1705 | the member functions the <code>state_machine</code> class template has. | |
1706 | Moreover, <code>asynchronous_state_machine<></code> subtype objects | |
1707 | cannot even be created or destroyed directly. Instead, all these operations | |
1708 | must be performed through the <code>Scheduler</code> object each | |
1709 | asynchronous state machine is associated with. All these | |
1710 | <code>Scheduler</code> member functions only push an appropriate item into | |
1711 | the schedulers' queue and then return immediately. A dedicated thread will | |
1712 | later pop the items out of the queue to have them processed.</p> | |
1713 | ||
1714 | <p>Applications will usually first create a | |
1715 | <code>fifo_scheduler<></code> object and then call | |
1716 | <code>fifo_scheduler<>::create_processor<>()</code> and | |
1717 | <code>fifo_scheduler<>::initiate_processor()</code> to schedule the | |
1718 | creation and initiation of one or more | |
1719 | <code>asynchronous_state_machine<></code> subtype objects. Finally, | |
1720 | <code>fifo_scheduler<>::operator()()</code> is either called directly | |
1721 | to let the machine(s) run in the current thread, or, a | |
1722 | <code>boost::function<></code> object referencing | |
1723 | <code>operator()()</code> is passed to a new <code>boost::thread</code>. | |
1724 | Alternatively, the latter could also be done right after constructing the | |
1725 | <code>fifo_scheduler<></code> object. In the following code, we are | |
1726 | running one state machine in a new <code>boost::thread</code> and the other | |
1727 | in the main thread (see the PingPong example for the full source code):</p> | |
1728 | <pre> | |
1729 | struct Waiting; | |
1730 | struct Player : | |
1731 | sc::asynchronous_state_machine< Player, Waiting > | |
1732 | { | |
1733 | // ... | |
1734 | }; | |
1735 | ||
1736 | // ... | |
1737 | ||
1738 | int main() | |
1739 | { | |
1740 | // Create two schedulers that will wait for new events | |
1741 | // when their event queue runs empty | |
1742 | sc::fifo_scheduler<> scheduler1( true ); | |
1743 | sc::fifo_scheduler<> scheduler2( true ); | |
1744 | ||
1745 | // Each player is serviced by its own scheduler | |
1746 | sc::fifo_scheduler<>::processor_handle player1 = | |
1747 | scheduler1.create_processor< Player >( /* ... */ ); | |
1748 | scheduler1.initiate_processor( player1 ); | |
1749 | sc::fifo_scheduler<>::processor_handle player2 = | |
1750 | scheduler2.create_processor< Player >( /* ... */ ); | |
1751 | scheduler2.initiate_processor( player2 ); | |
1752 | ||
1753 | // the initial event that will start the game | |
1754 | boost::intrusive_ptr< BallReturned > pInitialBall = | |
1755 | new BallReturned(); | |
1756 | ||
1757 | // ... | |
1758 | ||
1759 | scheduler2.queue_event( player2, pInitialBall ); | |
1760 | ||
1761 | // ... | |
1762 | ||
1763 | // Up until here no state machines exist yet. They | |
1764 | // will be created when operator()() is called | |
1765 | ||
1766 | // Run first scheduler in a new thread | |
1767 | boost::thread otherThread( boost::bind( | |
1768 | &sc::fifo_scheduler<>::operator(), &scheduler1, 0 ) ); | |
1769 | scheduler2(); // Run second scheduler in this thread | |
1770 | otherThread.join(); | |
1771 | ||
1772 | return 0; | |
1773 | } | |
1774 | </pre> | |
1775 | ||
1776 | <p>We could just as well use two boost::threads:</p> | |
1777 | <pre> | |
1778 | int main() | |
1779 | { | |
1780 | // ... | |
1781 | ||
1782 | boost::thread thread1( boost::bind( | |
1783 | &sc::fifo_scheduler<>::operator(), &scheduler1, 0 ) ); | |
1784 | boost::thread thread2( boost::bind( | |
1785 | &sc::fifo_scheduler<>::operator(), &scheduler2, 0 ) ); | |
1786 | ||
1787 | // do something else ... | |
1788 | ||
1789 | thread1.join(); | |
1790 | thread2.join(); | |
1791 | ||
1792 | return 0; | |
1793 | } | |
1794 | </pre> | |
1795 | ||
1796 | <p>Or, run both machines in the same thread:</p> | |
1797 | <pre> | |
1798 | int main() | |
1799 | { | |
1800 | sc::fifo_scheduler<> scheduler1( true ); | |
1801 | ||
1802 | sc::fifo_scheduler<>::processor_handle player1 = | |
1803 | scheduler1.create_processor< Player >( /* ... */ ); | |
1804 | sc::fifo_scheduler<>::processor_handle player2 = | |
1805 | scheduler1.create_processor< Player >( /* ... */ ); | |
1806 | ||
1807 | // ... | |
1808 | ||
1809 | scheduler1(); | |
1810 | ||
1811 | return 0; | |
1812 | } | |
1813 | </pre> | |
1814 | ||
1815 | <p>In all the examples above, | |
1816 | <code>fifo_scheduler<>::operator()()</code> waits on an empty event | |
1817 | queue and will only return after a call to | |
1818 | <code>fifo_scheduler<>::terminate()</code>. The <code>Player</code> | |
1819 | state machine calls this function on its scheduler object right before | |
1820 | terminating.</p> | |
1821 | <hr> | |
1822 | ||
1823 | <p><a href="http://validator.w3.org/check?uri=referer"><img border="0" src= | |
1824 | "../../../doc/images/valid-html401.png" alt="Valid HTML 4.01 Transitional" | |
1825 | height="31" width="88"></a></p> | |
1826 | ||
1827 | <p>Revised | |
1828 | <!--webbot bot="Timestamp" s-type="EDITED" s-format="%d %B, %Y" startspan -->03 December, 2006<!--webbot bot="Timestamp" endspan i-checksum="38512" --></p> | |
1829 | ||
1830 | <p><i>Copyright © 2003-<!--webbot bot="Timestamp" s-type="EDITED" s-format="%Y" startspan -->2006<!--webbot bot="Timestamp" endspan i-checksum="770" --> | |
1831 | <a href="contact.html">Andreas Huber Dönni</a></i></p> | |
1832 | ||
1833 | <p><i>Distributed under the Boost Software License, Version 1.0. (See | |
1834 | accompanying file <a href="../../../LICENSE_1_0.txt">LICENSE_1_0.txt</a> or | |
1835 | copy at <a href= | |
1836 | "http://www.boost.org/LICENSE_1_0.txt">http://www.boost.org/LICENSE_1_0.txt</a>)</i></p> | |
1837 | </body> | |
1838 | </html> |