Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: downloads/boost_1_34_1/libs/statechart/doc/faq.html @ 33

Last change on this file since 33 was 29, checked in by landauf, 16 years ago

updated boost from 1_33_1 to 1_34_1

File size: 28.7 KB
Line 
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>
71struct Active;
72struct Stopped;
73struct Running;
74struct StopWatch : sc::state_machine&lt; StopWatch, Active &gt;
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&lt; const Stopped * &gt;() != 0 )
87    {
88      return elapsedTime_;
89    }
90    else if ( state_cast&lt; const Running * &gt;() != 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
106struct Active : sc::state&lt; Active, StopWatch, Stopped &gt;
107{
108  typedef sc::transition&lt; EvReset, Active &gt; 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&lt; Running, Active &gt;
117  {
118    typedef sc::transition&lt; EvStartStop, Stopped &gt; 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&lt; Stopped, Active &gt;
133  {
134    typedef sc::transition&lt; EvStartStop, Running &gt; 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&lt;&gt;</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&lt;&gt;::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
209struct Active; // the only visible forward
210struct StopWatch : sc::state_machine&lt; StopWatch, Active &gt;
211{
212  StopWatch();
213};
214</pre>
215
216  <p>StopWatch.cpp:</p>
217  <pre>
218struct Stopped;
219struct Active : sc::simple_state&lt; Active, StopWatch, Stopped &gt;
220{
221  typedef sc::transition&lt; EvReset, Active &gt; reactions;
222};
223
224  struct Running : sc::simple_state&lt; Running, Active &gt;
225  {
226    typedef sc::transition&lt; EvStartStop, Stopped &gt; reactions;
227  };
228
229  struct Stopped : sc::simple_state&lt; Stopped, Active &gt;
230  {
231    typedef sc::transition&lt; EvStartStop, Running &gt; reactions;
232  };
233
234StopWatch::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>
253struct EvStart : sc::event&lt; EvStart &gt; {};
254
255struct Idle;
256struct PumpBase : sc::state_machine&lt; PumpBase, Idle &gt;
257{
258  <b>virtual sc::result react(
259</b>    <b>Idle &amp; idle, const EvStart &amp; ) const;
260</b>};
261
262struct Idle : sc::simple_state&lt; Idle, PumpBase &gt;
263{
264  typedef sc::custom_reaction&lt; EvStart &gt; reactions;
265
266  sc::result react( const EvStart &amp; evt )
267  {
268    <b>return context&lt; PumpBase &gt;().react( *this, evt );</b>
269  }
270};
271
272struct Running : sc::simple_state&lt; Running, PumpBase &gt; {};
273
274sc::result PumpBase::react(
275  Idle &amp; idle, const EvStart &amp; ) const
276{
277  <b>return idle.transit&lt; Running &gt;();
278</b>}
279
280
281struct MyRunning : sc::simple_state&lt; MyRunning, PumpBase &gt; {};
282
283struct MyPump : PumpBase
284{
285  virtual sc::result react(
286    Idle &amp; idle, const EvStart &amp; ) const
287  {
288    <b>return idle.transit&lt; MyRunning &gt;();
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 &amp; 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&lt;&gt;::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&lt;&gt;</code> member functions and
362  <code>dynamic_cast</code>s. <code>std::allocator&lt;&gt;</code> member
363  function calls can be avoided by passing a custom allocator to
364  <code>event&lt;&gt;</code>, <code>state_machine&lt;&gt;</code>,
365  <code>asynchronous_state_machine&lt;&gt;</code>,
366  <code>fifo_scheduler&lt;&gt;</code> and <code>fifo_worker&lt;&gt;</code>.
367  <code>dynamic_cast</code>s can be avoided by not calling the
368  <code>state_cast&lt;&gt;</code> member functions of
369  <code>state_machine&lt;&gt;</code>, <code>simple_state&lt;&gt;</code> and
370  <code>state&lt;&gt;</code> but using the deterministic variant
371  <code>state_downcast&lt;&gt;</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 &lt;boost/statechart/state_machine.hpp&gt;
380#include &lt;boost/statechart/simple_state.hpp&gt;
381
382namespace sc = boost::statechart;
383
384template&lt; typename X &gt; struct A;
385struct Machine : sc::state_machine&lt; Machine, A&lt; int &gt; &gt; {};
386
387template&lt; typename X &gt; struct B;
388template&lt; typename X &gt;
389struct A : sc::simple_state&lt; A&lt; X &gt;, Machine, B&lt; X &gt; &gt; {};
390
391  template&lt; typename X &gt;
392  struct B : sc::simple_state&lt; B&lt; X &gt;, A&lt; X &gt; &gt; {};
393
394int 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&lt; X &gt;</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&lt;&gt;</code>, as
409  follows:</p>
410  <pre>
411struct A : sc::simple_state&lt;
412  A&lt; X &gt;, Machine, mpl::list&lt; B&lt; X &gt; &gt; &gt; {};
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>
433BOOST_STATIC_ASSERT( ( mpl::less&lt;
434  orthogonal_position,
435  typename context_type::no_of_orthogonal_regions &gt;::value ) );
436</pre>Most probably, there is an error in your code. The library has many
437such compile-time assertions to ensure that invalid state machines cannot be
438compiled (for an idea what kinds of errors are reported at compile time, see
439the compile-fail tests). Above each of these assertions there is a comment
440explaining the problem. On almost all current compilers an error in template
441code is accompanied by the current "instantiation stack". Very much like the
442call stack you see in the debugger, this "instantiation stack" allows you to
443trace the error back through instantiations of library code until you hit the
444line of your code that causes the problem. As an example, here's the MSVC7.1
445error 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&lt;x&gt;'
448  with
449  [
450    x=false
451  ]
452  ...\boost\statechart\shallow_history.hpp(34) : see reference to class template instantiation 'boost::STATIC_ASSERTION_FAILURE&lt;x&gt;' 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&lt;DefaultState&gt;' 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&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
463  with
464  [
465    MostDerived=A,
466    Context=InconsistentHistoryTest,
467    InnerInitial=boost::mpl::list&lt;boost::statechart::shallow_history&lt;B&gt;&gt;,
468    InnerList=boost::statechart::simple_state&lt;A,InconsistentHistoryTest,boost::mpl::list&lt;boost::statechart::shallow_history&lt;B&gt;&gt;&gt;::inner_initial_list
469  ]
470  ...\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
471  with
472  [
473    MostDerived=A,
474    Context=InconsistentHistoryTest,
475    InnerInitial=boost::mpl::list&lt;boost::statechart::shallow_history&lt;B&gt;&gt;
476  ]
477  ...\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;)'
478  with
479  [
480    MostDerived=A,
481    Context=InconsistentHistoryTest,
482    InnerInitial=boost::mpl::list&lt;boost::statechart::shallow_history&lt;B&gt;&gt;
483  ]
484  ...\libs\statechart\test\InconsistentHistoryTest1.cpp(29) : see reference to class template instantiation 'boost::statechart::simple_state&lt;MostDerived,Context,InnerInitial&gt;' being compiled
485  with
486  [
487    MostDerived=A,
488    Context=InconsistentHistoryTest,
489    InnerInitial=boost::mpl::list&lt;boost::statechart::shallow_history&lt;B&gt;&gt;
490  ]
491</pre>Depending on the IDE you use, it is possible that you need to switch to
492another window to see this full error message (e.g. for Visual Studio 2003,
493you need to switch to the Output window). Starting at the top and going down
494the list of instantiations you see that each of them is accompanied by a file
495name and a line number. Ignoring all files belonging to the library, we find
496the culprit close to the bottom in file InconsistentHistoryTest1.cpp on line
49729.
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&lt;&gt;</code> and
513  <code>simple_state&lt;&gt;</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 &amp; 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&lt;&gt;</code>
552    and <code>simple_state&lt;&gt;</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>
567template&lt; class MostDerived &gt;
568struct EvButtonPressed : sc::event&lt; MostDerived &gt;
569{
570  // common code
571};
572
573struct EvPlayButtonPressed :
574  EvButtonPressed&lt; EvPlayButtonPressed &gt; {};
575struct EvStopButtonPressed :
576  EvButtonPressed&lt; EvStopButtonPressed &gt; {};
577struct EvForwardButtonPressed :
578  EvButtonPressed&lt; EvForwardButtonPressed &gt; {};
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.
586struct Off : sc::simple_state&lt; Off, Mp3Player &gt;
587{
588  typedef mpl::list&lt;
589    sc::custom_reaction&lt; EvPlayButtonPressed &gt;,
590    sc::custom_reaction&lt; EvStopButtonPressed &gt;,
591    sc::custom_reaction&lt; EvForwardButtonPressed &gt;
592  &gt; reactions;
593
594  template&lt; class MostDerived &gt;
595  sc::result react( const EvButtonPressed&lt; MostDerived &gt; &amp; )
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&lt;&gt;</code> and
615  <code>state&lt;&gt;</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 &lt;boost/statechart/state_machine.hpp&gt;
620#include &lt;boost/statechart/simple_state.hpp&gt;
621
622namespace sc = boost::statechart;
623
624class EntryExitDisplayer
625{
626  protected:
627    EntryExitDisplayer( const char * pName ) :
628      pName_( pName )
629    {
630      std::cout &lt;&lt; pName_ &lt;&lt; " entered\n";
631    }
632
633    ~EntryExitDisplayer()
634    {
635      std::cout &lt;&lt; pName_ &lt;&lt; " exited\n";
636    }
637
638  private:
639    const char * const pName_;
640};
641
642struct Outer;
643struct Machine : sc::state_machine&lt; Machine, Outer &gt; {};
644struct Inner;
645struct Outer : EntryExitDisplayer, sc::simple_state&lt;
646  Outer, Machine, Inner &gt;
647{
648  Outer() : EntryExitDisplayer( "Outer" ) {}
649};
650
651struct Inner : EntryExitDisplayer,
652  sc::simple_state&lt; Inner, Outer &gt;
653{
654  Inner() : EntryExitDisplayer( "Inner" ) {}
655};
656
657int 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>
667Outer entered
668Inner entered
669Outer exited
670Inner 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&lt;&gt;</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>
690Outer::~Outer()
691simple_state&lt; Inner, ... &gt;::~simple_state()
692Inner::~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&lt;
699  Outer, ... &gt;</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&lt;&gt;</code> and <code>state&lt;&gt;</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 &copy; 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&ouml;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>
Note: See TracBrowser for help on using the repository browser.