123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307 |
- <html><head>
- <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
- <title>Back-end</title><link rel="stylesheet" href="boostbook.css" type="text/css"><meta name="generator" content="DocBook XSL-NS Stylesheets V1.75.2"><link rel="home" href="index.html" title="Meta State Machine (MSM)"><link rel="up" href="ch03.html" title="Chapter 3. Tutorial"><link rel="prev" href="ch03s04.html" title="eUML"><link rel="next" href="ch04.html" title="Chapter 4. Performance / Compilers"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">Back-end</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="ch03s04.html">Prev</a> </td><th width="60%" align="center">Chapter 3. Tutorial</th><td width="20%" align="right"> <a accesskey="n" href="ch04.html">Next</a></td></tr></table><hr></div><div class="sect1" title="Back-end"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="d0e2298"></a>Back-end</h2></div></div></div><p>There is, at the moment, one back-end. This back-end contains the library
- engine and defines the performance and functionality trade-offs. The currently
- available back-end implements most of the functionality defined by the UML 2.0
- standard at very high runtime speed, in exchange for longer compile-time. The
- runtime speed is due to a constant-time double-dispatch and self-adapting
- capabilities allowing the framework to adapt itself to the features used by a
- given concrete state machine. All unneeded features either disable themselves or
- can be manually disabled. See section 5.1 for a complete description of the
- run-to-completion algorithm.</p><div class="sect2" title="Creation"><div class="titlepage"><div><div><h3 class="title"><a name="d0e2303"></a>Creation </h3></div></div></div><p>MSM being divided between front and back-end, one needs to first define a
- front-end. Then, to create a real state machine, the back-end must be
- declared:
- </p><pre class="programlisting">typedef msm::back::state_machine<my_front_end> my_fsm;</pre><p>We now have a fully functional state machine type. The next sections will
- describe what can be done with it.</p></div><div class="sect2" title="Starting and stopping a state machine"><div class="titlepage"><div><div><h3 class="title"><a name="d0e2312"></a><span class="command"><strong><a name="backend-start"></a></strong></span>Starting and stopping a state
- machine</h3></div></div></div><p>The <code class="code">start()</code> method starts the state machine, meaning it will
- activate the initial state, which means in turn that the initial state's
- entry behavior will be called. We need the start method because you do not
- always want the entry behavior of the initial state to be called immediately
- but only when your state machine is ready to process events. A good example
- of this is when you use a state machine to write an algorithm and each loop
- back to the initial state is an algorithm call. Each call to start will make
- the algorithm run once. The <a class="link" href="examples/iPodSearch.cpp" target="_top">iPodSearch</a> example uses this possibility.</p><p>The <code class="code">stop()</code> method works the same way. It will cause the exit
- actions of the currently active states(s) to be called.</p><p>Both methods are actually not an absolute need. Not calling them will
- simply cause your first entry or your last exit action not to be
- called.</p></div><div class="sect2" title="Event dispatching"><div class="titlepage"><div><div><h3 class="title"><a name="d0e2331"></a>Event dispatching</h3></div></div></div><p>The main reason to exist for a state machine is to dispatch events. For
- MSM, events are objects of a given event type. The object itself can contain
- data, but the event type is what decides of the transition to be taken. For
- MSM, if some_event is a given type (a simple struct for example) and e1 and
- e2 concrete instances of some_event, e1 and e2 are equivalent, from a
- transition perspective. Of course, e1 and e2 can have different values and
- you can use them inside actions. Events are dispatched as const reference,
- so actions cannot modify events for obvious side-effect reasons. To dispatch
- an event of type some_event, you can simply create one on the fly or
- instantiate if before processing: </p><pre class="programlisting">my_fsm fsm; fsm.process_event(some_event());
- some_event e1; fsm.process_event(e1)</pre><p>Creating an event on the fly will be optimized by the compiler so the
- performance will not degrade.</p></div><div class="sect2" title="Active state(s)"><div class="titlepage"><div><div><h3 class="title"><a name="d0e2340"></a>Active state(s)</h3></div></div></div><p>The backend also offers a way to know which state is active, though you
- will normally only need this for debugging purposes. If what you need simply
- is doing something with the active state, <span class="command"><strong><a class="command" href="ch02s02.html#UML-internal-transition">internal transitions</a></strong></span> or
- <span class="command"><strong><a class="command" href="ch03s05.html#backend-visitor">visitors</a></strong></span> are a better
- alternative. If you need to know what state is active, const int*
- current_state() will return an array of state ids. Please refer to the
- <span class="command"><strong><a class="command" href="ch06s03.html#internals-state-id">internals section</a></strong></span> to
- know how state ids are generated.</p></div><div class="sect2" title="Serialization"><div class="titlepage"><div><div><h3 class="title"><a name="d0e2354"></a><span class="command"><strong><a name="back-end-serialization"></a></strong></span>Serialization</h3></div></div></div><p>A common need is the ability to save a state machine and restore it at a
- different time. MSM supports this feature for the basic and functor
- front-ends, and in a more limited manner for eUML. MSM supports
- boost::serialization out of the box (by offering a <code class="code">serialize</code>
- function). Actually, for basic serialization, you need not do much, a MSM
- state machine is serializable almost like any other type. Without any
- special work, you can make a state machine remember its state, for
- example:</p><p>
- </p><pre class="programlisting">MyFsm fsm;
- // write to archive
- std::ofstream ofs("fsm.txt");
- // save fsm to archive
- {
- boost::archive::text_oarchive oa(ofs);
- // write class instance to archive
- oa << fsm;
- } </pre><p>
- </p><p>Loading back is very similar:</p><p>
- </p><pre class="programlisting">MyFsm fsm;
- {
- // create and open an archive for input
- std::ifstream ifs("fsm.txt");
- boost::archive::text_iarchive ia(ifs);
- // read class state from archive
- ia >> fsm;
- } </pre><p>
- </p><p>This will (de)serialize the state machine itself but not the concrete
- states' data. This can be done on a per-state basis to reduce the amount of
- typing necessary. To allow serialization of a concrete state, provide a
- do_serialize typedef and implement the serialize function:</p><p>
- </p><pre class="programlisting">struct Empty : public msm::front::state<>
- {
- // we want Empty to be serialized. First provide the typedef
- typedef int do_serialize;
- // then implement serialize
- template<class Archive>
- void serialize(Archive & ar, const unsigned int /* version */)
- {
- ar & some_dummy_data;
- }
- Empty():some_dummy_data(0){}
- int some_dummy_data;
- }; </pre><p>
- </p><p>You can also serialize data contained in the front-end class. Again, you
- need to provide the typedef and implement serialize:</p><p>
- </p><pre class="programlisting">struct player_ : public msm::front::state_machine_def<player_>
- {
- //we might want to serialize some data contained by the front-end
- int front_end_data;
- player_():front_end_data(0){}
- // to achieve this, provide the typedef
- typedef int do_serialize;
- // and implement serialize
- template<class Archive>
- void serialize(Archive & ar, const unsigned int )
- {
- ar & front_end_data;
- }
- ...
- }; </pre><p>
- </p><p>The saving of the back-end data (the current state(s)) is valid for all
- front-ends, so a front-end written using eUML can be serialized. However, to
- serialize a concrete state, the macros like
- <code class="code">BOOST_MSM_EUML_STATE</code> cannot be used, so the state will have
- to be implemented by directly inheriting from
- <code class="code">front::euml::euml_state</code>.</p><p>The only limitiation is that the event queues cannot be serialized so
- serializing must be done in a stable state, when no event is being
- processed. You can serialize during event processing only if using no queue
- (deferred or event queue).</p><p>This <a class="link" href="examples/Serialize.cpp" target="_top">example</a> shows a state machine which we serialize after processing an
- event. The <code class="code">Empty</code> state also has some data to serialize.</p></div><div class="sect2" title="Base state type"><div class="titlepage"><div><div><h3 class="title"><a name="d0e2407"></a><span class="command"><strong><a name="backend-base-state"></a></strong></span>Base state type </h3></div></div></div><p>Sometimes, one needs to customize states to avoid repetition and provide a
- common functionality, for example in the form of a virtual method. You might
- also want to make your states polymorphic so that you can call typeid on
- them for logging or debugging. It is also useful if you need a visitor, like
- the next section will show. You will notice that all front-ends offer the
- possibility of adding a base type. Note that all states and state machines
- must have the same base state, so this could reduce reuse. For example,
- using the basic front end, you need to:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Add the non-default base state in your msm::front::state<>
- definition, as first template argument (except for
- interrupt_states for which it is the second argument, the first
- one being the event ending the interrupt), for example,
- my_base_state being your new base state for all states in a
- given state machine:
- </p><pre class="programlisting">struct Empty : public msm::front::state<my_base_state></pre><p>
- Now, my_base_state is your new base state. If it has a virtual
- function, your states become polymorphic. MSM also provides a
- default polymorphic base type,
- <code class="code">msm::front::polymorphic_state</code>
- </p></li><li class="listitem"><p>Add the user-defined base state in the state machine frontend
- definition, as a second template argument, for example:
- </p><pre class="programlisting">struct player_ : public msm::front::state_machine<player_,my_base_state> </pre></li></ul></div><p>You can also ask for a state with a given id (which you might have gotten
- from current_state()) using <code class="code">const base_state* get_state_by_id(int id)
- const</code> where base_state is the one you just defined. You can now
- do something polymorphically.</p></div><div class="sect2" title="Visitor"><div class="titlepage"><div><div><h3 class="title"><a name="d0e2433"></a><span class="command"><strong><a name="backend-visitor"></a></strong></span>Visitor</h3></div></div></div><p>In some cases, having a pointer-to-base of the currently active states is
- not enough. You might want to call non-virtually a method of the currently
- active states. It will not be said that MSM forces the virtual keyword down
- your throat!</p><p>To achieve this goal, MSM provides its own variation of a visitor pattern
- using the previously described user-defined state technique. If you add to
- your user-defined base state an <code class="code">accept_sig</code> typedef giving the
- return value (unused for the moment) and parameters and provide an accept
- method with this signature, calling visit_current_states will cause accept
- to be called on the currently active states. Typically, you will also want
- to provide an empty default accept in your base state in order in order not
- to force all your states to implement accept. For example your base state
- could be:</p><pre class="programlisting">struct my_visitable_state
- {
- // signature of the accept function
- typedef args<void> accept_sig;
- // we also want polymorphic states
- virtual ~my_visitable_state() {}
- // default implementation for states who do not need to be visited
- void accept() const {}
- };</pre><p>This makes your states polymorphic and visitable. In this case, accept is
- made const and takes no argument. It could also be:</p><pre class="programlisting">struct SomeVisitor {…};
- struct my_visitable_state
- {
- // signature of the accept function
- typedef args<void,SomeVisitor&> accept_sig;
- // we also want polymorphic states
- virtual ~my_visitable_state() {}
- // default implementation for states who do not need to be visited
- void accept(SomeVisitor&) const {}
- };</pre><p>And now, <code class="code">accept</code> will take one argument (it could also be
- non-const). By default, <code class="code">accept</code> takes up to 2 arguments. To get
- more, set #define BOOST_MSM_VISITOR_ARG_SIZE to another value before
- including state_machine.hpp. For example:</p><pre class="programlisting">#define BOOST_MSM_VISITOR_ARG_SIZE 3
- #include <boost/msm/back/state_machine.hpp></pre><p>Note that accept will be called on ALL active states <span class="underline">and also automatically on sub-states of a
- submachine</span>.</p><p><span class="underline">Important warning</span>: The method
- visit_current_states takes its parameter by value, so if the signature of
- the accept function is to contain a parameter passed by reference, pass this
- parameter with a boost:ref/cref to avoid undesired copies or slicing. So,
- for example, in the above case, call:</p><pre class="programlisting">SomeVisitor vis; sm.visit_current_states(boost::ref(vis));</pre><p>This <a class="link" href="examples/SM-2Arg.cpp" target="_top">example</a> uses a
- visiting function with 2 arguments.</p></div><div class="sect2" title="Flags"><div class="titlepage"><div><div><h3 class="title"><a name="d0e2476"></a>Flags</h3></div></div></div><p>Flags is a MSM-only concept, supported by all front-ends, which base
- themselves on the functions: </p><pre class="programlisting">template <class Flag> bool is_flag_active()
- template <class Flag,class BinaryOp> bool is_flag_active()</pre><p>These functions return true if the currently active state(s) support the
- Flag property. The first variant ORs the result if there are several
- orthogonal regions, the second one expects OR or AND, for example:</p><pre class="programlisting">my_fsm.is_flag_active<MyFlag>()
- my_fsm.is_flag_active<MyFlag,my_fsm_type::Flag_OR>()</pre><p>Please refer to the front-ends sections for usage examples.</p></div><div class="sect2" title="Getting a state"><div class="titlepage"><div><div><h3 class="title"><a name="d0e2489"></a>Getting a state</h3></div></div></div><p>It is sometimes necessary to have the client code get access to the
- states' data. After all, the states are created once for good and hang
- around as long as the state machine does so why not use it? You simply just
- need sometimes to get information about any state, even inactive ones. An
- example is if you want to write a coverage tool and know how many times a
- state was visited. To get a state, use the get_state method giving the state
- name, for example: </p><pre class="programlisting">player::Stopped* tempstate = p.get_state<player::Stopped*>();</pre><p> or </p><pre class="programlisting">player::Stopped& tempstate2 = p.get_state<player::Stopped&>();</pre><p>depending on your personal taste. </p></div><div class="sect2" title="State machine constructor with arguments"><div class="titlepage"><div><div><h3 class="title"><a name="d0e2502"></a><span class="command"><strong><a name="backend-fsm-constructor-args"></a></strong></span> State machine constructor with arguments </h3></div></div></div><p>You might want to define a state machine with a non-default constructor.
- For example, you might want to write: </p><pre class="programlisting">struct player_ : public msm::front::state_machine_def<player_>
- {
- player_(int some_value){…}
- }; </pre><p>This is possible, using the back-end as forwarding object: </p><pre class="programlisting">typedef msm::back::state_machine<player_ > player; player p(3);</pre><p>The back-end will call the corresponding front-end constructor upon
- creation.</p><p>You can pass arguments up to the value of the
- BOOST_MSM_CONSTRUCTOR_ARG_SIZE macro (currently 5) arguments. Change this
- value before including any header if you need to overwrite the default. </p><p>You can also pass arguments by reference (or const-reference) using
- boost::ref (or boost::cref):</p><pre class="programlisting">struct player_ : public msm::front::state_machine_def<player_>
- {
- player_(SomeType& t, int some_value){…}
- };
- typedef msm::back::state_machine<player_ > player;
- SomeType data;
- player p(boost::ref(data),3);
- </pre><p>Normally, MSM default-constructs all its states or submachines. There are
- however cases where you might not want this. An example is when you use a
- state machine as submachine, and this submachine used the above defined
- constructors. You can add as first argument of the state machine constructor
- an expression where existing states are passed and copied:</p><pre class="programlisting">player p( back::states_ << state_1 << ... << state_n , boost::ref(data),3);</pre><p>Where state_1..n are instances of some or all of the states of the state
- machine. Submachines being state machines, this can recurse, for example, if
- Playing is a submachine containing a state Song1 having itself a constructor
- where some data is passed:</p><pre class="programlisting">player p( back::states_ << Playing(back::states_ << Song1(some_Song1_data)) ,
- boost::ref(data),3);</pre><p>It is also possible to replace a given state by a new instance at any time
- using <code class="code">set_states()</code> and the same syntax, for example:
- </p><pre class="programlisting">p.set_states( back::states_ << state_1 << ... << state_n );</pre><p>An <a class="link" href="examples/Constructor.cpp" target="_top">example</a> making intensive use of this capability is provided.</p></div><div class="sect2" title="Trading run-time speed for better compile-time / multi-TU compilation"><div class="titlepage"><div><div><h3 class="title"><a name="d0e2542"></a><span class="command"><strong><a name="backend-tradeof-rt-ct"></a></strong></span>Trading run-time speed for
- better compile-time / multi-TU compilation</h3></div></div></div><p>MSM is optimized for run-time speed at the cost of longer compile-time.
- This can become a problem with older compilers and big state machines,
- especially if you don't really care about run-time speed that much and would
- be satisfied by a performance roughly the same as most state machine
- libraries. MSM offers a back-end policy to help there. But before you try
- it, if you are using a VC compiler, deactivate the /Gm compiler option
- (default for debug builds). This option can cause builds to be 3 times
- longer... If the compile-time still is a problem, read further. MSM offers a
- policy which will speed up compiling in two main cases:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>many transition conflicts</p></li><li class="listitem"><p>submachines</p></li></ul></div><p>The back-end <code class="code">msm::back::state_machine</code> has a policy argument
- (first is the front-end, then the history policy) defaulting to
- <code class="code">favor_runtime_speed</code>. To switch to
- <code class="code">favor_compile_time</code>, which is declared in
- <code class="code"><msm/back/favor_compile_time.hpp></code>, you need to:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>switch the policy to <code class="code">favor_compile_time</code> for the
- main state machine (and possibly submachines)</p></li><li class="listitem"><p>move the submachine declarations into their own header which
- includes
- <code class="code"><msm/back/favor_compile_time.hpp></code></p></li><li class="listitem"><p>add for each submachine a cpp file including your header and
- calling a macro, which generates helper code, for
- example:</p><pre class="programlisting">#include "mysubmachine.hpp"
- BOOST_MSM_BACK_GENERATE_PROCESS_EVENT(mysubmachine)</pre></li><li class="listitem"><p>configure your compiler for multi-core compilation</p></li></ul></div><p>You will now compile your state machine on as many cores as you have
- submachines, which will greatly speed up the compilation if you factor your
- state machine into smaller submachines.</p><p>Independently, transition conflicts resolution will also be much
- faster.</p><p>This policy uses boost.any behind the hood, which means that we will lose
- a feature which MSM offers with the default policy, <a class="link" href="ch03s02.html#event-hierarchy">event hierarchy</a>. The following
- example takes our iPod example and speeds up compile-time by using this
- technique. We have:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p><a class="link" href="examples/iPod_distributed/iPod.cpp" target="_top">our main
- state machine and main function</a></p></li><li class="listitem"><p><a class="link" href="examples/iPod_distributed/PlayingMode.hpp" target="_top">PlayingMode moved to a separate header</a></p></li><li class="listitem"><p><a class="link" href="examples/iPod_distributed/PlayingMode.cpp" target="_top">a
- cpp for PlayingMode</a></p></li><li class="listitem"><p><a class="link" href="examples/iPod_distributed/MenuMode.hpp" target="_top">MenuMode moved to a separate header</a></p></li><li class="listitem"><p><a class="link" href="examples/iPod_distributed/MenuMode.cpp" target="_top">a
- cpp for MenuMode</a></p></li><li class="listitem"><p><a class="link" href="examples/iPod_distributed/Events.hpp" target="_top">events
- move to a separate header as all machines use
- it</a></p></li></ul></div><p>
- </p></div><div class="sect2" title="Compile-time state machine analysis"><div class="titlepage"><div><div><h3 class="title"><a name="d0e2624"></a><span class="command"><strong><a name="backend-compile-time-analysis"></a></strong></span>Compile-time state machine analysis </h3></div></div></div><p>A MSM state machine being a metaprogram, it is only logical that cheking
- for the validity of a concrete state machine happens compile-time. To this
- aim, using the compile-time graph library <a class="link" href="http://www.dynagraph.org/mpl_graph/" target="_top">mpl_graph</a> (delivered at the moment
- with MSM) from Gordon Woodhull, MSM provides several compile-time checks:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Check that orthogonal regions ar truly orthogonal.</p></li><li class="listitem"><p>Check that all states are either reachable from the initial
- states or are explicit entries / pseudo-entry states.</p></li></ul></div><p>To make use of this feature, the back-end provides a policy (default is no
- analysis), <code class="code">msm::back::mpl_graph_fsm_check</code>. For example:</p><pre class="programlisting"> typedef msm::back::state_machine< player_,msm::back::mpl_graph_fsm_check> player; </pre><p>As MSM is now using Boost.Parameter to declare policies, the policy choice
- can be made at any position after the front-end type (in this case
- <code class="code">player_</code>).</p><p>In case an error is detected, a compile-time assertion is provoked.</p><p>This feature is not enabled by default because it has a non-neglectable
- compile-time cost. The algorithm is linear if no explicit or pseudo entry
- states are found in the state machine, unfortunately still O(number of
- states * number of entry states) otherwise. This will be improved in future
- versions of MSM.</p><p>The same algorithm is also used in case you want to omit providing the
- region index in the <span class="command"><strong><a class="command" href="ch03s02.html#explicit-entry-no-region-id">explicit entry / pseudo entry state</a></strong></span> declaration.</p><p>The author's advice is to enable the checks after any state machine
- structure change and disable it again after sucessful analysis.</p><p>The <a class="link" href="examples/TestErrorOrthogonality.cpp" target="_top">following example</a> provokes an assertion if one of the first two lines
- of the transition table is used.</p></div><div class="sect2" title="Enqueueing events for later processing"><div class="titlepage"><div><div><h3 class="title"><a name="d0e2668"></a><span class="command"><strong><a name="backend-enqueueing"></a></strong></span> Enqueueing events for later
- processing </h3></div></div></div><p>Calling <code class="code">process_event(Event const&)</code> will immediately
- process the event with run-to-completion semantics. You can also enqueue the
- events and delay their processing by calling <code class="code">enqueue_event(Event
- const&)</code> instead. Calling <code class="code">execute_queued_events()</code>
- will then process all enqueued events (in FIFO order). Calling
- <code class="code">execute_single_queued_event()</code> will execute the oldest
- enqueued event.</p><p>You can query the queue size by calling <code class="code">get_message_queue_size()</code>.</p></div><div class="sect2" title="Customizing the message queues"><div class="titlepage"><div><div><h3 class="title"><a name="d0e2691"></a><span class="command"><strong><a name="backend-queues"></a></strong></span> Customizing the message queues </h3></div></div></div><p>MSM uses by default a std::deque for its queues (one message queue for
- events generated during run-to-completion or with
- <code class="code">enqueue_event</code>, one for deferred events). Unfortunately, on some
- STL implementations, it is a very expensive container in size and copying
- time. Should this be a problem, MSM offers an alternative based on
- boost::circular_buffer. The policy is msm::back::queue_container_circular.
- To use it, you need to provide it to the back-end definition:</p><pre class="programlisting"> typedef msm::back::state_machine< player_,msm::back::queue_container_circular> player; </pre><p>You can access the queues with get_message_queue and get_deferred_queue,
- both returning a reference or a const reference to the queues themselves.
- Boost::circular_buffer is outside of the scope of this documentation. What
- you will however need to define is the queue capacity (initially is 0) to
- what you think your queue will at most grow, for example (size 1 is
- common):</p><pre class="programlisting"> fsm.get_message_queue().set_capacity(1); </pre></div><div class="sect2" title="Policy definition with Boost.Parameter"><div class="titlepage"><div><div><h3 class="title"><a name="d0e2706"></a><span class="command"><strong><a name="backend-boost-parameter"></a></strong></span>Policy definition with Boost.Parameter </h3></div></div></div><p>MSM uses Boost.Parameter to allow easier definition of
- back::state_machine<> policy arguments (all except the front-end). This
- allows you to define policy arguments (history, compile-time / run-time,
- state machine analysis, container for the queues) at any position, in any
- number. For example: </p><pre class="programlisting"> typedef msm::back::state_machine< player_,msm::back::mpl_graph_fsm_check> player;
- typedef msm::back::state_machine< player_,msm::back::AlwaysHistory> player;
- typedef msm::back::state_machine< player_,msm::back::mpl_graph_fsm_check,msm::back::AlwaysHistory> player;
- typedef msm::back::state_machine< player_,msm::back::AlwaysHistory,msm::back::mpl_graph_fsm_check> player; </pre></div><div class="sect2" title="Choosing when to switch active states"><div class="titlepage"><div><div><h3 class="title"><a name="d0e2714"></a><span class="command"><strong><a name="backend-state-switch"></a></strong></span>Choosing when to switch active
- states </h3></div></div></div><p>The UML Standard is silent about a very important question: when a
- transition fires, at which exact point is the target state the new active
- state of a state machine? At the end of the transition? After the source
- state has been left? What if an exception is thrown? The Standard considers
- that run-to-completion means a transition completes in almost no time. But
- even this can be in some conditions a very very long time. Consider the
- following example. We have a state machine representing a network
- connection. We can be <code class="code">Connected</code> and <code class="code">Disconnected</code>. When we move from one
- state to another, we send a (Boost) Signal to another entity. By default,
- MSM makes the target state as the new state after the transition is
- completed. We want to send a signal based on a flag is_connected which is
- true when in state Connected.</p><p>We are in state <code class="code">Disconnected</code> and receive an event <code class="code">connect</code>. The transition
- action will ask the state machine <code class="code">is_flag_active<is_connected></code> and will
- get... false because we are still in <code class="code">Disconnected</code>. Hmm, what to do? We could
- queue the action and execute it later, but it means an extra queue, more
- work and higher run-time.</p><p>MSM provides the possibility (in form of a policy) for a front-end to
- decide when the target state becomes active. It can be:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>before the transition fires, if the guard will allow the
- transition to fire:
- <code class="code">active_state_switch_before_transition</code></p></li><li class="listitem"><p>after calling the exit action of the source state:
- <code class="code">active_state_switch_after_exit</code></p></li><li class="listitem"><p>after the transition action is executed:
- <code class="code">active_state_switch_after_transition_action</code></p></li><li class="listitem"><p>after the entry action of the target state is executed
- (default): <code class="code">active_state_switch_after_entry</code></p></li></ul></div><p>The problem and the solution is shown for the
- <a class="link" href="examples/ActiveStateSetBeforeTransition.cpp" target="_top">functor-front-end</a>
- and <a class="link" href="examples/ActivateStateBeforeTransitionEuml.cpp" target="_top">eUML</a>. Removing <code class="code">active_state_switch_before_transition</code>
- will show the default state. </p></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="ch03s04.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="ch03.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="ch04.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">eUML </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> Chapter 4. Performance / Compilers</td></tr></table></div></body></html>
|