ch03s03.html 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. <html><head>
  2. <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
  3. <title>Functor front-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&nbsp;3.&nbsp;Tutorial"><link rel="prev" href="ch03s02.html" title="Basic front-end"><link rel="next" href="ch03s04.html" title="eUML"></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">Functor front-end</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="ch03s02.html">Prev</a>&nbsp;</td><th width="60%" align="center">Chapter&nbsp;3.&nbsp;Tutorial</th><td width="20%" align="right">&nbsp;<a accesskey="n" href="ch03s04.html">Next</a></td></tr></table><hr></div><div class="sect1" title="Functor front-end"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="d0e1224"></a><span class="command"><strong><a name="functor-front-end"></a></strong></span>Functor front-end</h2></div></div></div><p>The functor front-end is the preferred front-end at the moment. It is more
  4. powerful than the standard front-end and has a more readable transition table.
  5. It also makes it easier to reuse parts of state machines. Like <span class="command"><strong><a class="command" href="ch03s04.html#eUML-front-end">eUML</a></strong></span>, it also comes with a good deal
  6. of predefined actions. Actually, eUML generates a functor front-end through
  7. Boost.Typeof and Boost.Proto so both offer the same functionality.</p><p>The rows which MSM offered in the previous front-end come in different
  8. flavors. We saw the a_row, g_row, _row, row, not counting internal rows. This is
  9. already much to know, so why define new rows? These types have some
  10. disadvantages: </p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>They are more typing and information than we would wish. This
  11. means syntactic noise and more to learn.</p></li><li class="listitem"><p>Function pointers are weird in C++.</p></li><li class="listitem"><p>The action/guard signature is limited and does not allow for more
  12. variations of parameters (source state, target state, current state
  13. machine, etc.)</p></li><li class="listitem"><p>It is not easy to reuse action code from a state machine to
  14. another.</p></li></ul></div><div class="sect2" title="Transition table"><div class="titlepage"><div><div><h3 class="title"><a name="d0e1248"></a> Transition table </h3></div></div></div><p>We can change the definition of the simple tutorial's transition table
  15. to:</p><pre class="programlisting">
  16. struct transition_table : mpl::vector&lt;
  17. // Start Event Target Action Guard
  18. // +---------+------------+-----------+---------------------------+----------------------------+
  19. Row &lt; Stopped , play , Playing , start_playback , none &gt;,
  20. Row &lt; Stopped , open_close , Open , open_drawer , none &gt;,
  21. Row &lt; Stopped , stop , Stopped , none , none &gt;,
  22. // +---------+------------+-----------+---------------------------+----------------------------+
  23. Row &lt; Open , open_close , Empty , close_drawer , none &gt;,
  24. // +---------+------------+-----------+---------------------------+----------------------------+
  25. Row &lt; Empty , open_close , Open , open_drawer , none &gt;,
  26. Row &lt; Empty , cd_detected, Stopped , store_cd_info , good_disk_format &gt;,
  27. g_row&lt; Empty , cd_detected, Playing , &amp;player_::store_cd_info , &amp;player_::auto_start &gt;,
  28. // +---------+------------+-----------+---------------------------+----------------------------+
  29. Row &lt; Playing , stop , Stopped , stop_playback , none &gt;,
  30. Row &lt; Playing , pause , Paused , pause_playback , none &gt;,
  31. Row &lt; Playing , open_close , Open , stop_and_open , none &gt;,
  32. // +---------+------------+-----------+---------------------------+----------------------------+
  33. Row &lt; Paused , end_pause , Playing , resume_playback , none &gt;,
  34. Row &lt; Paused , stop , Stopped , stop_playback , none &gt;,
  35. Row &lt; Paused , open_close , Open , stop_and_open , none &gt;
  36. // +---------+------------+-----------+---------------------------+----------------------------+
  37. &gt; {};
  38. </pre><p>Transitions are now of type "Row" with exactly 5 template arguments:
  39. source state, event, target state, action and guard. Wherever there is
  40. nothing (for example actions and guards), write "none". Actions and guards
  41. are no more methods but functors getting as arguments the detected event,
  42. the state machine, source and target state:</p><pre class="programlisting">struct store_cd_info
  43. {
  44. template &lt;class Fsm,class Evt,class SourceState,class TargetState&gt;
  45. void operator()(Evt const&amp;, Fsm&amp; fsm, SourceState&amp;,TargetState&amp; )
  46. {
  47. cout &lt;&lt; "player::store_cd_info" &lt;&lt; endl;
  48. fsm.process_event(play());
  49. }
  50. }; </pre><p>The advantage of functors compared to functions are that functors are
  51. generic and reusable. They also allow passing more parameters than just
  52. events. The guard functors are the same but have an operator() returning a
  53. bool.</p><p>It is also possible to mix rows from different front-ends. To show this, a
  54. g_row has been left in the transition table. <span class="underline">Note:</span> in case the action functor is used in the transition
  55. table of a state machine contained inside a top-level state machine, the
  56. &#8220;fsm&#8221; parameter refers to the lowest-level state machine (referencing this
  57. action), not the top-level one.</p><p>To illustrate the reusable point, MSM comes with a whole set of predefined
  58. functors. Please refer to eUML for the <a class="link" href="pt02.html#Reference-begin">full list</a>. For example, we are now going to replace the first
  59. action by an action sequence and the guard by a more complex functor.</p><p>We decide we now want to execute two actions in the first transition
  60. (Stopped -&gt; Playing). We only need to change the action start_playback to
  61. </p><pre class="programlisting">ActionSequence_&lt; mpl::vector&lt;some_action, start_playback&gt; &gt;</pre><p>and
  62. now will execute some_action and start_playback every time the transition is
  63. taken. ActionSequence_ is a functor calling each action of the mpl::vector
  64. in sequence.</p><p>We also want to replace good_disk_format by a condition of the type:
  65. &#8220;good_disk_format &amp;&amp; (some_condition || some_other_condition)&#8221;. We
  66. can achieve this using And_ and Or_ functors:
  67. </p><pre class="programlisting">And_&lt;good_disk_format,Or_&lt; some_condition , some_other_condition&gt; &gt;</pre><p>It
  68. even starts looking like functional programming. MSM ships with functors for
  69. operators, state machine usage, STL algorithms or container methods.</p></div><div class="sect2" title="Defining states with entry/exit actions"><div class="titlepage"><div><div><h3 class="title"><a name="d0e1281"></a>Defining states with entry/exit actions</h3></div></div></div><p>You probably noticed that we just showed a different transition table and
  70. that we even mixed rows from different front-ends. This means that you can
  71. do this and leave the definitions for states unchanged. Most examples are
  72. doing this as it is the simplest solution. You still enjoy the simplicity of
  73. the first front-end with the extended power of the new transition types.
  74. This <a class="link" href="examples/SimpleWithFunctors.cpp" target="_top">tutorial</a>,
  75. adapted from the earlier example does just this.</p><p>Of course, it is also possible to define states where entry and exit
  76. actions are also provided as functors as these are generated by eUML and
  77. both front-ends are equivalent. For example, we can define a state
  78. as:</p><pre class="programlisting">struct Empty_Entry
  79. {
  80. template &lt;class Event,class Fsm,class State&gt;
  81. void operator()(Event const&amp;,Fsm&amp;,State&amp;)
  82. {
  83. ...
  84. }
  85. }; // same for Empty_Exit
  86. struct Empty_tag {};
  87. struct Empty : public msm::front::euml::func_state&lt;Empty_tag,Empty_Entry,Empty_Exit&gt;{};</pre><p>This also means that you can, like in the transition table, write entry /
  88. exit actions made of more complicated action combinations. The previous
  89. example can therefore <a class="link" href="examples/SimpleWithFunctors2.cpp" target="_top">be
  90. rewritten</a>.</p><p>Usually, however, one will probably use the standard state definition as
  91. it provides the same capabilities as this front-end state definition, unless
  92. one needs some of the shipped predefined functors or is a fan of functional
  93. programming.</p></div><div class="sect2" title="What do you actually do inside actions / guards (Part 2)?"><div class="titlepage"><div><div><h3 class="title"><a name="d0e1300"></a><span class="command"><strong><a name="functor-front-end-actions"></a></strong></span>What do you actually do inside actions / guards (Part 2)?</h3></div></div></div><p>Using the basic front-end, we saw how to pass data to actions through the
  94. event, that data common to all states could be stored in the state machine,
  95. state relevant data could be stored in the state and access as template
  96. parameter in the entry / exit actions. What was however missing was the
  97. capability to access relevant state data in the transition action. This is
  98. possible with this front-end. A transition's source and target state are
  99. also given as arguments. If the current calculation's state was to be found
  100. in the transition's source state (whatever it is), we could access
  101. it:</p><pre class="programlisting">struct send_rocket
  102. {
  103. template &lt;class Fsm,class Evt,class SourceState,class TargetState&gt;
  104. void operator()(Evt const&amp;, Fsm&amp; fsm, SourceState&amp; src,TargetState&amp; )
  105. {
  106. fire_rocket(evt.direction, src.current_calculation);
  107. }
  108. }; </pre><p>It was a little awkward to generate new events inside actions with the basic
  109. front-end. With the functor front-end it is much cleaner:</p><pre class="programlisting">struct send_rocket
  110. {
  111. template &lt;class Fsm,class Evt,class SourceState,class TargetState&gt;
  112. void operator()(Evt const&amp; evt, Fsm&amp; fsm, SourceState&amp; src,TargetState&amp;)
  113. {
  114. fire_rocket(evt.direction, src.current_calculation);
  115. fsm.process_event(rocket_launched());
  116. }
  117. }; </pre></div><div class="sect2" title="Defining a simple state machine"><div class="titlepage"><div><div><h3 class="title"><a name="d0e1312"></a>Defining a simple state machine</h3></div></div></div><p>Like states, state machines can be defined using the previous front-end,
  118. as the previous example showed, or with the functor front-end, which allows
  119. you to define a state machine entry and exit functions as functors, as in
  120. <a class="link" href="examples/SimpleWithFunctors2.cpp" target="_top">this
  121. example</a>.</p></div><div class="sect2" title="Anonymous transitions"><div class="titlepage"><div><div><h3 class="title"><a name="d0e1320"></a>Anonymous transitions</h3></div></div></div><p>Anonymous (completion) transitions are transitions without a named event.
  122. We saw how this front-end uses <code class="code">none</code> when no action or guard is
  123. required. We can also use <code class="code">none</code> instead of an event to mark an
  124. anonymous transition. For example, the following transition makes an
  125. immediate transition from State1 to State2:</p><pre class="programlisting">Row &lt; State1 , none , State2 &gt;</pre><p>The following transition does the same but calling an action in the
  126. process:</p><pre class="programlisting">Row &lt; State1 , none , State2 , State1ToState2, none &gt;</pre><p>The following diagram shows an example and its <a class="link" href="examples/AnonymousTutorialWithFunctors.cpp" target="_top">implementation</a>:</p><p><span class="inlinemediaobject"><img src="../images/Anonymous.jpg" width="70%"></span></p></div><div class="sect2" title="Internal transitions"><div class="titlepage"><div><div><h3 class="title"><a name="d0e1346"></a><span class="command"><strong><a name="functor-internal-transitions"></a></strong></span>Internal
  127. transitions</h3></div></div></div><p>The <a class="link" href="examples/SimpleTutorialInternalFunctors.cpp" target="_top">following example</a> uses internal transitions with the functor
  128. front-end. As for the simple standard front-end, both methods of defining
  129. internal transitions are supported:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>providing a <code class="code">Row</code> in the state machine's transition
  130. table with <code class="code">none</code> as target state defines an internal
  131. transition.</p></li><li class="listitem"><p>providing an <code class="code">internal_transition_table</code> made of
  132. <code class="code">Internal</code> rows inside a state or submachine
  133. defines UML-conform internal transitions with higher
  134. priority.</p></li><li class="listitem"><p>transitions defined inside
  135. <code class="code">internal_transition_table</code> require no source or
  136. target state as the source state is known (<code class="code">Internal</code>
  137. really are <code class="code">Row</code> without a source or target state)
  138. .</p></li></ul></div><p>Like for the <span class="command"><strong><a class="command" href="ch03s02.html#internal-transitions-note">standard front-end internal transitions</a></strong></span>, internal transition
  139. tables are added into the main state machine's table, thus allowing you to
  140. distribute the transition table definition and reuse states.</p><p>There is an added bonus offered for submachines, which can have both the
  141. standard transition_table and an internal_transition_table (which has higher
  142. priority). This makes it easier if you decide to make a full submachine from
  143. a state later. It is also slightly faster than the standard alternative,
  144. adding orthogonal regions, because event dispatching will, if accepted by
  145. the internal table, not continue to the subregions. This gives you a O(1)
  146. dispatch instead of O(number of regions). While the example is with eUML,
  147. the same is also possible with this front-end.</p></div><div class="sect2" title="Kleene (any) event"><div class="titlepage"><div><div><h3 class="title"><a name="d0e1392"></a><span class="command"><strong><a name="any-event"></a></strong></span>Kleene (any) event</h3></div></div></div><p>Normally, MSM requires an event to fire a transition. But there are cases,
  148. where any event, no matter which one would do:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>If you want to reduce the number of transitions: any event
  149. would do, possibly will guards decide what happens</p></li><li class="listitem"><p>Pseudo entry states do not necessarily want to know the event
  150. which caused their activation, or they might want to know only a
  151. property of it.</p></li></ul></div><p>MSM supports a boost::any as an acceptable event. This event will match
  152. any event, meaning that if a transition with boost::any as event originates
  153. from the current state, this transition would fire (provided no guards or
  154. transition with a higher priority fires first). This event is named Kleene,
  155. as reference top the Kleene star used in a regex.</p><p>For example, this transition on a state machine instance named fsm:</p><pre class="programlisting">Row &lt; State1, boost::any, State2&gt;</pre><p>will fire if State1 is active and an event is processed:</p><pre class="programlisting">fsm.process_event(whatever_event());</pre><p>At this point, you can use this <span class="italic">any</span>
  156. event in transition actions to get back to the original event by calling for
  157. example<span class="italic"> boost::any::type()</span>.</p><p>It is also possible to support your own Kleene events by specializing
  158. boost::msm::is_kleene_event for a given event, for example:</p><pre class="programlisting">namespace boost { namespace msm{
  159. template&lt;&gt;
  160. struct is_kleene_event&lt; my_event &gt;
  161. {
  162. typedef boost::mpl::true_ type;
  163. };
  164. }}</pre><p>The only requirement is that this event must have a copy constructor from
  165. the event originally processed on the state machine.</p></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="ch03s02.html">Prev</a>&nbsp;</td><td width="20%" align="center"><a accesskey="u" href="ch03.html">Up</a></td><td width="40%" align="right">&nbsp;<a accesskey="n" href="ch03s04.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">Basic front-end&nbsp;</td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top">&nbsp;eUML</td></tr></table></div></body></html>