| 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 | "http://www.w3.org/Icons/valid-html401" 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> |
|---|