[29] | 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 - FAQ</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">Frequently Asked Questions (FAQs)</h2> |
---|
| 27 | </td> |
---|
| 28 | </tr> |
---|
| 29 | </table> |
---|
| 30 | <hr> |
---|
| 31 | |
---|
| 32 | <dl class="page-index"> |
---|
| 33 | <dt><a href="#StateLocalStorage">What's so cool about state-local |
---|
| 34 | storage?</a></dt> |
---|
| 35 | |
---|
| 36 | <dt><a href="#HideInnerWorkings">How can I hide the inner workings of a |
---|
| 37 | state machine from its clients?</a></dt> |
---|
| 38 | |
---|
| 39 | <dt><a href="#MachineInheritance">Is it possible to inherit from a given |
---|
| 40 | state machine and modify its layout in the subclass?</a></dt> |
---|
| 41 | |
---|
| 42 | <dt><a href="#Uml2">What about UML2.0 features?</a></dt> |
---|
| 43 | |
---|
| 44 | <dt><a href="#EmbeddedApplications">Is Boost.Statechart suitable for |
---|
| 45 | embedded applications?</a></dt> |
---|
| 46 | |
---|
| 47 | <dt><a href="#HardRealtime">Is your library suitable for applications |
---|
| 48 | with hard real-time requirements?</a></dt> |
---|
| 49 | |
---|
| 50 | <dt><a href="#TemplatedStates">With templated states I get an error that |
---|
| 51 | 'inner_context_type' is not defined. What's wrong?</a></dt> |
---|
| 52 | |
---|
| 53 | <dt><a href="#CompilerError">My compiler reports an error in the library |
---|
| 54 | code. Is this a bug in Boost.Statechart?</a></dt> |
---|
| 55 | |
---|
| 56 | <dt><a href="#Dll">How can I compile a state machine into a dynamic link |
---|
| 57 | library (DLL)?</a></dt> |
---|
| 58 | |
---|
| 59 | <dt><a href="#PolymorphicEvents">Does Boost.Statechart support |
---|
| 60 | polymorphic events?</a></dt> |
---|
| 61 | |
---|
| 62 | <dt><a href="#WrongExitActionOrder">Why are exit-actions called in the |
---|
| 63 | wrong order when I use multiple inheritance?</a></dt> |
---|
| 64 | </dl> |
---|
| 65 | |
---|
| 66 | <h2><a name="StateLocalStorage" id="StateLocalStorage">What's so cool about |
---|
| 67 | state-local storage?</a></h2> |
---|
| 68 | |
---|
| 69 | <p>This is best explained with an example:</p> |
---|
| 70 | <pre> |
---|
| 71 | struct Active; |
---|
| 72 | struct Stopped; |
---|
| 73 | struct Running; |
---|
| 74 | struct StopWatch : sc::state_machine< StopWatch, Active > |
---|
| 75 | { |
---|
| 76 | // startTime_ remains uninitialized, because there is no reasonable default |
---|
| 77 | StopWatch() : elapsedTime_( 0.0 ) {} |
---|
| 78 | ~StopWatch() |
---|
| 79 | { |
---|
| 80 | terminate(); |
---|
| 81 | } |
---|
| 82 | |
---|
| 83 | double ElapsedTime() const |
---|
| 84 | { |
---|
| 85 | // Ugly switch over the current state. |
---|
| 86 | if ( state_cast< const Stopped * >() != 0 ) |
---|
| 87 | { |
---|
| 88 | return elapsedTime_; |
---|
| 89 | } |
---|
| 90 | else if ( state_cast< const Running * >() != 0 ) |
---|
| 91 | { |
---|
| 92 | return elapsedTime_ + std::difftime( std::time( 0 ), startTime_ ); |
---|
| 93 | } |
---|
| 94 | else // we're terminated |
---|
| 95 | { |
---|
| 96 | throw std::bad_cast(); |
---|
| 97 | } |
---|
| 98 | } |
---|
| 99 | |
---|
| 100 | // elapsedTime_ is only meaningful when the machine is not terminated |
---|
| 101 | double elapsedTime_; |
---|
| 102 | // startTime_ is only meaningful when the machine is in Running |
---|
| 103 | std::time_t startTime_; |
---|
| 104 | }; |
---|
| 105 | |
---|
| 106 | struct Active : sc::state< Active, StopWatch, Stopped > |
---|
| 107 | { |
---|
| 108 | typedef sc::transition< EvReset, Active > reactions; |
---|
| 109 | |
---|
| 110 | Active( my_context ctx ) : my_base( ctx ) |
---|
| 111 | { |
---|
| 112 | outermost_context().elapsedTime_ = 0.0; |
---|
| 113 | } |
---|
| 114 | }; |
---|
| 115 | |
---|
| 116 | struct Running : sc::state< Running, Active > |
---|
| 117 | { |
---|
| 118 | typedef sc::transition< EvStartStop, Stopped > reactions; |
---|
| 119 | |
---|
| 120 | Running( my_context ctx ) : my_base( ctx ) |
---|
| 121 | { |
---|
| 122 | outermost_context().startTime_ = std::time( 0 ); |
---|
| 123 | } |
---|
| 124 | |
---|
| 125 | ~Running() |
---|
| 126 | { |
---|
| 127 | outermost_context().elapsedTime_ += |
---|
| 128 | std::difftime( std::time( 0 ), outermost_context().startTime_ ); |
---|
| 129 | } |
---|
| 130 | }; |
---|
| 131 | |
---|
| 132 | struct Stopped : sc::simple_state< Stopped, Active > |
---|
| 133 | { |
---|
| 134 | typedef sc::transition< EvStartStop, Running > reactions; |
---|
| 135 | }; |
---|
| 136 | </pre> |
---|
| 137 | |
---|
| 138 | <p>This StopWatch does not make any use of state-local storage while |
---|
| 139 | implementing the same behavior as the <a href= |
---|
| 140 | "tutorial.html#BasicTopicsAStopWatch">tutorial StopWatch</a>. Even though |
---|
| 141 | this code is probably easier to read for the untrained eye, it does have a |
---|
| 142 | few problems that are absent in the original:</p> |
---|
| 143 | |
---|
| 144 | <ul> |
---|
| 145 | <li>This StopWatch class has data members that have a meaningful value |
---|
| 146 | only if the state machine happens to be in a certain state. That is, the |
---|
| 147 | lifetimes of these variables are not identical with the one of the |
---|
| 148 | StopWatch object containing them. Since the lifetimes are managed by the |
---|
| 149 | entry and exit actions of states, we need to use an ugly switch over the |
---|
| 150 | current state (see <code>StopWatch::ElapsedTime()</code>) if we want to |
---|
| 151 | access them from a context where the current state is unclear. This |
---|
| 152 | essentially duplicates some of the state logic of the FSM. Therefore, |
---|
| 153 | whenever we need to change the layout of the state machine we will likely |
---|
| 154 | also need to change the ugly switch. Even worse, if we forget to change |
---|
| 155 | the switch, the code will probably still compile and maybe even silently |
---|
| 156 | do the wrong thing. Note that this is impossible with the version in the |
---|
| 157 | tutorial, which will at least throw an exception and often just refuse to |
---|
| 158 | compile. Moreover, for the tutorial StopWatch there's a much higher |
---|
| 159 | chance that a programmer will get a change correct the first time since |
---|
| 160 | the code that calculates the elapsed time is located close to the code |
---|
| 161 | that updates the variables</li> |
---|
| 162 | |
---|
| 163 | <li>We need to change the StopWatch class whenever we want to introduce a |
---|
| 164 | new variable or change the type of an already existing variable. That is, |
---|
| 165 | many changes in the FSM will likely lead to a change in the StopWatch |
---|
| 166 | class. In all FSMs that do not employ state-local storage, the |
---|
| 167 | <code>state_machine<></code> subtype will therefore be a change |
---|
| 168 | hotspot, which is a pretty sure indicator for a bad design</li> |
---|
| 169 | </ul> |
---|
| 170 | |
---|
| 171 | <p>Both points are not much of a problem in a small example like this, |
---|
| 172 | which can easily be implemented in a single translation unit by a single |
---|
| 173 | programmer. However, they quickly become a major problem for a big complex |
---|
| 174 | machine spread over multiple translation units, which are possibly even |
---|
| 175 | maintained by different programmers.</p> |
---|
| 176 | |
---|
| 177 | <h2><a name="HideInnerWorkings" id="HideInnerWorkings">How can I hide the |
---|
| 178 | inner workings of a state machine from its clients?</a></h2> |
---|
| 179 | |
---|
| 180 | <p>To see why and how this is possible it is important to recall the |
---|
| 181 | following facts:</p> |
---|
| 182 | |
---|
| 183 | <ul> |
---|
| 184 | <li>Member functions of a C++ class template are instantiated at the |
---|
| 185 | point where they're actually called. If the function is never called, it |
---|
| 186 | will not be instantiated and not a single assembly instruction will ever |
---|
| 187 | be generated</li> |
---|
| 188 | |
---|
| 189 | <li>The <code>InitialState</code> template parameter of |
---|
| 190 | <code>sc::state_machine</code> can be an incomplete type (i.e. forward |
---|
| 191 | declared)</li> |
---|
| 192 | </ul> |
---|
| 193 | |
---|
| 194 | <p>The class template member function |
---|
| 195 | <code>state_machine<>::initiate()</code> creates an object of the |
---|
| 196 | initial state. So, the definition of this state must be known before the |
---|
| 197 | compiler reaches the point where <code>initiate()</code> is called. To be |
---|
| 198 | able to hide the initial state of a state machine in a .cpp file we must |
---|
| 199 | therefore no longer let clients call <code>initiate()</code>. Instead, we |
---|
| 200 | do so in the .cpp file, at a point where the full definition of the initial |
---|
| 201 | state is known.</p> |
---|
| 202 | |
---|
| 203 | <p>Example:</p> |
---|
| 204 | |
---|
| 205 | <p>StopWatch.hpp:</p> |
---|
| 206 | <pre> |
---|
| 207 | // define events ... |
---|
| 208 | |
---|
| 209 | struct Active; // the only visible forward |
---|
| 210 | struct StopWatch : sc::state_machine< StopWatch, Active > |
---|
| 211 | { |
---|
| 212 | StopWatch(); |
---|
| 213 | }; |
---|
| 214 | </pre> |
---|
| 215 | |
---|
| 216 | <p>StopWatch.cpp:</p> |
---|
| 217 | <pre> |
---|
| 218 | struct Stopped; |
---|
| 219 | struct Active : sc::simple_state< Active, StopWatch, Stopped > |
---|
| 220 | { |
---|
| 221 | typedef sc::transition< EvReset, Active > reactions; |
---|
| 222 | }; |
---|
| 223 | |
---|
| 224 | struct Running : sc::simple_state< Running, Active > |
---|
| 225 | { |
---|
| 226 | typedef sc::transition< EvStartStop, Stopped > reactions; |
---|
| 227 | }; |
---|
| 228 | |
---|
| 229 | struct Stopped : sc::simple_state< Stopped, Active > |
---|
| 230 | { |
---|
| 231 | typedef sc::transition< EvStartStop, Running > reactions; |
---|
| 232 | }; |
---|
| 233 | |
---|
| 234 | StopWatch::StopWatch() |
---|
| 235 | { |
---|
| 236 | // For example, we might want to ensure that the state |
---|
| 237 | // machine is already started after construction. |
---|
| 238 | // Alternatively, we could add our own initiate() function |
---|
| 239 | // to StopWatch and call the base class initiate() in the |
---|
| 240 | // implementation. |
---|
| 241 | <b>initiate();</b> |
---|
| 242 | } |
---|
| 243 | </pre> |
---|
| 244 | |
---|
| 245 | <h2><a name="MachineInheritance" id="MachineInheritance">Is it possible to |
---|
| 246 | inherit from a given state machine and modify its layout in the |
---|
| 247 | subclass?</a></h2> |
---|
| 248 | |
---|
| 249 | <p>Yes, but contrary to what some FSM code generators allow, |
---|
| 250 | Boost.Statechart machines can do so only in a way that was foreseen by the |
---|
| 251 | designer of the base state machine:</p> |
---|
| 252 | <pre> |
---|
| 253 | struct EvStart : sc::event< EvStart > {}; |
---|
| 254 | |
---|
| 255 | struct Idle; |
---|
| 256 | struct PumpBase : sc::state_machine< PumpBase, Idle > |
---|
| 257 | { |
---|
| 258 | <b>virtual sc::result react( |
---|
| 259 | </b> <b>Idle & idle, const EvStart & ) const; |
---|
| 260 | </b>}; |
---|
| 261 | |
---|
| 262 | struct Idle : sc::simple_state< Idle, PumpBase > |
---|
| 263 | { |
---|
| 264 | typedef sc::custom_reaction< EvStart > reactions; |
---|
| 265 | |
---|
| 266 | sc::result react( const EvStart & evt ) |
---|
| 267 | { |
---|
| 268 | <b>return context< PumpBase >().react( *this, evt );</b> |
---|
| 269 | } |
---|
| 270 | }; |
---|
| 271 | |
---|
| 272 | struct Running : sc::simple_state< Running, PumpBase > {}; |
---|
| 273 | |
---|
| 274 | sc::result PumpBase::react( |
---|
| 275 | Idle & idle, const EvStart & ) const |
---|
| 276 | { |
---|
| 277 | <b>return idle.transit< Running >(); |
---|
| 278 | </b>} |
---|
| 279 | |
---|
| 280 | |
---|
| 281 | struct MyRunning : sc::simple_state< MyRunning, PumpBase > {}; |
---|
| 282 | |
---|
| 283 | struct MyPump : PumpBase |
---|
| 284 | { |
---|
| 285 | virtual sc::result react( |
---|
| 286 | Idle & idle, const EvStart & ) const |
---|
| 287 | { |
---|
| 288 | <b>return idle.transit< MyRunning >(); |
---|
| 289 | </b> } |
---|
| 290 | }; |
---|
| 291 | </pre> |
---|
| 292 | |
---|
| 293 | <h2><a name="Uml2" id="Uml2">What about UML 2.0 features?</a></h2> |
---|
| 294 | |
---|
| 295 | <p>The library was designed before 2.0 came along. Therefore, if not |
---|
| 296 | explicitly noted otherwise, the library implements the behavior mandated by |
---|
| 297 | the UML1.5 standard. Here's an incomplete list of differences between the |
---|
| 298 | 2.0 semantics & Boost.Statechart semantics:</p> |
---|
| 299 | |
---|
| 300 | <ul> |
---|
| 301 | <li>All transitions are always external. Local transitions are not |
---|
| 302 | supported at all. Unfortunately, the UML2.0 specifications are not |
---|
| 303 | entirely clear how local transitions are supposed to work, see <a href= |
---|
| 304 | "http://thread.gmane.org/gmane.comp.lib.boost.user/18641">here</a> for |
---|
| 305 | more information</li> |
---|
| 306 | |
---|
| 307 | <li>There is no direct support for the UML2.0 elements entry point and |
---|
| 308 | exit point. However, both can easily be simulated, the former with a |
---|
| 309 | typedef and the latter with a state that is a template (with the |
---|
| 310 | transition destination as a template parameter)</li> |
---|
| 311 | </ul> |
---|
| 312 | |
---|
| 313 | <h2><a name="EmbeddedApplications" id="EmbeddedApplications">Is |
---|
| 314 | Boost.Statechart suitable for embedded applications?</a></h2> |
---|
| 315 | |
---|
| 316 | <p>It depends. As explained under <a href= |
---|
| 317 | "performance.html#SpeedVersusScalabilityTradeoffs">Speed versus scalability |
---|
| 318 | tradeoffs</a> on the Performance page, the virtually limitless scalability |
---|
| 319 | offered by this library does have its price. Especially small and simple |
---|
| 320 | FSMs can easily be implemented so that they consume fewer cycles and less |
---|
| 321 | memory and occupy less code space in the executable. Here are some |
---|
| 322 | obviously <b>very rough</b> estimates:</p> |
---|
| 323 | |
---|
| 324 | <ul> |
---|
| 325 | <li>For a state machine with at most one simultaneously active state |
---|
| 326 | (that is, the machine is flat and does not have orthogonal regions) with |
---|
| 327 | trivial actions, customized memory management and compiled with a good |
---|
| 328 | optimizing compiler, a Pentium 4 class CPU should not spend more than |
---|
| 329 | 1000 cycles inside <code>state_machine<>::process_event()</code>. |
---|
| 330 | This worst-case time to process one event scales more or less linearly |
---|
| 331 | with the number of simultaneously active states for more complex state |
---|
| 332 | machines, with the typical average being much lower than that. So, a |
---|
| 333 | fairly complex machine with at most 10 simultaneously active states |
---|
| 334 | running on a 100MHz CPU should be able to process more than 10'000 events |
---|
| 335 | per second</li> |
---|
| 336 | |
---|
| 337 | <li>A single state machine object uses typically less than 1KB of memory, |
---|
| 338 | even if it implements a very complex machine</li> |
---|
| 339 | |
---|
| 340 | <li>For code size, it is difficult to give a concrete guideline but tests |
---|
| 341 | with the BitMachine example suggest that code size scales more or less |
---|
| 342 | linearly with the number of states (transitions seem to have only little |
---|
| 343 | impact). When compiled with MSVC7.1 on Windows, 32 states and 224 |
---|
| 344 | transitions seem to fit in ~108KB executable code (with all optimizations |
---|
| 345 | turned on).<br> |
---|
| 346 | Moreover, the library can be compiled with C++ RTTI and exception |
---|
| 347 | handling turned off, resulting in significant savings on most |
---|
| 348 | platforms</li> |
---|
| 349 | </ul> |
---|
| 350 | |
---|
| 351 | <p>As mentioned above, these are very rough estimates derived from the use |
---|
| 352 | of the library on a desktop PC, so they should only be used to decide |
---|
| 353 | whether there is a point in making your own performance tests on your |
---|
| 354 | target platform.</p> |
---|
| 355 | |
---|
| 356 | <h2><a name="HardRealtime" id="HardRealtime">Is your library suitable for |
---|
| 357 | applications with hard real-time requirements?</a></h2> |
---|
| 358 | |
---|
| 359 | <p>Yes. Out of the box, the only operations taking potentially |
---|
| 360 | non-deterministic time that the library performs are calls to |
---|
| 361 | <code>std::allocator<></code> member functions and |
---|
| 362 | <code>dynamic_cast</code>s. <code>std::allocator<></code> member |
---|
| 363 | function calls can be avoided by passing a custom allocator to |
---|
| 364 | <code>event<></code>, <code>state_machine<></code>, |
---|
| 365 | <code>asynchronous_state_machine<></code>, |
---|
| 366 | <code>fifo_scheduler<></code> and <code>fifo_worker<></code>. |
---|
| 367 | <code>dynamic_cast</code>s can be avoided by not calling the |
---|
| 368 | <code>state_cast<></code> member functions of |
---|
| 369 | <code>state_machine<></code>, <code>simple_state<></code> and |
---|
| 370 | <code>state<></code> but using the deterministic variant |
---|
| 371 | <code>state_downcast<></code> instead.</p> |
---|
| 372 | |
---|
| 373 | <h2><a name="TemplatedStates" id="TemplatedStates">With templated states I |
---|
| 374 | get an error that 'inner_context_type' is not defined. What's |
---|
| 375 | wrong?</a></h2> |
---|
| 376 | |
---|
| 377 | <p>The following code generates such an error:</p> |
---|
| 378 | <pre> |
---|
| 379 | #include <boost/statechart/state_machine.hpp> |
---|
| 380 | #include <boost/statechart/simple_state.hpp> |
---|
| 381 | |
---|
| 382 | namespace sc = boost::statechart; |
---|
| 383 | |
---|
| 384 | template< typename X > struct A; |
---|
| 385 | struct Machine : sc::state_machine< Machine, A< int > > {}; |
---|
| 386 | |
---|
| 387 | template< typename X > struct B; |
---|
| 388 | template< typename X > |
---|
| 389 | struct A : sc::simple_state< A< X >, Machine, B< X > > {}; |
---|
| 390 | |
---|
| 391 | template< typename X > |
---|
| 392 | struct B : sc::simple_state< B< X >, A< X > > {}; |
---|
| 393 | |
---|
| 394 | int main() |
---|
| 395 | { |
---|
| 396 | Machine machine; |
---|
| 397 | machine.initiate(); |
---|
| 398 | return 0; |
---|
| 399 | } |
---|
| 400 | </pre> |
---|
| 401 | |
---|
| 402 | <p>If the templates <code>A</code> and <code>B</code> are replaced with |
---|
| 403 | normal types, the above code compiles without errors. This is rooted in the |
---|
| 404 | fact that C++ treats forward-declared templates differently than |
---|
| 405 | forward-declared types. Namely, the compiler tries to access member |
---|
| 406 | typedefs of <code>B< X ></code> at a point where the template has not |
---|
| 407 | yet been defined. Luckily, this can easily be avoided by putting all inner |
---|
| 408 | initial state arguments in an <code>mpl::list<></code>, as |
---|
| 409 | follows:</p> |
---|
| 410 | <pre> |
---|
| 411 | struct A : sc::simple_state< |
---|
| 412 | A< X >, Machine, mpl::list< B< X > > > {}; |
---|
| 413 | </pre> |
---|
| 414 | |
---|
| 415 | <p>See <a href= |
---|
| 416 | "http://article.gmane.org/gmane.comp.lib.boost.devel/128741">this post</a> |
---|
| 417 | for technical details.</p> |
---|
| 418 | |
---|
| 419 | <h2><a name="CompilerError" id="CompilerError">My compiler reports an error |
---|
| 420 | in the library code. Is this a bug in Boost.Statechart?</a></h2> |
---|
| 421 | |
---|
| 422 | <p>Probably not. There are several possible reasons for such compile-time |
---|
| 423 | errors:</p> |
---|
| 424 | |
---|
| 425 | <ol> |
---|
| 426 | <li>Your compiler is too buggy to compile the library, see <a href= |
---|
| 427 | "index.html#SupportedPlatforms">here</a> for information on the status of |
---|
| 428 | your compiler. If you absolutely must use such a compiler for your |
---|
| 429 | project, I'm afraid Boost.Statechart is not for you.</li> |
---|
| 430 | |
---|
| 431 | <li>The error is reported on a line similar to the following: |
---|
| 432 | <pre> |
---|
| 433 | BOOST_STATIC_ASSERT( ( mpl::less< |
---|
| 434 | orthogonal_position, |
---|
| 435 | typename context_type::no_of_orthogonal_regions >::value ) ); |
---|
| 436 | </pre>Most probably, there is an error in your code. The library has many |
---|
| 437 | such compile-time assertions to ensure that invalid state machines cannot be |
---|
| 438 | compiled (for an idea what kinds of errors are reported at compile time, see |
---|
| 439 | the compile-fail tests). Above each of these assertions there is a comment |
---|
| 440 | explaining the problem. On almost all current compilers an error in template |
---|
| 441 | code is accompanied by the current "instantiation stack". Very much like the |
---|
| 442 | call stack you see in the debugger, this "instantiation stack" allows you to |
---|
| 443 | trace the error back through instantiations of library code until you hit the |
---|
| 444 | line of your code that causes the problem. As an example, here's the MSVC7.1 |
---|
| 445 | error message for the code in InconsistentHistoryTest1.cpp: |
---|
| 446 | <pre> |
---|
| 447 | ...\boost\statechart\shallow_history.hpp(34) : error C2027: use of undefined type 'boost::STATIC_ASSERTION_FAILURE<x>' |
---|
| 448 | with |
---|
| 449 | [ |
---|
| 450 | x=false |
---|
| 451 | ] |
---|
| 452 | ...\boost\statechart\shallow_history.hpp(34) : see reference to class template instantiation 'boost::STATIC_ASSERTION_FAILURE<x>' being compiled |
---|
| 453 | with |
---|
| 454 | [ |
---|
| 455 | x=false |
---|
| 456 | ] |
---|
| 457 | ...\boost\statechart\simple_state.hpp(861) : see reference to class template instantiation 'boost::statechart::shallow_history<DefaultState>' being compiled |
---|
| 458 | with |
---|
| 459 | [ |
---|
| 460 | DefaultState=B |
---|
| 461 | ] |
---|
| 462 | ...\boost\statechart\simple_state.hpp(599) : see reference to function template instantiation 'void boost::statechart::simple_state<MostDerived,Context,InnerInitial>::deep_construct_inner_impl_non_empty::deep_construct_inner_impl<InnerList>(const boost::statechart::simple_state<MostDerived,Context,InnerInitial>::inner_context_ptr_type &,boost::statechart::simple_state<MostDerived,Context,InnerInitial>::outermost_context_base_type &)' being compiled |
---|
| 463 | with |
---|
| 464 | [ |
---|
| 465 | MostDerived=A, |
---|
| 466 | Context=InconsistentHistoryTest, |
---|
| 467 | InnerInitial=boost::mpl::list<boost::statechart::shallow_history<B>>, |
---|
| 468 | InnerList=boost::statechart::simple_state<A,InconsistentHistoryTest,boost::mpl::list<boost::statechart::shallow_history<B>>>::inner_initial_list |
---|
| 469 | ] |
---|
| 470 | ...\boost\statechart\simple_state.hpp(567) : see reference to function template instantiation 'void boost::statechart::simple_state<MostDerived,Context,InnerInitial>::deep_construct_inner<boost::statechart::simple_state<MostDerived,Context,InnerInitial>::inner_initial_list>(const boost::statechart::simple_state<MostDerived,Context,InnerInitial>::inner_context_ptr_type &,boost::statechart::simple_state<MostDerived,Context,InnerInitial>::outermost_context_base_type &)' being compiled |
---|
| 471 | with |
---|
| 472 | [ |
---|
| 473 | MostDerived=A, |
---|
| 474 | Context=InconsistentHistoryTest, |
---|
| 475 | InnerInitial=boost::mpl::list<boost::statechart::shallow_history<B>> |
---|
| 476 | ] |
---|
| 477 | ...\boost\statechart\simple_state.hpp(563) : while compiling class-template member function 'void boost::statechart::simple_state<MostDerived,Context,InnerInitial>::deep_construct(const boost::statechart::simple_state<MostDerived,Context,InnerInitial>::context_ptr_type & ,boost::statechart::simple_state<MostDerived,Context,InnerInitial>::outermost_context_base_type &)' |
---|
| 478 | with |
---|
| 479 | [ |
---|
| 480 | MostDerived=A, |
---|
| 481 | Context=InconsistentHistoryTest, |
---|
| 482 | InnerInitial=boost::mpl::list<boost::statechart::shallow_history<B>> |
---|
| 483 | ] |
---|
| 484 | ...\libs\statechart\test\InconsistentHistoryTest1.cpp(29) : see reference to class template instantiation 'boost::statechart::simple_state<MostDerived,Context,InnerInitial>' being compiled |
---|
| 485 | with |
---|
| 486 | [ |
---|
| 487 | MostDerived=A, |
---|
| 488 | Context=InconsistentHistoryTest, |
---|
| 489 | InnerInitial=boost::mpl::list<boost::statechart::shallow_history<B>> |
---|
| 490 | ] |
---|
| 491 | </pre>Depending on the IDE you use, it is possible that you need to switch to |
---|
| 492 | another window to see this full error message (e.g. for Visual Studio 2003, |
---|
| 493 | you need to switch to the Output window). Starting at the top and going down |
---|
| 494 | the list of instantiations you see that each of them is accompanied by a file |
---|
| 495 | name and a line number. Ignoring all files belonging to the library, we find |
---|
| 496 | the culprit close to the bottom in file InconsistentHistoryTest1.cpp on line |
---|
| 497 | 29. |
---|
| 498 | </li> |
---|
| 499 | |
---|
| 500 | <li>The error is reported on a line nowhere near a BOOST_STATIC_ASSERT. |
---|
| 501 | Use the technique described under point 2 to see what line of your code |
---|
| 502 | causes the problem. If your code is correct then you've found a bug in |
---|
| 503 | either the compiler or Boost.Statechart. Please <a href= |
---|
| 504 | "contact.html">send me</a> a small but complete program showing the |
---|
| 505 | problem. Thank you!</li> |
---|
| 506 | </ol> |
---|
| 507 | |
---|
| 508 | <h2><a name="Dll" id="Dll">How can I compile a state machine into a dynamic |
---|
| 509 | link library (DLL)?</a></h2> |
---|
| 510 | |
---|
| 511 | <p>Invisible to the user, the library uses static data members to implement |
---|
| 512 | its own speed-optimized RTTI-mechanism for <code>event<></code> and |
---|
| 513 | <code>simple_state<></code> subtypes. Whenever such a subtype is |
---|
| 514 | defined in a header file and then included in multiple TUs, the linker |
---|
| 515 | later needs to eliminate the duplicate definitions of static data members. |
---|
| 516 | This usually works flawlessly as long as all these TUs are |
---|
| 517 | <b>statically</b> linked into the same binary. It is a lot more complex |
---|
| 518 | when DLLs are involved. The TuTest*.?pp files illustrate this:</p> |
---|
| 519 | |
---|
| 520 | <ul> |
---|
| 521 | <li><a href="../test/TuTest.hpp">TuTest.hpp</a>: Instantiates a class |
---|
| 522 | template containing a static data member</li> |
---|
| 523 | |
---|
| 524 | <li><a href="../test/TuTest.cpp">TuTest.cpp</a>: Includes TuTest.hpp and |
---|
| 525 | is compiled into a DLL</li> |
---|
| 526 | |
---|
| 527 | <li><a href="../test/TuTestMain.cpp">TuTestMain.cpp</a>: Includes |
---|
| 528 | TuTest.hpp and is compiled into an executable</li> |
---|
| 529 | </ul> |
---|
| 530 | |
---|
| 531 | <p>Without any precautions (e.g. <code>__declspec(dllexport)</code> on MSVC |
---|
| 532 | compatible compilers), on most platforms both binaries (exe & dll) now |
---|
| 533 | contain their own instance of the static data member. Since the RTTI |
---|
| 534 | mechanism assumes that there is exactly one object of that member at |
---|
| 535 | runtime, the mechanism fails spectacularly when the process running the exe |
---|
| 536 | also loads the dll. Different platforms deal differently with this |
---|
| 537 | problem:</p> |
---|
| 538 | |
---|
| 539 | <ul> |
---|
| 540 | <li>On some platforms (e.g. MinGW) there simply doesn't seem to be a way |
---|
| 541 | to enforce that such a member only exists once at runtime. Therefore, the |
---|
| 542 | internal RTTI mechanism cannot be used reliably in conjunction with DLLs. |
---|
| 543 | Disabling it by defining <a href= |
---|
| 544 | "configuration.html#ApplicationDefinedMacros">BOOST_STATECHART_USE_NATIVE_RTTI</a> |
---|
| 545 | in all TUs will <b>usually</b> work around the problem</li> |
---|
| 546 | |
---|
| 547 | <li>MSVC-compatible compilers support <code>__declspec(dllimport)</code> |
---|
| 548 | and <code>__declspec(dllexport)</code>, which allow to define exactly |
---|
| 549 | what needs to be loaded from a DLL (see TuTest for an example how to do |
---|
| 550 | this). Therefore, the internal RTTI mechanism can be used but care must |
---|
| 551 | be taken to correctly export and import all <code>event<></code> |
---|
| 552 | and <code>simple_state<></code> subtypes defined in headers that |
---|
| 553 | are compiled into more than one binary. Alternatively, of course <a href= |
---|
| 554 | "configuration.html#ApplicationDefinedMacros">BOOST_STATECHART_USE_NATIVE_RTTI</a> |
---|
| 555 | can also be used to save the work of importing and exporting</li> |
---|
| 556 | </ul> |
---|
| 557 | |
---|
| 558 | <h2><a name="PolymorphicEvents" id="PolymorphicEvents">Does |
---|
| 559 | Boost.Statechart support polymorphic events?</a></h2> |
---|
| 560 | |
---|
| 561 | <p>No. Although events can be derived from each other to write common code |
---|
| 562 | only once, <a href="definitions.html#Reaction">reactions</a> can only be |
---|
| 563 | defined for most-derived events.</p> |
---|
| 564 | |
---|
| 565 | <p>Example:</p> |
---|
| 566 | <pre> |
---|
| 567 | template< class MostDerived > |
---|
| 568 | struct EvButtonPressed : sc::event< MostDerived > |
---|
| 569 | { |
---|
| 570 | // common code |
---|
| 571 | }; |
---|
| 572 | |
---|
| 573 | struct EvPlayButtonPressed : |
---|
| 574 | EvButtonPressed< EvPlayButtonPressed > {}; |
---|
| 575 | struct EvStopButtonPressed : |
---|
| 576 | EvButtonPressed< EvStopButtonPressed > {}; |
---|
| 577 | struct EvForwardButtonPressed : |
---|
| 578 | EvButtonPressed< EvForwardButtonPressed > {}; |
---|
| 579 | |
---|
| 580 | /* ... */ |
---|
| 581 | |
---|
| 582 | // We want to turn the player on, no matter what button we |
---|
| 583 | // press in the Off state. Although we can write the reaction |
---|
| 584 | // code only once, we must mention all most-derived events in |
---|
| 585 | // the reaction list. |
---|
| 586 | struct Off : sc::simple_state< Off, Mp3Player > |
---|
| 587 | { |
---|
| 588 | typedef mpl::list< |
---|
| 589 | sc::custom_reaction< EvPlayButtonPressed >, |
---|
| 590 | sc::custom_reaction< EvStopButtonPressed >, |
---|
| 591 | sc::custom_reaction< EvForwardButtonPressed > |
---|
| 592 | > reactions; |
---|
| 593 | |
---|
| 594 | template< class MostDerived > |
---|
| 595 | sc::result react( const EvButtonPressed< MostDerived > & ) |
---|
| 596 | { |
---|
| 597 | // ... |
---|
| 598 | } |
---|
| 599 | }; |
---|
| 600 | </pre> |
---|
| 601 | |
---|
| 602 | <h2><a name="WrongExitActionOrder" id="WrongExitActionOrder">Why are |
---|
| 603 | exit-actions called in the wrong order when I use multiple |
---|
| 604 | inheritance?</a></h2> |
---|
| 605 | |
---|
| 606 | <p><b>Update</b>: The implementation has changed considerably in this area. |
---|
| 607 | It is still possible to get this behavior under rare circumstances (when an |
---|
| 608 | action propagates an exception in a state machine with orthogonal regions |
---|
| 609 | <b>and</b> if the statechart layout satisfies certain conditions), but it |
---|
| 610 | can no longer be demonstrated with the example program below. However, the |
---|
| 611 | described workaround is still valid and ensures that this behavior will |
---|
| 612 | never show up.</p> |
---|
| 613 | |
---|
| 614 | <p>They definitely aren't for the <code>simple_state<></code> and |
---|
| 615 | <code>state<></code> subtypes, but the destructors of additional |
---|
| 616 | bases might be called in construction order (rather than the reverse |
---|
| 617 | construction order):</p> |
---|
| 618 | <pre> |
---|
| 619 | #include <boost/statechart/state_machine.hpp> |
---|
| 620 | #include <boost/statechart/simple_state.hpp> |
---|
| 621 | |
---|
| 622 | namespace sc = boost::statechart; |
---|
| 623 | |
---|
| 624 | class EntryExitDisplayer |
---|
| 625 | { |
---|
| 626 | protected: |
---|
| 627 | EntryExitDisplayer( const char * pName ) : |
---|
| 628 | pName_( pName ) |
---|
| 629 | { |
---|
| 630 | std::cout << pName_ << " entered\n"; |
---|
| 631 | } |
---|
| 632 | |
---|
| 633 | ~EntryExitDisplayer() |
---|
| 634 | { |
---|
| 635 | std::cout << pName_ << " exited\n"; |
---|
| 636 | } |
---|
| 637 | |
---|
| 638 | private: |
---|
| 639 | const char * const pName_; |
---|
| 640 | }; |
---|
| 641 | |
---|
| 642 | struct Outer; |
---|
| 643 | struct Machine : sc::state_machine< Machine, Outer > {}; |
---|
| 644 | struct Inner; |
---|
| 645 | struct Outer : EntryExitDisplayer, sc::simple_state< |
---|
| 646 | Outer, Machine, Inner > |
---|
| 647 | { |
---|
| 648 | Outer() : EntryExitDisplayer( "Outer" ) {} |
---|
| 649 | }; |
---|
| 650 | |
---|
| 651 | struct Inner : EntryExitDisplayer, |
---|
| 652 | sc::simple_state< Inner, Outer > |
---|
| 653 | { |
---|
| 654 | Inner() : EntryExitDisplayer( "Inner" ) {} |
---|
| 655 | }; |
---|
| 656 | |
---|
| 657 | int main() |
---|
| 658 | { |
---|
| 659 | Machine myMachine; |
---|
| 660 | myMachine.initiate(); |
---|
| 661 | return 0; |
---|
| 662 | } |
---|
| 663 | </pre> |
---|
| 664 | |
---|
| 665 | <p>This program will produce the following output:</p> |
---|
| 666 | <pre> |
---|
| 667 | Outer entered |
---|
| 668 | Inner entered |
---|
| 669 | Outer exited |
---|
| 670 | Inner exited |
---|
| 671 | </pre> |
---|
| 672 | |
---|
| 673 | <p>That is, the <b><code>EntryExitDisplayer</code> base class portion</b> |
---|
| 674 | of <code>Outer</code> is destructed before the one of <code>Inner</code> |
---|
| 675 | although <code>Inner::~Inner()</code> is called before |
---|
| 676 | <code>Outer::~Outer()</code>. This somewhat counter-intuitive behavior is |
---|
| 677 | caused by the following facts:</p> |
---|
| 678 | |
---|
| 679 | <ul> |
---|
| 680 | <li>The <code>simple_state<></code> base class portion of |
---|
| 681 | <code>Inner</code> is responsible to destruct <code>Outer</code></li> |
---|
| 682 | |
---|
| 683 | <li>Destructors of base class portions are called in the reverse order of |
---|
| 684 | construction</li> |
---|
| 685 | </ul> |
---|
| 686 | |
---|
| 687 | <p>So, when the <code>Outer</code> destructor is called the call stack |
---|
| 688 | looks as follows:</p> |
---|
| 689 | <pre> |
---|
| 690 | Outer::~Outer() |
---|
| 691 | simple_state< Inner, ... >::~simple_state() |
---|
| 692 | Inner::~Inner() |
---|
| 693 | </pre> |
---|
| 694 | |
---|
| 695 | <p>Note that <code>Inner::~Inner()</code> did not yet have a chance to |
---|
| 696 | destroy its <code>EntryExitDisplayer</code> base class portion, as it first |
---|
| 697 | has to call the destructor of the <b>second</b> base class. Now |
---|
| 698 | <code>Outer::~Outer()</code> will first destruct its <code>simple_state< |
---|
| 699 | Outer, ... ></code> base class portion and then do the same with its |
---|
| 700 | <code>EntryExitDisplayer</code> base class portion. The stack then unwinds |
---|
| 701 | back to <code>Inner::~Inner()</code>, which can then finally finish by |
---|
| 702 | calling <code>EntryExitDisplayer::~EntryExitDisplayer()</code>.</p> |
---|
| 703 | |
---|
| 704 | <p>Luckily, there is an easy work-around: Always let |
---|
| 705 | <code>simple_state<></code> and <code>state<></code> be the |
---|
| 706 | first base class of a state. This ensures that destructors of additional |
---|
| 707 | bases are called before recursion employed by state base destructors can |
---|
| 708 | alter the order of destruction.</p> |
---|
| 709 | <hr> |
---|
| 710 | |
---|
| 711 | <p><a href="http://validator.w3.org/check?uri=referer"><img border="0" src= |
---|
| 712 | "http://www.w3.org/Icons/valid-html401" alt="Valid HTML 4.01 Transitional" |
---|
| 713 | height="31" width="88"></a></p> |
---|
| 714 | |
---|
| 715 | <p>Revised |
---|
| 716 | <!--webbot bot="Timestamp" s-type="EDITED" s-format="%d %B, %Y" startspan -->03 December, 2006<!--webbot bot="Timestamp" endspan i-checksum="38512" --></p> |
---|
| 717 | |
---|
| 718 | <p><i>Copyright © 2003-<!--webbot bot="Timestamp" s-type="EDITED" s-format="%Y" startspan -->2006<!--webbot bot="Timestamp" endspan i-checksum="770" --> |
---|
| 719 | <a href="contact.html">Andreas Huber Dönni</a></i></p> |
---|
| 720 | |
---|
| 721 | <p><i>Distributed under the Boost Software License, Version 1.0. (See |
---|
| 722 | accompanying file <a href="../../../LICENSE_1_0.txt">LICENSE_1_0.txt</a> or |
---|
| 723 | copy at <a href= |
---|
| 724 | "http://www.boost.org/LICENSE_1_0.txt">http://www.boost.org/LICENSE_1_0.txt</a>)</i></p> |
---|
| 725 | </body> |
---|
| 726 | </html> |
---|