123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783 |
- <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
- <html>
- <head>
- <meta http-equiv="Content-Language" content="en-us">
- <meta http-equiv="Content-Type" content="text/html; charset=us-ascii">
- <meta name="GENERATOR" content="Microsoft FrontPage 6.0">
- <meta name="ProgId" content="FrontPage.Editor.Document">
- <link rel="stylesheet" type="text/css" href="../../../boost.css">
- <title>The Boost Statechart Library - FAQ</title>
- </head>
- <body link="#0000FF" vlink="#800080">
- <table border="0" cellpadding="7" cellspacing="0" width="100%" summary=
- "header">
- <tr>
- <td valign="top" width="300">
- <h3><a href="../../../index.htm"><img alt="C++ Boost" src=
- "../../../boost.png" border="0" width="277" height="86"></a></h3>
- </td>
- <td valign="top">
- <h1 align="center">The Boost Statechart Library</h1>
- <h2 align="center">Frequently Asked Questions (FAQs)</h2>
- </td>
- </tr>
- </table>
- <hr>
- <dl class="page-index">
- <dt><a href="#StateLocalStorage">What's so cool about state-local
- storage?</a></dt>
- <dt><a href="#HideInnerWorkings">How can I hide the inner workings of a
- state machine from its clients?</a></dt>
- <dt><a href="#MachineInheritance">Is it possible to inherit from a given
- state machine and modify its layout in the subclass?</a></dt>
- <dt><a href="#Uml2">What about UML2.0 features?</a></dt>
-
- <dt><a href="#AssertInStateDestructor">Why do I get an assert when I
- access the state machine from a state destructor?</a></dt>
- <dt><a href="#EmbeddedApplications">Is Boost.Statechart suitable for
- embedded applications?</a></dt>
- <dt><a href="#HardRealtime">Is your library suitable for applications
- with hard real-time requirements?</a></dt>
- <dt><a href="#TemplatedStates">With templated states I get an error that
- 'inner_context_type' is not defined. What's wrong?</a></dt>
- <dt><a href="#CompilerError">My compiler reports an error in the library
- code. Is this a bug in Boost.Statechart?</a></dt>
- <dt><a href="#DisableHistory">Is it possible to disable history for a
- state at runtime?</a></dt>
- <dt><a href="#Dll">How can I compile a state machine into a dynamic link
- library (DLL)?</a></dt>
- <dt><a href="#PolymorphicEvents">Does Boost.Statechart support
- polymorphic events?</a></dt>
- <dt><a href="#WrongExitActionOrder">Why are exit-actions called in the
- wrong order when I use multiple inheritance?</a></dt>
- </dl>
- <h2><a name="StateLocalStorage" id="StateLocalStorage">What's so cool about
- state-local storage?</a></h2>
- <p>This is best explained with an example:</p>
- <pre>
- struct Active;
- struct Stopped;
- struct Running;
- struct StopWatch : sc::state_machine< StopWatch, Active >
- {
- // startTime_ remains uninitialized, because there is no reasonable default
- StopWatch() : elapsedTime_( 0.0 ) {}
- ~StopWatch()
- {
- terminate();
- }
- double ElapsedTime() const
- {
- // Ugly switch over the current state.
- if ( state_cast< const Stopped * >() != 0 )
- {
- return elapsedTime_;
- }
- else if ( state_cast< const Running * >() != 0 )
- {
- return elapsedTime_ + std::difftime( std::time( 0 ), startTime_ );
- }
- else // we're terminated
- {
- throw std::bad_cast();
- }
- }
- // elapsedTime_ is only meaningful when the machine is not terminated
- double elapsedTime_;
- // startTime_ is only meaningful when the machine is in Running
- std::time_t startTime_;
- };
- struct Active : sc::state< Active, StopWatch, Stopped >
- {
- typedef sc::transition< EvReset, Active > reactions;
- Active( my_context ctx ) : my_base( ctx )
- {
- outermost_context().elapsedTime_ = 0.0;
- }
- };
- struct Running : sc::state< Running, Active >
- {
- typedef sc::transition< EvStartStop, Stopped > reactions;
- Running( my_context ctx ) : my_base( ctx )
- {
- outermost_context().startTime_ = std::time( 0 );
- }
- ~Running()
- {
- outermost_context().elapsedTime_ +=
- std::difftime( std::time( 0 ), outermost_context().startTime_ );
- }
- };
- struct Stopped : sc::simple_state< Stopped, Active >
- {
- typedef sc::transition< EvStartStop, Running > reactions;
- };
- </pre>
- <p>This StopWatch does not make any use of state-local storage while
- implementing the same behavior as the <a href=
- "tutorial.html#BasicTopicsAStopWatch">tutorial StopWatch</a>. Even though
- this code is probably easier to read for the untrained eye, it does have a
- few problems that are absent in the original:</p>
- <ul>
- <li>This StopWatch class has data members that have a meaningful value
- only if the state machine happens to be in a certain state. That is, the
- lifetimes of these variables are not identical with the one of the
- StopWatch object containing them. Since the lifetimes are managed by the
- entry and exit actions of states, we need to use an ugly switch over the
- current state (see <code>StopWatch::ElapsedTime()</code>) if we want to
- access them from a context where the current state is unclear. This
- essentially duplicates some of the state logic of the FSM. Therefore,
- whenever we need to change the layout of the state machine we will likely
- also need to change the ugly switch. Even worse, if we forget to change
- the switch, the code will probably still compile and maybe even silently
- do the wrong thing. Note that this is impossible with the version in the
- tutorial, which will at least throw an exception and often just refuse to
- compile. Moreover, for the tutorial StopWatch there's a much higher
- chance that a programmer will get a change correct the first time since
- the code that calculates the elapsed time is located close to the code
- that updates the variables</li>
- <li>We need to change the StopWatch class whenever we want to introduce a
- new variable or change the type of an already existing variable. That is,
- many changes in the FSM will likely lead to a change in the StopWatch
- class. In all FSMs that do not employ state-local storage, the
- <code>state_machine<></code> subtype will therefore be a change
- hotspot, which is a pretty sure indicator for a bad design</li>
- </ul>
- <p>Both points are not much of a problem in a small example like this,
- which can easily be implemented in a single translation unit by a single
- programmer. However, they quickly become a major problem for a big complex
- machine spread over multiple translation units, which are possibly even
- maintained by different programmers.</p>
- <h2><a name="HideInnerWorkings" id="HideInnerWorkings">How can I hide the
- inner workings of a state machine from its clients?</a></h2>
- <p>To see why and how this is possible it is important to recall the
- following facts:</p>
- <ul>
- <li>Member functions of a C++ class template are instantiated at the
- point where they're actually called. If the function is never called, it
- will not be instantiated and not a single assembly instruction will ever
- be generated</li>
- <li>The <code>InitialState</code> template parameter of
- <code>sc::state_machine</code> can be an incomplete type (i.e. forward
- declared)</li>
- </ul>
- <p>The class template member function
- <code>state_machine<>::initiate()</code> creates an object of the
- initial state. So, the definition of this state must be known before the
- compiler reaches the point where <code>initiate()</code> is called. To be
- able to hide the initial state of a state machine in a .cpp file we must
- therefore no longer let clients call <code>initiate()</code>. Instead, we
- do so in the .cpp file, at a point where the full definition of the initial
- state is known.</p>
- <p>Example:</p>
- <p>StopWatch.hpp:</p>
- <pre>
- // define events ...
- struct Active; // the only visible forward
- struct StopWatch : sc::state_machine< StopWatch, Active >
- {
- StopWatch();
- };
- </pre>
- <p>StopWatch.cpp:</p>
- <pre>
- struct Stopped;
- struct Active : sc::simple_state< Active, StopWatch, Stopped >
- {
- typedef sc::transition< EvReset, Active > reactions;
- };
- struct Running : sc::simple_state< Running, Active >
- {
- typedef sc::transition< EvStartStop, Stopped > reactions;
- };
- struct Stopped : sc::simple_state< Stopped, Active >
- {
- typedef sc::transition< EvStartStop, Running > reactions;
- };
- StopWatch::StopWatch()
- {
- // For example, we might want to ensure that the state
- // machine is already started after construction.
- // Alternatively, we could add our own initiate() function
- // to StopWatch and call the base class initiate() in the
- // implementation.
- <b>initiate();</b>
- }
- </pre>
- <p>The PingPong example demonstrates how the inner workings of an
- asynchronous_state_machine<> subclass can be hidden.</p>
- <h2><a name="MachineInheritance" id="MachineInheritance">Is it possible to
- inherit from a given state machine and modify its layout in the
- subclass?</a></h2>
- <p>Yes, but contrary to what some FSM code generators allow,
- Boost.Statechart machines can do so only in a way that was foreseen by the
- designer of the base state machine:</p>
- <pre>
- struct EvStart : sc::event< EvStart > {};
- struct Idle;
- struct PumpBase : sc::state_machine< PumpBase, Idle >
- {
- <b>virtual sc::result react(
- </b> <b>Idle & idle, const EvStart & ) const;
- </b>};
- struct Idle : sc::simple_state< Idle, PumpBase >
- {
- typedef sc::custom_reaction< EvStart > reactions;
- sc::result react( const EvStart & evt )
- {
- <b>return context< PumpBase >().react( *this, evt );</b>
- }
- };
- struct Running : sc::simple_state< Running, PumpBase > {};
- sc::result PumpBase::react(
- Idle & idle, const EvStart & ) const
- {
- <b>return idle.transit< Running >();
- </b>}
- struct MyRunning : sc::simple_state< MyRunning, PumpBase > {};
- struct MyPump : PumpBase
- {
- virtual sc::result react(
- Idle & idle, const EvStart & ) const
- {
- <b>return idle.transit< MyRunning >();
- </b> }
- };
- </pre>
- <h2><a name="Uml2" id="Uml2">What about UML 2.0 features?</a></h2>
- <p>The library was designed before 2.0 came along. Therefore, if not
- explicitly noted otherwise, the library implements the behavior mandated by
- the UML1.5 standard. Here's an incomplete list of differences between the
- 2.0 semantics & Boost.Statechart semantics:</p>
- <ul>
- <li>All transitions are always external. Local transitions are not
- supported at all. Unfortunately, the UML2.0 specifications are not
- entirely clear how local transitions are supposed to work, see <a href=
- "http://thread.gmane.org/gmane.comp.lib.boost.user/18641">here</a> for
- more information</li>
- <li>There is no direct support for the UML2.0 elements entry point and
- exit point. However, both can easily be simulated, the former with a
- typedef and the latter with a state that is a template (with the
- transition destination as a template parameter)</li>
- </ul>
- <h2><a name="AssertInStateDestructor" id="AssertInStateDestructor">Why do I
- get an assert when I access the state machine from a state destructor?</a>
- </h2>
- <p>When compiled with <code>NDEBUG</code> undefined, running the following
- program results in a failed assert:</p>
- <pre>#include <boost/statechart/state_machine.hpp>
- #include <boost/statechart/simple_state.hpp>
- #include <iostream>
- struct Initial;
- struct Machine : boost::statechart::state_machine< Machine, Initial >
- {
- Machine() { someMember_ = 42; }
- int someMember_;
- };
- struct Initial : boost::statechart::simple_state< Initial, Machine >
- {
- ~Initial() { std::cout << outermost_context().someMember_; }
- };
- int main()
- {
- Machine().initiate();
- return 0;
- }</pre>
- <p>The problem arises because <code>state_machine<>::~state_machine</code>
- inevitably destructs all remaining active states. At this time,
- <code>Machine::~Machine</code> has already been run, making it illegal to
- access any of the <code>Machine</code> members. This problem can be avoided
- by defining the following destructor:</p>
- <pre>~Machine() { terminate(); }</pre>
- <h2><a name="EmbeddedApplications" id="EmbeddedApplications">Is
- Boost.Statechart suitable for embedded applications?</a></h2>
- <p>It depends. As explained under <a href=
- "performance.html#SpeedVersusScalabilityTradeoffs">Speed versus scalability
- tradeoffs</a> on the Performance page, the virtually limitless scalability
- offered by this library does have its price. Especially small and simple
- FSMs can easily be implemented so that they consume fewer cycles and less
- memory and occupy less code space in the executable. Here are some
- obviously <b>very rough</b> estimates:</p>
- <ul>
- <li>For a state machine with at most one simultaneously active state
- (that is, the machine is flat and does not have orthogonal regions) with
- trivial actions, customized memory management and compiled with a good
- optimizing compiler, a Pentium 4 class CPU should not spend more than
- 1000 cycles inside <code>state_machine<>::process_event()</code>.
- This worst-case time to process one event scales more or less linearly
- with the number of simultaneously active states for more complex state
- machines, with the typical average being much lower than that. So, a
- fairly complex machine with at most 10 simultaneously active states
- running on a 100MHz CPU should be able to process more than 10'000 events
- per second</li>
- <li>A single state machine object uses typically less than 1KB of memory,
- even if it implements a very complex machine</li>
- <li>For code size, it is difficult to give a concrete guideline but tests
- with the BitMachine example suggest that code size scales more or less
- linearly with the number of states (transitions seem to have only little
- impact). When compiled with MSVC7.1 on Windows, 32 states and 224
- transitions seem to fit in ~108KB executable code (with all optimizations
- turned on).<br>
- Moreover, the library can be compiled with C++ RTTI and exception
- handling turned off, resulting in significant savings on most
- platforms</li>
- </ul>
- <p>As mentioned above, these are very rough estimates derived from the use
- of the library on a desktop PC, so they should only be used to decide
- whether there is a point in making your own performance tests on your
- target platform.</p>
- <h2><a name="HardRealtime" id="HardRealtime">Is your library suitable for
- applications with hard real-time requirements?</a></h2>
- <p>Yes. Out of the box, the only operations taking potentially
- non-deterministic time that the library performs are calls to
- <code>std::allocator<></code> member functions and
- <code>dynamic_cast</code>s. <code>std::allocator<></code> member
- function calls can be avoided by passing a custom allocator to
- <code>event<></code>, <code>state_machine<></code>,
- <code>asynchronous_state_machine<></code>,
- <code>fifo_scheduler<></code> and <code>fifo_worker<></code>.
- <code>dynamic_cast</code>s can be avoided by not calling the
- <code>state_cast<></code> member functions of
- <code>state_machine<></code>, <code>simple_state<></code> and
- <code>state<></code> but using the deterministic variant
- <code>state_downcast<></code> instead.</p>
- <h2><a name="TemplatedStates" id="TemplatedStates">With templated states I
- get an error that 'inner_context_type' is not defined. What's
- wrong?</a></h2>
- <p>The following code generates such an error:</p>
- <pre>
- #include <boost/statechart/state_machine.hpp>
- #include <boost/statechart/simple_state.hpp>
- namespace sc = boost::statechart;
- template< typename X > struct A;
- struct Machine : sc::state_machine< Machine, A< int > > {};
- template< typename X > struct B;
- template< typename X >
- struct A : sc::simple_state< A< X >, Machine, B< X > > {};
- template< typename X >
- struct B : sc::simple_state< B< X >, A< X > > {};
- int main()
- {
- Machine machine;
- machine.initiate();
- return 0;
- }
- </pre>
- <p>If the templates <code>A</code> and <code>B</code> are replaced with
- normal types, the above code compiles without errors. This is rooted in the
- fact that C++ treats forward-declared templates differently than
- forward-declared types. Namely, the compiler tries to access member
- typedefs of <code>B< X ></code> at a point where the template has not
- yet been defined. Luckily, this can easily be avoided by putting all inner
- initial state arguments in an <code>mpl::list<></code>, as
- follows:</p>
- <pre>
- struct A : sc::simple_state<
- A< X >, Machine, mpl::list< B< X > > > {};
- </pre>
- <p>See <a href=
- "http://article.gmane.org/gmane.comp.lib.boost.devel/128741">this post</a>
- for technical details.</p>
- <h2><a name="CompilerError" id="CompilerError">My compiler reports an error
- in the library code. Is this a bug in Boost.Statechart?</a></h2>
- <p>Probably not. There are several possible reasons for such compile-time
- errors:</p>
- <ol>
- <li>Your compiler is too buggy to compile the library, see <a href=
- "index.html#SupportedPlatforms">here</a> for information on the status of
- your compiler. If you absolutely must use such a compiler for your
- project, I'm afraid Boost.Statechart is not for you.</li>
- <li>The error is reported on a line similar to the following:
- <pre>
- BOOST_STATIC_ASSERT( ( mpl::less<
- orthogonal_position,
- typename context_type::no_of_orthogonal_regions >::value ) );
- </pre>Most probably, there is an error in your code. The library has many
- such compile-time assertions to ensure that invalid state machines cannot be
- compiled (for an idea what kinds of errors are reported at compile time, see
- the compile-fail tests). Above each of these assertions there is a comment
- explaining the problem. On almost all current compilers an error in template
- code is accompanied by the current "instantiation stack". Very much like the
- call stack you see in the debugger, this "instantiation stack" allows you to
- trace the error back through instantiations of library code until you hit the
- line of your code that causes the problem. As an example, here's the MSVC7.1
- error message for the code in InconsistentHistoryTest1.cpp:
- <pre>
- ...\boost\statechart\shallow_history.hpp(34) : error C2027: use of undefined type 'boost::STATIC_ASSERTION_FAILURE<x>'
- with
- [
- x=false
- ]
- ...\boost\statechart\shallow_history.hpp(34) : see reference to class template instantiation 'boost::STATIC_ASSERTION_FAILURE<x>' being compiled
- with
- [
- x=false
- ]
- ...\boost\statechart\simple_state.hpp(861) : see reference to class template instantiation 'boost::statechart::shallow_history<DefaultState>' being compiled
- with
- [
- DefaultState=B
- ]
- ...\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
- with
- [
- MostDerived=A,
- Context=InconsistentHistoryTest,
- InnerInitial=boost::mpl::list<boost::statechart::shallow_history<B>>,
- InnerList=boost::statechart::simple_state<A,InconsistentHistoryTest,boost::mpl::list<boost::statechart::shallow_history<B>>>::inner_initial_list
- ]
- ...\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
- with
- [
- MostDerived=A,
- Context=InconsistentHistoryTest,
- InnerInitial=boost::mpl::list<boost::statechart::shallow_history<B>>
- ]
- ...\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 &)'
- with
- [
- MostDerived=A,
- Context=InconsistentHistoryTest,
- InnerInitial=boost::mpl::list<boost::statechart::shallow_history<B>>
- ]
- ...\libs\statechart\test\InconsistentHistoryTest1.cpp(29) : see reference to class template instantiation 'boost::statechart::simple_state<MostDerived,Context,InnerInitial>' being compiled
- with
- [
- MostDerived=A,
- Context=InconsistentHistoryTest,
- InnerInitial=boost::mpl::list<boost::statechart::shallow_history<B>>
- ]
- </pre>Depending on the IDE you use, it is possible that you need to switch to
- another window to see this full error message (e.g. for Visual Studio 2003,
- you need to switch to the Output window). Starting at the top and going down
- the list of instantiations you see that each of them is accompanied by a file
- name and a line number. Ignoring all files belonging to the library, we find
- the culprit close to the bottom in file InconsistentHistoryTest1.cpp on line
- 29.
- </li>
- <li>The error is reported on a line nowhere near a BOOST_STATIC_ASSERT.
- Use the technique described under point 2 to see what line of your code
- causes the problem. If your code is correct then you've found a bug in
- either the compiler or Boost.Statechart. Please <a href=
- "contact.html">send me</a> a small but complete program showing the
- problem. Thank you!</li>
- </ol>
- <h2><a name="DisableHistory" id="DisableHistory">Is it possible to disable
- history for a state at runtime?</a></h2>
- <p>Yes, see <a href=
- "reference.html#clear_shallow_history">simple_state::clear_shallow_history()</a>
- and <a href=
- "reference.html#clear_deep_history">simple_state::clear_deep_history()</a>.
- Calling these functions is often preferable to introducting additional
- normal transitions when ...</p>
- <ul>
- <li>a state with history is the target of many transitions,
- <b>and/or</b></li>
- <li>the decision to ignore history is made in a different place than
- the transition to a state with history</li>
- </ul>
- <h2><a name="Dll" id="Dll">How can I compile a state machine into a dynamic
- link library (DLL)?</a></h2>
- <p>Invisible to the user, the library uses static data members to implement
- its own speed-optimized RTTI-mechanism for <code>event<></code> and
- <code>simple_state<></code> subtypes. Whenever such a subtype is
- defined in a header file and then included in multiple TUs, the linker
- later needs to eliminate the duplicate definitions of static data members.
- This usually works flawlessly as long as all these TUs are
- <b>statically</b> linked into the same binary. It is a lot more complex
- when DLLs are involved. The TuTest*.?pp files illustrate this:</p>
- <ul>
- <li><a href="../test/TuTest.hpp">TuTest.hpp</a>: Instantiates a class
- template containing a static data member</li>
- <li><a href="../test/TuTest.cpp">TuTest.cpp</a>: Includes TuTest.hpp and
- is compiled into a DLL</li>
- <li><a href="../test/TuTestMain.cpp">TuTestMain.cpp</a>: Includes
- TuTest.hpp and is compiled into an executable</li>
- </ul>
- <p>Without any precautions (e.g. <code>__declspec(dllexport)</code> on MSVC
- compatible compilers), on most platforms both binaries (exe & dll) now
- contain their own instance of the static data member. Since the RTTI
- mechanism assumes that there is exactly one object of that member at
- runtime, the mechanism fails spectacularly when the process running the exe
- also loads the dll. Different platforms deal differently with this
- problem:</p>
- <ul>
- <li>On some platforms (e.g. MinGW) there simply doesn't seem to be a way
- to enforce that such a member only exists once at runtime. Therefore, the
- internal RTTI mechanism cannot be used reliably in conjunction with DLLs.
- Disabling it by defining <a href=
- "configuration.html#ApplicationDefinedMacros">BOOST_STATECHART_USE_NATIVE_RTTI</a>
- in all TUs will <b>usually</b> work around the problem</li>
- <li>MSVC-compatible compilers support <code>__declspec(dllimport)</code>
- and <code>__declspec(dllexport)</code>, which allow to define exactly
- what needs to be loaded from a DLL (see TuTest for an example how to do
- this). Therefore, the internal RTTI mechanism can be used but care must
- be taken to correctly export and import all <code>event<></code>
- and <code>simple_state<></code> subtypes defined in headers that
- are compiled into more than one binary. Alternatively, of course <a href=
- "configuration.html#ApplicationDefinedMacros">BOOST_STATECHART_USE_NATIVE_RTTI</a>
- can also be used to save the work of importing and exporting</li>
- </ul>
- <h2><a name="PolymorphicEvents" id="PolymorphicEvents">Does
- Boost.Statechart support polymorphic events?</a></h2>
- <p>No. Although events can be derived from each other to write common code
- only once, <a href="definitions.html#Reaction">reactions</a> can only be
- defined for most-derived events.</p>
- <p>Example:</p>
- <pre>
- template< class MostDerived >
- struct EvButtonPressed : sc::event< MostDerived >
- {
- // common code
- };
- struct EvPlayButtonPressed :
- EvButtonPressed< EvPlayButtonPressed > {};
- struct EvStopButtonPressed :
- EvButtonPressed< EvStopButtonPressed > {};
- struct EvForwardButtonPressed :
- EvButtonPressed< EvForwardButtonPressed > {};
- /* ... */
- // We want to turn the player on, no matter what button we
- // press in the Off state. Although we can write the reaction
- // code only once, we must mention all most-derived events in
- // the reaction list.
- struct Off : sc::simple_state< Off, Mp3Player >
- {
- typedef mpl::list<
- sc::custom_reaction< EvPlayButtonPressed >,
- sc::custom_reaction< EvStopButtonPressed >,
- sc::custom_reaction< EvForwardButtonPressed >
- > reactions;
- template< class MostDerived >
- sc::result react( const EvButtonPressed< MostDerived > & )
- {
- // ...
- }
- };
- </pre>
- <h2><a name="WrongExitActionOrder" id="WrongExitActionOrder">Why are
- exit-actions called in the wrong order when I use multiple
- inheritance?</a></h2>
- <p><b>Update</b>: The implementation has changed considerably in this area.
- It is still possible to get this behavior under rare circumstances (when an
- action propagates an exception in a state machine with orthogonal regions
- <b>and</b> if the statechart layout satisfies certain conditions), but it
- can no longer be demonstrated with the example program below. However, the
- described workaround is still valid and ensures that this behavior will
- never show up.</p>
- <p>They definitely aren't for the <code>simple_state<></code> and
- <code>state<></code> subtypes, but the destructors of additional
- bases might be called in construction order (rather than the reverse
- construction order):</p>
- <pre>
- #include <boost/statechart/state_machine.hpp>
- #include <boost/statechart/simple_state.hpp>
- namespace sc = boost::statechart;
- class EntryExitDisplayer
- {
- protected:
- EntryExitDisplayer( const char * pName ) :
- pName_( pName )
- {
- std::cout << pName_ << " entered\n";
- }
- ~EntryExitDisplayer()
- {
- std::cout << pName_ << " exited\n";
- }
- private:
- const char * const pName_;
- };
- struct Outer;
- struct Machine : sc::state_machine< Machine, Outer > {};
- struct Inner;
- struct Outer : EntryExitDisplayer, sc::simple_state<
- Outer, Machine, Inner >
- {
- Outer() : EntryExitDisplayer( "Outer" ) {}
- };
- struct Inner : EntryExitDisplayer,
- sc::simple_state< Inner, Outer >
- {
- Inner() : EntryExitDisplayer( "Inner" ) {}
- };
- int main()
- {
- Machine myMachine;
- myMachine.initiate();
- return 0;
- }
- </pre>
- <p>This program will produce the following output:</p>
- <pre>
- Outer entered
- Inner entered
- Outer exited
- Inner exited
- </pre>
- <p>That is, the <b><code>EntryExitDisplayer</code> base class portion</b>
- of <code>Outer</code> is destructed before the one of <code>Inner</code>
- although <code>Inner::~Inner()</code> is called before
- <code>Outer::~Outer()</code>. This somewhat counter-intuitive behavior is
- caused by the following facts:</p>
- <ul>
- <li>The <code>simple_state<></code> base class portion of
- <code>Inner</code> is responsible to destruct <code>Outer</code></li>
- <li>Destructors of base class portions are called in the reverse order of
- construction</li>
- </ul>
- <p>So, when the <code>Outer</code> destructor is called the call stack
- looks as follows:</p>
- <pre>
- Outer::~Outer()
- simple_state< Inner, ... >::~simple_state()
- Inner::~Inner()
- </pre>
- <p>Note that <code>Inner::~Inner()</code> did not yet have a chance to
- destroy its <code>EntryExitDisplayer</code> base class portion, as it first
- has to call the destructor of the <b>second</b> base class. Now
- <code>Outer::~Outer()</code> will first destruct its <code>simple_state<
- Outer, ... ></code> base class portion and then do the same with its
- <code>EntryExitDisplayer</code> base class portion. The stack then unwinds
- back to <code>Inner::~Inner()</code>, which can then finally finish by
- calling <code>EntryExitDisplayer::~EntryExitDisplayer()</code>.</p>
- <p>Luckily, there is an easy work-around: Always let
- <code>simple_state<></code> and <code>state<></code> be the
- first base class of a state. This ensures that destructors of additional
- bases are called before recursion employed by state base destructors can
- alter the order of destruction.</p>
- <hr>
- <p><a href="http://validator.w3.org/check?uri=referer"><img border="0" src=
- "../../../doc/images/valid-html401.png" alt="Valid HTML 4.01 Transitional"
- height="31" width="88"></a></p>
- <p>Revised 05 January, 2008</p>
- <p><i>Copyright © 2003-2008 <a href="contact.html">Andreas Huber
- Dönni</a></i></p>
- <p><i>Distributed under the Boost Software License, Version 1.0. (See
- accompanying file <a href="../../../LICENSE_1_0.txt">LICENSE_1_0.txt</a> or
- copy at <a href=
- "http://www.boost.org/LICENSE_1_0.txt">http://www.boost.org/LICENSE_1_0.txt</a>)</i></p>
- </body>
- </html>
|