1 <!DOCTYPE html PUBLIC
"-//W3C//DTD HTML 4.01 Transitional//EN">
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">
11 <title>The Boost Statechart Library - FAQ
</title>
14 <body link=
"#0000FF" vlink=
"#800080">
15 <table border=
"0" cellpadding=
"7" cellspacing=
"0" width=
"100%" summary=
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>
24 <h1 align=
"center">The Boost Statechart Library
</h1>
26 <h2 align=
"center">Frequently Asked Questions (FAQs)
</h2>
32 <dl class=
"page-index">
33 <dt><a href=
"#StateLocalStorage">What's so cool about state-local
36 <dt><a href=
"#HideInnerWorkings">How can I hide the inner workings of a
37 state machine from its clients?
</a></dt>
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>
42 <dt><a href=
"#Uml2">What about UML2.0 features?
</a></dt>
44 <dt><a href=
"#AssertInStateDestructor">Why do I get an assert when I
45 access the state machine from a state destructor?
</a></dt>
47 <dt><a href=
"#EmbeddedApplications">Is Boost.Statechart suitable for
48 embedded applications?
</a></dt>
50 <dt><a href=
"#HardRealtime">Is your library suitable for applications
51 with hard real-time requirements?
</a></dt>
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>
56 <dt><a href=
"#CompilerError">My compiler reports an error in the library
57 code. Is this a bug in Boost.Statechart?
</a></dt>
59 <dt><a href=
"#DisableHistory">Is it possible to disable history for a
60 state at runtime?
</a></dt>
62 <dt><a href=
"#Dll">How can I compile a state machine into a dynamic link
63 library (DLL)?
</a></dt>
65 <dt><a href=
"#PolymorphicEvents">Does Boost.Statechart support
66 polymorphic events?
</a></dt>
68 <dt><a href=
"#WrongExitActionOrder">Why are exit-actions called in the
69 wrong order when I use multiple inheritance?
</a></dt>
72 <h2><a name=
"StateLocalStorage" id=
"StateLocalStorage">What's so cool about
73 state-local storage?
</a></h2>
75 <p>This is best explained with an example:
</p>
80 struct StopWatch : sc::state_machine
< StopWatch, Active
>
82 // startTime_ remains uninitialized, because there is no reasonable default
83 StopWatch() : elapsedTime_(
0.0 ) {}
89 double ElapsedTime() const
91 // Ugly switch over the current state.
92 if ( state_cast
< const Stopped *
>() !=
0 )
96 else if ( state_cast
< const Running *
>() !=
0 )
98 return elapsedTime_ + std::difftime( std::time(
0 ), startTime_ );
100 else // we're terminated
102 throw std::bad_cast();
106 // elapsedTime_ is only meaningful when the machine is not terminated
108 // startTime_ is only meaningful when the machine is in Running
109 std::time_t startTime_;
112 struct Active : sc::state
< Active, StopWatch, Stopped
>
114 typedef sc::transition
< EvReset, Active
> reactions;
116 Active( my_context ctx ) : my_base( ctx )
118 outermost_context().elapsedTime_ =
0.0;
122 struct Running : sc::state
< Running, Active
>
124 typedef sc::transition
< EvStartStop, Stopped
> reactions;
126 Running( my_context ctx ) : my_base( ctx )
128 outermost_context().startTime_ = std::time(
0 );
133 outermost_context().elapsedTime_ +=
134 std::difftime( std::time(
0 ), outermost_context().startTime_ );
138 struct Stopped : sc::simple_state
< Stopped, Active
>
140 typedef sc::transition
< EvStartStop, Running
> reactions;
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>
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>
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
<></code> subtype will therefore be a change
174 hotspot, which is a pretty sure indicator for a bad design
</li>
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>
183 <h2><a name=
"HideInnerWorkings" id=
"HideInnerWorkings">How can I hide the
184 inner workings of a state machine from its clients?
</a></h2>
186 <p>To see why and how this is possible it is important to recall the
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
195 <li>The
<code>InitialState
</code> template parameter of
196 <code>sc::state_machine
</code> can be an incomplete type (i.e. forward
200 <p>The class template member function
201 <code>state_machine
<>::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
211 <p>StopWatch.hpp:
</p>
215 struct Active; // the only visible forward
216 struct StopWatch : sc::state_machine
< StopWatch, Active
>
222 <p>StopWatch.cpp:
</p>
225 struct Active : sc::simple_state
< Active, StopWatch, Stopped
>
227 typedef sc::transition
< EvReset, Active
> reactions;
230 struct Running : sc::simple_state
< Running, Active
>
232 typedef sc::transition
< EvStartStop, Stopped
> reactions;
235 struct Stopped : sc::simple_state
< Stopped, Active
>
237 typedef sc::transition
< EvStartStop, Running
> reactions;
240 StopWatch::StopWatch()
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
250 <p>The PingPong example demonstrates how the inner workings of an
251 asynchronous_state_machine<
> subclass can be hidden.
</p>
253 <h2><a name=
"MachineInheritance" id=
"MachineInheritance">Is it possible to
254 inherit from a given state machine and modify its layout in the
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>
261 struct EvStart : sc::event
< EvStart
> {};
264 struct PumpBase : sc::state_machine
< PumpBase, Idle
>
266 <b>virtual sc::result react(
267 </b> <b>Idle
& idle, const EvStart
& ) const;
270 struct Idle : sc::simple_state
< Idle, PumpBase
>
272 typedef sc::custom_reaction
< EvStart
> reactions;
274 sc::result react( const EvStart
& evt )
276 <b>return context
< PumpBase
>().react( *this, evt );
</b>
280 struct Running : sc::simple_state
< Running, PumpBase
> {};
282 sc::result PumpBase::react(
283 Idle
& idle, const EvStart
& ) const
285 <b>return idle.transit
< Running
>();
289 struct MyRunning : sc::simple_state
< MyRunning, PumpBase
> {};
291 struct MyPump : PumpBase
293 virtual sc::result react(
294 Idle
& idle, const EvStart
& ) const
296 <b>return idle.transit
< MyRunning
>();
301 <h2><a name=
"Uml2" id=
"Uml2">What about UML
2.0 features?
</a></h2>
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
& Boost.Statechart semantics:
</p>
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>
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>
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>
325 <p>When compiled with
<code>NDEBUG
</code> undefined, running the following
326 program results in a failed assert:
</p>
327 <pre>#include
<boost/statechart/state_machine.hpp
>
328 #include
<boost/statechart/simple_state.hpp
>
329 #include
<iostream
>
332 struct Machine : boost::statechart::state_machine
< Machine, Initial
>
334 Machine() { someMember_ =
42; }
338 struct Initial : boost::statechart::simple_state
< Initial, Machine
>
340 ~Initial() { std::cout
<< outermost_context().someMember_; }
345 Machine().initiate();
348 <p>The problem arises because
<code>state_machine
<>::~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>
355 <h2><a name=
"EmbeddedApplications" id=
"EmbeddedApplications">Is
356 Boost.Statechart suitable for embedded applications?
</a></h2>
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>
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
<>::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
379 <li>A single state machine object uses typically less than
1KB of memory,
380 even if it implements a very complex machine
</li>
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
388 Moreover, the library can be compiled with C++ RTTI and exception
389 handling turned off, resulting in significant savings on most
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
398 <h2><a name=
"HardRealtime" id=
"HardRealtime">Is your library suitable for
399 applications with hard real-time requirements?
</a></h2>
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
<></code> member functions and
404 <code>dynamic_cast
</code>s.
<code>std::allocator
<></code> member
405 function calls can be avoided by passing a custom allocator to
406 <code>event
<></code>,
<code>state_machine
<></code>,
407 <code>asynchronous_state_machine
<></code>,
408 <code>fifo_scheduler
<></code> and
<code>fifo_worker
<></code>.
409 <code>dynamic_cast
</code>s can be avoided by not calling the
410 <code>state_cast
<></code> member functions of
411 <code>state_machine
<></code>,
<code>simple_state
<></code> and
412 <code>state
<></code> but using the deterministic variant
413 <code>state_downcast
<></code> instead.
</p>
415 <h2><a name=
"TemplatedStates" id=
"TemplatedStates">With templated states I
416 get an error that 'inner_context_type' is not defined. What's
419 <p>The following code generates such an error:
</p>
421 #include
<boost/statechart/state_machine.hpp
>
422 #include
<boost/statechart/simple_state.hpp
>
424 namespace sc = boost::statechart;
426 template
< typename X
> struct A;
427 struct Machine : sc::state_machine
< Machine, A
< int
> > {};
429 template
< typename X
> struct B;
430 template
< typename X
>
431 struct A : sc::simple_state
< A
< X
>, Machine, B
< X
> > {};
433 template
< typename X
>
434 struct B : sc::simple_state
< B
< X
>, A
< X
> > {};
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
< X
></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
<></code>, as
453 struct A : sc::simple_state
<
454 A
< X
>, Machine, mpl::list
< B
< X
> > > {};
458 "http://article.gmane.org/gmane.comp.lib.boost.devel/128741">this post
</a>
459 for technical details.
</p>
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>
464 <p>Probably not. There are several possible reasons for such compile-time
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>
473 <li>The error is reported on a line similar to the following:
475 BOOST_STATIC_ASSERT( ( mpl::less
<
477 typename context_type::no_of_orthogonal_regions
>::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:
489 ...\boost\statechart\shallow_history.hpp(
34) : error C2027: use of undefined type 'boost::STATIC_ASSERTION_FAILURE
<x
>'
494 ...\boost\statechart\shallow_history.hpp(
34) : see reference to class template instantiation 'boost::STATIC_ASSERTION_FAILURE
<x
>' being compiled
499 ...\boost\statechart\simple_state.hpp(
861) : see reference to class template instantiation 'boost::statechart::shallow_history
<DefaultState
>' being compiled
504 ...\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
508 Context=InconsistentHistoryTest,
509 InnerInitial=boost::mpl::list
<boost::statechart::shallow_history
<B
>>,
510 InnerList=boost::statechart::simple_state
<A,InconsistentHistoryTest,boost::mpl::list
<boost::statechart::shallow_history
<B
>>>::inner_initial_list
512 ...\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
516 Context=InconsistentHistoryTest,
517 InnerInitial=boost::mpl::list
<boost::statechart::shallow_history
<B
>>
519 ...\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
&)'
523 Context=InconsistentHistoryTest,
524 InnerInitial=boost::mpl::list
<boost::statechart::shallow_history
<B
>>
526 ...\libs\statechart\test\InconsistentHistoryTest1.cpp(
29) : see reference to class template instantiation 'boost::statechart::simple_state
<MostDerived,Context,InnerInitial
>' being compiled
530 Context=InconsistentHistoryTest,
531 InnerInitial=boost::mpl::list
<boost::statechart::shallow_history
<B
>>
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
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>
550 <h2><a name=
"DisableHistory" id=
"DisableHistory">Is it possible to disable
551 history for a state at runtime?
</a></h2>
554 "reference.html#clear_shallow_history">simple_state::clear_shallow_history()
</a>
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>
560 <li>a state with history is the target of many transitions,
562 <li>the decision to ignore history is made in a different place than
563 the transition to a state with history
</li>
566 <h2><a name=
"Dll" id=
"Dll">How can I compile a state machine into a dynamic
567 link library (DLL)?
</a></h2>
569 <p>Invisible to the user, the library uses static data members to implement
570 its own speed-optimized RTTI-mechanism for
<code>event
<></code> and
571 <code>simple_state
<></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>
579 <li><a href=
"../test/TuTest.hpp">TuTest.hpp
</a>: Instantiates a class
580 template containing a static data member
</li>
582 <li><a href=
"../test/TuTest.cpp">TuTest.cpp
</a>: Includes TuTest.hpp and
583 is compiled into a DLL
</li>
585 <li><a href=
"../test/TuTestMain.cpp">TuTestMain.cpp
</a>: Includes
586 TuTest.hpp and is compiled into an executable
</li>
589 <p>Without any precautions (e.g.
<code>__declspec(dllexport)
</code> on MSVC
590 compatible compilers), on most platforms both binaries (exe
& 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
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>
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
<></code>
610 and
<code>simple_state
<></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>
616 <h2><a name=
"PolymorphicEvents" id=
"PolymorphicEvents">Does
617 Boost.Statechart support polymorphic events?
</a></h2>
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>
625 template
< class MostDerived
>
626 struct EvButtonPressed : sc::event
< MostDerived
>
631 struct EvPlayButtonPressed :
632 EvButtonPressed
< EvPlayButtonPressed
> {};
633 struct EvStopButtonPressed :
634 EvButtonPressed
< EvStopButtonPressed
> {};
635 struct EvForwardButtonPressed :
636 EvButtonPressed
< EvForwardButtonPressed
> {};
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
< Off, Mp3Player
>
646 typedef mpl::list
<
647 sc::custom_reaction
< EvPlayButtonPressed
>,
648 sc::custom_reaction
< EvStopButtonPressed
>,
649 sc::custom_reaction
< EvForwardButtonPressed
>
652 template
< class MostDerived
>
653 sc::result react( const EvButtonPressed
< MostDerived
> & )
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>
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
672 <p>They definitely aren't for the
<code>simple_state
<></code> and
673 <code>state
<></code> subtypes, but the destructors of additional
674 bases might be called in construction order (rather than the reverse
675 construction order):
</p>
677 #include
<boost/statechart/state_machine.hpp
>
678 #include
<boost/statechart/simple_state.hpp
>
680 namespace sc = boost::statechart;
682 class EntryExitDisplayer
685 EntryExitDisplayer( const char * pName ) :
688 std::cout
<< pName_
<< " entered\n";
691 ~EntryExitDisplayer()
693 std::cout
<< pName_
<< " exited\n";
697 const char * const pName_;
701 struct Machine : sc::state_machine
< Machine, Outer
> {};
703 struct Outer : EntryExitDisplayer, sc::simple_state
<
704 Outer, Machine, Inner
>
706 Outer() : EntryExitDisplayer(
"Outer" ) {}
709 struct Inner : EntryExitDisplayer,
710 sc::simple_state
< Inner, Outer
>
712 Inner() : EntryExitDisplayer(
"Inner" ) {}
718 myMachine.initiate();
723 <p>This program will produce the following output:
</p>
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>
738 <li>The
<code>simple_state
<></code> base class portion of
739 <code>Inner
</code> is responsible to destruct
<code>Outer
</code></li>
741 <li>Destructors of base class portions are called in the reverse order of
745 <p>So, when the
<code>Outer
</code> destructor is called the call stack
746 looks as follows:
</p>
749 simple_state
< Inner, ...
>::~simple_state()
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
<
757 Outer, ...
></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>
762 <p>Luckily, there is an easy work-around: Always let
763 <code>simple_state
<></code> and
<code>state
<></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>
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>
773 <p>Revised
05 January,
2008</p>
775 <p><i>Copyright
© 2003-
2008 <a href=
"contact.html">Andreas Huber
776 D
önni
</a></i></p>
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
781 "http://www.boost.org/LICENSE_1_0.txt">http://www.boost.org/LICENSE_1_0.txt
</a>)
</i></p>