]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
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 3. 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> </td><th width="60%" align="center">Chapter 3. Tutorial</th><td width="20%" align="right"> <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< | |
17 | // Start Event Target Action Guard | |
18 | // +---------+------------+-----------+---------------------------+----------------------------+ | |
19 | Row < Stopped , play , Playing , start_playback , none >, | |
20 | Row < Stopped , open_close , Open , open_drawer , none >, | |
21 | Row < Stopped , stop , Stopped , none , none >, | |
22 | // +---------+------------+-----------+---------------------------+----------------------------+ | |
23 | Row < Open , open_close , Empty , close_drawer , none >, | |
24 | // +---------+------------+-----------+---------------------------+----------------------------+ | |
25 | Row < Empty , open_close , Open , open_drawer , none >, | |
26 | Row < Empty , cd_detected, Stopped , store_cd_info , good_disk_format >, | |
27 | g_row< Empty , cd_detected, Playing , &player_::store_cd_info , &player_::auto_start >, | |
28 | // +---------+------------+-----------+---------------------------+----------------------------+ | |
29 | Row < Playing , stop , Stopped , stop_playback , none >, | |
30 | Row < Playing , pause , Paused , pause_playback , none >, | |
31 | Row < Playing , open_close , Open , stop_and_open , none >, | |
32 | // +---------+------------+-----------+---------------------------+----------------------------+ | |
33 | Row < Paused , end_pause , Playing , resume_playback , none >, | |
34 | Row < Paused , stop , Stopped , stop_playback , none >, | |
35 | Row < Paused , open_close , Open , stop_and_open , none > | |
36 | // +---------+------------+-----------+---------------------------+----------------------------+ | |
37 | > {}; | |
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 <class Fsm,class Evt,class SourceState,class TargetState> | |
45 | void operator()(Evt const&, Fsm& fsm, SourceState&,TargetState& ) | |
46 | { | |
47 | cout << "player::store_cd_info" << 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 | “fsm” 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 -> Playing). We only need to change the action start_playback to | |
61 | </p><pre class="programlisting">ActionSequence_< mpl::vector<some_action, start_playback> ></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 | “good_disk_format && (some_condition || some_other_condition)”. We | |
66 | can achieve this using And_ and Or_ functors: | |
67 | </p><pre class="programlisting">And_<good_disk_format,Or_< some_condition , some_other_condition> ></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 <class Event,class Fsm,class State> | |
81 | void operator()(Event const&,Fsm&,State&) | |
82 | { | |
83 | ... | |
84 | } | |
85 | }; // same for Empty_Exit | |
86 | struct Empty_tag {}; | |
87 | struct Empty : public msm::front::euml::func_state<Empty_tag,Empty_Entry,Empty_Exit>{};</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 <class Fsm,class Evt,class SourceState,class TargetState> | |
104 | void operator()(Evt const&, Fsm& fsm, SourceState& src,TargetState& ) | |
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 <class Fsm,class Evt,class SourceState,class TargetState> | |
112 | void operator()(Evt const& evt, Fsm& fsm, SourceState& src,TargetState&) | |
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 < State1 , none , State2 ></pre><p>The following transition does the same but calling an action in the | |
126 | process:</p><pre class="programlisting">Row < State1 , none , State2 , State1ToState2, none ></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 < State1, boost::any, State2></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<> | |
160 | struct is_kleene_event< my_event > | |
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> </td><td width="20%" align="center"><a accesskey="u" href="ch03.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="ch03s04.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">Basic front-end </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> eUML</td></tr></table></div></body></html> |