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