diff options
author | Anas Nashif <anas.nashif@intel.com> | 2012-10-30 12:57:26 -0700 |
---|---|---|
committer | Anas Nashif <anas.nashif@intel.com> | 2012-10-30 12:57:26 -0700 |
commit | 1a78a62555be32868418fe52f8e330c9d0f95d5a (patch) | |
tree | d3765a80e7d3b9640ec2e930743630cd6b9fce2b /libs/msm | |
download | boost-1a78a62555be32868418fe52f8e330c9d0f95d5a.tar.gz boost-1a78a62555be32868418fe52f8e330c9d0f95d5a.tar.bz2 boost-1a78a62555be32868418fe52f8e330c9d0f95d5a.zip |
Imported Upstream version 1.49.0upstream/1.49.0
Diffstat (limited to 'libs/msm')
230 files changed, 48708 insertions, 0 deletions
diff --git a/libs/msm/doc/HTML/boostbook.css b/libs/msm/doc/HTML/boostbook.css new file mode 100644 index 0000000000..a428546c06 --- /dev/null +++ b/libs/msm/doc/HTML/boostbook.css @@ -0,0 +1,594 @@ +/*============================================================================= + Copyright (c) 2004 Joel de Guzman + http://spirit.sourceforge.net/ + + Distributed under the Boost Software License, Version 1.0. (See accompany- + ing file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ + +/*============================================================================= + Body defaults +=============================================================================*/ + + body + { + margin: 1em; + font-family: sans-serif; + } + +/*============================================================================= + Paragraphs +=============================================================================*/ + + p + { + text-align: left; + font-size: 10pt; + line-height: 1.15; + } + +/*============================================================================= + Program listings +=============================================================================*/ + + /* Code on paragraphs */ + p tt.computeroutput + { + font-size: 9pt; + } + + pre.synopsis + { + font-size: 90%; + margin: 1pc 4% 0pc 4%; + padding: 0.5pc 0.5pc 0.5pc 0.5pc; + } + + .programlisting, + .screen + { + font-size: 9pt; + display: block; + margin: 1pc 4% 0pc 4%; + padding: 0.5pc 0.5pc 0.5pc 0.5pc; + } + + /* Program listings in tables don't get borders */ + td .programlisting, + td .screen + { + margin: 0pc 0pc 0pc 0pc; + padding: 0pc 0pc 0pc 0pc; + } + +/*============================================================================= + Headings +=============================================================================*/ + + h1, h2, h3, h4, h5, h6 + { + text-align: left; + margin: 1em 0em 0.5em 0em; + font-weight: bold; + } + + h1 { font: 140% } + h2 { font: bold 140% } + h3 { font: bold 130% } + h4 { font: bold 120% } + h5 { font: italic 110% } + h6 { font: italic 100% } + + /* Top page titles */ + title, + h1.title, + h2.title + h3.title, + h4.title, + h5.title, + h6.title, + .refentrytitle + { + font-weight: bold; + margin-bottom: 1pc; + } + + h1.title { font-size: 140% } + h2.title { font-size: 140% } + h3.title { font-size: 130% } + h4.title { font-size: 120% } + h5.title { font-size: 110% } + h6.title { font-size: 100% } + + .section h1 + { + margin: 0em 0em 0.5em 0em; + font-size: 140%; + } + + .section h2 { font-size: 140% } + .section h3 { font-size: 130% } + .section h4 { font-size: 120% } + .section h5 { font-size: 110% } + .section h6 { font-size: 100% } + + /* Code on titles */ + h1 tt.computeroutput { font-size: 140% } + h2 tt.computeroutput { font-size: 140% } + h3 tt.computeroutput { font-size: 130% } + h4 tt.computeroutput { font-size: 120% } + h5 tt.computeroutput { font-size: 110% } + h6 tt.computeroutput { font-size: 100% } + +/*============================================================================= + Author +=============================================================================*/ + + h3.author + { + font-size: 100% + } + +/*============================================================================= + Lists +=============================================================================*/ + + li + { + font-size: 10pt; + line-height: 1.3; + } + + /* Unordered lists */ + ul + { + text-align: left; + } + + /* Ordered lists */ + ol + { + text-align: left; + } + +/*============================================================================= + Links +=============================================================================*/ + + a + { + text-decoration: none; /* no underline */ + } + + a:hover + { + text-decoration: underline; + } + +/*============================================================================= + Spirit style navigation +=============================================================================*/ + + .spirit-nav + { + text-align: right; + } + + .spirit-nav a + { + color: white; + padding-left: 0.5em; + } + + .spirit-nav img + { + border-width: 0px; + } + +/*============================================================================= + Copyright footer +=============================================================================*/ + .copyright-footer + { + text-align: right; + font-size: 70%; + } + + .copyright-footer p + { + text-align: right; + font-size: 80%; + } + +/*============================================================================= + Table of contents +=============================================================================*/ + + .toc + { + margin: 1pc 4% 0pc 4%; + padding: 0.1pc 1pc 0.1pc 1pc; + font-size: 80%; + line-height: 1.15; + } + + .boost-toc + { + float: right; + padding: 0.5pc; + } + +/*============================================================================= + Tables +=============================================================================*/ + + .table-title, + div.table p.title + { + margin-left: 4%; + padding-right: 0.5em; + padding-left: 0.5em; + } + + .informaltable table, + .table table + { + width: 92%; + margin-left: 4%; + margin-right: 4%; + } + + div.informaltable table, + div.table table + { + padding: 4px; + } + + /* Table Cells */ + div.informaltable table tr td, + div.table table tr td + { + padding: 0.5em; + text-align: left; + font-size: 9pt; + } + + div.informaltable table tr th, + div.table table tr th + { + padding: 0.5em 0.5em 0.5em 0.5em; + border: 1pt solid white; + font-size: 80%; + } + + table.simplelist + { + width: auto !important; + margin: 0em !important; + padding: 0em !important; + border: none !important; + } + table.simplelist td + { + margin: 0em !important; + padding: 0em !important; + text-align: left !important; + font-size: 9pt !important; + border: none !important; + } + +/*============================================================================= + Blurbs +=============================================================================*/ + + div.note, + div.tip, + div.important, + div.caution, + div.warning, + p.blurb + { + font-size: 9pt; /* A little bit smaller than the main text */ + line-height: 1.2; + display: block; + margin: 1pc 4% 0pc 4%; + padding: 0.5pc 0.5pc 0.5pc 0.5pc; + } + + p.blurb img + { + padding: 1pt; + } + +/*============================================================================= + Variable Lists +=============================================================================*/ + + div.variablelist + { + margin: 1em 0; + } + + /* Make the terms in definition lists bold */ + div.variablelist dl dt, + span.term + { + font-weight: bold; + font-size: 10pt; + } + + div.variablelist table tbody tr td + { + text-align: left; + vertical-align: top; + padding: 0em 2em 0em 0em; + font-size: 10pt; + margin: 0em 0em 0.5em 0em; + line-height: 1; + } + + div.variablelist dl dt + { + margin-bottom: 0.2em; + } + + div.variablelist dl dd + { + margin: 0em 0em 0.5em 2em; + font-size: 10pt; + } + + div.variablelist table tbody tr td p, + div.variablelist dl dd p + { + margin: 0em 0em 0.5em 0em; + line-height: 1; + } + +/*============================================================================= + Misc +=============================================================================*/ + + /* Title of books and articles in bibliographies */ + span.title + { + font-style: italic; + } + + span.underline + { + text-decoration: underline; + } + + span.strikethrough + { + text-decoration: line-through; + } + + /* Copyright, Legal Notice */ + div div.legalnotice p + { + text-align: left + } + +/*============================================================================= + Colors +=============================================================================*/ + + @media screen + { + body { + background-color: #FFFFFF; + color: #000000; + } + + /* Links */ + a + { + color: #005a9c; + } + + a:visited + { + color: #9c5a9c; + } + + h1 a, h2 a, h3 a, h4 a, h5 a, h6 a, + h1 a:hover, h2 a:hover, h3 a:hover, h4 a:hover, h5 a:hover, h6 a:hover, + h1 a:visited, h2 a:visited, h3 a:visited, h4 a:visited, h5 a:visited, h6 a:visited + { + text-decoration: none; /* no underline */ + color: #000000; + } + + /* Syntax Highlighting */ + .keyword { color: #0000AA; } + .identifier { color: #000000; } + .special { color: #707070; } + .preprocessor { color: #402080; } + .char { color: teal; } + .comment { color: #800000; } + .string { color: teal; } + .number { color: teal; } + .white_bkd { background-color: #FFFFFF; } + .dk_grey_bkd { background-color: #999999; } + + /* Copyright, Legal Notice */ + .copyright + { + color: #666666; + font-size: small; + } + + div div.legalnotice p + { + color: #666666; + } + + /* Program listing */ + pre.synopsis + { + border: 1px solid #DCDCDC; + } + + .programlisting, + .screen + { + border: 1px solid #DCDCDC; + } + + td .programlisting, + td .screen + { + border: 0px solid #DCDCDC; + } + + /* Blurbs */ + div.note, + div.tip, + div.important, + div.caution, + div.warning, + p.blurb + { + border: 1px solid #DCDCDC; + } + + /* Table of contents */ + .toc + { + border: 1px solid #DCDCDC; + } + + /* Tables */ + div.informaltable table tr td, + div.table table tr td + { + border: 1px solid #DCDCDC; + } + + div.informaltable table tr th, + div.table table tr th + { + background-color: #F0F0F0; + border: 1px solid #DCDCDC; + } + + .copyright-footer + { + color: #8F8F8F; + } + + /* Misc */ + span.highlight + { + color: #00A000; + } + } + + @media print + { + /* Links */ + a + { + color: black; + } + + a:visited + { + color: black; + } + + .spirit-nav + { + display: none; + } + + /* Program listing */ + pre.synopsis + { + border: 1px solid gray; + } + + .programlisting, + .screen + { + border: 1px solid gray; + } + + td .programlisting, + td .screen + { + border: 0px solid #DCDCDC; + } + + /* Table of contents */ + .toc + { + border: 1px solid gray; + } + + .informaltable table, + .table table + { + border: 1px solid gray; + border-collapse: collapse; + } + + /* Tables */ + div.informaltable table tr td, + div.table table tr td + { + border: 1px solid gray; + } + + div.informaltable table tr th, + div.table table tr th + { + border: 1px solid gray; + } + + table.simplelist tr td + { + border: none !important; + } + + /* Misc */ + span.highlight + { + font-weight: bold; + } + } + +/*============================================================================= + Images +=============================================================================*/ + + span.inlinemediaobject img + { + vertical-align: middle; + } + +/*============================================================================== + Super and Subscript: style so that line spacing isn't effected, see + http://www.adobe.com/cfusion/communityengine/index.cfm?event=showdetails&productId=1&postId=5341 +==============================================================================*/ + +sup, +sub { + height: 0; + line-height: 1; + vertical-align: baseline; + _vertical-align: bottom; + position: relative; + +} + +sup { + bottom: 1ex; +} + +sub { + top: .5ex; +} + diff --git a/libs/msm/doc/HTML/ch01.html b/libs/msm/doc/HTML/ch01.html new file mode 100644 index 0000000000..3509c7d38a --- /dev/null +++ b/libs/msm/doc/HTML/ch01.html @@ -0,0 +1,45 @@ +<html><head> + <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> + <title>Chapter 1. Founding idea</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="pt01.html" title="Part I. User' guide"><link rel="prev" href="pt01.html" title="Part I. User' guide"><link rel="next" href="ch02.html" title="Chapter 2. UML Short Guide"></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">Chapter 1. Founding idea</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="pt01.html">Prev</a> </td><th width="60%" align="center">Part I. User' guide</th><td width="20%" align="right"> <a accesskey="n" href="ch02.html">Next</a></td></tr></table><hr></div><div class="chapter" title="Chapter 1. Founding idea"><div class="titlepage"><div><div><h2 class="title"><a name="d0e99"></a>Chapter 1. Founding idea</h2></div></div></div><p>Let's start with an example taken from the C++ Template Metaprogramming + book:</p><pre class="programlisting">class player : public state_machine<player> +{ + // The list of FSM states enum states { Empty, Open, Stopped, Playing, Paused , initial_state = Empty }; + + // transition actions + void start_playback(play const&) { std::cout << "player::start_playback\n"; } + void open_drawer(open_close const&) { std::cout << "player::open_drawer\n"; } + // more transition actions + ... + typedef player p; // makes transition table cleaner + struct transition_table : mpl::vector11< + // Start Event Target Action + // +---------+------------+-----------+---------------------------+ + row< Stopped , play , Playing , &p::start_playback >, + row< Stopped , open_close , Open , &::open_drawer >, + // +---------+------------+-----------+---------------------------+ + row< Open , open_close , Empty , &p::close_drawer >, + // +---------+------------+-----------+---------------------------+ + row< Empty , open_close , Open , &p::open_drawer >, + row< Empty , cd_detected, Stopped , &p::store_cd_info >, + // +---------+------------+-----------+---------------------------+ + row< Playing , stop , Stopped , &p::stop_playback >, + row< Playing , pause , Paused , &p::pause_playback >, + row< Playing , open_close , Open , &p::stop_and_open >, + // +---------+------------+-----------+---------------------------+ + row< Paused , play , Playing , &p::resume_playback >, + row< Paused , stop , Stopped , &p::stop_playback >, + row< Paused , open_close , Open , &p::stop_and_open > + // +---------+------------+-----------+---------------------------+ + > {}; + // Replaces the default no-transition response. + template <class Event> + int no_transition(int state, Event const& e) + { + std::cout << "no transition from state " << state << " on event " << typeid(e).name() << std::endl; + return state; + } +}; </pre><p>This example is the foundation for the idea driving MSM: a descriptive and + expressive language based on a transition table with as little syntactic noise as + possible, all this while offering as many features from the UML 2.0 standard as + possible. MSM also offers several expressive state machine definition syntaxes with + different trade-offs.</p></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="pt01.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="pt01.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="ch02.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">Part I. User' guide </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> Chapter 2. UML Short Guide</td></tr></table></div></body></html>
\ No newline at end of file diff --git a/libs/msm/doc/HTML/ch02.html b/libs/msm/doc/HTML/ch02.html new file mode 100644 index 0000000000..52c1c2055f --- /dev/null +++ b/libs/msm/doc/HTML/ch02.html @@ -0,0 +1,9 @@ +<html><head> + <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> + <title>Chapter 2. UML Short Guide</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="pt01.html" title="Part I. User' guide"><link rel="prev" href="ch01.html" title="Chapter 1. Founding idea"><link rel="next" href="ch02s02.html" title="Concepts"></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">Chapter 2. UML Short Guide</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="ch01.html">Prev</a> </td><th width="60%" align="center">Part I. User' guide</th><td width="20%" align="right"> <a accesskey="n" href="ch02s02.html">Next</a></td></tr></table><hr></div><div class="chapter" title="Chapter 2. UML Short Guide"><div class="titlepage"><div><div><h2 class="title"><a name="d0e108"></a>Chapter 2. UML Short Guide</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="sect1"><a href="ch02.html#d0e111">What are state machines?</a></span></dt><dt><span class="sect1"><a href="ch02s02.html">Concepts</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch02s02.html#d0e121">State machine, state, transition, event </a></span></dt><dt><span class="sect2"><a href="ch02s02.html#d0e151">Submachines, orthogonal regions, pseudostates </a></span></dt><dt><span class="sect2"><a href="ch02s02.html#d0e194"> + History </a></span></dt><dt><span class="sect2"><a href="ch02s02.html#d0e208">Completion transitions / anonymous + transitions</a></span></dt><dt><span class="sect2"><a href="ch02s02.html#d0e220"> Internal transitions </a></span></dt><dt><span class="sect2"><a href="ch02s02.html#d0e226"> + Conflicting transitions </a></span></dt></dl></dd><dt><span class="sect1"><a href="ch02s03.html">State machine glossary</a></span></dt></dl></div><div class="sect1" title="What are state machines?"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="d0e111"></a>What are state machines?</h2></div></div></div><p>State machines are the description of a thing's lifeline. They describe the + different stages of the lifeline, the events influencing it, and what it does + when a particular event is detected at a particular stage. They offer the + complete specification of the dynamic behavior of the thing.</p></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="ch01.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="pt01.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="ch02s02.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">Chapter 1. Founding idea </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> Concepts</td></tr></table></div></body></html>
\ No newline at end of file diff --git a/libs/msm/doc/HTML/ch02s02.html b/libs/msm/doc/HTML/ch02s02.html new file mode 100644 index 0000000000..805cabed54 --- /dev/null +++ b/libs/msm/doc/HTML/ch02s02.html @@ -0,0 +1,98 @@ +<html><head> + <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> + <title>Concepts</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="ch02.html" title="Chapter 2. UML Short Guide"><link rel="prev" href="ch02.html" title="Chapter 2. UML Short Guide"><link rel="next" href="ch02s03.html" title="State machine glossary"></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">Concepts</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="ch02.html">Prev</a> </td><th width="60%" align="center">Chapter 2. UML Short Guide</th><td width="20%" align="right"> <a accesskey="n" href="ch02s03.html">Next</a></td></tr></table><hr></div><div class="sect1" title="Concepts"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="d0e116"></a>Concepts</h2></div></div></div><p>Thinking in terms of state machines is a bit surprising at first, so let us + have a quick glance at the concepts.</p><div class="sect2" title="State machine, state, transition, event"><div class="titlepage"><div><div><h3 class="title"><a name="d0e121"></a>State machine, state, transition, event </h3></div></div></div><p>A state machine is a concrete model describing the behavior of a system. + It is composed of a finite number of states and transitions.</p><p> + <span class="inlinemediaobject"><img src="../images/sm.gif"></span></p><p>A simple state has no sub states. It can have data, entry and exit + behaviors and deferred events. One can provide entry and exit behaviors + (also called actions) to states (or state machines), which are executed + whenever a state is entered or left, no matter how. A state can also have + internal transitions which cause no entry or exit behavior to be called. A + state can mark events as deferred. This means the event cannot be processed + if this state is active, but it must be retained. Next time a state not + deferring this event is active, the event will be processed, as if it had + just been fired. </p><p><span class="inlinemediaobject"><img src="../images/state.gif"></span></p><p>A transition is the switching between active states, triggered by an + event. Actions and guard conditions can be attached to the transition. The + action executes when the transition fires, the guard is a Boolean operation + executed first and which can prevent the transition from firing by returning + false.</p><p> + <span class="inlinemediaobject"><img src="../images/transition.jpg"></span></p><p>An initial state marks the first active state of a state machine. It has + no real existence and neither has the transition originating from it.</p><p> + <span class="inlinemediaobject"><img src="../images/init_state.gif"></span></p></div><div class="sect2" title="Submachines, orthogonal regions, pseudostates"><div class="titlepage"><div><div><h3 class="title"><a name="d0e151"></a>Submachines, orthogonal regions, pseudostates </h3></div></div></div><p>A composite state is a state containing a region or decomposed in two or + more regions. A composite state contains its own set of states and regions. </p><p>A submachine is a state machine inserted as a state in another state + machine. The same submachine can be inserted more than once. </p><p>Orthogonal regions are parts of a composite state or submachine, each + having its own set of mutually exclusive set of states and transitions. </p><p><span class="inlinemediaobject"><img src="../images/regions.gif" width="60%"></span></p><p>UML also defines a number of pseudo states, which are considered important + concepts to model, but not enough to make them first-class citizens. The + terminate pseudo state terminates the execution of a state machine (MSM + handles this slightly differently. The state machine is not destroyed but no + further event processing occurs.). </p><p><span class="inlinemediaobject"><img src="../images/terminate.gif"></span></p><p>An exit point pseudo state exits a composite state or a submachine and + forces termination of execution in all contained regions.</p><p><span class="inlinemediaobject"><img src="../images/exit.gif" width="60%"></span></p><p>An entry point pseudo state allows a kind of controlled entry inside a + composite. Precisely, it connects a transition outside the composite to a + transition inside the composite. An important point is that this mechanism + only allows a single region to be entered. In the above diagram, in region1, + the initial state would become active. </p><p><span class="inlinemediaobject"><img src="../images/entry_point.gif"></span></p><p>There are also two more ways to enter a submachine (apart the obvious and + more common case of a transition terminating on the submachine as shown in + the region case). An explicit entry means that an inside state is the target + of a transition. Unlike with direct entry, no tentative encapsulation is + made, and only one transition is executed. An explicit exit is a transition + from an inner state to a state outside the submachine (not supported by + MSM). I would not recommend using explicit entry or exit. </p><p><span class="inlinemediaobject"><img src="../images/explicit.gif"></span></p><p>The last entry possibility is using fork. A fork is an explicit entry into + one or more regions. Other regions are again activated using their initial + state. </p><p><span class="inlinemediaobject"><img src="../images/fork.gif" width="70%"></span></p></div><div class="sect2" title="History"><div class="titlepage"><div><div><h3 class="title"><a name="d0e194"></a> + <span class="command"><strong><a name="uml-history"></a></strong></span>History </h3></div></div></div><p>UML defines two kinds of history, shallow history and deep history. + Shallow history is a pseudo state representing the most recent substate of a + submachine. A submachine can have at most one shallow history. A transition + with a history pseudo state as target is equivalent to a transition with the + most recent substate as target. And very importantly, only one transition + may originate from the history. Deep history is a shallow history + recursively reactivating the substates of the most recent substate. It is + represented like the shallow history with a star (H* inside a + circle).</p><p> + <span class="inlinemediaobject"><img src="../images/history.gif" width="60%"></span></p><p>History is not a completely satisfying concept. First of all, there can be + just one history pseudo state and only one transition may originate from it. + So they do not mix well with orthogonal regions as only one region can be + “remembered”. Deep history is even worse and looks like a last-minute + addition. History has to be activated by a transition and only one + transition originates from it, so how to model the transition originating + from the deep history pseudo state and pointing to the most recent substate + of the substate? As a bonus, it is also inflexible and does not accept new + types of histories. Let's face it, history sounds great and is useful in + theory, but the UML version is not quite making the cut. And therefore, MSM + provides a different version of this useful concept. </p></div><div class="sect2" title="Completion transitions / anonymous transitions"><div class="titlepage"><div><div><h3 class="title"><a name="d0e208"></a><span class="command"><strong><a name="uml-anonymous"></a></strong></span>Completion transitions / anonymous + transitions</h3></div></div></div><p>Completion events (or transitions), also called anonymous transitions, are + defined as transitions having no defined event triggering them. This means + that such transitions will immediately fire when a state being the source of + an anonymous transition becomes active, provided that a guard allows it. + They are useful in modeling algorithms as an activity diagram would normally + do. In the real-time world, they have the advantage of making it easier to + estimate how long a periodically executed action will last. For example, + consider the following diagram. </p><p><span class="inlinemediaobject"><img src="../images/completion.gif"></span></p><p>The designer now knows at any time that he will need a maximum of 4 + transitions. Being able to estimate how long a transition takes, he can + estimate how much of a time frame he will need to require (real-time tasks + are often executed at regular intervals). If he can also estimate the + duration of actions, he can even use graph algorithms to better estimate his + timing requirements. </p></div><div class="sect2" title="Internal transitions"><div class="titlepage"><div><div><h3 class="title"><a name="d0e220"></a><span class="command"><strong><a name="UML-internal-transition"></a></strong></span> Internal transitions </h3></div></div></div><p>Internal transitions are transitions executing in the scope of the active + state, being a simple state or a submachine. One can see them as a + self-transition of this state, without an entry or exit action + called.</p></div><div class="sect2" title="Conflicting transitions"><div class="titlepage"><div><div><h3 class="title"><a name="d0e226"></a> + <span class="command"><strong><a name="transition-conflict"></a></strong></span>Conflicting transitions </h3></div></div></div><p>If, for a given event, several transitions are enabled, they are said to + be in conflict. There are two kinds of conflicts: </p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>For a given source state, several transitions are defined, + triggered by the same event. Normally, the guard condition in + each transition defines which one is fired.</p></li><li class="listitem"><p>The source state is a submachine or simple state and the + conflict is between a transition internal to this state and a + transition triggered by the same event and having as target + another state.</p></li></ul></div><p>The first one is simple; one only needs to define two or more + rows in the transition table, with the same source and trigger, with a + different guard condition. Beware, however, that the UML standard wants + these conditions to be not overlapping. If they do, the standard says + nothing except that this is incorrect, so the implementer is free to + implement it the way he sees fit. In the case of MSM, the transition + appearing last in the transition table gets selected first, if it returns + false (meaning disabled), the library tries with the previous one, and so + on.</p><p> + <span class="inlinemediaobject"><img src="../images/conflict1.gif"></span></p><p>In the second case, UML defines that the most inner transition gets + selected first, which makes sense, otherwise no exit point pseudo state + would be possible (the inner transition brings us to the exit point, from + where the containing state machine can take over). </p><p><span class="inlinemediaobject"><img src="../images/conflict2.gif" width="60%"></span></p><p>MSM handles both cases itself, so the designer needs only concentrate on + its state machine and the UML subtleties (not overlapping conditions), not + on implementing this behavior himself. </p></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="ch02.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="ch02.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="ch02s03.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">Chapter 2. UML Short Guide </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> State machine glossary</td></tr></table></div></body></html>
\ No newline at end of file diff --git a/libs/msm/doc/HTML/ch02s03.html b/libs/msm/doc/HTML/ch02s03.html new file mode 100644 index 0000000000..9f82003af6 --- /dev/null +++ b/libs/msm/doc/HTML/ch02s03.html @@ -0,0 +1,37 @@ +<html><head> + <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> + <title>State machine glossary</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="ch02.html" title="Chapter 2. UML Short Guide"><link rel="prev" href="ch02s02.html" title="Concepts"><link rel="next" href="ch03.html" title="Chapter 3. Tutorial"></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">State machine glossary</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="ch02s02.html">Prev</a> </td><th width="60%" align="center">Chapter 2. UML Short Guide</th><td width="20%" align="right"> <a accesskey="n" href="ch03.html">Next</a></td></tr></table><hr></div><div class="sect1" title="State machine glossary"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="d0e254"></a>State machine glossary</h2></div></div></div><p> + </p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>state machine: the life cycle of a thing. It is made of states, + regions, transitions and processes incoming events.</p></li><li class="listitem"><p>state: a stage in the life cycle of a state machine. A state (like + a submachine) can have an entry and exit behaviors.</p></li><li class="listitem"><p>event: an incident provoking (or not) a reaction of the state + machine</p></li><li class="listitem"><p>transition: a specification of how a state machine reacts to an + event. It specifies a source state, the event triggering the + transition, the target state (which will become the newly active + state if the transition is triggered), guard and actions.</p></li><li class="listitem"><p>action: an operation executed during the triggering of the + transition.</p></li><li class="listitem"><p>guard: a boolean operation being able to prevent the triggering of + a transition which would otherwise fire.</p></li><li class="listitem"><p>transition table: representation of a state machine. A state + machine diagram is a graphical, but incomplete representation of the + same model. A transition table, on the other hand, is a complete + representation.</p></li><li class="listitem"><p>initial state: The state in which the state machine starts. Having + several orthogonal regions means having as many initial + states.</p></li><li class="listitem"><p>submachine: A submachine is a state machine inserted as a state in + another state machine and can be found several times in a same state + machine.</p></li><li class="listitem"><p>orthogonal regions: (logical) parallel flow of execution of a + state machine. Every region of a state machine gets a chance to + process an incoming event.</p></li><li class="listitem"><p>terminate pseudo-state: when this state becomes active, it + terminates the execution of the whole state machine. MSM does not + destroy the state machine as required by the UML standard, however, + which lets you keep all the state machine's data.</p></li><li class="listitem"><p>entry/exit pseudo state: defined for submachines and are defined + as a connection between a transition outside of the submachine and a + transition inside the submachine. It is a way to enter or leave a + submachine through a predefined point.</p></li><li class="listitem"><p>fork: a fork allows explicit entry into several orthogonal regions + of a submachine.</p></li><li class="listitem"><p>history: a history is a way to remember the active state of a + submachine so that the submachine can proceed in its last active + state next time it becomes active.</p></li><li class="listitem"><p>completion events (also called completion/anonymous transitions): + when a transition has no named event triggering it, it automatically + fires when the source state is active, unless a guard forbids + it.</p></li><li class="listitem"><p>transition conflict: a conflict is present if for a given source + state and incoming event, several transitions are possible. UML + specifies that guard conditions have to solve the conflict.</p></li><li class="listitem"><p>internal transitions: transition from a state to itself without + having exit and entry actions being called.</p></li></ul></div><p> + </p></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="ch02s02.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="ch02.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="ch03.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">Concepts </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> Chapter 3. Tutorial</td></tr></table></div></body></html>
\ No newline at end of file diff --git a/libs/msm/doc/HTML/ch03.html b/libs/msm/doc/HTML/ch03.html new file mode 100644 index 0000000000..abcc6a8097 --- /dev/null +++ b/libs/msm/doc/HTML/ch03.html @@ -0,0 +1,21 @@ +<html><head> + <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> + <title>Chapter 3. Tutorial</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="pt01.html" title="Part I. User' guide"><link rel="prev" href="ch02s03.html" title="State machine glossary"><link rel="next" href="ch03s02.html" title="Basic front-end"></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">Chapter 3. Tutorial</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="ch02s03.html">Prev</a> </td><th width="60%" align="center">Part I. User' guide</th><td width="20%" align="right"> <a accesskey="n" href="ch03s02.html">Next</a></td></tr></table><hr></div><div class="chapter" title="Chapter 3. Tutorial"><div class="titlepage"><div><div><h2 class="title"><a name="d0e312"></a>Chapter 3. Tutorial</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="sect1"><a href="ch03.html#d0e315">Design</a></span></dt><dt><span class="sect1"><a href="ch03s02.html">Basic front-end</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch03s02.html#d0e334">A simple example</a></span></dt><dt><span class="sect2"><a href="ch03s02.html#d0e348">Transition table</a></span></dt><dt><span class="sect2"><a href="ch03s02.html#d0e396">Defining states with entry/exit actions</a></span></dt><dt><span class="sect2"><a href="ch03s02.html#d0e409">What do you actually do inside actions / guards?</a></span></dt><dt><span class="sect2"><a href="ch03s02.html#d0e461">Defining a simple state machine</a></span></dt><dt><span class="sect2"><a href="ch03s02.html#d0e519">Defining a submachine</a></span></dt><dt><span class="sect2"><a href="ch03s02.html#d0e554">Orthogonal regions, terminate state, event deferring</a></span></dt><dt><span class="sect2"><a href="ch03s02.html#d0e645">History</a></span></dt><dt><span class="sect2"><a href="ch03s02.html#d0e690">Completion (anonymous) transitions</a></span></dt><dt><span class="sect2"><a href="ch03s02.html#d0e717">Internal transitions</a></span></dt><dt><span class="sect2"><a href="ch03s02.html#d0e819">more row types</a></span></dt><dt><span class="sect2"><a href="ch03s02.html#d0e852">Explicit entry / entry and exit pseudo-state / fork</a></span></dt><dt><span class="sect2"><a href="ch03s02.html#d0e1041">Flags</a></span></dt><dt><span class="sect2"><a href="ch03s02.html#d0e1095">Event Hierarchy</a></span></dt><dt><span class="sect2"><a href="ch03s02.html#d0e1116">Customizing a state machine / Getting more speed</a></span></dt><dt><span class="sect2"><a href="ch03s02.html#d0e1165">Choosing the initial event</a></span></dt><dt><span class="sect2"><a href="ch03s02.html#d0e1178"> Containing state machine (deprecated)</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch03s03.html">Functor front-end</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch03s03.html#d0e1217"> Transition table </a></span></dt><dt><span class="sect2"><a href="ch03s03.html#d0e1250">Defining states with entry/exit actions</a></span></dt><dt><span class="sect2"><a href="ch03s03.html#d0e1269">What do you actually do inside actions / guards (Part 2)?</a></span></dt><dt><span class="sect2"><a href="ch03s03.html#d0e1281">Defining a simple state machine</a></span></dt><dt><span class="sect2"><a href="ch03s03.html#d0e1289">Anonymous transitions</a></span></dt><dt><span class="sect2"><a href="ch03s03.html#d0e1315">Internal + transitions</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch03s04.html">eUML (experimental)</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch03s04.html#d0e1394">Transition table</a></span></dt><dt><span class="sect2"><a href="ch03s04.html#d0e1435">A simple example: rewriting only our transition table</a></span></dt><dt><span class="sect2"><a href="ch03s04.html#d0e1466">Defining events, actions and states with entry/exit actions</a></span></dt><dt><span class="sect2"><a href="ch03s04.html#d0e1579">Wrapping up a simple state machine and first complete examples</a></span></dt><dt><span class="sect2"><a href="ch03s04.html#d0e1627">Defining a submachine</a></span></dt><dt><span class="sect2"><a href="ch03s04.html#d0e1643"> + Attributes / Function call</a></span></dt><dt><span class="sect2"><a href="ch03s04.html#d0e1743">Orthogonal regions, flags, event deferring</a></span></dt><dt><span class="sect2"><a href="ch03s04.html#d0e1855"> + Customizing a state machine / Getting + more speed</a></span></dt><dt><span class="sect2"><a href="ch03s04.html#d0e1890">Completion / Anonymous transitions</a></span></dt><dt><span class="sect2"><a href="ch03s04.html#d0e1908">Internal transitions</a></span></dt><dt><span class="sect2"><a href="ch03s04.html#d0e1939">Other state types</a></span></dt><dt><span class="sect2"><a href="ch03s04.html#d0e2003">Helper functions</a></span></dt><dt><span class="sect2"><a href="ch03s04.html#d0e2106">Phoenix-like STL support</a></span></dt><dt><span class="sect2"><a href="ch03s04.html#d0e2159">Writing actions with Boost.Phoenix (in development)</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch03s05.html">Back-end</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch03s05.html#d0e2218">Creation </a></span></dt><dt><span class="sect2"><a href="ch03s05.html#d0e2227">Starting and stopping a state + machine</a></span></dt><dt><span class="sect2"><a href="ch03s05.html#d0e2246">Event dispatching</a></span></dt><dt><span class="sect2"><a href="ch03s05.html#d0e2255">Active state(s)</a></span></dt><dt><span class="sect2"><a href="ch03s05.html#d0e2269">Serialization</a></span></dt><dt><span class="sect2"><a href="ch03s05.html#d0e2322">Base state type </a></span></dt><dt><span class="sect2"><a href="ch03s05.html#d0e2348">Visitor</a></span></dt><dt><span class="sect2"><a href="ch03s05.html#d0e2391">Flags</a></span></dt><dt><span class="sect2"><a href="ch03s05.html#d0e2404">Getting a state</a></span></dt><dt><span class="sect2"><a href="ch03s05.html#d0e2417"> State machine constructor with arguments </a></span></dt><dt><span class="sect2"><a href="ch03s05.html#d0e2457">Trading run-time speed for + better compile-time / multi-TU compilation</a></span></dt><dt><span class="sect2"><a href="ch03s05.html#d0e2539">Compile-time state machine analysis </a></span></dt><dt><span class="sect2"><a href="ch03s05.html#d0e2583"> Enqueueing events for later + processing </a></span></dt><dt><span class="sect2"><a href="ch03s05.html#d0e2603"> Customizing the message queues </a></span></dt><dt><span class="sect2"><a href="ch03s05.html#d0e2618">Policy definition with Boost.Parameter </a></span></dt><dt><span class="sect2"><a href="ch03s05.html#d0e2626">Choosing when to switch active + states </a></span></dt></dl></dd></dl></div><div class="sect1" title="Design"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="d0e315"></a>Design</h2></div></div></div><p>MSM is divided between front–ends and back-ends. At the moment, there is just + one back-end. On the front-end side, you will find three of them which are as + many state machine description languages, with many more possible. For potential + language writers, this document contains a <a class="link" href="ch06s02.html#internals-front-back-interface">description of the interface + between front-end and back-end</a>.</p><p>The first front-end is an adaptation of the example provided in the <a class="link" href="http://boostpro.com/mplbook" target="_top">MPL book</a> with actions + defined as pointers to state or state machine methods. The second one is based + on functors. The third, eUML (embedded UML) is an experimental language based on + Boost.Proto and Boost.Typeof and hiding most of the metaprogramming to increase + readability. Both eUML and the functor front-end also offer a functional library + (a bit like Boost.Phoenix) for use as action language (UML defining + none).</p></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="ch02s03.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="pt01.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="ch03s02.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">State machine glossary </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> Basic front-end</td></tr></table></div></body></html>
\ No newline at end of file diff --git a/libs/msm/doc/HTML/ch03s02.html b/libs/msm/doc/HTML/ch03s02.html new file mode 100644 index 0000000000..cea3b9d639 --- /dev/null +++ b/libs/msm/doc/HTML/ch03s02.html @@ -0,0 +1,654 @@ +<html><head> + <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> + <title>Basic 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="ch03.html" title="Chapter 3. Tutorial"><link rel="next" href="ch03s03.html" title="Functor front-end"></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">Basic front-end</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="ch03.html">Prev</a> </td><th width="60%" align="center">Chapter 3. Tutorial</th><td width="20%" align="right"> <a accesskey="n" href="ch03s03.html">Next</a></td></tr></table><hr></div><div class="sect1" title="Basic front-end"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="d0e328"></a><span class="command"><strong><a name="basic-front-end"></a></strong></span>Basic front-end</h2></div></div></div><p>This is the historical front-end, inherited from the MPL book. It provides a + transition table made of rows of different names and functionality. Actions and + guards are defined as methods and referenced through a pointer in the + transition. This front-end provides a simple interface making easy state + machines easy to define, but more complex state machines a bit harder.</p><div class="sect2" title="A simple example"><div class="titlepage"><div><div><h3 class="title"><a name="d0e334"></a>A simple example</h3></div></div></div><p>Let us have a look at a state machine diagram of the founding + example:</p><p><span class="inlinemediaobject"><img src="../images/SimpleTutorial.jpg" width="60%"></span></p><p>We are now going to build it with MSM's basic front-end. An <a class="link" href="examples/SimpleTutorial.cpp" target="_top">implementation</a> is also + provided.</p></div><div class="sect2" title="Transition table"><div class="titlepage"><div><div><h3 class="title"><a name="d0e348"></a>Transition table</h3></div></div></div><p>As previously stated, MSM is based on the transition table, so let us + define one:</p><pre class="programlisting"> +struct transition_table : mpl::vector< +// Start Event Target Action Guard +// +---------+------------+-----------+---------------------------+----------------------------+ +a_row< Stopped , play , Playing , &player_::start_playback >, +a_row< Stopped , open_close , Open , &player_::open_drawer >, + _row< Stopped , stop , Stopped >, +// +---------+------------+-----------+---------------------------+----------------------------+ +a_row< Open , open_close , Empty , &player_::close_drawer >, +// +---------+------------+-----------+---------------------------+----------------------------+ +a_row< Empty , open_close , Open , &player_::open_drawer >, + row< Empty , cd_detected, Stopped , &player_::store_cd_info , &player_::good_disk_format >, + row< Empty , cd_detected, Playing , &player_::store_cd_info , &player_::auto_start >, +// +---------+------------+-----------+---------------------------+----------------------------+ +a_row< Playing , stop , Stopped , &player_::stop_playback >, +a_row< Playing , pause , Paused , &player_::pause_playback >, +a_row< Playing , open_close , Open , &player_::stop_and_open >, +// +---------+------------+-----------+---------------------------+----------------------------+ +a_row< Paused , end_pause , Playing , &player_::resume_playback >, +a_row< Paused , stop , Stopped , &player_::stop_playback >, +a_row< Paused , open_close , Open , &player_::stop_and_open > +// +---------+------------+-----------+---------------------------+----------------------------+ +> {}; + </pre><p>You will notice that this is almost exactly our founding example. The only + change in the transition table is the different types of transitions (rows). + The founding example forces one to define an action method and offers no + guards. You have 4 basic row types:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p><code class="code">row</code> takes 5 arguments: start state, event, target + state, action and guard.</p></li><li class="listitem"><p><code class="code">a_row</code> (“a” for action) allows defining only the + action and omit the guard condition.</p></li><li class="listitem"><p><code class="code">g_row</code> (“g” for guard) allows omitting the action + behavior and defining only the guard.</p></li><li class="listitem"><p><code class="code">_row</code> allows omitting action and guard.</p></li></ul></div><p>The signature for an action methods is void method_name (event + const&), for example:</p><pre class="programlisting">void stop_playback(stop const&)</pre><p>Action methods return nothing and take the argument as const reference. Of + course nothing forbids you from using the same action for several + events:</p><pre class="programlisting">template <class Event> void stop_playback(Eventconst&)</pre><p>Guards have as only difference the return value, which is a + boolean:</p><pre class="programlisting">bool good_disk_format(cd_detected const& evt)</pre><p>The transition table is actually a MPL vector (or list), which brings the + limitation that the default maximum size of the table is 20. If you need + more transitions, overriding this default behavior is necessary, so you need + to add before any header:</p><pre class="programlisting">#define BOOST_MPL_CFG_NO_PREPROCESSED_HEADERS +#define BOOST_MPL_LIMIT_VECTOR_SIZE 30 //or whatever you need +#define BOOST_MPL_LIMIT_MAP_SIZE 30 //or whatever you need </pre><p>The other limitation is that the MPL types are defined only up to 50 + entries. For the moment, the only solution to achieve more is to add headers + to the MPL (luckily, this is not very complicated).</p></div><div class="sect2" title="Defining states with entry/exit actions"><div class="titlepage"><div><div><h3 class="title"><a name="d0e396"></a>Defining states with entry/exit actions</h3></div></div></div><p>While states were enums in the MPL book, they now are classes, which + allows them to hold data, provide entry, exit behaviors and be reusable (as + they do not know anything about the containing state machine). To define a + state, inherit from the desired state type. You will mainly use simple + states:</p><p>struct Empty : public msm::front::state<> {};</p><p>They can optionally provide entry and exit behaviors:</p><pre class="programlisting"> +struct Empty : public msm::front::state<> +{ + template <class Event, class Fsm> + void on_entry(Event const&, Fsm& ) + {std::cout <<"entering: Empty" << std::endl;} + template <class Event, class Fsm> + void on_exit(Event const&, Fsm& ) + {std::cout <<"leaving: Empty" << std::endl;} +}; + </pre><p>Notice how the entry and exit behaviors are templatized on the event and + state machine. Being generic facilitates reuse. There are more state types + (terminate, interrupt, pseudo states, etc.) corresponding to the UML + standard state types. These will be described in details in the next + sections.</p></div><div class="sect2" title="What do you actually do inside actions / guards?"><div class="titlepage"><div><div><h3 class="title"><a name="d0e409"></a>What do you actually do inside actions / guards?</h3></div></div></div><p>State machines define a structure and important parts of the complete + behavior, but not all. For example if you need to send a rocket to Alpha + Centauri, you can have a transition to a state "SendRocketToAlphaCentauri" + but no code actually sending the rocket. This is where you need actions. So + a simple action could be:</p><pre class="programlisting">template <class Fire> void send_rocket(Fire const&) +{ + fire_rocket(); +}</pre><p>Ok, this was simple. Now, we might want to give a direction. Let us suppose + this information is externally given when needed, it makes sense do use the + event for this:</p><pre class="programlisting">// Event +struct Fire {Direction direction;}; +template <class Fire> void send_rocket(Fire const& evt) +{ + fire_rocket(evt.direction); +}</pre><p>We might want to calculate the direction based not only on external data + but also on data accumulated during previous work. In this case, you might + want to have this data in the state machine itself. As transition actions + are members of the front-end, you can directly access the data:</p><pre class="programlisting">// Event +struct Fire {Direction direction;}; +//front-end definition, see down +struct launcher_ : public msm::front::state_machine_def<launcher_>{ +Data current_calculation; +template <class Fire> void send_rocket(Fire const& evt) +{ + fire_rocket(evt.direction, current_calculation); +} +... +};</pre><p>Entry and exit actions represent a behavior common to a state, no matter + through which transition it is entered or left. States being reusable, it + might make sense to locate your data there instead of in the state machine, + to maximize reuse and make code more readable. Entry and exit actions have + access to the state data (being state members) but also to the event and + state machine, like transition actions. This happens through the Event and + Fsm template parameters:</p><pre class="programlisting">struct Launching : public msm::front::state<> +{ + template <class Event, class Fsm> + void on_entry(Event const& evt, Fsm& fsm) + { + fire_rocket(evt.direction, fsm.current_calculation); + } +};</pre><p>Exit actions are also ideal for clanup when the state becomes + inactive.</p><p>Another possible use of the entry action is to pass data to substates / + submachines. Launching is a substate containing a <code class="code">data</code> attribute:</p><pre class="programlisting">struct launcher_ : public msm::front::state_machine_def<launcher_>{ +Data current_calculation; +// state machines also have entry/exit actions +template <class Event, class Fsm> +void on_entry(Event const& evt, Fsm& fsm) +{ + launcher_::Launching& s = fsm.get_state<launcher_::Launching&>(); + s.data = fsm.current_calculation; +} +... +};</pre><p>The <span class="command"><strong><a class="command" href="ch03s05.html#backend-fsm-constructor-args">set_states</a></strong></span> back-end method allows you to replace a complete + state.</p><p>The <span class="command"><strong><a class="command" href="ch03s03.html#functor-front-end-actions">functor</a></strong></span> front-end and eUML offer more capabilities.</p><p>However, this basic front-end also has special capabilities using the row2 + / irow2 transitions.<span class="command"><strong><a class="command" href="ch03s02.html#basic-row2">_row2, a_row2, row2, + g_row2, a_irow2, irow2, g_irow2</a></strong></span> let you call an action located + in any state of the current fsm or in the front-end itself, thus letting you + place useful data anywhere you see fit.</p><p>It is sometimes desirable to generate new events for the state machine + inside actions. Since the process_event method belongs to the back end, you + first need to gain a reference to it. The back end derives from the front + end, so one way of doing this is to use a cast:</p><pre class="programlisting">struct launcher_ : public msm::front::state_machine_def<launcher_>{ +template <class Fire> void send_rocket(Fire const& evt) +{ + fire_rocket(); + msm::back::state_machine<launcher_> &fsm = static_cast<msm::back::state_machine<launcher_> &>(*this); + fsm.process_event(rocket_launched()); +} +... +};</pre><p>The same can be implemented inside entry/exit actions. Admittedly, this is + a bit awkward. A more natural mechanism is available using the <span class="command"><strong><a class="command" href="ch03s03.html#functor-front-end-actions">functor</a></strong></span> + front-end.</p></div><div class="sect2" title="Defining a simple state machine"><div class="titlepage"><div><div><h3 class="title"><a name="d0e461"></a>Defining a simple state machine</h3></div></div></div><p>Declaring a state machine is straightforward and is done with a high + signal / noise ratio. In our player example, we declare the state machine + as:</p><pre class="programlisting">struct player_ : public msm::front::state_machine_def<player_>{ + /* see below */}</pre><p>This declares a state machine using the basic front-end. We now declare + inside the state machine structure the initial state:</p><p> + </p><pre class="programlisting">typedef Empty initial_state;</pre><p> + </p><p>And that is about all of what is absolutely needed. In the example, the + states are declared inside the state machine for readability but this is not + a requirements, states can be declared wherever you like.</p><p>All what is left to do is to pick a back-end (which is quite simple as + there is only one at the moment):</p><p> + </p><pre class="programlisting">typedef msm::back::state_machine<player_> player;</pre><p> + </p><p>You now have a ready-to-use state machine with entry/exit actions, guards, + transition actions, a message queue so that processing an event can generate + another event. The state machine also adapted itself to your need and + removed almost all features we didn't use in this simple example. Note that + this is not per default the fastest possible state machine. See the section + "getting more speed" to know how to get the maximum speed. In a nutshell, + MSM cannot know about your usage of some features so you will have to + explicitly tell it.</p><p>State objects are built automatically with the state machine. They will + exist until state machine destruction. MSM is using Boost.Fusion behind the + hood. This unfortunately means that if you define more than 10 states, you + will need to extend the default:</p><p> + </p><pre class="programlisting">#define FUSION_MAX_VECTOR_SIZE 20 // or whatever you need + </pre><p> + </p><p>When an unexpected event is fired, the <code class="code">no_transition(event, state + machine, state id)</code> method of the state machine is called . By + default, this method simply asserts when called. It is possible to overwrite + the <code class="code">no_transition</code> method to define a different handling:</p><p> + </p><pre class="programlisting">template <class Fsm,class Event> +void no_transition(Event const& e, Fsm& ,int state){...}</pre><p> + </p><p><span class="underline">Note</span>: you might have noticed that + the tutorial calls <code class="code">start()</code> on the state machine just after + creation. The start method will initiate the state machine, meaning it will + activate the initial state, which means in turn that the initial state's + entry behavior will be called. The reason why we need this will be explained + in the <a class="link" href="ch03s05.html#backend-start">back-end part</a>. After a call + to start, the state machine is ready to process events. The same way, + calling <code class="code">stop()</code> will cause the last exit actions to be called.</p></div><div class="sect2" title="Defining a submachine"><div class="titlepage"><div><div><h3 class="title"><a name="d0e519"></a>Defining a submachine</h3></div></div></div><p>We now want to extend our last state machine by making the Playing state a + state machine itself (a submachine).</p><p><span class="inlinemediaobject"><img src="../images/CompositeTutorial.jpg" width="60%"></span></p><p>Again, an <a class="link" href="examples/CompositeTutorial.cpp" target="_top">example</a> + is also provided.</p><p>A submachine really is a state machine itself, so we declare Playing as + such, choosing a front-end and a back-end:</p><p> + </p><pre class="programlisting">struct Playing_ : public msm::front::state_machine_def<Playing_>{...} +typedef msm::back::state_machine<Playing_> Playing;</pre><p> + </p><p>Like for any state machine, one also needs a transition table and an + initial state:</p><p> + </p><pre class="programlisting"> +struct transition_table : mpl::vector< +// Start Event Target Action Guard +// +--------+---------+--------+---------------------------+------+ +a_row< Song1 , NextSong, Song2 , &Playing_::start_next_song >, +a_row< Song2 , NextSong, Song1 , &Playing_::start_prev_song >, +a_row< Song2 , NextSong, Song3 , &Playing_::start_next_song >, +a_row< Song3 , NextSong, Song2 , &Playing_::start_prev_song > +// +--------+---------+--------+---------------------------+------+ +> {}; + </pre><p> + </p><p> + </p><pre class="programlisting">typedef Song1 initial_state; </pre><p> + </p><p>This is about all you need to do. MSM will now automatically recognize + Playing as a submachine and all events handled by Playing (NextSong and + PreviousSong) will now be automatically forwarded to Playing whenever this + state is active. All other state machine features described later are also + available. You can even decide to use a state machine sometimes as + submachine or sometimes as an independent state machine.</p></div><div class="sect2" title="Orthogonal regions, terminate state, event deferring"><div class="titlepage"><div><div><h3 class="title"><a name="d0e554"></a>Orthogonal regions, terminate state, event deferring</h3></div></div></div><p>It is a very common problem in many state machines to have to handle + errors. It usually involves defining a transition from all the states to a + special error state. Translation: not fun. It is also not practical to find + from which state the error originated. The following diagram shows an + example of what clearly becomes not very readable:</p><p><span class="inlinemediaobject"><img src="../images/error_no_regions.jpg" width="60%"></span></p><p>This is neither very readable nor beautiful. And we do not even have any + action on the transitions yet to make it even less readable.</p><p>Luckily, UML provides a helpful concept, orthogonal regions. See them as + lightweight state machines running at the same time inside a common state + machine and having the capability to influence one another. The effect is + that you have several active states at any time. We can therefore keep our + state machine from the previous example and just define a new region made of + two states, AllOk and ErrorMode. AllOk is most of the time active. But the + error_found error event makes the second region move to the new active state + ErrorMode. This event does not interest the main region so it will simply be + ignored. "<code class="code">no_transition</code>" will be called only if no region at + all handles the event. Also, as UML mandates, every region gets a chance of + handling the event, in the order as declared by the + <code class="code">initial_state</code> type.</p><p>Adding an orthogonal region is easy, one only needs to declare more states + in the <code class="code">initial_state</code> typedef. So, adding a new region with + AllOk as the region's initial state is:</p><p> + </p><pre class="programlisting">typedef mpl::vector<Empty,AllOk> initial_state;</pre><p> + </p><p><span class="inlinemediaobject"><img src="../images/Orthogonal-deferred.jpg" width="60%"></span></p><p>Furthermore, when you detect an error, you usually do not want events to + be further processed. To achieve this, we use another UML feature, terminate + states. When any region moves to a terminate state, the state machine + “terminates” (the state machine and all its states stay alive) and all + events are ignored. This is of course not mandatory, one can use orthogonal + regions without terminate states. MSM also provides a small extension to + UML, interrupt states. If you declare ErrorMode as interrupt state instead + of terminate state, the state machine will not handle any event other than + the one which ends the interrupt. So it's like a terminate state, with the + difference that you are allowed to resume the state machine when a condition + (like handling of the original error) is met. </p><p><span class="command"><strong><a name="basic-defer"></a></strong></span>Last but not least, this example also shows + here the handling of event deferring. Let's say someone puts a disc and + immediately presses play. The event cannot be handled, yet you'd want it to + be handled at a later point and not force the user to press play again. The + solution is to define it as deferred in the Empty and Open states and get it + handled in the first state where the event is not to be deferred. It can + then be handled or rejected. In this example, when Stopped becomes active, + the event will be handled because only Empty and Open defer the + event.</p><p>UML defines event deferring as a state property. To accommodate this, MSM + lets you specify this in states by providing a <code class="code">deferred_events</code> + type:</p><pre class="programlisting">struct Empty : public msm::front::state<> +{ + // if the play event is fired while in this state, defer it until a state + // handles or rejects it + typedef mpl::vector<play> deferred_events; +... +}; </pre><p>Please have a look at the <a class="link" href="examples/Orthogonal-deferred.cpp" target="_top">complete + example</a>.</p><p>While this is wanted by UML and is simple, it is not always practical + because one could wish to defer only in certain conditions. One could also + want to make this be part of a transition action with the added bonus of a + guard for more sophisticated behaviors. It would also be conform to the MSM + philosophy to get as much as possible in the transition table, where you + have the whole state machine structure. This is also possible but not + practical with this front-end so we will need to pick a different row from + the functor front-end. For a complete description of the <code class="code">Row</code> + type, please have a look at the <span class="command"><strong><a class="command" href="ch03s03.html#functor-front-end">functor front-end.</a></strong></span></p><p>First, as there is no state where MSM can automatically find out the usage + of this feature, we need to require deferred events capability explicitly, + by adding a type in the state machine definition:</p><pre class="programlisting">struct player_ : public msm::front::state_machine_def<player_> +{ + typedef int activate_deferred_events; +... +}; </pre><p>We can now defer an event in any transition of the transition table by + using as action the predefined <code class="code">msm::front::Defer</code> functor, for + example:</p><p> + </p><pre class="programlisting">Row < Empty , play , none , Defer , none ></pre><p> + </p><p>This is an internal transition row(see <span class="command"><strong><a class="command" href="ch03s02.html#internal-transitions">internal transitions</a></strong></span>) but + you can ignore this for the moment. It just means that we are not leaving + the Empty state. What matters is that we use Defer as action. This is + roughly equivalent to the previous syntax but has the advantage of giving + you all the information in the transition table with the added power of + transition behavior.</p><p>The second difference is that as we now have a transition defined, this + transition can play in the resolution of <span class="command"><strong><a class="command" href="ch02s02.html#transition-conflict">transition conflicts</a></strong></span>. For + example, we could model an "if (condition2) move to Playing else if + (condition1) defer play event":</p><p> + </p><pre class="programlisting">Row < Empty , play , none , Defer , condition1 >, +g_row < Empty , play , Playing , &player_::condition2 ></pre><p> + </p><p>Please have a look at <a class="link" href="examples/Orthogonal-deferred2.cpp" target="_top">this possible implementation</a>.</p></div><div class="sect2" title="History"><div class="titlepage"><div><div><h3 class="title"><a name="d0e645"></a>History</h3></div></div></div><p>UML defines two types of history, Shallow History and Deep History. In the + previous examples, if the player was playing the second song and the user + pressed pause, leaving Playing, at the next press on the play button, the + Playing state would become active and the first song would play again. Soon + would the first client complaints follow. They'd of course demand, that if + the player was paused, then it should remember which song was playing. But + it the player was stopped, then it should restart from the first song. How + can it be done? Of course, you could add a bit of programming logic and + generate extra events to make the second song start if coming from Pause. + Something like: </p><p> + </p><pre class="programlisting">if (Event == end_pause) +{ + for (int i=0;i< song number;++i) {player.process_event(NextSong()); } +} </pre><p> + </p><p>Not much to like in this example, isn't it? To solve this problem, you + define what is called a shallow or a deep history. A shallow history + reactivates the last active substate of a submachine when this submachine + becomes active again. The deep history does the same recursively, so if this + last active substate of the submachine was itself a submachine, its last + active substate would become active and this will continue recursively until + an active state is a normal state. For example, let us have a look at the + following UML diagram: </p><p><span class="inlinemediaobject"><img src="../images/HistoryTutorial.jpg" width="60%"></span></p><p>Notice that the main difference compared to previous diagrams is that the + initial state is gone and replaced by a History symbol (the H inside a + circle).</p><p>As explained in the <span class="command"><strong><a class="command" href="ch02s02.html#uml-history">small UML + tutorial</a></strong></span>, History is a good concept with a not completely + satisfying specification. MSM kept the concept but not the specification and + goes another way by making this a policy and you can add your own history + types (the <a class="link" href="re02.html#history-interface">reference</a> explains + what needs to be done). Furthermore, History is a backend policy. This + allows you to reuse the same state machine definition with different history + policies in different contexts.</p><p>Concretely, your frontend stays unchanged:</p><p> + </p><pre class="programlisting">struct Playing_ : public msm::front::state_machine_def<Playing_></pre><p> + </p><p>You then add the policy to the backend as second parameter:</p><p> + </p><pre class="programlisting">typedef msm::back::state_machine<Playing_, + msm::back::ShallowHistory<mpl::vector<end_pause> > > Playing;</pre><p> + </p><p>This states that a shallow history must be activated if the Playing state + machine gets activated by the end_pause event and only this one (or any + other event added to the mpl::vector). If the state machine was in the + Stopped state and the event play was generated, the history would not be + activated and the normal initial state would become active. By default, + history is disabled. For your convenience the library provides in addition + to ShallowHistory a non-UML standard AlwaysHistory policy (likely to be your + main choice) which always activates history, whatever event triggers the + submachine activation. Deep history is not available as a policy (but could + be added). The reason is that it would conflict with policies which + submachines could define. Of course, if for example, Song1 were a state + machine itself, it could use the ShallowHistory policy itself thus creating + Deep History for itself. An <a class="link" href="examples/History.cpp" target="_top">example</a> is also provided.</p></div><div class="sect2" title="Completion (anonymous) transitions"><div class="titlepage"><div><div><h3 class="title"><a name="d0e690"></a>Completion (anonymous) transitions</h3></div></div></div><p><span class="command"><strong><a name="anonymous-transitions"></a></strong></span>The following diagram shows an + example making use of this feature:</p><p><span class="inlinemediaobject"><img src="../images/Anonymous.jpg" width="60%"></span></p><p>Anonymous transitions are transitions without a named event. This means + that the transition automatically fires when the predecessor state is + entered (to be exact, after the entry action). Otherwise it is a normal + transition with actions and guards. Why would you need something like that? + A possible case would be if a part of your state machine implements some + algorithm, where states are steps of the algorithm implementation. Then, + using several anonymous transitions with different guard conditions, you are + actually implementing some if/else statement. Another possible use would be + a real-time system called at regular intervals and always doing the same + thing, meaning implementing the same algorithm. The advantage is that once + you know how long a transition takes to execute on the system, by + calculating the longest path (the number of transitions from start to end), + you can pretty much know how long your algorithm will take in the worst + case, which in turns tells you how much of a time frame you are to request + from a scheduler. </p><p>If you are using Executable UML (a good book describing it is "Executable + UML, a foundation for Model-Driven Architecture"), you will notice that it + is common for a state machine to generate an event to itself only to force + leaving a state. Anonymous transitions free you from this constraint.</p><p>If you do not use this feature in a concrete state machine, MSM will + deactivate it and you will not pay for it. If you use it, there is however a + small performance penalty as MSM will try to fire a compound event (the + other UML name for anonymous transitions) after every taken transition. This + will therefore double the event processing cost, which is not as bad as it + sounds as MSM’s execution speed is very high anyway.</p><p>To define such a transition, use “none” as event in the transition table, + for example:</p><p> + </p><pre class="programlisting">row < State3 , none , State4 , &p::State3ToState4 , &p::always_true ></pre><p> + </p><p><a class="link" href="examples/AnonymousTutorial.cpp" target="_top">An implementation</a> + of the state machine diagram is also provided.</p></div><div class="sect2" title="Internal transitions"><div class="titlepage"><div><div><h3 class="title"><a name="d0e717"></a><span class="command"><strong><a name="internal-transitions"></a></strong></span>Internal transitions</h3></div></div></div><p>Internal transitions are transitions executing in the scope of the active + state, a simple state or a submachine. One can see them as a self-transition + of this state, without an entry or exit action called. This is useful when + all you want is to execute some code for a given event in a given + state.</p><p>Internal transitions are specified as having a higher priority than normal + transitions. While it makes sense for a submachine with exit points, it is + surprising for a simple state. MSM lets you define the transition priority + by setting the transition’s position inside the transition table (see + <span class="command"><strong><a class="command" href="ch06.html#run-to-completion">internals</a></strong></span> ). The + difference between "normal" and internal transitions is that internal + transitions have no target state, therefore we need new row types. We had + a_row, g_row, _row and row, we now add a_irow, g_irow, _irow and irow which + are like normal transitions but define no target state. For, example an + internal transition with a guard condition could be:</p><p> + </p><pre class="programlisting">g_irow < Empty /*state*/,cd_detected/*event*/,&p::internal_guard/* guard */></pre><p> + </p><p>These new row types can be placed anywhere in the transition table so that + you can still have your state machine structure grouped together. The only + difference of behavior with the UML standard is the missing notion of higher + priority for internal transitions. Please have a look at <a class="link" href="examples/SimpleTutorialInternal.cpp" target="_top">the + example</a>.</p><p>It is also possible to do it the UML-conform way by declaring a transition + table called <code class="code">internal transition_table</code> inside the state itself + and using internal row types. For example:</p><pre class="programlisting">struct Empty : public msm::front::state<> +{ + struct internal_transition_table : mpl::vector< + a_internal < cd_detected , Empty, &Empty::internal_action > + > {}; +};</pre><p>This declares an internal transition table called + internal_transition_table and reacting on the event cd_detected by calling + internal_action on Empty. Let us note a few points:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>internal tables are NOT called transition_table but + internal_transition_table</p></li><li class="listitem"><p>they use different but similar row types: a_internal, + g_internal, _internal and internal.</p></li><li class="listitem"><p>These types take as first template argument the triggering + event and then the action and guard method. Note that the only + real difference to classical rows is the extra argument before + the function pointer. This is the type on which the function + will be called.</p></li><li class="listitem"><p>This also allows you, if you wish, to use actions and guards + from another state of the state machine or in the state machine + itself.</p></li><li class="listitem"><p>submachines can have an internal transition table and a + classical transition table.</p></li></ul></div><p>The <a class="link" href="examples/TestInternal.cpp" target="_top">following example</a> + makes use of an a_internal. It also uses functor-based internal transitions + which will be explained in <span class="command"><strong><a class="command" href="ch03s03.html#functor-internal-transitions">the functor + front-end</a></strong></span>, please ignore them for the moment. Also note that + the state-defined internal transitions, having the highest priority (as + mandated by the UML standard), are tried before those defined inside the + state machine transition table.</p><p>Which method should you use? It depends on what you need:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>the first version (using irow) is simpler and likely to + compile faster. It also lets you choose the priority of your + internal transition.</p></li><li class="listitem"><p>the second version is more logical from a UML perspective and + lets you make states more useful and reusable. It also allows + you to call actions and guards on any state of the state + machine.</p></li></ul></div><p> + <span class="command"><strong><a name="internal-transitions-note"></a></strong></span><span class="underline"><span class="bold"><strong>Note</strong></span></span>: There is an added + possibility coming from this feature. The + <code class="code">internal_transition_table</code> transitions being added directly + inside the main state machine's transition table, it is possible, if it is + more to your state, to distribute your state machine definition a bit like + Boost.Statechart, leaving to the state machine itself the only task of + declaring the states it wants to use using the + <code class="code">explicit_creation</code> type definition. While this is not the + author's favorite way, it is still possible. A simplified example using only + two states will show this possibility:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p><a class="link" href="examples/distributed_table/DistributedTable.cpp" target="_top">state machine definition</a></p></li><li class="listitem"><p>Empty <a class="link" href="examples/distributed_table/Empty.hpp" target="_top">header</a> and <a class="link" href="examples/distributed_table/Empty.cpp" target="_top">cpp</a></p></li><li class="listitem"><p>Open <a class="link" href="examples/distributed_table/Open.hpp" target="_top">header</a> and <a class="link" href="examples/distributed_table/Open.cpp" target="_top">cpp</a></p></li><li class="listitem"><p><a class="link" href="examples/distributed_table/Events.hpp" target="_top">events definition</a></p></li></ul></div><p>There is an added bonus offered for submachines, which can have both the + standard transition_table and an internal_transition_table (which has a + higher priority). This makes it easier if you decide to make a full + submachine from a state. It is also slightly faster than the standard + alternative, adding orthogonal regions, because event dispatching will, if + accepted by the internal table, not continue to the subregions. This gives + you a O(1) dispatch instead of O(number of regions). While the example is + with eUML, the same is also possible with any front-end.</p></div><div class="sect2" title="more row types"><div class="titlepage"><div><div><h3 class="title"><a name="d0e819"></a><span class="command"><strong><a name="basic-row2"></a></strong></span>more row types</h3></div></div></div><p>It is also possible to write transitions using actions and guards not just + from the state machine but also from its contained states. In this case, one + must specify not just a method pointer but also the object on which to call + it. This transition row is called, not very originally, <code class="code">row2</code>. + They come, like normal transitions in four flavors: <code class="code">a_row2, g_row2, + _row2 and row2</code>. For example, a transition calling an action from + the state Empty could be:</p><p> + </p><pre class="programlisting">a_row2<Stopped,open_close,Open,Empty + /*action source*/,&Empty::open_drawer/*action*/></pre><p> + </p><p>The same capabilities are also available for internal transitions so that + we have: <code class="code">a_irow2, g_irow2, _irow2 and row2</code>. For transitions + defined as part of the <code class="code">internal_transition_table</code>, you can use + the <span class="command"><strong><a class="command" href="ch03s02.html#internal-transitions">a_internal, g_internal, + _internal, internal</a></strong></span> row types from the previous + sections.</p><p>These row types allow us to distribute the state machine code among + states, making them reusable and more useful. Using transition tables inside + states also contributes to this possibility. An <a class="link" href="examples/SimpleTutorial2.cpp" target="_top">example</a> of these new + rows is also provided.</p></div><div class="sect2" title="Explicit entry / entry and exit pseudo-state / fork"><div class="titlepage"><div><div><h3 class="title"><a name="d0e852"></a>Explicit entry / entry and exit pseudo-state / fork</h3></div></div></div><p>MSM (almost) fully supports these features, described in the <span class="command"><strong><a class="command" href="ch02s02.html#uml-history">small UML tutorial</a></strong></span>. Almost because + there are currently two limitations: </p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>it is only possible to explicitly enter a sub- state of the + target but not a sub-sub state.</p></li><li class="listitem"><p>it is not possible to explicitly exit. Exit points must be + used.</p></li></ul></div><p>Let us see a concrete example:</p><p><span class="inlinemediaobject"><img src="../images/entrytutorial.jpg" width="60%"></span></p><p>We find in this diagram:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>A “normal” activation of SubFsm2, triggered by event1. In each + region, the initial state is activated, i.e. SubState1 and + SubState1b.</p></li><li class="listitem"><p>An explicit entry into SubFsm2::SubState2 for region “1” with + event2 as trigger, meaning that in region “2” the initial state, + SubState1b, activated.</p></li><li class="listitem"><p>A fork into regions “1” and “2” to the explicit entries + SubState2 and SubState2b, triggered by event3. Both states + become active so no region is default activated (if we had a + third one, it would be).</p></li><li class="listitem"><p>A connection of two transitions through an entry pseudo state, + SubFsm2::PseudoEntry1, triggered by event4 and triggering also + the second transition on the same event (both transitions must + be triggered by the same event). Region “2” is default-activated + and SubState1b becomes active.</p></li><li class="listitem"><p>An exit from SubFsm2 using an exit pseudo-state, PseudoExit1, + triggered by event5 and connecting two transitions using the + same event. Again, the event is forwarded to the second + transition and both regions are exited, as SubFsm2 becomes + inactive. Note that if no transition is defined from + PseudoExit1, an error (as defined in the UML standard) will be + detected and no_transition called.</p></li></ul></div><p>The example is also <a class="link" href="examples/DirectEntryTutorial.cpp" target="_top">fully implemented</a>.</p><p>This sounds complicated but the syntax is simple.</p><div class="sect3" title="Explicit entry"><div class="titlepage"><div><div><h4 class="title"><a name="d0e898"></a>Explicit entry</h4></div></div></div><p>First, to define that a state is an explicit entry, you have to make + it a state and mark it as explicit, giving as template parameters the + region id (the region id starts with 0 and corresponds to the first + initial state of the initial_state type sequence).</p><p> + </p><pre class="programlisting">struct SubFsm2_ : public msm::front::state_machine_def<SubFsm2_> +{ + struct SubState2 : public msm::front::state<> , + public msm::front::explicit_entry<0> + {...}; +... +};</pre><p> + </p><p>And define the submachine as:</p><p> + </p><pre class="programlisting">typedef msm::back::state_machine<SubFsm2_> SubFsm2;</pre><p> + </p><p>You can then use it as target in a transition with State1 as + source:</p><p> + </p><pre class="programlisting">_row < State1, Event2, SubFsm2::direct< SubFsm2_::SubState2> > //SubFsm2_::SubState2: complete name of SubState2 (defined within SubFsm2_)</pre><p> + </p><p>The syntax deserves some explanation. SubFsm2_ is a front end. + SubState2 is a nested state, therefore the SubFsm2_::SubState2 syntax. + The containing machine (containing State1 and SubFsm2) refers to the + backend instance (SubFsm2). SubFsm2::direct states that an explicit + entry is desired.</p><p><span class="command"><strong><a name="explicit-entry-no-region-id"></a></strong></span>Thanks to the <span class="command"><strong><a class="command" href="ch03s05.html#backend-compile-time-analysis">mpl_graph</a></strong></span> library you can also omit to provide the region + index and let MSM find out for you. The are however two points to note:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>MSM can only find out the region index if the explicit + entry state is somehow connected to an initial state through + a transition, no matter the direction.</p></li><li class="listitem"><p>There is a compile-time cost for this feature.</p></li></ul></div><p><span class="underline">Note (also valid for forks)</span>: in + order to make compile time more bearable for the more standard cases, + and unlike initial states, explicit entry states which are also not + found in the transition table of the entered submachine (a rare case) do + NOT get automatically created. To explicitly create such states, you + need to add in the state machine containing the explicit states a simple + typedef giving a sequence of states to be explicitly created + like:</p><p> + </p><pre class="programlisting">typedef mpl::vector<SubState2,SubState2b> explicit_creation;</pre><p> + </p><p><span class="underline">Note (also valid for forks)</span>: At + the moment, it is not possible to use a submachine as the target of an + explicit entry. Please use entry pseudo states for an almost identical + effect.</p></div><div class="sect3" title="Fork"><div class="titlepage"><div><div><h4 class="title"><a name="d0e950"></a>Fork</h4></div></div></div><p>Need a fork instead of an explicit entry? As a fork is an explicit + entry into states of different regions, we do not change the state + definition compared to the explicit entry and specify as target a list + of explicit entry states:</p><p> + </p><pre class="programlisting">_row < State1, Event3, + mpl::vector<SubFsm2::direct<SubFsm2_::SubState2>, + SubFsm2::direct <SubFsm2_::SubState2b> + ></pre><p> + </p><p>With SubState2 defined as before and SubState2b defined as being in + the second region (Caution: MSM does not check that the region is + correct):</p><p> + </p><pre class="programlisting">struct SubState2b : public msm::front::state<> , + public msm::front::explicit_entry<1></pre><p> + </p></div><div class="sect3" title="Entry pseudo states"><div class="titlepage"><div><div><h4 class="title"><a name="d0e967"></a>Entry pseudo states</h4></div></div></div><p> To define an entry pseudo state, you need derive from the + corresponding class and give the region id:</p><p> + </p><pre class="programlisting">struct PseudoEntry1 : public msm::front::entry_pseudo_state<0></pre><p> + </p><p>And add the corresponding transition in the top-level state machine's + transition table:</p><p> + </p><pre class="programlisting">_row < State1, Event4, SubFsm2::entry_pt<SubFsm2_::PseudoEntry1> ></pre><p> + </p><p>And another in the SubFsm2_ submachine definition (remember that UML + defines an entry point as a connection between two transitions), for + example this time with an action method:</p><p> + </p><pre class="programlisting">_row < PseudoEntry1, Event4, SubState3,&SubFsm2_::entry_action ></pre><p> + </p></div><div class="sect3" title="Exit pseudo states"><div class="titlepage"><div><div><h4 class="title"><a name="d0e991"></a> Exit pseudo states </h4></div></div></div><p>And finally, exit pseudo states are to be used almost the same way, + but defined differently: it takes as template argument the event to be + forwarded (no region id is necessary):</p><p> + </p><pre class="programlisting">struct PseudoExit1 : public exit_pseudo_state<event6></pre><p> + </p><p>And you need, like for entry pseudo states, two transitions, one in + the submachine:</p><p> + </p><pre class="programlisting">_row < SubState3, Event5, PseudoExit1 ></pre><p> + </p><p>And one in the containing state machine:</p><p> + </p><pre class="programlisting">_row < SubFsm2::exit_pt<SubFsm2_::PseudoExit1>, Event6,State2 ></pre><p> + </p><p><span class="underline">Important note 1:</span> UML defines + transiting to an entry pseudo state and having either no second + transition or one with a guard as an error but defines no error + handling. MSM will tolerate this behavior; the entry pseudo state will + simply be the newly active state.</p><p><span class="underline">Important note 2</span>: UML defines + transiting to an exit pseudo state and having no second transition as an + error, and also defines no error handling. Therefore, it was decided to + implement exit pseudo state as terminate states and the containing + composite not properly exited will stay terminated as it was technically + “exited”.</p><p><span class="underline">Important note 3:</span> UML states + that for the exit point, the same event must be used in both + transitions. MSM relaxes this rule and only wants the event on the + inside transition to be convertible to the one of the outside + transition. In our case, event6 is convertible from event5. Notice that + the forwarded event must be named in the exit point definition. For + example, we could define event6 as simply as:</p><p> + </p><pre class="programlisting">struct event +{ + event(){} + template <class Event> + event(Event const&){} +}; //convertible from any event</pre><p> + <span class="underline">Note</span>: There is a current + limitation if you need not only convert but also get some data from the + original event. Consider:</p><pre class="programlisting">struct event1 +{ + event1(int val_):val(val_) {} + int val; +}; // forwarded from exit point +struct event2 +{ + template <class Event> + event2(Event const& e):val(e.val){} // compiler will complain about another event not having any val + int val; +}; // what the higher-level fsm wants to get</pre><p>The solution is to provide two constructors:</p><pre class="programlisting">struct event2 +{ + template <class Event> + event2(Event const& ):val(0){} // will not be used + event2(event1 const& e)):val(e.val){} // the conversion constructor + int val; +}; // what the higher-level fsm wants to get</pre></div></div><div class="sect2" title="Flags"><div class="titlepage"><div><div><h3 class="title"><a name="d0e1041"></a>Flags</h3></div></div></div><p>This <a class="link" href="examples/Flags.cpp" target="_top">tutorial</a> is devoted to a + concept not defined in UML: flags. It has been added into MSM after proving + itself useful on many occasions. Please, do not be frightened as we are not + talking about ugly shortcuts made of an improbable collusion of + Booleans.</p><p>If you look into the Boost.Statechart documentation you'll find this + code:</p><pre class="programlisting">if ( ( state_downcast< const NumLockOff * >() != 0 ) && + ( state_downcast< const CapsLockOff * >() != 0 ) && + ( state_downcast< const ScrollLockOff * >() != 0 ) ) + </pre><p>While correct and found in many UML books, this can be error-prone and a + potential time-bomb when your state machine grows and you add new states or + orthogonal regions.</p><p>And most of all, it hides the real question, which would be “does my state + machine's current state define a special property”? In this special case + “are my keys in a lock state”? So let's apply the Fundamental Theorem of + Software Engineering and move one level of abstraction higher.</p><p>In our player example, let's say we need to know if the player has a + loaded CD. We could do the same:</p><pre class="programlisting">if ( ( state_downcast< const Stopped * >() != 0 ) && + ( state_downcast< const Open * >() != 0 ) && + ( state_downcast< const Paused * >() != 0 ) && + ( state_downcast< const Playing * >() != 0 )) </pre><p>Or flag these 4 states as CDLoaded-able. You add a flag_list type into + each flagged state:</p><p> + </p><pre class="programlisting">typedef mpl::vector1<CDLoaded> flag_list;</pre><p> + </p><p>You can even define a list of flags, for example in Playing:</p><p> + </p><pre class="programlisting">typedef mpl::vector2<PlayingPaused,CDLoaded> flag_list;</pre><p> + </p><p>This means that Playing supports both properties. To check if your player + has a loaded CD, check if your flag is active in the current state:</p><p> + </p><pre class="programlisting">player p; if (p.is_flag_active<CDLoaded>()) ... </pre><p> + </p><p>And what if you have orthogonal regions? How to decide if a state machine + is in a flagged state? By default, you keep the same code and the current + states will be OR'ed, meaning if one of the active states has the flag, then + is_flag_active returns true. Of course, in some cases, you might want that + all of the active states are flagged for the state to be active. You can + also AND the active states:</p><p> + </p><pre class="programlisting">if (p.is_flag_active<CDLoaded,player::Flag_AND>()) ...</pre><p> + </p><p>The following diagram displays the flag situation in the tutorial.</p><p><span class="inlinemediaobject"><img src="../images/FlagsTutorial.jpg" width="60%"></span></p></div><div class="sect2" title="Event Hierarchy"><div class="titlepage"><div><div><h3 class="title"><a name="d0e1095"></a><span class="command"><strong><a name="event-hierarchy"></a></strong></span>Event Hierarchy</h3></div></div></div><p>There are cases where one needs transitions based on categories of events. + An example is text parsing. Let's say you want to parse a string and use a + state machine to manage your parsing state. You want to parse 4 digits and + decide to use a state for every matched digit. Your state machine could look + like:</p><p><span class="inlinemediaobject"><img src="../images/ParsingDigits.jpg" width="30%"></span></p><p>But how to detect the digit event? We would like to avoid defining 10 + transitions on char_0, char_1... between two states as it would force us to + write 4 x 10 transitions and the compile-time would suffer. To solve this + problem, MSM supports the triggering of a transition on a subclass event. + For example, if we define digits as: </p><pre class="programlisting">struct digit {}; +struct char_0 : public digit {}; </pre><p>And to the same for other digits, we can now fire char_0, char_1 events + and this will cause a transition with "digit" as trigger to be taken.</p><p>An <a class="link" href="examples/ParsingDigits.cpp" target="_top">example</a> with + performance measurement, taken from the documentation of Boost.Xpressive + illustrates this example. You might notice that the performance is actually + very good (in this case even better).</p></div><div class="sect2" title="Customizing a state machine / Getting more speed"><div class="titlepage"><div><div><h3 class="title"><a name="d0e1116"></a>Customizing a state machine / Getting more speed</h3></div></div></div><p>MSM is offering many UML features at a high-speed, but sometimes, you just + need more speed and are ready to give up some features in exchange. A + process_event is handling several tasks: </p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>checking for terminate/interrupt states</p></li><li class="listitem"><p>handling the message queue (for entry/exit/transition actions + generating themselves events)</p></li><li class="listitem"><p>handling deferred events</p></li><li class="listitem"><p>catching exceptions (or not)</p></li><li class="listitem"><p>handling the state switching and action calls</p></li></ul></div><p>Of these tasks, only the last one is absolutely necessary to + a state machine (its core job), the other ones are nice-to-haves which cost + CPU time. In many cases, it is not so important, but in embedded systems, + this can lead to ad-hoc state machine implementations. MSM detects by itself + if a concrete state machine makes use of terminate/interrupt states and + deferred events and deactivates them if not used. For the other two, if you + do not need them, you need to help by indicating it in your implementation. + This is done with two simple typedefs:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p><code class="code">no_exception_thrown</code> indicates that behaviors will + never throw and MSM does not need to catch anything</p></li><li class="listitem"><p><code class="code">no_message_queue</code> indicates that no action will + itself generate a new event and MSM can save us the message + queue.</p></li></ul></div><p>The third configuration possibility, explained <a class="link" href="ch03s02.html#basic-defer">here</a>, is to manually activate deferred + events, using <code class="code">activate_deferred_events</code>. For example, the + following state machine sets all three configuration types:</p><pre class="programlisting">struct player_ : public msm::front::state_machine_def<player_> +{ + // no need for exception handling or message queue + typedef int no_exception_thrown; + typedef int no_message_queue; + // also manually enable deferred events + typedef int activate_deferred_events + ...// rest of implementation + };</pre><p><span class="underline">Important note</span>: As exit pseudo + states are using the message queue to forward events out of a submachine, + the <code class="code">no_message_queue</code> option cannot be used with state machines + containing an exit pseudo state.</p></div><div class="sect2" title="Choosing the initial event"><div class="titlepage"><div><div><h3 class="title"><a name="d0e1165"></a>Choosing the initial event</h3></div></div></div><p>A state machine is started using the <code class="code">start</code> method. This + causes the initial state's entry behavior to be executed. Like every entry + behavior, it becomes as parameter the event causing the state to be entered. + But when the machine starts, there was no event triggered. In this case, MSM + sends <code class="code">msm::back::state_machine<...>::InitEvent</code>, which might + not be the default you'd want. For this special case, MSM provides a + configuration mechanism in the form of a typedef. If the state machine's + front-end definition provides an initial_event typedef set to another event, + this event will be used. For example:</p><pre class="programlisting">struct my_initial_event{}; +struct player_ : public msm::front::state_machine_def<player_>{ +... +typedef my_initial_event initial_event; +};</pre></div><div class="sect2" title="Containing state machine (deprecated)"><div class="titlepage"><div><div><h3 class="title"><a name="d0e1178"></a> Containing state machine (deprecated)</h3></div></div></div><p>This feature is still supported in MSM for backward compatibility but made + obsolete by the fact that every guard/action/entry action/exit action get + the state machine passed as argument and might be removed at a later + time.</p><p>All of the states defined in the state machine are created upon state + machine construction. This has the huge advantage of a reduced syntactic + noise. The cost is a small loss of control for the user on the state + creation and access. But sometimes you needed a way for a state to get + access to its containing state machine. Basically, a state needs to change + its declaration to:</p><pre class="programlisting">struct Stopped : public msm::front::state<sm_ptr></pre><p>And to provide a set_sm_ptr function: <code class="code">void set_sm_ptr(player* + pl)</code></p><p>to get a pointer to the containing state machine. The same applies to + terminate_state / interrupt_state and entry_pseudo_state / + exit_pseudo_state. </p></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="ch03.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="ch03s03.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">Chapter 3. Tutorial </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> Functor front-end</td></tr></table></div></body></html>
\ No newline at end of file diff --git a/libs/msm/doc/HTML/ch03s03.html b/libs/msm/doc/HTML/ch03s03.html new file mode 100644 index 0000000000..d320292eab --- /dev/null +++ b/libs/msm/doc/HTML/ch03s03.html @@ -0,0 +1,146 @@ +<html><head> + <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> + <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 (experimental)"></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="d0e1193"></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 + powerful than the standard front-end and has a more readable transition table. + 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 + of predefined actions. Actually, eUML generates a functor front-end through + 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 + flavors. We saw the a_row, g_row, _row, row, not counting internal rows. This is + already much to know, so why define new rows? These types have some + 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 + 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 + variations of parameters (source state, target state, current state + machine, etc.)</p></li><li class="listitem"><p>It is not easy to reuse action code from a state machine to + another.</p></li></ul></div><div class="sect2" title="Transition table"><div class="titlepage"><div><div><h3 class="title"><a name="d0e1217"></a> Transition table </h3></div></div></div><p>We can change the definition of the simple tutorial's transition table + to:</p><pre class="programlisting"> +struct transition_table : mpl::vector< +// Start Event Target Action Guard +// +---------+------------+-----------+---------------------------+----------------------------+ +Row < Stopped , play , Playing , start_playback , none >, +Row < Stopped , open_close , Open , open_drawer , none >, +Row < Stopped , stop , Stopped , none , none >, +// +---------+------------+-----------+---------------------------+----------------------------+ +Row < Open , open_close , Empty , close_drawer , none >, +// +---------+------------+-----------+---------------------------+----------------------------+ +Row < Empty , open_close , Open , open_drawer , none >, +Row < Empty , cd_detected, Stopped , store_cd_info , good_disk_format >, +g_row< Empty , cd_detected, Playing , &player_::store_cd_info , &player_::auto_start >, +// +---------+------------+-----------+---------------------------+----------------------------+ +Row < Playing , stop , Stopped , stop_playback , none >, +Row < Playing , pause , Paused , pause_playback , none >, +Row < Playing , open_close , Open , stop_and_open , none >, +// +---------+------------+-----------+---------------------------+----------------------------+ +Row < Paused , end_pause , Playing , resume_playback , none >, +Row < Paused , stop , Stopped , stop_playback , none >, +Row < Paused , open_close , Open , stop_and_open , none > +// +---------+------------+-----------+---------------------------+----------------------------+ +> {}; + </pre><p>Transitions are now of type "Row" with exactly 5 template arguments: + source state, event, target state, action and guard. Wherever there is + nothing (for example actions and guards), write "none". Actions and guards + are no more methods but functors getting as arguments the detected event, + the state machine, source and target state:</p><pre class="programlisting">struct store_cd_info +{ + template <class Fsm,class Evt,class SourceState,class TargetState> + void operator()(Evt const&, Fsm& fsm, SourceState&,TargetState& ) + { + cout << "player::store_cd_info" << endl; + fsm.process_event(play()); + } +}; </pre><p>The advantage of functors compared to functions are that functors are + generic and reusable. They also allow passing more parameters than just + events. The guard functors are the same but have an operator() returning a + bool.</p><p>It is also possible to mix rows from different front-ends. To show this, a + g_row has been left in the transition table. <span class="underline">Note:</span> in case the action functor is used in the transition + table of a state machine contained inside a top-level state machine, the + “fsm” parameter refers to the lowest-level state machine (referencing this + action), not the top-level one.</p><p>To illustrate the reusable point, MSM comes with a whole set of predefined + 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 + 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 + (Stopped -> Playing). We only need to change the action start_playback to + </p><pre class="programlisting">ActionSequence_< mpl::vector<some_action, start_playback> ></pre><p>and + now will execute some_action and start_playback every time the transition is + taken. ActionSequence_ is a functor calling each action of the mpl::vector + in sequence.</p><p>We also want to replace good_disk_format by a condition of the type: + “good_disk_format && (some_condition || some_other_condition)”. We + can achieve this using And_ and Or_ functors: + </p><pre class="programlisting">And_<good_disk_format,Or_< some_condition , some_other_condition> ></pre><p>It + even starts looking like functional programming. MSM ships with functors for + 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="d0e1250"></a>Defining states with entry/exit actions</h3></div></div></div><p>You probably noticed that we just showed a different transition table and + that we even mixed rows from different front-ends. This means that you can + do this and leave the definitions for states unchanged. Most examples are + doing this as it is the simplest solution. You still enjoy the simplicity of + the first front-end with the extended power of the new transition types. + This <a class="link" href="examples/SimpleWithFunctors.cpp" target="_top">tutorial</a>, + adapted from the earlier example does just this.</p><p>Of course, it is also possible to define states where entry and exit + actions are also provided as functors as these are generated by eUML and + both front-ends are equivalent. For example, we can define a state + as:</p><pre class="programlisting">struct Empty_Entry +{ + template <class Event,class Fsm,class State> + void operator()(Event const&,Fsm&,State&) + { + ... + } +}; // same for Empty_Exit +struct Empty : public msm::front::euml::func_state<Empty_Entry,Empty_Exit>{};</pre><p>This also means that you can, like in the transition table, write entry / + exit actions made of more complicated action combinations. The previous + example can therefore <a class="link" href="examples/SimpleWithFunctors2.cpp" target="_top">be + rewritten</a>.</p><p>Usually, however, one will probably use the standard state definition as + it provides the same capabilities as this front-end state definition, unless + one needs some of the shipped predefined functors or is a fan of functional + 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="d0e1269"></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 + event, that data common to all states could be stored in the state machine, + state relevant data could be stored in the state and access as template + parameter in the entry / exit actions. What was however missing was the + capability to access relevant state data in the transition action. This is + possible with this front-end. A transition's source and target state are + also given as arguments. If the current calculation's state was to be found + in the transition's source state (whatever it is), we could access + it:</p><pre class="programlisting">struct send_rocket +{ + template <class Fsm,class Evt,class SourceState,class TargetState> + void operator()(Evt const&, Fsm& fsm, SourceState& src,TargetState& ) + { + fire_rocket(evt.direction, src.current_calculation); + } +}; </pre><p>It was a little awkward to generate new events inside actions with the basic + front-end. With the functor front-end it is much cleaner:</p><pre class="programlisting">struct send_rocket +{ + template <class Fsm,class Evt,class SourceState,class TargetState> + void operator()(Evt const& evt, Fsm& fsm, SourceState& src,TargetState&) + { + fire_rocket(evt.direction, src.current_calculation); + fsm.process_event(rocket_launched()); + } +}; </pre></div><div class="sect2" title="Defining a simple state machine"><div class="titlepage"><div><div><h3 class="title"><a name="d0e1281"></a>Defining a simple state machine</h3></div></div></div><p>Like states, state machines can be defined using the previous front-end, + as the previous example showed, or with the functor front-end, which allows + you to define a state machine entry and exit functions as functors, as in + <a class="link" href="examples/SimpleWithFunctors2.cpp" target="_top">this + example</a>.</p></div><div class="sect2" title="Anonymous transitions"><div class="titlepage"><div><div><h3 class="title"><a name="d0e1289"></a>Anonymous transitions</h3></div></div></div><p>Anonymous (completion) transitions are transitions without a named event. + We saw how this front-end uses <code class="code">none</code> when no action or guard is + required. We can also use <code class="code">none</code> instead of an event to mark an + anonymous transition. For example, the following transition makes an + 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 + 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="d0e1315"></a><span class="command"><strong><a name="functor-internal-transitions"></a></strong></span>Internal + 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 + front-end. As for the simple standard front-end, both methods of defining + 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 + table with <code class="code">none</code> as target state defines an internal + transition.</p></li><li class="listitem"><p>providing an <code class="code">internal_transition_table</code> made of + <code class="code">Internal</code> rows inside a state or submachine + defines UML-conform internal transitions with higher + priority.</p></li><li class="listitem"><p>transitions defined inside + <code class="code">internal_transition_table</code> require no source or + target state as the source state is known (<code class="code">Internal</code> + really are <code class="code">Row</code> without a source or target state) + .</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 + tables are added into the main state machine's table, thus allowing you to + distribute the transition table definition and reuse states.</p><p>There is an added bonus offered for submachines, which can have both the + standard transition_table and an internal_transition_table (which has higher + priority). This makes it easier if you decide to make a full submachine from + a state later. It is also slightly faster than the standard alternative, + adding orthogonal regions, because event dispatching will, if accepted by + the internal table, not continue to the subregions. This gives you a O(1) + dispatch instead of O(number of regions). While the example is with eUML, + the same is also possible with this front-end.</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 (experimental)</td></tr></table></div></body></html>
\ No newline at end of file diff --git a/libs/msm/doc/HTML/ch03s04.html b/libs/msm/doc/HTML/ch03s04.html new file mode 100644 index 0000000000..f70bd93ab4 --- /dev/null +++ b/libs/msm/doc/HTML/ch03s04.html @@ -0,0 +1,487 @@ +<html><head> + <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> + <title>eUML (experimental)</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="ch03s03.html" title="Functor front-end"><link rel="next" href="ch03s05.html" title="Back-end"></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">eUML (experimental)</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="ch03s03.html">Prev</a> </td><th width="60%" align="center">Chapter 3. Tutorial</th><td width="20%" align="right"> <a accesskey="n" href="ch03s05.html">Next</a></td></tr></table><hr></div><div class="sect1" title="eUML (experimental)"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="d0e1361"></a><span class="command"><strong><a name="eUML-front-end"></a></strong></span>eUML (experimental)</h2></div></div></div><p><span class="underline">Important note</span>: eUML requires a compiler + supporting Boost.Typeof. More generally, eUML has experimental status because + some compilers will start crashing when a state machine becomes too big (usually + when you write huge actions).</p><p>The previous front-ends are simple to write but still force an amount of + noise, mostly MPL types, so it would be nice to write code looking like C++ + (with a C++ action language) directly inside the transition table, like UML + designers like to do on their state machine diagrams. If it were functional + programming, it would be even better. This is what eUML is for.</p><p>eUML is a Boost.Proto and Boost.Typeof-based compile-time domain specific + embedded language. It provides grammars which allow the definition of + actions/guards directly inside the transition table or entry/exit in the state + definition. There are grammars for actions, guards, flags, attributes, deferred + events, initial states.</p><p>It also relies on Boost.Typeof as a wrapper around the new decltype C++0x + feature to provide a compile-time evaluation of all the grammars. Unfortunately, + all the underlying Boost libraries are not Typeof-enabled, so for the moment, + you will need a compiler where Typeof is supported (like VC9-10, g++ >= + 4.3).</p><p>Examples will be provided in the next paragraphs. You need to include eUML + basic features: </p><p> + </p><pre class="programlisting">#include <msm/front/euml/euml.hpp></pre><p> + </p><p>To add STL support (at possible cost of longer compilation times), include: </p><p> + </p><pre class="programlisting">#include <msm/front/euml/stl.hpp></pre><p> + </p><p>eUML is defined in the namespace <code class="code">msm::front::euml</code>.</p><div class="sect2" title="Transition table"><div class="titlepage"><div><div><h3 class="title"><a name="d0e1394"></a>Transition table</h3></div></div></div><p>A transition can be defined using eUML as: </p><p> + </p><pre class="programlisting">source + event [guard] / action == target</pre><p> + </p><p>or as</p><p> + </p><pre class="programlisting">target == source + event [guard] / action</pre><p> + </p><p>The first version looks like a drawn transition in a diagram, the second + one seems natural to a C++ developer.</p><p>The simple transition table written with the <span class="command"><strong><a class="command" href="ch03s03.html#functor-front-end">functor front-end</a></strong></span> can now be + written as:</p><pre class="programlisting">BOOST_MSM_EUML_TRANSITION_TABLE(( +Stopped + play [some_guard] / (some_action , start_playback) == Playing , +Stopped + open_close/ open_drawer == Open , +Stopped + stop == Stopped , +Open + open_close / close_drawer == Empty , +Empty + open_close / open_drawer == Open , +Empty + cd_detected [good_disk_format] / store_cd_info == Stopped +),transition_table) </pre><p>Or, using the alternative notation, it can be:</p><pre class="programlisting">BOOST_MSM_EUML_TRANSITION_TABLE(( +Playing == Stopped + play [some_guard] / (some_action , start_playback) , +Open == Stopped + open_close/ open_drawer , +Stopped == Stopped + stop , +Empty == Open + open_close / close_drawer , +Open == Empty + open_close / open_drawer , +Stopped == Empty + cd_detected [good_disk_format] / store_cd_info +),transition_table) </pre><p>The transition table now looks like a list of (readable) rules with little + noise.</p><p>UML defines guards between “[ ]” and actions after a “/”, so the chosen + syntax is already more readable for UML designers. UML also allows designers + to define several actions sequentially (our previous ActionSequence_) + separated by a comma. The first transition does just this: two actions + separated by a comma and enclosed inside parenthesis to respect C++ operator + precedence.</p><p>If this seems to you like it will cost you run-time performance, don't + worry, eUML is based on typeof (or decltype) which only evaluates the + parameters to BOOST_MSM_EUML_TRANSITION_TABLE and no run-time cost occurs. + Actually, eUML is only a metaprogramming layer on top of "standard" MSM + metaprogramming and this first layer generates the previously-introduced + <span class="command"><strong><a class="command" href="ch03s03.html#functor-front-end">functor + front-end</a></strong></span>.</p><p>UML also allows designers to define more complicated guards, like + [good_disk_format && (some_condition || some_other_condition)]. This + was possible with our previously defined functors, but using a complicated + template syntax. This syntax is now possible exactly as written, which means + without any syntactic noise at all.</p></div><div class="sect2" title="A simple example: rewriting only our transition table"><div class="titlepage"><div><div><h3 class="title"><a name="d0e1435"></a>A simple example: rewriting only our transition table</h3></div></div></div><p>As an introduction to eUML, we will rewrite our tutorial's transition + table using eUML. This will require two or three changes, depending on the compiler:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>events must inherit from msm::front::euml::euml_event< + event_name ></p></li><li class="listitem"><p>states must inherit from msm::front::euml::euml_state< + state_name ></p></li><li class="listitem"><p>with VC, states must be declared before the front-end</p></li></ul></div><p>We now can write the transition table like just shown, using + BOOST_MSM_EUML_DECLARE_TRANSITION_TABLE instead of + BOOST_MSM_EUML_TRANSITION_TABLE. The <a class="link" href="examples/SimpleTutorialWithEumlTable.cpp" target="_top">implementation</a> is pretty + straightforward.</p><p>The <a class="link" href="examples/CompositeTutorialWithEumlTable.cpp" target="_top">composite</a> implementation is slightly trickier because the submachine + has to be a msm::back::state_machine and a msm::front::euml::state. For + example:</p><pre class="programlisting">// front-end like always +struct front_end : public boost::msm::front::state_machine_def<front_end> +{ +... +}; +// back-end like always +typedef boost::msm::back::state_machine<front_end> back_end; +// this is new: make the submachine a eUML type +struct submachine : public back_end, + public boost::msm::front::euml::euml_state<back_end> +{ +};</pre><p>Unfortunately, there is a bug with VC, which appears from time to time and + causes in a stack overflow. If you get a warning that the program is + recursive on all paths, revert to either standard eUML or another front-end + as Microsoft doesn't seem to intend to fix it.</p><p>We now have a new, more readable transition table with few changes to our + example. eUML can do much more so please follow the guide.</p></div><div class="sect2" title="Defining events, actions and states with entry/exit actions"><div class="titlepage"><div><div><h3 class="title"><a name="d0e1466"></a>Defining events, actions and states with entry/exit actions</h3></div></div></div><div class="sect3" title="Events"><div class="titlepage"><div><div><h4 class="title"><a name="d0e1469"></a>Events</h4></div></div></div><p>Events must be proto-enabled. To achieve this, they must inherit from + a proto terminal (euml_event<event-name>). eUML also provides a macro + to make this easier:</p><p> + </p><pre class="programlisting">BOOST_MSM_EUML_EVENT(play)</pre><p> + </p><p>This declares an event type and an instance of this type called + <code class="code">play</code>, which is now ready to use in state or transition + behaviors.</p><p>There is a second macro, BOOST_MSM_EUML_EVENT_WITH_ATTRIBUTES, which + takes as second parameter the attributes an event will contain, using + the <span class="command"><strong><a class="command" href="ch03s04.html#eUML-attributes">attribute + syntax</a></strong></span>.</p><p><span class="underline">Note</span>: as we now have events + defined as instances instead of just types, can we still process an + event by creating one on the fly, like: + <code class="code">fsm.process_event(play());</code> or do we have to write: + <code class="code">fsm.process_event(play);</code></p><p>The answer is you can do both. The second one is easier but unlike + other front-ends, the second uses a defined operator(), which creates an + event on the fly.</p></div><div class="sect3" title="Actions"><div class="titlepage"><div><div><h4 class="title"><a name="d0e1500"></a>Actions</h4></div></div></div><p>Actions (returning void) and guards (returning a bool) are defined + like previous functors, with the difference that they also must be + proto-enabled. This can be done by inheriting from euml_action< + functor-name >. eUML also provides a macro:</p><pre class="programlisting">BOOST_MSM_EUML_ACTION(some_condition) +{ + template <class Fsm,class Evt,class SourceState,class TargetState> + bool operator()(Evt const& ,Fsm& ,SourceState&,TargetState& ) + { return true; } +}; </pre><p>Like for events, this macro declares a functor type and an instance + for use in transition or state behaviors.</p><p>It is possible to use the same action grammar from the transition + table to define state entry and exit behaviors. So + <code class="code">(action1,action2)</code> is a valid entry or exit behavior + executing both actions in turn.</p><p>The state functors have a slightly different signature as there is no + source and target state but only a current state (entry/exit actions are + transition-independent), for example:</p><pre class="programlisting">BOOST_MSM_EUML_ACTION(Empty_Entry) +{ + template <class Evt,class Fsm,class State> + void operator()(Evt const& ,Fsm& ,State& ) { ... } + }; </pre><p><span class="command"><strong><a name="eUML-reuse-functor"></a></strong></span>It is also possible to reuse the functors from the functor front-end. + The syntax is however slightly less comfortable as we need to pretend + creating one on the fly for typeof. For example:</p><pre class="programlisting">struct start_playback +{ + template <class Fsm,class Evt,class SourceState,class TargetState> + void operator()(Evt const& ,Fsm&,SourceState& ,TargetState& ) + { + ... + } +}; +BOOST_MSM_EUML_TRANSITION_TABLE(( +Playing == Stopped + play / start_playback() , +... +),transition_table)</pre></div><div class="sect3" title="States"><div class="titlepage"><div><div><h4 class="title"><a name="d0e1523"></a>States</h4></div></div></div><p>There is also a macro for states. This macro has 2 arguments, first + the expression defining the state, then the state (instance) + name:</p><pre class="programlisting">BOOST_MSM_EUML_STATE((),Paused)</pre><p>This defines a simple state without entry or exit action. You can + provide in the expression parameter the state behaviors (entry and exit) + using the action grammar, like in the transition table:</p><pre class="programlisting">BOOST_MSM_EUML_STATE(((Empty_Entry,Dummy_Entry)/*2 entryactions*/, + Empty_Exit/*1 exit action*/ ), + Empty)</pre><p>This means that Empty is defined as a state with an entry action made + of two sub-actions, Empty_Entry and Dummy_Entry (enclosed inside + parenthesis), and an exit action, Empty_Exit.</p><p>There are several possibilitites for the <span class="command"><strong><a name="eUML-build-state"></a></strong></span> expression syntax:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>(): state without entry or exit action.</p></li><li class="listitem"><p>(Expr1): state with entry but no exit action.</p></li><li class="listitem"><p>(Expr1,Expr2): state with entry and exit action.</p></li><li class="listitem"><p>(Expr1,Expr2,Attributes): state with entry and exit + action, defining some attributes (read further on).</p></li><li class="listitem"><p>(Expr1,Expr2,Attributes,Configure): state with entry and + exit action, defining some attributes (read further on) and + flags (standard MSM flags) or deferred events (standard MSM + deferred events).</p></li><li class="listitem"><p>(Expr1,Expr2,Attributes,Configure,Base): state with entry + and exit action, defining some attributes (read further on), + flags and deferred events (plain msm deferred events) and a + non-default base state (as defined in standard MSM).</p></li></ul></div><p>no_action is also defined, which does, well, nothing except being a + placeholder (needed for example as entry action if we have no entry but + an exit). Expr1 and Expr2 are a sequence of actions, obeying the same + action grammar as in the transition table (following the “/” + symbol).</p><p>The BOOST_MSM_EUML_STATE macro will allow you to define most common + states, but sometimes you will need more, for example provide in your + states some special behavior. In this case, you will have to do the + macro's job by hand, which is not very complicated. The state will need + to inherit from <code class="code">msm::front::state<></code>, like any state, and + from <code class="code">euml_state<state-name></code> to be proto-enabled. You + will then need to declare an instance for use in the transition table. + For example:</p><pre class="programlisting">struct Empty_impl : public msm::front::state<> , public euml_state<Empty_impl> +{ + void activate_empty() {std::cout << "switching to Empty " << std::endl;} + template <class Event,class Fsm> + void on_entry(Event const& evt,Fsm&fsm){...} + template <class Event,class Fsm> + void on_exit(Event const& evt,Fsm&fsm){...} +}; +//instance for use in the transition table +Empty_impl const Empty;</pre><p>Notice also that we defined a method named activate_empty. We would + like to call it inside a behavior. This can be done using the + BOOST_MSM_EUML_METHOD macro. </p><pre class="programlisting">BOOST_MSM_EUML_METHOD(ActivateEmpty_,activate_empty,activate_empty_,void,void)</pre><p>The first parameter is the name of the underlying functor, which you + could use with the functor front-end, the second is the state method + name, the third is the eUML-generated function, the fourth and fifth the + return value when used inside a transition or a state behavior. You can + now use this inside a transition:</p><pre class="programlisting">Empty == Open + open_close / (close_drawer,activate_empty_(target_))</pre></div></div><div class="sect2" title="Wrapping up a simple state machine and first complete examples"><div class="titlepage"><div><div><h3 class="title"><a name="d0e1579"></a>Wrapping up a simple state machine and first complete examples</h3></div></div></div><p>You can reuse the state machine definition method from the standard + front-end and simply replace the transition table by this new one. You can + also use eUML to define a state machine "on the fly" (if, for example, you + need to provide an on_entry/on_exit for this state machine as a functor). + For this, there is also a macro, <span class="command"><strong><a name="eUML-build-sm"></a></strong></span>BOOST_MSM_EUML_DECLARE_STATE_MACHINE, which has 2 arguments, an expression + describing the state machine and the state machine name. The expression has + up to 8 arguments:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>(Stt, Init): simplest state machine where only the transition + table and initial state(s) are defined.</p></li><li class="listitem"><p>(Stt, Init, Expr1): state machine where the transition table, + initial state and entry action are defined.</p></li><li class="listitem"><p>(Stt, Init, Expr1, Expr2): state machine where the transition + table, initial state, entry and exit actions are defined.</p></li><li class="listitem"><p>(Stt, Init, Expr1, Expr2, Attributes): state machine where the + transition table, initial state, entry and exit actions are + defined. Furthermore, some attributes are added (read further + on).</p></li><li class="listitem"><p>(Stt, Init, Expr1, Expr2, Attributes, Configure): state + machine where the transition table, initial state, entry and + exit actions are defined. Furthermore, some attributes (read + further on), flags, deferred events and <a class="link" href="ch03s04.html#eUML-Configuration">configuration + capabilities</a> (no message queue / no exception + catching) are added.</p></li><li class="listitem"><p>(Stt, Init, Expr1, Expr2, Attributes, Flags, Deferred , Base): + state machine where the transition table, initial state, entry + and exit actions are defined. Furthermore, attributes (read + further on), flags , deferred events and configuration + capabilities (no message queue / no exception catching) are + added and a non-default base state (see the <a class="link" href="ch03s05.html#backend-base-state">back-end + description</a>) is defined.</p></li></ul></div><p>For example, a minimum state machine could be defined + as:</p><pre class="programlisting">BOOST_MSM_EUML_TRANSITION_TABLE(( +),transition_table) </pre><pre class="programlisting">BOOST_MSM_EUML_DECLARE_STATE_MACHINE((transition_table,init_ << Empty ), + player_)</pre><p>Please have a look at the player tutorial written using eUML's <a class="link" href="examples/SimpleTutorialEuml2.cpp" target="_top">first syntax</a> and + <a class="link" href="examples/SimpleTutorialEuml.cpp" target="_top">second syntax</a>. + The BOOST_MSM_EUML_DECLARE_ATTRIBUTE macro, to which we will get back + shortly, declares attributes given to an eUML type (state or event) using + the <span class="command"><strong><a class="command" href="ch03s04.html#eUML-attributes">attribute + syntax</a></strong></span>.</p></div><div class="sect2" title="Defining a submachine"><div class="titlepage"><div><div><h3 class="title"><a name="d0e1627"></a>Defining a submachine</h3></div></div></div><p>Defining a submachine (see <a class="link" href="examples/CompositeTutorialEuml.cpp" target="_top">tutorial</a>) with + other front-ends simply means using a state which is a state machine in the + transition table of another state machine. This is the same with eUML. One + only needs define a second state machine and reference it in the transition + table of the containing state machine.</p><p>Unlike the state or event definition macros, + BOOST_MSM_EUML_DECLARE_STATE_MACHINE defines a type, not an instance because + a type is what the back-end requires. This means that you will need to + declare yourself an instance to reference your submachine into another state + machine, for example:</p><pre class="programlisting">BOOST_MSM_EUML_DECLARE_STATE_MACHINE(...,Playing_) +typedef msm::back::state_machine<Playing_> Playing_type; +Playing_type const Playing;</pre><p>We can now use this instance inside the transition table of the containing + state machine:</p><pre class="programlisting">Paused == Playing + pause / pause_playback</pre></div><div class="sect2" title="Attributes / Function call"><div class="titlepage"><div><div><h3 class="title"><a name="d0e1643"></a> + <span class="command"><strong><a name="eUML-attributes"></a></strong></span>Attributes / Function call</h3></div></div></div><p>We now want to make our grammar more useful. Very often, one needs only + very simple action methods, for example ++Counter or Counter > 5 where + Counter is usually defined as some attribute of the class containing the + state machine. It seems like a waste to write a functor for such a simple + action. Furthermore, states within MSM are also classes so they can have + attributes, and we would also like to provide them with attributes. </p><p>If you look back at our examples using the <a class="link" href="examples/SimpleTutorialEuml2.cpp" target="_top">first</a> and <a class="link" href="examples/SimpleTutorialEuml.cpp" target="_top">second</a> syntaxes, you + will find a BOOST_MSM_EUML_DECLARE_ATTRIBUTE and a BOOST_MSM_EUML_ATTRIBUTES + macro. The first one declares possible attributes:</p><pre class="programlisting">BOOST_MSM_EUML_DECLARE_ATTRIBUTE(std::string,cd_name) +BOOST_MSM_EUML_DECLARE_ATTRIBUTE(DiskTypeEnum,cd_type)</pre><p>This declares two attributes: cd_name of type std::string and cd_type of + type DiskTypeEnum. These attributes are not part of any event or state in + particular, we just declared a name and a type. Now, we can add attributes + to our cd_detected event using the second one:</p><pre class="programlisting">BOOST_MSM_EUML_ATTRIBUTES((attributes_ << cd_name << cd_type ), + cd_detected_attributes)</pre><p>This declares an attribute list which is not linked to anything in + particular yet. It can be attached to a state or an event. For example, if + we want the event cd_detected to have these defined attributes we + write:</p><pre class="programlisting">BOOST_MSM_EUML_EVENT_WITH_ATTRIBUTES(cd_detected,cd_detected_attributes)</pre><p>For states, we use the BOOST_MSM_EUML_STATE macro, which has an expression + form where one can provide attributes. For example:</p><pre class="programlisting">BOOST_MSM_EUML_STATE((no_action /*entry*/,no_action/*exit*/, + attributes_ << cd_detected_attributes), + some_state)</pre><p>OK, great, we now have a way to add attributes to a class, which we could + have done more easily, so what is the point? The point is that we can now + reference these attributes directly, at compile-time, in the transition + table. For example, in the example, you will find this transition:</p><pre class="programlisting">Stopped==Empty+cd_detected[good_disk_format&&(event_(cd_type)==Int_<DISK_CD>())] </pre><p>Read event_(cd_type) as event_->cd_type with event_ a type generic for + events, whatever the concrete event is (in this particular case, it happens + to be a cd_detected as the transition shows).</p><p>The main advantage of this feature is that you do not need to define a new + functor and you do not need to look inside the functor to know what it does, + you have all at hand.</p><p>MSM provides more generic objects for state machine types:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>event_ : used inside any action, the event triggering the + transition</p></li><li class="listitem"><p>state_: used inside entry and exit actions, the entered / + exited state</p></li><li class="listitem"><p>source_: used inside a transition action, the source + state</p></li><li class="listitem"><p>target_: used inside a transition action, the target + state</p></li><li class="listitem"><p>fsm_: used inside any action, the (lowest-level) state machine + processing the transition</p></li><li class="listitem"><p>Int_<int value>: a functor representing an int</p></li><li class="listitem"><p>Char_<value>: a functor representing a char</p></li><li class="listitem"><p>Size_t_<value>: a functor representing a size_t</p></li><li class="listitem"><p>String_<mpl::string> (boost >= 1.40): a functor + representing a string.</p></li></ul></div><p>These helpers can be used in two different ways:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>helper(attribute_name) returns the attribute with name + attribute_name</p></li><li class="listitem"><p>helper returns the state / event type itself.</p></li></ul></div><p>The second form is helpful if you want to provide your states with their + own methods, which you also want to use inside the transition table. In the + <a class="link" href="examples/SimpleTutorialEuml.cpp" target="_top">above + tutorial</a>, we provide Empty with an activate_empty method. We would + like to create a eUML functor and call it from inside the transition table. + This is done using the MSM_EUML_METHOD / MSM_EUML_FUNCTION macros. The first + creates a functor to a method, the second to a free function. In the + tutorial, we write:</p><pre class="programlisting">MSM_EUML_METHOD(ActivateEmpty_,activate_empty,activate_empty_,void,void)</pre><p>The first parameter is the functor name, for use with the functor + front-end. The second is the name of the method to call. The third is the + function name for use with eUML, the fourth is the return type of the + function if used in the context of a transition action, the fifth is the + result type if used in the context of a state entry / exit action (usually + fourth and fifth are the same). We now have a new eUML function calling a + method of "something", and this "something" is one of the five previously + shown generic helpers. We can now use this in a transition, for + example:</p><pre class="programlisting">Empty == Open + open_close / (close_drawer,activate_empty_(target_))</pre><p>The action is now defined as a sequence of two actions: close_drawer and + activate_empty, which is called on the target itself. The target being Empty + (the state defined left), this really will call Empty::activate_empty(). + This method could also have an (or several) argument(s), for example the + event, we could then call activate_empty_(target_ , event_).</p><p>More examples can be found in the <a class="link" href="examples/CompilerStressTestEuml.cpp" target="_top">terrible compiler + stress test</a>, the <a class="link" href="examples/SimpleTimer.cpp" target="_top">timer example</a> or in the <a class="link" href="examples/iPodSearchEuml.cpp" target="_top">iPodSearch with eUML</a> + (for String_ and more).</p></div><div class="sect2" title="Orthogonal regions, flags, event deferring"><div class="titlepage"><div><div><h3 class="title"><a name="d0e1743"></a>Orthogonal regions, flags, event deferring</h3></div></div></div><p>Defining orthogonal regions really means providing more initial states. To + add more initial states, “shift left” some, for example, if we had another + initial state named AllOk :</p><pre class="programlisting">BOOST_MSM_EUML_DECLARE_STATE_MACHINE((transition_table, + init_ << Empty << AllOk ), + player_)</pre><p>You remember from the <span class="command"><strong><a class="command" href="ch03s04.html#eUML-build-state">BOOST_MSM_EUML_STATE </a></strong></span> and <span class="command"><strong><a class="command" href="ch03s04.html#eUML-build-sm">BOOST_MSM_EUML_DECLARE_STATE_MACHINE</a></strong></span> signatures that just + after attributes, we can define flags, like in the basic MSM front-end. To + do this, we have another "shift-left" grammar, for example:</p><pre class="programlisting">BOOST_MSM_EUML_STATE((no_action,no_action, attributes_ <<no_attributes_, + /* flags */ configure_<< PlayingPaused << CDLoaded), + Paused)</pre><p>We now defined that Paused will get two flags, PlayingPaused and CDLoaded, + defined, with another macro:</p><pre class="programlisting">BOOST_MSM_EUML_FLAG(CDLoaded)</pre><p>This corresponds to the following basic front-end definition of + Paused:</p><pre class="programlisting">struct Paused : public msm::front::state<> +{ + typedef mpl::vector2<PlayingPaused,CDLoaded> flag_list; +};</pre><p>Under the hood, what you get really is a mpl::vector2.</p><p><span class="underline">Note</span>: As we use the version of + BOOST_MSM_EUML_STATE's expression with 4 arguments, we need to tell eUML + that we need no attributes. Similarly to a <code class="code">cout << endl</code>, + we need a <code class="code">attributes_ << no_attributes_</code> syntax.</p><p>You can use the flag with the is_flag_active method of a state machine. + You can also use the provided helper function is_flag_ (returning a bool) + for state and transition behaviors. For example, in the <a class="link" href="examples/iPodEuml.cpp" target="_top">iPod implementation with eUML</a>, + you find the following transition:</p><pre class="programlisting">ForwardPressed == NoForward + EastPressed[!is_flag_(NoFastFwd)]</pre><p>The function also has an optional second parameter which is the state + machine on which the function is called. By default, fsm_ is used (the + current state machine) but you could provide a functor returning a reference + to another state machine.</p><p>eUML also supports defining deferred events in the state (state machine) + definition. To this aim, we can reuse the flag grammar. For example:</p><pre class="programlisting">BOOST_MSM_EUML_STATE((Empty_Entry,Empty_Exit, attributes_ << no_attributes_, + /* deferred */ configure_<< play ),Empty) </pre><p>The configure_ left shift is also responsible for deferring events. Shift + inside configure_ a flag and the state will get a flag, shift an event and + it will get a deferred event. This replaces the basic front-end + definition:</p><pre class="programlisting">typedef mpl::vector<play> deferred_events;</pre><p>In <a class="link" href="examples/OrthogonalDeferredEuml.cpp" target="_top">this + tutorial</a>, player is defining a second orthogonal region with + AllOk as initial state. The <code class="code">Empty</code> and <code class="code">Open</code> states + also defer the event <code class="code">play</code>. <code class="code">Open</code>, + <code class="code">Stopped</code> and <code class="code">Pause</code> also support the flag + <code class="code">CDLoaded</code> using the same left shift into + <code class="code">configure_</code>.</p><p>In the functor front-end, we also had the possibility to defer an event + inside a transition, which makes possible conditional deferring. This is + also possible with eUML through the use of the defer_ order, as shown in + <a class="link" href="examples/OrthogonalDeferredEuml.cpp" target="_top">this + tutorial</a>. You will find the following transition:</p><pre class="programlisting">Open + play / defer_</pre><p>This is an <span class="command"><strong><a class="command" href="ch03s04.html#eUML-internal">internal + transition</a></strong></span>. Ignore it for the moment. Interesting is, that + when the event <code class="code">play</code> is fired and <code class="code">Open</code> is active, + the event will be deferred. Now add a guard and you can conditionally defer + the event, for example:</p><pre class="programlisting">Open + play [ some_condition ] / defer_</pre><p>This is similar to what we did with the functor front-end. This means that + we have the same constraints. Using defer_ instead of a state declaration, + we need to tell MSM that we have deferred events in this state machine. We + do this (again) using a configure_ declaration in the state machine + definition in which we shift the deferred_events configuration flag:</p><pre class="programlisting">BOOST_MSM_EUML_DECLARE_STATE_MACHINE((transition_table, + init_ << Empty << AllOk, + Entry_Action, + Exit_Action, + attributes_ << no_attributes_, + configure_<< deferred_events ), + player_)</pre><p>A <a class="link" href="examples/OrthogonalDeferredEuml2.cpp" target="_top">tutorial</a> + illustrates this possibility.</p></div><div class="sect2" title="Customizing a state machine / Getting more speed"><div class="titlepage"><div><div><h3 class="title"><a name="d0e1855"></a> + <span class="command"><strong><a name="eUML-Configuration"></a></strong></span>Customizing a state machine / Getting + more speed</h3></div></div></div><p>We just saw how to use configure_ to define deferred events or flags. We + can also use it to configure our state machine like we did with the other front-ends:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p><code class="code">configure_ << no_exception</code>: disables + exception handling</p></li><li class="listitem"><p><code class="code">configure_ << no_msg_queue</code> deactivates the + message queue</p></li><li class="listitem"><p><code class="code">configure_ << deferred_events</code> manually + enables event deferring</p></li></ul></div><p>Deactivating the first two features and not activating the third if not + needed greatly improves the event dispatching speed of your state machine. + Our <a class="link" href="examples/EumlSimple.cpp" target="_top">speed testing</a> example + with eUML does this for the best performance.</p><p><span class="underline">Important note</span>: As exit pseudo + states are using the message queue to forward events out of a submachine, + the <code class="code">no_message_queue</code> option cannot be used with state machines + containing an exit pseudo state.</p></div><div class="sect2" title="Completion / Anonymous transitions"><div class="titlepage"><div><div><h3 class="title"><a name="d0e1890"></a>Completion / Anonymous transitions</h3></div></div></div><p>Anonymous transitions (See <span class="command"><strong><a class="command" href="ch02s02.html#uml-anonymous">UML + tutorial</a></strong></span>) are transitions without a named event, which are + therefore triggered immediately when the source state becomes active, + provided a guard allows it. As there is no event, to define such a + transition, simply omit the “+” part of the transition (the event), for + example: </p><pre class="programlisting">State3 == State4 [always_true] / State3ToState4 +State4 [always_true] / State3ToState4 == State3</pre><p>Please have a look at <a class="link" href="examples/AnonymousTutorialEuml.cpp" target="_top">this example</a>, + which implements the <span class="command"><strong><a class="command" href="ch03s02.html#anonymous-transitions">previously + defined</a></strong></span> state machine with eUML.</p></div><div class="sect2" title="Internal transitions"><div class="titlepage"><div><div><h3 class="title"><a name="d0e1908"></a><span class="command"><strong><a name="eUML-internal"></a></strong></span>Internal transitions</h3></div></div></div><p>Like both other front-ends, eUML supports two ways of defining internal transitions:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>in the state machine's transition table. In this case, you + need to specify a source state, event, actions and guards but no + target state, which eUML will interpret as an internal + transition, for example this defines a transition internal to + Open, on the event open_close:</p><pre class="programlisting">Open + open_close [internal_guard1] / internal_action1</pre><p><a class="link" href="examples/EumlInternal.cpp" target="_top">A full + example</a> is also provided.</p></li><li class="listitem"><p>in a state's <code class="code">internal_transition_table</code>. For + example:</p><pre class="programlisting">BOOST_MSM_EUML_DECLARE_STATE((Open_Entry,Open_Exit),Open_def) +struct Open_impl : public Open_def +{ + BOOST_MSM_EUML_DECLARE_INTERNAL_TRANSITION_TABLE(( + open_close [internal_guard1] / internal_action1 + )) +};</pre><p>Notice how we do not need to repeat that the transition + originates from Open as we already are in Open's context. </p><p>The <a class="link" href="examples/EumlInternalDistributed.cpp" target="_top">implementation</a> also shows the added bonus offered + for submachines, which can have both the standard + transition_table and an internal_transition_table (which has + higher priority). This makes it easier if you decide to make a + full submachine from a state. It is also slightly faster than + the standard alternative, adding orthogonal regions, because + event dispatching will, if accepted by the internal table, not + continue to the subregions. This gives you a O(1) dispatch + instead of O(number of regions).</p></li></ul></div></div><div class="sect2" title="Other state types"><div class="titlepage"><div><div><h3 class="title"><a name="d0e1939"></a>Other state types</h3></div></div></div><p>We saw the <span class="command"><strong><a class="command" href="ch03s04.html#eUML-build-state">build_state</a></strong></span> + function, which creates a simple state. Likewise, eUML provides other + state-building macros for other types of states:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>BOOST_MSM_EUML_TERMINATE_STATE takes the same arguments as + BOOST_MSM_EUML_STATE and defines, well, a terminate + state.</p></li><li class="listitem"><p>BOOST_MSM_EUML_INTERRUPT_STATE takes the same arguments as + BOOST_MSM_EUML_STATE and defines an interrupt state. However, + the expression argument must contain as first element the event + ending the interruption, for example: + <code class="code">BOOST_MSM_EUML_INTERRUPT_STATE(( end_error /*end + interrupt event*/,ErrorMode_Entry,ErrorMode_Exit + ),ErrorMode)</code></p></li><li class="listitem"><p>BOOST_MSM_EUML_EXIT_STATE takes the same arguments as + BOOST_MSM_EUML_STATE and defines an exit pseudo state. However, + the expression argument must contain as first element the event + propagated from the exit point: + <code class="code">BOOST_MSM_EUML_EXIT_STATE(( event6 /*propagated + event*/,PseudoExit1_Entry,PseudoExit1_Exit + ),PseudoExit1)</code></p></li><li class="listitem"><p>BOOST_MSM_EUML_EXPLICIT_ENTRY_STATE defines an entry pseudo + state. It takes 3 parameters: the region index to be entered is + defined as an int argument, followed by the configuration + expression like BOOST_MSM_EUML_STATE and the state name, so that + <code class="code">BOOST_MSM_EUML_EXPLICIT_ENTRY_STATE(0 /*region + index*/,( SubState2_Entry,SubState2_Exit ),SubState2)</code> + defines an entry state into the first region of a + submachine.</p></li><li class="listitem"><p>BOOST_MSM_EUML_ENTRY_STATE defines an entry pseudo state. It + takes 3 parameters: the region index to be entered is defined as + an int argument, followed by the configuration expression like + BOOST_MSM_EUML_STATE and the state name, so that + <code class="code">BOOST_MSM_EUML_ENTRY_STATE(0,( + PseudoEntry1_Entry,PseudoEntry1_Exit ),PseudoEntry1)</code> + defines a pseudo entry state into the first region of a + submachine.</p></li></ul></div><p>To use these states in the transition table, eUML offers the functions + <code class="code">explicit_</code>, <code class="code">exit_pt_</code> and + <code class="code">entry_pt_</code>. For example, a direct entry into the substate + SubState2 from SubFsm2 could be:</p><pre class="programlisting">explicit_(SubFsm2,SubState2) == State1 + event2</pre><p>Forks being a list on direct entries, eUML supports a logical syntax + (state1, state2, ...), for example:</p><pre class="programlisting">(explicit_(SubFsm2,SubState2), + explicit_(SubFsm2,SubState2b), + explicit_(SubFsm2,SubState2c)) == State1 + event3 </pre><p>An entry point is entered using the same syntax as explicit entries: + </p><pre class="programlisting">entry_pt_(SubFsm2,PseudoEntry1) == State1 + event4</pre><p>For exit points, it is again the same syntax except that exit points are + used as source of the transition: + </p><pre class="programlisting">State2 == exit_pt_(SubFsm2,PseudoExit1) + event6 </pre><p>The <a class="link" href="examples/DirectEntryEuml.cpp" target="_top">entry tutorial</a> + is also available with eUML.</p></div><div class="sect2" title="Helper functions"><div class="titlepage"><div><div><h3 class="title"><a name="d0e2003"></a>Helper functions</h3></div></div></div><p>We saw a few helpers but there are more, so let us have a more complete description:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>event_ : used inside any action, the event triggering the + transition</p></li><li class="listitem"><p>state_: used inside entry and exit actions, the entered / + exited state</p></li><li class="listitem"><p>source_: used inside a transition action, the source + state</p></li><li class="listitem"><p>target_: used inside a transition action, the target + state</p></li><li class="listitem"><p>fsm_: used inside any action, the (deepest-level) state + machine processing the transition</p></li><li class="listitem"><p>These objects can also be used as a function and return an + attribute, for example event_(cd_name)</p></li><li class="listitem"><p>Int_<int value>: a functor representing an int</p></li><li class="listitem"><p>Char_<value>: a functor representing a char</p></li><li class="listitem"><p>Size_t_<value>: a functor representing a size_t</p></li><li class="listitem"><p>True_ and False_ functors returning true and false + respectively</p></li><li class="listitem"><p>String_<mpl::string> (boost >= 1.40): a functor + representing a string.</p></li><li class="listitem"><p>if_then_else_(guard, action, action) where action can be an + action sequence</p></li><li class="listitem"><p>if_then_(guard, action) where action can be an action + sequence</p></li><li class="listitem"><p>while_(guard, action) where action can be an action + sequence</p></li><li class="listitem"><p>do_while_(guard, action) where action can be an action + sequence</p></li><li class="listitem"><p>for_(action, guard, action, action) where action can be an + action sequence</p></li><li class="listitem"><p>process_(some_event [, some state machine] [, some state + machine] [, some state machine] [, some state machine]) will + call process_event (some_event) on the current state machine or + on the one(s) passed as 2nd , 3rd, 4th, 5th argument. This allow + sending events to several external machines</p></li><li class="listitem"><p>process_(event_): reprocesses the event which triggered the + transition</p></li><li class="listitem"><p>reprocess_(): same as above but shorter to write</p></li><li class="listitem"><p>process2_(some_event,Value [, some state machine] [, some + state machine] [, some state machine]) will call process_event + (some_event(Value)) on the current state machine or on the + one(s) passed as 3rd, 4th, 5th argument</p></li><li class="listitem"><p>is_ flag_(some_flag[, some state machine]) will call + is_flag_active on the current state machine or on the one passed + as 2nd argument</p></li><li class="listitem"><p>Predicate_<some predicate>: Used in STL algorithms. Wraps + unary/binary functions to make them eUML-compatible so that they + can be used in STL algorithms</p></li></ul></div><p>This can be quite fun. For example, </p><pre class="programlisting">/( if_then_else_(--fsm_(m_SongIndex) > Int_<0>(),/*if clause*/ + show_playing_song, /*then clause*/ + (fsm_(m_SongIndex)=Int_<1>(),process_(EndPlay))/*else clause*/ + ) + )</pre><p>means: if (fsm.SongIndex > 0, call show_playing_song else + {fsm.SongIndex=1; process EndPlay on fsm;}</p><p>A few examples are using these features:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>the iPod example introduced at the BoostCon09 <a class="link" href="examples/iPodEuml.cpp" target="_top">has been rewritten</a> + with eUML (weak compilers please move on...)</p></li><li class="listitem"><p>the iPodSearch example also introduced at the BoostCon09 <a class="link" href="examples/iPodSearchEuml.cpp" target="_top">has been + rewritten</a> with eUML. In this example, you will also + find some examples of STL functor usage.</p></li><li class="listitem"><p><a class="link" href="examples/SimpleTimer.cpp" target="_top">A simpler + timer</a> example is a good starting point. </p></li></ul></div><p>There is unfortunately a small catch. Defining a functor using + MSM_EUML_METHOD or MSM_EUML_FUNCTION will create a correct functor. Your own + eUML functors written as described at the beginning of this section will + also work well, <span class="underline">except</span>, for the + moment, with the while_, if_then_, if_then_else_ functions.</p></div><div class="sect2" title="Phoenix-like STL support"><div class="titlepage"><div><div><h3 class="title"><a name="d0e2106"></a>Phoenix-like STL support</h3></div></div></div><p>eUML supports most C++ operators (except address-of). For example it is + possible to write event_(some_attribute)++ or [source_(some_bool) && + fsm_(some_other_bool)]. But a programmer needs more than operators in his + daily programming. The STL is clearly a must have. Therefore, eUML comes in + with a lot of functors to further reduce the need for your own functors for + the transition table. For almost every algorithm or container method of the + STL, a corresponding eUML function is defined. Like Boost.Phoenix, “.” And + “->” of call on objects are replaced by a functional programming paradigm, + for example:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>begin_(container), end_(container): return iterators of a + container.</p></li><li class="listitem"><p>empty_(container): returns container.empty()</p></li><li class="listitem"><p>clear_(container): container.clear()</p></li><li class="listitem"><p>transform_ : std::transform</p></li></ul></div><p>In a nutshell, almost every STL method or algorithm is matched by a + corresponding functor, which can then be used in the transition table or + state actions. The <a class="link" href="pt02.html#Reference-begin">reference</a> + lists all eUML functions and the underlying functor (so that this + possibility is not reserved to eUML but also to the functor-based + front-end). The file structure of this Phoenix-like library matches the one + of Boost.Phoenix. All functors for STL algorithms are to be found in:</p><pre class="programlisting">#include <msm/front/euml/algorithm.hpp></pre><p>The algorithms are also divided into sub-headers, matching the phoenix + structure for simplicity:</p><pre class="programlisting">#include < msm/front/euml/iteration.hpp> +#include < msm/front/euml/transformation.hpp> +#include < msm/front/euml/querying.hpp> </pre><p>Container methods can be found in:</p><pre class="programlisting">#include < msm/front/euml/container.hpp></pre><p>Or one can simply include the whole STL support (you will also need to + include euml.hpp):</p><pre class="programlisting">#include < msm/front/euml/stl.hpp></pre><p>A few examples (to be found in <a class="link" href="examples/iPodSearchEuml.cpp" target="_top">this tutorial</a>):</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p><code class="code">push_back_(fsm_(m_tgt_container),event_(m_song))</code>: + the state machine has an attribute m_tgt_container of type + std::vector<OneSong> and the event has an attribute m_song of + type OneSong. The line therefore pushes m_song at the end of + m_tgt_container</p></li><li class="listitem"><p><code class="code">if_then_( state_(m_src_it) != + end_(fsm_(m_src_container)), + process2_(OneSong(),*(state_(m_src_it)++)) )</code>: the + current state has an attribute m_src_it (an iterator). If this + iterator != fsm.m_src_container.end(), process OneSong on fsm, + copy-constructed from state.m_src_it which we + post-increment</p></li></ul></div></div><div class="sect2" title="Writing actions with Boost.Phoenix (in development)"><div class="titlepage"><div><div><h3 class="title"><a name="d0e2159"></a><span class="command"><strong><a name="eUML-phoenix"></a></strong></span>Writing actions with Boost.Phoenix (in development)</h3></div></div></div><p> It is also possible to write actions, guards, state entry and exit + actions using a reduced set of Boost.Phoenix capabilities. This feature + is still in development stage, so you might get here and there some + surprise. Simple cases, however, should work well. What will not work + will be mixing of eUML and Phoenix functors. Writing guards in one + language and actions in another is ok though.</p><p>Phoenix also supports a larger syntax than what will ever be possible + with eUML, so you can only use a reduced set of phoenix's grammar. This + is due to the nature of eUML. The run-time transition table definition + is translated to a type using Boost.Typeof. The result is a "normal" MSM + transition table made of functor types. As C++ does not allow mixing + run-time and compile-time constructs, there will be some limit (trying + to instantiate a template class MyTemplateClass<i> where i is an int + will give you an idea). This means following valid Phoenix constructs + will not work:</p><p> + </p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>literals</p></li><li class="listitem"><p>function pointers</p></li><li class="listitem"><p>bind</p></li><li class="listitem"><p>->*</p></li></ul></div><p> + </p><p>MSM also provides placeholders which make more sense in its context + than arg1.. argn:</p><p> + </p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>_event: the event triggering the transition</p></li><li class="listitem"><p>_fsm: the state machine processing the event</p></li><li class="listitem"><p>_source: the source state of the transition</p></li><li class="listitem"><p>_target: the target state of the transition</p></li><li class="listitem"><p>_state: for state entry/exit actions, the entry/exit + state</p></li></ul></div><p> + </p><p>Future versions of MSM will support Phoenix better. You can contribute + by finding out cases which do not work but should, so that they can be + added.</p><p>Phoenix support is not activated by default. To activate it, add + before any MSM header: #define BOOST_MSM_EUML_PHOENIX_SUPPORT.</p><p>A <a class="link" href="examples/SimplePhoenix.cpp" target="_top">simple example</a> shows some basic capabilities.</p></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="ch03s03.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="ch03s05.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">Functor front-end </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> Back-end</td></tr></table></div></body></html>
\ No newline at end of file diff --git a/libs/msm/doc/HTML/ch03s05.html b/libs/msm/doc/HTML/ch03s05.html new file mode 100644 index 0000000000..1745ad10a7 --- /dev/null +++ b/libs/msm/doc/HTML/ch03s05.html @@ -0,0 +1,305 @@ +<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 (experimental)"><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="d0e2213"></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="d0e2218"></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="d0e2227"></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="d0e2246"></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="d0e2255"></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="d0e2269"></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="d0e2322"></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="d0e2348"></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="d0e2391"></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="d0e2404"></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="d0e2417"></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="d0e2457"></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 + one 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="d0e2539"></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="d0e2583"></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).</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="d0e2603"></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="d0e2618"></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="d0e2626"></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 (experimental) </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>
\ No newline at end of file diff --git a/libs/msm/doc/HTML/ch04.html b/libs/msm/doc/HTML/ch04.html new file mode 100644 index 0000000000..a0f93be3a3 --- /dev/null +++ b/libs/msm/doc/HTML/ch04.html @@ -0,0 +1,13 @@ +<html><head> + <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> + <title>Chapter 4. Performance / Compilers</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="pt01.html" title="Part I. User' guide"><link rel="prev" href="ch03s05.html" title="Back-end"><link rel="next" href="ch04s02.html" title="Executable size"></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">Chapter 4. Performance / Compilers</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="ch03s05.html">Prev</a> </td><th width="60%" align="center">Part I. User' guide</th><td width="20%" align="right"> <a accesskey="n" href="ch04s02.html">Next</a></td></tr></table><hr></div><div class="chapter" title="Chapter 4. Performance / Compilers"><div class="titlepage"><div><div><h2 class="title"><a name="d0e2685"></a>Chapter 4. Performance / Compilers</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="sect1"><a href="ch04.html#d0e2707">Speed</a></span></dt><dt><span class="sect1"><a href="ch04s02.html">Executable size</a></span></dt><dt><span class="sect1"><a href="ch04s03.html">Supported compilers</a></span></dt><dt><span class="sect1"><a href="ch04s04.html"> Limitations </a></span></dt><dt><span class="sect1"><a href="ch04s05.html"> Compilers corner </a></span></dt></dl></div><p>Tests were made on different PCs running Windows XP and Vista and compiled with + VC9 SP1 or Ubuntu and compiled with g++ 4.2 and 4.3. For these tests, the same + player state machine was written using Boost.Statechart, as a <a class="link" href="examples/SCSimple.cpp" target="_top">state machine with only simple states</a> + and as a <a class="link" href="examples/SCComposite.cpp" target="_top">state machine with a composite + state</a>. The same simple and composite state machines are implemented with + MSM with a standard frontend <a class="link" href="examples/MsmSimple.cpp" target="_top">(simple)</a><a class="link" href="examples/MsmComposite.cpp" target="_top">(composite)</a>, + the simple one also with <a class="link" href="examples/MsmSimpleFunctors.cpp" target="_top">functors</a> and with <a class="link" href="examples/EumlSimple.cpp" target="_top">eUML</a>. As these simple machines need no terminate/interrupt states, no + message queue and have no-throw guarantee on their actions, the MSM state machines + are defined with minimum functionality. Test machine is a Q6600 2.4GHz, Vista + 64.</p><div class="sect1" title="Speed"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="d0e2707"></a>Speed</h2></div></div></div><p>VC9:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>The simple test completes 90 times faster with MSM than with + Boost.Statechart</p></li><li class="listitem"><p>The composite test completes 25 times faster with MSM</p></li></ul></div><p>gcc 4.2.3 (Ubuntu 8.04 in VMWare, same PC):</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>The simple test completes 46 times faster with MSM</p></li><li class="listitem"><p>The composite test completes 19 times faster with Msm</p></li></ul></div></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="ch03s05.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="pt01.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="ch04s02.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">Back-end </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> Executable size</td></tr></table></div></body></html>
\ No newline at end of file diff --git a/libs/msm/doc/HTML/ch04s02.html b/libs/msm/doc/HTML/ch04s02.html new file mode 100644 index 0000000000..f1a1cd020e --- /dev/null +++ b/libs/msm/doc/HTML/ch04s02.html @@ -0,0 +1,14 @@ +<html><head> + <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> + <title>Executable size</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="ch04.html" title="Chapter 4. Performance / Compilers"><link rel="prev" href="ch04.html" title="Chapter 4. Performance / Compilers"><link rel="next" href="ch04s03.html" title="Supported 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">Executable size</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="ch04.html">Prev</a> </td><th width="60%" align="center">Chapter 4. Performance / Compilers</th><td width="20%" align="right"> <a accesskey="n" href="ch04s03.html">Next</a></td></tr></table><hr></div><div class="sect1" title="Executable size"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="d0e2728"></a>Executable size</h2></div></div></div><p>There are some worries that MSM generates huge code. Is it true? The 2 + compilers I tested disagree with this claim. On VC9, the test state machines + used in the performance section produce executables of 14kB (for simple and + eUML) and 21kB (for the composite). This includes the test code and iostreams. + By comparison, an empty executable with iostreams generated by VC9 has a size of + 7kB. Boost.Statechart generates executables of 43kB and 54kB. As a bonus, eUML + comes for “free” in terms of executable size. You even get a speed gain. With + g++ 4.3, it strongly depends on the compiler options (much more than VC). A good + size state machine with –O3 can generate an executable of 600kB, and with eUML + you can get to 1.5MB. Trying with –Os –s I come down to 18kB and 30kB for the + test state machines, while eUML will go down to 1MB (which is still big), so in + this case eUML does not come for free.</p></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="ch04.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="ch04.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="ch04s03.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">Chapter 4. Performance / Compilers </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> Supported compilers</td></tr></table></div></body></html>
\ No newline at end of file diff --git a/libs/msm/doc/HTML/ch04s03.html b/libs/msm/doc/HTML/ch04s03.html new file mode 100644 index 0000000000..b307f8f5fe --- /dev/null +++ b/libs/msm/doc/HTML/ch04s03.html @@ -0,0 +1,7 @@ +<html><head> + <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> + <title>Supported compilers</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="ch04.html" title="Chapter 4. Performance / Compilers"><link rel="prev" href="ch04s02.html" title="Executable size"><link rel="next" href="ch04s04.html" title="Limitations"></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">Supported compilers</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="ch04s02.html">Prev</a> </td><th width="60%" align="center">Chapter 4. Performance / Compilers</th><td width="20%" align="right"> <a accesskey="n" href="ch04s04.html">Next</a></td></tr></table><hr></div><div class="sect1" title="Supported compilers"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="d0e2733"></a>Supported compilers</h2></div></div></div><p>For a current status, have a look at the <a class="link" href="http://www.boost.org/development/tests/trunk/developer/msm.html" target="_top">regression tests</a>.</p><p>MSM was successfully tested with: </p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>VC8 (partly), VC9, VC10</p></li><li class="listitem"><p>g++ 4.0.1 and higher</p></li><li class="listitem"><p>Intel 10.1 and higher</p></li><li class="listitem"><p>Clang 2.9</p></li><li class="listitem"><p>Green Hills Software MULTI for ARM v5.0.5 patch 4416 (Simple and + Composite tutorials)</p></li><li class="listitem"><p>Partial support for IBM compiler</p></li></ul></div><p>VC8 and to some lesser extent VC9 suffer from a bug. Enabling the option + "Enable Minimal Rebuild" (/Gm) will cause much higher compile-time (up to three + times with VC8!). This option being activated per default in Debug mode, this + can be a big problem.</p></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="ch04s02.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="ch04.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="ch04s04.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">Executable size </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> Limitations </td></tr></table></div></body></html>
\ No newline at end of file diff --git a/libs/msm/doc/HTML/ch04s04.html b/libs/msm/doc/HTML/ch04s04.html new file mode 100644 index 0000000000..6c3d009d33 --- /dev/null +++ b/libs/msm/doc/HTML/ch04s04.html @@ -0,0 +1,15 @@ +<html><head> + <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> + <title>Limitations</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="ch04.html" title="Chapter 4. Performance / Compilers"><link rel="prev" href="ch04s03.html" title="Supported compilers"><link rel="next" href="ch04s05.html" title="Compilers corner"></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"> Limitations </th></tr><tr><td width="20%" align="left"><a accesskey="p" href="ch04s03.html">Prev</a> </td><th width="60%" align="center">Chapter 4. Performance / Compilers</th><td width="20%" align="right"> <a accesskey="n" href="ch04s05.html">Next</a></td></tr></table><hr></div><div class="sect1" title="Limitations"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="d0e2764"></a> Limitations </h2></div></div></div><p> + </p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Compilation times of state machines with > 80 transitions that are + going to make you storm the CFO's office and make sure you get a + shiny octocore with 12GB RAM by next week, unless he's interested in + paying you watch the compiler agonize for hours... (Make sure you + ask for dual 24" as well, it doesn't hurt).</p></li><li class="listitem"><p>eUML allows very long constructs but will also quickly increase + your compile time on some compilers (VC9, VC10) with buggy decltype + support (I suspect some at least quadratic algorithms there). Even + g++ 4.4 shows some regression compared to 4.3 and will crash if the + constructs become too big.</p></li><li class="listitem"><p>Need to overwrite the mpl::vector/list default-size-limit of 20 + and fusion default vector size of 10 if more than 10 states found in + a state machine</p></li></ul></div><p> + </p></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="ch04s03.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="ch04.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="ch04s05.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">Supported compilers </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> Compilers corner </td></tr></table></div></body></html>
\ No newline at end of file diff --git a/libs/msm/doc/HTML/ch04s05.html b/libs/msm/doc/HTML/ch04s05.html new file mode 100644 index 0000000000..da88c2f1b2 --- /dev/null +++ b/libs/msm/doc/HTML/ch04s05.html @@ -0,0 +1,35 @@ +<html><head> + <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> + <title>Compilers corner</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="ch04.html" title="Chapter 4. Performance / Compilers"><link rel="prev" href="ch04s04.html" title="Limitations"><link rel="next" href="ch05.html" title="Chapter 5. Questions & Answers, tips"></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"> Compilers corner </th></tr><tr><td width="20%" align="left"><a accesskey="p" href="ch04s04.html">Prev</a> </td><th width="60%" align="center">Chapter 4. Performance / Compilers</th><td width="20%" align="right"> <a accesskey="n" href="ch05.html">Next</a></td></tr></table><hr></div><div class="sect1" title="Compilers corner"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="d0e2780"></a> Compilers corner </h2></div></div></div><p>Compilers are sometimes full of surprises and such strange errors happened in + the course of the development that I wanted to list the most fun for readers’ + entertainment.</p><p><span class="underline">VC8</span>: </p><pre class="programlisting">template <class StateType> +typename ::boost::enable_if< + typename ::boost::mpl::and_< + typename ::boost::mpl::not_< + typename has_exit_pseudo_states<StateType>::type + >::type, + typename ::boost::mpl::not_< + typename is_pseudo_exit<StateType>::type + >::type + >::type, + BaseState*>::type </pre><p>I get the following error:</p><p>error C2770: invalid explicit template argument(s) for '`global + namespace'::boost::enable_if<...>::...' </p><p>If I now remove the first “::” in ::boost::mpl , the compiler shuts up. So in + this case, it is not possible to follow Boost’s guidelines.</p><p><span class="underline">VC9</span>:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>This one is my all times’ favorite. Do you know why the exit + pseudo states are referenced in the transition table with a + “submachine::exit_pt” ? Because “exit” will crash the compiler. + “Exit” is not possible either because it will crash the compiler on + one machine, but not on another (the compiler was installed from the + same disk).</p></li><li class="listitem"><p>Sometimes, removing a policy crashes the compiler, so some + versions are defining a dummy policy called WorkaroundVC9.</p></li><li class="listitem"><p>Typeof: While g++ and VC9 compile “standard” state machines in + comparable times, Typeof (while in both ways natively supported) + seems to behave in a quadratic complexity with VC9 and VC10.</p></li><li class="listitem"><p>eUML: in case of a compiler crash, changing the order of state + definitions (first states without entry or exit) sometimes solves + the problem.</p></li></ul></div><p><span class="underline">g++ 4.x</span>: Boring compiler, almost all is + working almost as expected. Being not a language lawyer I am unsure about the + following “Typeof problem”. VC9 and g++ disagree on the question if you can + derive from the BOOST_TYPEOF generated type without first defining a typedef. I + will be thankful for an answer on this. I only found two ways to break the compiler:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Add more eUML constructs until something explodes (especially with + g++-4.4) </p></li><li class="listitem"><p>The build_terminate function uses 2 mpl::push_back instead of + mpl::insert_range because g++ would not accept insert_range.</p></li></ul></div><p>You can test your compiler’s decltype implementation with the <a class="link" href="examples/CompilerStressTestEuml.cpp" target="_top">following stress + test</a> and reactivate the commented-out code until the compiler + crashes.</p></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="ch04s04.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="ch04.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="ch05.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top"> Limitations </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> Chapter 5. Questions & Answers, tips</td></tr></table></div></body></html>
\ No newline at end of file diff --git a/libs/msm/doc/HTML/ch05.html b/libs/msm/doc/HTML/ch05.html new file mode 100644 index 0000000000..d68aa71f4d --- /dev/null +++ b/libs/msm/doc/HTML/ch05.html @@ -0,0 +1,44 @@ +<html><head> + <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> + <title>Chapter 5. Questions & Answers, tips</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="pt01.html" title="Part I. User' guide"><link rel="prev" href="ch04s05.html" title="Compilers corner"><link rel="next" href="ch06.html" title="Chapter 6. Internals"></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">Chapter 5. Questions & Answers, tips</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="ch04s05.html">Prev</a> </td><th width="60%" align="center">Part I. User' guide</th><td width="20%" align="right"> <a accesskey="n" href="ch06.html">Next</a></td></tr></table><hr></div><div class="chapter" title="Chapter 5. Questions & Answers, tips"><div class="titlepage"><div><div><h2 class="title"><a name="d0e2830"></a>Chapter 5. Questions & Answers, tips</h2></div></div></div><p><span class="underline">Where should I define a state machine?</span>: The + tutorials are implemented in a simple cpp source file for simplicity. I want to + model dynamic behavior of a class as a state machine, how should I define the state + machine?</p><p><span class="underline">Answer</span>: Usually you'll want to implement the + state machine as an attribute of the class. Unfortunately, a concrete state machine + is a typedef, which cannot be forward-declared. This leaves you with two + possibilities: </p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Provide the state machine definition inside the header class and + contain an instance as attribute. Simple, but with several drawbacks: + using namespace directives are not advised, and compile-time cost for + all modules including the header.</p></li><li class="listitem"><p>Keep the state machine as (shared) pointer to void inside the <a class="link" href="examples/FsmAsPtr.hpp" target="_top">class definition</a>, and + implement the state machine in the <a class="link" href="examples/FsmAsPtr.cpp" target="_top">cpp file</a>. Minimum + compile-time, using directives are okay, but the state machine is now + located inside the heap.</p></li></ul></div><p><span class="underline">Question</span>: on_entry gets as argument, the + sent event. What event do I get when the state becomes default-activated (because it + is an initial state)?</p><p> + <span class="underline">Answer</span>: To allow you to know that the state + was default-activated, MSM generates a boost::msm::InitEvent default event. </p><p><span class="underline">Question</span>: Why do I see no call to + no_transition in my submachine? </p><p><span class="underline">Answer</span>: Because of the priority rule defined + by UML. It says that in case of transition conflict, the most inner state has a + higher priority. So after asking the inner state, the containing composite has to be + also asked to handle the transition and could find a possible transition.</p><p><span class="underline">Question</span>: Why do I get a compile error + saying the compiler cannot convert to a function ...Fsm::*(some_event)? </p><p><span class="underline">Answer</span>: You probably defined a transition + triggered by the event some_event, but used a guard/action method taking another + event. </p><p><span class="underline">Question</span>: Why do I get a compile error + saying something like “too few” or “too many” template arguments? </p><p><span class="underline">Answer</span>: You probably defined a transition in + form of a a_row or g_row where you wanted just a _row or the other way around. With + Row, it could mean that you forgot a "none". </p><p><span class="underline">Question</span>: Why do I get a very long compile + error when I define more than 20 rows in the transition table? </p><p><span class="underline">Answer</span>: MSM uses Boost.MPL under the hood + and this is the default maximum size. Please define the following 3 macros before + including any MSM headers: </p><pre class="programlisting">#define BOOST_MPL_CFG_NO_PREPROCESSED_HEADERS +#define BOOST_MPL_LIMIT_VECTOR_SIZE 30 // or whatever you need +#define BOOST_MPL_LIMIT_MAP_SIZE 30 // or whatever you need </pre><p><span class="underline">Question</span>: Why do I get this error: ”error + C2977: 'boost::mpl::vector' : too many template arguments”? </p><p><span class="underline">Answer</span>: The first possibility is that you + defined a transition table as, say, vector17 and have 18 entries. The second is that + you have 17 entries and have a composite state. Under the hood, MSM adds a row for + every event in the composite transition table. The third one is that you used a + mpl::vector without the number of entries but are close to the MPL default of 50 and + have a composite, thus pushing you above 50. Then you need mpl/vector60/70….hpp and + a mpl/map60/70….hpp </p><p><span class="underline">Question</span>: Why do I get a very long compile + error when I define more than 10 states in a state machine? </p><p><span class="underline">Answer</span>: MSM uses Boost.Fusion under the hood + and this is the default maximum size. Please define the following macro before + including any MSM headers: </p><pre class="programlisting">#define FUSION_MAX_VECTOR_SIZE 20 // or whatever you need </pre></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="ch04s05.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="pt01.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="ch06.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top"> Compilers corner </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> Chapter 6. Internals</td></tr></table></div></body></html>
\ No newline at end of file diff --git a/libs/msm/doc/HTML/ch06.html b/libs/msm/doc/HTML/ch06.html new file mode 100644 index 0000000000..9f9506e37a --- /dev/null +++ b/libs/msm/doc/HTML/ch06.html @@ -0,0 +1,61 @@ +<html><head> + <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> + <title>Chapter 6. Internals</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="pt01.html" title="Part I. User' guide"><link rel="prev" href="ch05.html" title="Chapter 5. Questions & Answers, tips"><link rel="next" href="ch06s02.html" title="Frontend / Backend interface"></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">Chapter 6. Internals</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="ch05.html">Prev</a> </td><th width="60%" align="center">Part I. User' guide</th><td width="20%" align="right"> <a accesskey="n" href="ch06s02.html">Next</a></td></tr></table><hr></div><div class="chapter" title="Chapter 6. Internals"><div class="titlepage"><div><div><h2 class="title"><a name="d0e2915"></a>Chapter 6. Internals</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="sect1"><a href="ch06.html#d0e2920">Backend: Run To Completion</a></span></dt><dt><span class="sect1"><a href="ch06s02.html">Frontend / Backend + interface</a></span></dt><dt><span class="sect1"><a href="ch06s03.html"> Generated state ids </a></span></dt><dt><span class="sect1"><a href="ch06s04.html">Metaprogramming tools</a></span></dt></dl></div><p>This chapter describes the internal machinery of the back-end, which can be useful + for UML experts but can be safely ignored for most users. For implementers, the + interface between front- and back- end is also described in detail.</p><div class="sect1" title="Backend: Run To Completion"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="d0e2920"></a><span class="command"><strong><a name="run-to-completion"></a></strong></span>Backend: Run To Completion</h2></div></div></div><p>The back-end implements the following run-to completion algorithm:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Check if one region of the concrete state machine is in a + terminate or interrupt state. If yes, event processing is disabled + while the condition lasts (forever for a terminate pseudo-state, + while active for an interrupt pseudo-state).</p></li><li class="listitem"><p>If the message queue feature is enabled and if the state machine + is already processing an event, push the currently processed event + into the queue and end processing. Otherwise, remember that the + state machine is now processing an event and continue.</p></li><li class="listitem"><p>If the state machine detected that no deferred event is used, skip + this step. Otherwise, mark the first deferred event from the + deferred queue as active.</p></li><li class="listitem"><p>Now start the core of event dispatching. If exception handling is + activated, this will happen inside a try/catch block and the + front-end <code class="code">exception_caught</code> is called if an exception + occurs. </p></li><li class="listitem"><p>The event is now dispatched in turn to every region, in the order + defined by the initial state front-end definition. This will, for + every region, call the corresponding front-end transition definition + (the "row" or "Row" of the transition table).</p></li><li class="listitem"><p>Without transition conflict, if for a given region a transition is + possible, the guard condition is checked. If it returns + <code class="code">true</code>, the transition processing continues and the + current state's exit action is called, followed by the transition + action behavior and the new active state's entry behavior.</p></li><li class="listitem"><p>With transition conflicts (several possible transitions, + disambiguated by mutually exclusive guard conditions), the guard + conditions are tried in reverse order of their transition definition + in the transition table. The first one returning <code class="code">true</code> + selects its transition. Note that this is not defined by the UML + standard, which simply specifies that if the guard conditions are + not mutually exclusive, the state machine is ill-formed and the + behaviour undefined. Relying on this implementation-specific + behaviour will make it harder for the developer to support another + state machine framework.</p></li><li class="listitem"><p>If at least one region processes the event, this event is seen as + having been accepted. If not, the library calls + <code class="code">no_transition</code> on the state machine for every + contained region.</p></li><li class="listitem"><p>If the currently active state is a submachine, the behaviour is + slightly different. The UML standard specifies that internal + transitions have to be tried first, so the event is first dispatched + to the submachine. Only if the submachine does not accept the event + are other (non internal) transitions tried.</p></li><li class="listitem"><p>This back-end supports simple states' and submachines' internal + transitions. These are provided in the state's + <code class="code">internal_transition_table</code> type. Transitions defined + in this table are added at the end of the main state machine's + transition table, but with a lesser priority than the submachine's + transitions (defined in <code class="code">transition_table</code>). This means, + for simple states, that these transitions have higher priority than + non-internal transitions, conform to the UML standard which gives + higher priority to deeper-level transitions. For submachines, this + is a non-standard addition which can help make event processing + faster by giving a chance to bypass subregion processing. With + standard UML, one would need to add a subregion only to process + these internal transitions, which would be slower.</p></li><li class="listitem"><p>After the dispatching itself, the deferred event marked in step 3 + (if any) now gets a chance of processing.</p></li><li class="listitem"><p>Then, events queued in the message queue also get a dispatching + chance</p></li><li class="listitem"><p>Finally, completion / anonymous transitions, if to be found in the + transition table, also get their dispatching chance.</p></li></ul></div><p>This algorithm illustrates how the back-end configures itself at compile-time + as much as possible. Every feature not found in a given state machine definition + is deactivated and has therefore no runtime cost. Completion events, deferred + events, terminate states, dispatching to several regions, internal transitions + are all deactivated if not used. User configuration is only for exception + handling and message queue necessary.</p></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="ch05.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="pt01.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="ch06s02.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">Chapter 5. Questions & Answers, tips </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> Frontend / Backend + interface</td></tr></table></div></body></html>
\ No newline at end of file diff --git a/libs/msm/doc/HTML/ch06s02.html b/libs/msm/doc/HTML/ch06s02.html new file mode 100644 index 0000000000..605c758a67 --- /dev/null +++ b/libs/msm/doc/HTML/ch06s02.html @@ -0,0 +1,60 @@ +<html><head> + <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> + <title>Frontend / Backend interface</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="ch06.html" title="Chapter 6. Internals"><link rel="prev" href="ch06.html" title="Chapter 6. Internals"><link rel="next" href="ch06s03.html" title="Generated state ids"></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">Frontend / Backend + interface</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="ch06.html">Prev</a> </td><th width="60%" align="center">Chapter 6. Internals</th><td width="20%" align="right"> <a accesskey="n" href="ch06s03.html">Next</a></td></tr></table><hr></div><div class="sect1" title="Frontend / Backend interface"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="d0e2986"></a><span class="command"><strong><a name="internals-front-back-interface"></a></strong></span>Frontend / Backend + interface</h2></div></div></div><p>The design of MSM tries to make front-ends and back-ends (later) to be as + interchangeable as possible. Of course, no back-end will ever implement every + feature defined by any possible front-end and inversely, but the goal is to make + it as easy as possible to extend the current state of the library.</p><p>To achieve this, MSM divides the functionality between both sides: the + front-end is a sort of user interface and is descriptive, the back-end + implements the state machine engine.</p><p>MSM being based on a transition table, a concrete state machine (or a given + front-end) must provide a transition_table. This transition table must be made + of rows. And each row must tell what kind of transition it is and implement the + calls to the actions and guards. A state machine must also define its regions + (marked by initial states) And that is about the only constraints for + front-ends. How the rows are described is implementer's choice. </p><p>Every row must provide:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>A <code class="code">Source</code> typedef indicating, well, the type of the source + state.</p></li><li class="listitem"><p>A <code class="code">Target</code> typedef indicating, well, the type of the target + state.</p></li><li class="listitem"><p>A <code class="code">Evt</code> typedef indicating the type of the event triggering + the transition.</p></li><li class="listitem"><p>A <code class="code">row_type_tag</code> typedef indicating the type of the + transition.</p></li><li class="listitem"><p>Rows having a type requiring transition actions must provide a static + function <code class="code">action_call</code> with the following signature: <code class="code"> + template <class Fsm,class SourceState,class TargetState,class + AllStates> </code></p><p><code class="code">static void action_call (Fsm& fsm, Event const& evt, + SourceState&, TargetState&, AllStates&) </code></p><p>The function gets as parameters the (back-end) state machine, the + event, source and target states and a container (in the current + back-end, a fusion::set) of all the states defined in the state machine. + For example, as the back-end has the front-end as basic class, + <code class="code">action_call</code> is simply defined as + <code class="code">(fsm.*action)(evt)</code>.</p></li><li class="listitem"><p>Rows having a type requiring a guard must provide a static function + <code class="code">guard_call</code> with the following signature:<code class="code"> </code></p><p><code class="code">template <class Fsm,class SourceState,class TargetState,class + AllStates></code></p><p><code class="code">static bool guard_call (Fsm&, Event const&, + SourceState&, TargetState&, AllStates&)</code></p></li><li class="listitem"><p>The possible transition (row) types are:</p><div class="itemizedlist"><ul class="itemizedlist" type="circle"><li class="listitem"><p>a_row_tag: a transition with actions and no guard</p></li><li class="listitem"><p>g_row_type: a transition with a guard and no + actions</p></li><li class="listitem"><p>_row_tag: a transition without actions or guard</p></li><li class="listitem"><p>row_tag: a transition with guard and actions</p></li><li class="listitem"><p>a_irow_tag: an internal transition (defined inside the + <code class="code">transition_table</code>) with actions</p></li><li class="listitem"><p>g_irow_tag: an internal transition (defined inside the + <code class="code">transition_table</code>) with guard</p></li><li class="listitem"><p>irow_tag: an internal transition (defined inside the + <code class="code">transition_table</code>) with actions and + guards</p></li><li class="listitem"><p>_irow_tag: an internal transition (defined inside the + <code class="code">transition_table</code>) without action or guard. + Due to higher priority for internal transitions, this is + equivalent to a "ignore event"</p></li><li class="listitem"><p>sm_a_i_row_tag: an internal transition (defined inside the + <code class="code">internal_transition_table</code>) with + actions</p></li><li class="listitem"><p>sm_g_i_row_tag: an internal transition (defined inside the + <code class="code">internal_transition_table</code>) with + guard</p></li><li class="listitem"><p>sm_i_row_tag: an internal transition (defined inside the + <code class="code">internal_transition_table</code>) with actions and + guards</p></li><li class="listitem"><p>sm__i_row_tag: an internal transition (defined inside the + <code class="code">internal_transition_table</code>) without action + or guard. Due to higher priority for internal transitions, + this is quivalent to a "ignore event"</p></li></ul></div></li></ul></div><p>Furthermore, a front-end must provide the definition of states and state + machines. State machine definitions must provide (the implementer is free to + provide it or let it be done by every concrete state machine. Different MSM + front-ends took one or the other approach):</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p><code class="code">initial_state</code>: This typedef can be a single state or + a mpl container and provides the initial states defining one or + several orthogonal regions.</p></li><li class="listitem"><p><code class="code">transition_table</code>: This typedef is a MPL sequence of + transition rows.</p></li><li class="listitem"><p><code class="code">configuration</code>: this typedef is a MPL sequence of + known types triggering special behavior in the back-end, for example + if a concrete fsm requires a message queue or exception + catching.</p></li></ul></div><p>States and state machines must both provide a (possibly empty) definition of:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p><code class="code">flag_list</code>: the flags being active when this state or + state machine become the current state of the fsm.</p></li><li class="listitem"><p><code class="code">deferred_events</code>: events being automatically deferred + when the state is the current state of the fsm.</p></li><li class="listitem"><p><code class="code">internal_transition_table</code>: the internal transitions + of this state.</p></li><li class="listitem"><p><code class="code">on_entry</code> and <code class="code">on_exit</code> methods.</p></li></ul></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="ch06.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="ch06.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="ch06s03.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">Chapter 6. Internals </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> Generated state ids </td></tr></table></div></body></html>
\ No newline at end of file diff --git a/libs/msm/doc/HTML/ch06s03.html b/libs/msm/doc/HTML/ch06s03.html new file mode 100644 index 0000000000..7c1a611f84 --- /dev/null +++ b/libs/msm/doc/HTML/ch06s03.html @@ -0,0 +1,23 @@ +<html><head> + <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> + <title>Generated state ids</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="ch06.html" title="Chapter 6. Internals"><link rel="prev" href="ch06s02.html" title="Frontend / Backend interface"><link rel="next" href="ch06s04.html" title="Metaprogramming tools"></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"> Generated state ids </th></tr><tr><td width="20%" align="left"><a accesskey="p" href="ch06s02.html">Prev</a> </td><th width="60%" align="center">Chapter 6. Internals</th><td width="20%" align="right"> <a accesskey="n" href="ch06s04.html">Next</a></td></tr></table><hr></div><div class="sect1" title="Generated state ids"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="d0e3164"></a><span class="command"><strong><a name="internals-state-id"></a></strong></span> Generated state ids </h2></div></div></div><p>Normally, one does not need to know the ids are generated for all the states + of a state machine, unless for debugging purposes, like the pstate function does + in the tutorials in order to display the name of the current state. This section + will show how to automatically display typeid-generated names, but these are not + very readable on all platforms, so it can help to know how the ids are + generated. The ids are generated using the transition table, from the “Start” + column up to down, then from the “Next” column, up to down, as shown in the next + image: </p><p><span class="inlinemediaobject"><img src="../images/AnnexA.jpg" width="90%"></span></p><p>Stopped will get id 0, Open id 1, ErrorMode id 6 and SleepMode (seen only in + the “Next” column) id 7. If you have some implicitly created states, like + transition-less initial states or states created using the explicit_creation + typedef, these will be added as a source at the end of the transition table. If + you have submachine states, a row will be added for them at the end of the + table, after the automatically or explicitly created states, which can change + their id. The next help you will need for debugging would be to call the + current_state method of the state_machine class, then the display_type helper to + generate a readable name from the id. If you do not want to go through the + transition table to fill an array of names, the library provides another helper, + fill_state_names, which, given an array of sufficient size (please see next + section to know how many states are defined in the state machine), will fill it + with typeid-generated names. </p></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="ch06s02.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="ch06.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="ch06s04.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">Frontend / Backend + interface </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> Metaprogramming tools</td></tr></table></div></body></html>
\ No newline at end of file diff --git a/libs/msm/doc/HTML/ch06s04.html b/libs/msm/doc/HTML/ch06s04.html new file mode 100644 index 0000000000..a170d33544 --- /dev/null +++ b/libs/msm/doc/HTML/ch06s04.html @@ -0,0 +1,26 @@ +<html><head> + <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> + <title>Metaprogramming tools</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="ch06.html" title="Chapter 6. Internals"><link rel="prev" href="ch06s03.html" title="Generated state ids"><link rel="next" href="ch07.html" title="Chapter 7. Acknowledgements"></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">Metaprogramming tools</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="ch06s03.html">Prev</a> </td><th width="60%" align="center">Chapter 6. Internals</th><td width="20%" align="right"> <a accesskey="n" href="ch07.html">Next</a></td></tr></table><hr></div><div class="sect1" title="Metaprogramming tools"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="d0e3176"></a>Metaprogramming tools</h2></div></div></div><p>We can find for the transition table more uses than what we have seen so far. + Let's suppose you need to write a coverage tool. A state machine would be + perfect for such a job, if only it could provide some information about its + structure. Thanks to the transition table and Boost.MPL, it does.</p><p>What is needed for a coverage tool? You need to know how many states are + defined in the state machine, and how many events can be fired. This way you can + log the fired events and the states visited in the life of a concrete machine + and be able to perform some coverage analysis, like “fired 65% of all possible + events and visited 80% of the states defined in the state machine”. To achieve + this, MSM provides a few useful tools:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>generate_state_set<transition table>: returns a mpl::set of all + the states defined in the table.</p></li><li class="listitem"><p>generate_event_set<transition table>: returns a mpl::set of all + the events defined in the table.</p></li><li class="listitem"><p>using mpl::size<>::value you can get the number of elements in + the set.</p></li><li class="listitem"><p>display_type defines an operator() sending typeid(Type).name() to + cout.</p></li><li class="listitem"><p>fill_state_names fills an array of char const* with names of all + states (found by typeid)</p></li><li class="listitem"><p>using mpl::for_each on the result of generate_state_set and + generate_event_set passing display_type as argument will display all + the states of the state machine.</p></li><li class="listitem"><p>let's suppose you need to recursively find the states and events + defined in the composite states and thus also having a transition + table. Calling recursive_get_transition_table<Composite> will + return you the transition table of the composite state, recursively + adding the transition tables of all sub-state machines and + sub-sub...-sub-state machines. Then call generate_state_set or + generate_event_set on the result to get the full list of states and + events. </p></li></ul></div><p> An <a class="link" href="examples/BoostCon09Full.cpp" target="_top">example</a> shows the + tools in action. </p></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="ch06s03.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="ch06.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="ch07.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top"> Generated state ids </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> Chapter 7. Acknowledgements</td></tr></table></div></body></html>
\ No newline at end of file diff --git a/libs/msm/doc/HTML/ch07.html b/libs/msm/doc/HTML/ch07.html new file mode 100644 index 0000000000..306e090766 --- /dev/null +++ b/libs/msm/doc/HTML/ch07.html @@ -0,0 +1,14 @@ +<html><head> + <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> + <title>Chapter 7. Acknowledgements</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="pt01.html" title="Part I. User' guide"><link rel="prev" href="ch06s04.html" title="Metaprogramming tools"><link rel="next" href="ch07s02.html" title="MSM v1"></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">Chapter 7. Acknowledgements</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="ch06s04.html">Prev</a> </td><th width="60%" align="center">Part I. User' guide</th><td width="20%" align="right"> <a accesskey="n" href="ch07s02.html">Next</a></td></tr></table><hr></div><div class="chapter" title="Chapter 7. Acknowledgements"><div class="titlepage"><div><div><h2 class="title"><a name="d0e3210"></a>Chapter 7. Acknowledgements</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="sect1"><a href="ch07.html#d0e3215">MSM v2</a></span></dt><dt><span class="sect1"><a href="ch07s02.html"> MSM v1</a></span></dt></dl></div><p>I am in debt to the following people who helped MSM along the way.</p><div class="sect1" title="MSM v2"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="d0e3215"></a>MSM v2</h2></div></div></div><p> + </p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Thanks to Dave Abrahams for managing the review</p></li><li class="listitem"><p>Thanks to Eric Niebler for his patience correcting my grammar + errors</p></li><li class="listitem"><p>Special thanks to Joel de Guzman who gave me very good ideas at + the BoostCon09. These ideas were the starting point of the redesign. + Any time again, Joel ☺</p></li><li class="listitem"><p>Thanks to Richard O’Hara for making Green Hills bring a patch in + less than 1 week, thus adding one more compiler to the supported + list.</p></li><li class="listitem"><p>Big thanks to those who took the time to write a review: Franz + Alt, David Bergman, Michael Caisse, Barend Gehrels, Darryl Greene, + Juraj Ivancic, Erik Nelson, Kenny Riddile.</p></li><li class="listitem"><p>Thanks to Matt Calabrese, Juraj Ivancic, Adam Merz and Joseph Wu + for reporting bugs.</p></li><li class="listitem"><p>Thanks to Thomas Mistretta for providing an addition to the + section "What do you actually do inside actions / guards".</p></li></ul></div><p> + </p></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="ch06s04.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="pt01.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="ch07s02.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">Metaprogramming tools </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> MSM v1</td></tr></table></div></body></html>
\ No newline at end of file diff --git a/libs/msm/doc/HTML/ch07s02.html b/libs/msm/doc/HTML/ch07s02.html new file mode 100644 index 0000000000..6e0b1c608c --- /dev/null +++ b/libs/msm/doc/HTML/ch07s02.html @@ -0,0 +1,13 @@ +<html><head> + <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> + <title>MSM v1</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="ch07.html" title="Chapter 7. Acknowledgements"><link rel="prev" href="ch07.html" title="Chapter 7. Acknowledgements"><link rel="next" href="ch08.html" title="Chapter 8. Version history"></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"> MSM v1</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="ch07.html">Prev</a> </td><th width="60%" align="center">Chapter 7. Acknowledgements</th><td width="20%" align="right"> <a accesskey="n" href="ch08.html">Next</a></td></tr></table><hr></div><div class="sect1" title="MSM v1"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="d0e3243"></a> MSM v1</h2></div></div></div><p> + </p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>The original version of this framework is based on the brilliant + work of David Abrahams and Aleksey Gurtovoy who laid down the base + and the principles of the framework in their excellent book, “C++ + template Metaprogramming”. The implementation also makes heavy use + of the boost::mpl.</p></li><li class="listitem"><p>Thanks to Jeff Flinn for his idea of the user-defined base state + and his review which allowed MSM to be presented at the + BoostCon09.</p></li><li class="listitem"><p>Thanks to my MSM v1 beta testers, Christoph Woskowski and Franz + Alt for using the framework with little documentation and to my + private reviewer, Edouard Alligand</p></li></ul></div><p> + </p></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="ch07.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="ch07.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="ch08.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">Chapter 7. Acknowledgements </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> Chapter 8. Version history</td></tr></table></div></body></html>
\ No newline at end of file diff --git a/libs/msm/doc/HTML/ch08.html b/libs/msm/doc/HTML/ch08.html new file mode 100644 index 0000000000..0e3e33dfd1 --- /dev/null +++ b/libs/msm/doc/HTML/ch08.html @@ -0,0 +1,21 @@ +<html><head> + <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> + <title>Chapter 8. Version history</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="pt01.html" title="Part I. User' guide"><link rel="prev" href="ch07s02.html" title="MSM v1"><link rel="next" href="ch08s02.html" title="From V2.20 to V2.21 (Boost 1.47)"></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">Chapter 8. Version history</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="ch07s02.html">Prev</a> </td><th width="60%" align="center">Part I. User' guide</th><td width="20%" align="right"> <a accesskey="n" href="ch08s02.html">Next</a></td></tr></table><hr></div><div class="chapter" title="Chapter 8. Version history"><div class="titlepage"><div><div><h2 class="title"><a name="d0e3259"></a>Chapter 8. Version history</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="sect1"><a href="ch08.html#d0e3262">From V2.21 to V2.22 (Boost 1.48)</a></span></dt><dt><span class="sect1"><a href="ch08s02.html">From V2.20 to V2.21 (Boost 1.47)</a></span></dt><dt><span class="sect1"><a href="ch08s03.html">From V2.12 to V2.20 (Boost 1.46)</a></span></dt><dt><span class="sect1"><a href="ch08s04.html">From V2.10 to V2.12 (Boost 1.45)</a></span></dt><dt><span class="sect1"><a href="ch08s05.html">From V2.0 to V2.12 (Boost 1.44)</a></span></dt></dl></div><div class="sect1" title="From V2.21 to V2.22 (Boost 1.48)"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="d0e3262"></a>From V2.21 to V2.22 (Boost 1.48)</h2></div></div></div><p> + </p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>eUML: added easier event reprocessing: + <code class="code">process(event_)</code> and <code class="code">reprocess()</code></p></li><li class="listitem"><p>Rewrite of internal transition tables. There were a few bugs + (failing recursivity in internal transition tables of sub-sub + machines) and a missing feature (unused internal transition table of + the main state machine).</p></li><li class="listitem"><p>Bugfixes</p><div class="itemizedlist"><ul class="itemizedlist" type="circle"><li class="listitem"><p>Reverted favor_compile_time policy to Boost 1.46 + state</p></li><li class="listitem"><p><code class="code">none</code> event now is convertible from any + other event </p></li><li class="listitem"><p>eUML and pseudo exit states</p></li><li class="listitem"><p>Fixed not working Flag_AND</p></li><li class="listitem"><p>Fixed rare bugs causing multiple processing of the + same event in a submachine whose transition table + contains this event and a base event of it.</p></li><li class="listitem"><p>gcc warnings about unused variables</p></li></ul></div></li><li class="listitem"><p>Breaking change: the new internal transition table feature causes + a minor breaking change. In a submachine, the "Fsm" template + parameter for guards / actions of an internal table declared using + <code class="code">internal_transition_table</code> now is the submachine, + not the higher-level state machine. Internal transitions declared + using internal rows in the higher-level state machine keep their + behavior (the "Fsm" parameter is the higher-level state machine). To + sum up, the internal transition "Fsm" parameter is the closest state + machine containing this transition.</p></li></ul></div><p> + </p></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="ch07s02.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="pt01.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="ch08s02.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top"> MSM v1 </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> From V2.20 to V2.21 (Boost 1.47)</td></tr></table></div></body></html>
\ No newline at end of file diff --git a/libs/msm/doc/HTML/ch08s02.html b/libs/msm/doc/HTML/ch08s02.html new file mode 100644 index 0000000000..75ea5bc7bd --- /dev/null +++ b/libs/msm/doc/HTML/ch08s02.html @@ -0,0 +1,10 @@ +<html><head> + <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> + <title>From V2.20 to V2.21 (Boost 1.47)</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="ch08.html" title="Chapter 8. Version history"><link rel="prev" href="ch08.html" title="Chapter 8. Version history"><link rel="next" href="ch08s03.html" title="From V2.12 to V2.20 (Boost 1.46)"></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">From V2.20 to V2.21 (Boost 1.47)</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="ch08.html">Prev</a> </td><th width="60%" align="center">Chapter 8. Version history</th><td width="20%" align="right"> <a accesskey="n" href="ch08s03.html">Next</a></td></tr></table><hr></div><div class="sect1" title="From V2.20 to V2.21 (Boost 1.47)"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="d0e3310"></a>From V2.20 to V2.21 (Boost 1.47)</h2></div></div></div><p> + </p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Added a <span class="command"><strong><a class="command" href="ch03s05.html#backend-start">stop()</a></strong></span> + method in the back-end.</p></li><li class="listitem"><p><span class="command"><strong><a class="command" href="ch03s04.html#eUML-phoenix">Added partial support for + Boost.Phoenix functors in eUML</a></strong></span></p></li><li class="listitem"><p>Added the possibility to choose when <span class="command"><strong><a class="command" href="ch03s05.html#backend-state-switch">state switching</a></strong></span> + occurs.</p></li><li class="listitem"><p>Bugfixes</p><div class="itemizedlist"><ul class="itemizedlist" type="circle"><li class="listitem"><p>Trac 5117, 5253, 5533, 5573</p></li><li class="listitem"><p>gcc warnings about unused variables</p></li><li class="listitem"><p>better implemenation of favor_compile_time back-end + policy</p></li><li class="listitem"><p>bug with eUML and state construction</p></li><li class="listitem"><p>incorrect eUML event and state macros</p></li><li class="listitem"><p>incorrect event type passed to a direct entry state's + on_entry action</p></li><li class="listitem"><p>more examples</p></li></ul></div></li></ul></div><p> + </p></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="ch08.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="ch08.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="ch08s03.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">Chapter 8. Version history </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> From V2.12 to V2.20 (Boost 1.46)</td></tr></table></div></body></html>
\ No newline at end of file diff --git a/libs/msm/doc/HTML/ch08s03.html b/libs/msm/doc/HTML/ch08s03.html new file mode 100644 index 0000000000..fa6e199110 --- /dev/null +++ b/libs/msm/doc/HTML/ch08s03.html @@ -0,0 +1,12 @@ +<html><head> + <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> + <title>From V2.12 to V2.20 (Boost 1.46)</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="ch08.html" title="Chapter 8. Version history"><link rel="prev" href="ch08s02.html" title="From V2.20 to V2.21 (Boost 1.47)"><link rel="next" href="ch08s04.html" title="From V2.10 to V2.12 (Boost 1.45)"></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">From V2.12 to V2.20 (Boost 1.46)</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="ch08s02.html">Prev</a> </td><th width="60%" align="center">Chapter 8. Version history</th><td width="20%" align="right"> <a accesskey="n" href="ch08s04.html">Next</a></td></tr></table><hr></div><div class="sect1" title="From V2.12 to V2.20 (Boost 1.46)"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="d0e3358"></a>From V2.12 to V2.20 (Boost 1.46)</h2></div></div></div><p> + </p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Compile-time state machine analysis using mpl_graph:</p><div class="itemizedlist"><ul class="itemizedlist" type="circle"><li class="listitem"><p><span class="command"><strong><a class="command" href="ch03s05.html#backend-compile-time-analysis">checking of region orthogonality</a></strong></span>.</p></li><li class="listitem"><p><span class="command"><strong><a class="command" href="ch03s05.html#backend-compile-time-analysis">search for unreachable states</a></strong></span>.</p></li><li class="listitem"><p><span class="command"><strong><a class="command" href="ch03s02.html#explicit-entry-no-region-id">automatic region index search for pseudo entry or explicit + entry states</a></strong></span>.</p></li></ul></div></li><li class="listitem"><p><span class="command"><strong><a class="command" href="ch03s05.html#backend-boost-parameter">Boost.Parameter interface definition</a></strong></span> for + msm::back::state_machine<> template arguments.</p></li><li class="listitem"><p><span class="command"><strong><a class="command" href="ch03s05.html#backend-queues">Possibility to provide a + container</a></strong></span> for the event and deferred event queues. A + policy implementation based on a more efficient Boost.CircularBuffer + is provided.</p></li><li class="listitem"><p>msm::back::state_machine<>::is_flag_active method made + const.</p></li><li class="listitem"><p>added possibility to <span class="command"><strong><a class="command" href="ch03s05.html#backend-enqueueing">enqueue events</a></strong></span> for delayed processing.</p></li><li class="listitem"><p>Bugfixes</p><div class="itemizedlist"><ul class="itemizedlist" type="circle"><li class="listitem"><p>Trac 4926</p></li><li class="listitem"><p>stack overflow using the Defer functor</p></li><li class="listitem"><p>anonymous transition of a submachine not called for + the initial state</p></li></ul></div></li></ul></div><p> + </p></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="ch08s02.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="ch08.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="ch08s04.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">From V2.20 to V2.21 (Boost 1.47) </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> From V2.10 to V2.12 (Boost 1.45)</td></tr></table></div></body></html>
\ No newline at end of file diff --git a/libs/msm/doc/HTML/ch08s04.html b/libs/msm/doc/HTML/ch08s04.html new file mode 100644 index 0000000000..9a0bf6a54c --- /dev/null +++ b/libs/msm/doc/HTML/ch08s04.html @@ -0,0 +1,9 @@ +<html><head> + <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> + <title>From V2.10 to V2.12 (Boost 1.45)</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="ch08.html" title="Chapter 8. Version history"><link rel="prev" href="ch08s03.html" title="From V2.12 to V2.20 (Boost 1.46)"><link rel="next" href="ch08s05.html" title="From V2.0 to V2.12 (Boost 1.44)"></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">From V2.10 to V2.12 (Boost 1.45)</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="ch08s03.html">Prev</a> </td><th width="60%" align="center">Chapter 8. Version history</th><td width="20%" align="right"> <a accesskey="n" href="ch08s05.html">Next</a></td></tr></table><hr></div><div class="sect1" title="From V2.10 to V2.12 (Boost 1.45)"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="d0e3416"></a>From V2.10 to V2.12 (Boost 1.45)</h2></div></div></div><p> + </p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Support for <span class="command"><strong><a class="command" href="ch03s05.html#back-end-serialization">serialization</a></strong></span></p></li><li class="listitem"><p><span class="command"><strong><a class="command" href="ch03s04.html#eUML-reuse-functor">Possibility to use + normal functors</a></strong></span> (from functor front-end) in + eUML.</p></li><li class="listitem"><p><span class="command"><strong><a class="command" href="ch03s05.html#backend-fsm-constructor-args">New constructors</a></strong></span> where substates / submachines can be taken as + arguments. This allows passing arguments to the constructor of a + submachine.</p></li><li class="listitem"><p>Bugfixes</p></li></ul></div><p> + </p></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="ch08s03.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="ch08.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="ch08s05.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">From V2.12 to V2.20 (Boost 1.46) </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> From V2.0 to V2.12 (Boost 1.44)</td></tr></table></div></body></html>
\ No newline at end of file diff --git a/libs/msm/doc/HTML/ch08s05.html b/libs/msm/doc/HTML/ch08s05.html new file mode 100644 index 0000000000..b8bec66e18 --- /dev/null +++ b/libs/msm/doc/HTML/ch08s05.html @@ -0,0 +1,9 @@ +<html><head> + <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> + <title>From V2.0 to V2.12 (Boost 1.44)</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="ch08.html" title="Chapter 8. Version history"><link rel="prev" href="ch08s04.html" title="From V2.10 to V2.12 (Boost 1.45)"><link rel="next" href="pt02.html" title="Part II. Reference"></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">From V2.0 to V2.12 (Boost 1.44)</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="ch08s04.html">Prev</a> </td><th width="60%" align="center">Chapter 8. Version history</th><td width="20%" align="right"> <a accesskey="n" href="pt02.html">Next</a></td></tr></table><hr></div><div class="sect1" title="From V2.0 to V2.12 (Boost 1.44)"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="d0e3441"></a>From V2.0 to V2.12 (Boost 1.44)</h2></div></div></div><p> + </p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>New documentation</p></li><li class="listitem"><p>Internal transitions. Either as part of the transition table or + using a state's internal transition table</p></li><li class="listitem"><p>increased dispatch and copy speed</p></li><li class="listitem"><p><span class="command"><strong><a class="command" href="ch03s02.html#basic-row2">new row types</a></strong></span> for the + basic front-end</p></li><li class="listitem"><p>new eUML syntax, better attribute support, macros to ease + developer's life. Even VC8 seems to like it better.</p></li><li class="listitem"><p>New policy for reduced compile-time at the cost of dispatch + speed</p></li><li class="listitem"><p>Support for base events</p></li><li class="listitem"><p>possibility to choose the initial event</p></li></ul></div><p> + </p></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="ch08s04.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="ch08.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="pt02.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">From V2.10 to V2.12 (Boost 1.45) </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> Part II. Reference</td></tr></table></div></body></html>
\ No newline at end of file diff --git a/libs/msm/doc/HTML/ch09.html b/libs/msm/doc/HTML/ch09.html new file mode 100644 index 0000000000..9804ddbf09 --- /dev/null +++ b/libs/msm/doc/HTML/ch09.html @@ -0,0 +1,24 @@ +<html><head> + <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> + <title>Chapter 9. eUML operators and basic helpers</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="pt02.html" title="Part II. Reference"><link rel="prev" href="pt02.html" title="Part II. Reference"><link rel="next" href="ch10.html" title="Chapter 10. Functional programming"></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">Chapter 9. eUML operators and basic helpers</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="pt02.html">Prev</a> </td><th width="60%" align="center">Part II. Reference</th><td width="20%" align="right"> <a accesskey="n" href="ch10.html">Next</a></td></tr></table><hr></div><div class="chapter" title="Chapter 9. eUML operators and basic helpers"><div class="titlepage"><div><div><h2 class="title"><a name="d0e3478"></a>Chapter 9. eUML operators and basic helpers</h2></div></div></div><p>The following table lists the supported operators: </p><p> + </p><div class="table"><a name="d0e3485"></a><p class="title"><b>Table 9.1. Operators and state machine helpers</b></p><div class="table-contents"><table summary="Operators and state machine helpers" border="1"><colgroup><col><col><col></colgroup><thead><tr><th>eUML function / operator</th><th>Description</th><th>Functor</th></tr></thead><tbody><tr><td>&&</td><td>Calls lazily Action1&& Action2</td><td>And_</td></tr><tr><td>||</td><td>Calls lazily Action1|| Action2</td><td>Or_</td></tr><tr><td>!</td><td>Calls lazily !Action1</td><td>Not_</td></tr><tr><td>!=</td><td>Calls lazily Action1 != Action2</td><td>NotEqualTo_</td></tr><tr><td>==</td><td>Calls lazily Action1 == Action2</td><td>EqualTo_</td></tr><tr><td>></td><td>Calls lazily Action1 > Action2</td><td>Greater_</td></tr><tr><td>>=</td><td>Calls lazily Action1 >= Action2</td><td>Greater_Equal_</td></tr><tr><td><</td><td>Calls lazily Action1 < Action2</td><td>Less_</td></tr><tr><td><=</td><td>Calls lazily Action1 <= Action2</td><td>Less_Equal_</td></tr><tr><td>&</td><td>Calls lazily Action1 & Action2</td><td>Bitwise_And_</td></tr><tr><td>|</td><td>Calls lazily Action1 | Action2</td><td>Bitwise_Or_</td></tr><tr><td>^</td><td>Calls lazily Action1 ^ Action2</td><td>Bitwise_Xor_</td></tr><tr><td>--</td><td>Calls lazily --Action1 / Action1--</td><td>Pre_Dec_ / Post_Dec_</td></tr><tr><td>++</td><td>Calls lazily ++Action1 / Action1++</td><td>Pre_Inc_ / Post_Inc_</td></tr><tr><td>/</td><td>Calls lazily Action1 / Action2</td><td>Divides_</td></tr><tr><td>/=</td><td>Calls lazily Action1 /= Action2</td><td>Divides_Assign_</td></tr><tr><td>*</td><td>Calls lazily Action1 * Action2</td><td>Multiplies_</td></tr><tr><td>*=</td><td>Calls lazily Action1 *= Action2</td><td>Multiplies_Assign_</td></tr><tr><td>+ (binary)</td><td>Calls lazily Action1 + Action2</td><td>Plus_</td></tr><tr><td>+ (unary)</td><td>Calls lazily +Action1</td><td>Unary_Plus_</td></tr><tr><td>+=</td><td>Calls lazily Action1 += Action2</td><td>Plus_Assign_</td></tr><tr><td>- (binary)</td><td>Calls lazily Action1 - Action2</td><td>Minus_</td></tr><tr><td>- (unary)</td><td>Calls lazily -Action1</td><td>Unary_Minus_</td></tr><tr><td>-=</td><td>Calls lazily Action1 -= Action2</td><td>Minus_Assign_</td></tr><tr><td>%</td><td>Calls lazily Action1 % Action2</td><td>Modulus_</td></tr><tr><td>%=</td><td>Calls lazily Action1 %= Action2</td><td>Modulus_Assign_</td></tr><tr><td>>></td><td>Calls lazily Action1 >> Action2</td><td>ShiftRight_</td></tr><tr><td>>>=</td><td>Calls lazily Action1 >>= Action2</td><td>ShiftRight_Assign_</td></tr><tr><td><<</td><td>Calls lazily Action1 << Action2</td><td>ShiftLeft_</td></tr><tr><td><<=</td><td>Calls lazily Action1 <<= Action2</td><td>ShiftLeft_Assign_</td></tr><tr><td>[] (works on vector, map, arrays)</td><td>Calls lazily Action1 [Action2]</td><td>Subscript_</td></tr><tr><td>if_then_else_(Condition,Action1,Action2)</td><td>Returns either the result of calling Action1 or the result of + calling Action2</td><td>If_Else_</td></tr><tr><td>if_then_(Condition,Action)</td><td>Returns the result of calling Action if Condition</td><td>If_Then_</td></tr><tr><td>while_(Condition, Body)</td><td>While Condition(), calls Body(). Returns nothing</td><td>While_Do_</td></tr><tr><td>do_while_(Condition, Body)</td><td>Calls Body() while Condition(). Returns nothing</td><td>Do_While_</td></tr><tr><td>for_(Begin,Stop,EndLoop,Body)</td><td>Calls for(Begin;Stop;EndLoop){Body;}</td><td>For_Loop_</td></tr><tr><td>process_(Event [,fsm1] [,fsm2] [,fsm3] [,fsm4])</td><td>Processes Event on the current state machine (if no fsm + specified) or on up to 4 state machines returned by an + appropriate functor.</td><td>Process_</td></tr><tr><td>process2_(Event, Data [,fsm1] [,fsm2] [,fsm3])</td><td>Processes Event on the current state machine (if no fsm + specified) or on up to 2 state machines returned by an + appropriate functor. The event is copy-constructed from what + Data() returns.</td><td>Process2_</td></tr><tr><td>is_flag_(Flag [,fsm])</td><td>Calls is_flag_active() on the current state machine or the + one returned by calling fsm.</td><td>Get_Flag_</td></tr><tr><td>event_ [(attribute name)]</td><td>Returns the current event (as const reference)</td><td>GetEvent_</td></tr><tr><td>source_ [(attribute name)]</td><td>Returns the source state of the currently triggered + transition (as reference). If an attribute name is provided, + returns the attribute by reference.</td><td>GetSource_</td></tr><tr><td>target_ [(attribute name)]</td><td>Returns the target state of the currently triggered + transition (as reference). If an attribute name is provided, + returns the attribute by reference.</td><td>GetTarget_</td></tr><tr><td>state_ [(attribute name)]</td><td>Returns the source state of the currently active state (as + reference). Valid inside a state entry/exit action. If an + attribute name is provided, returns the attribute by + reference.</td><td>GetState_</td></tr><tr><td>fsm_ [(attribute name)]</td><td>Returns the current state machine (as reference). Valid + inside a state entry/exit action or a transition. If an + attribute name is provided, returns the attribute by + reference.</td><td>GetFsm_</td></tr><tr><td>substate_(state_name [,fsm])</td><td>Returns (as reference) the state state_name referenced in the + current state machine or the one given as argument.</td><td>SubState_</td></tr></tbody></table></div></div><p><br class="table-break"> + </p><p>To use these functions, you need to include: </p><p><code class="code">#include <msm/front/euml/euml.hpp></code></p></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="pt02.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="pt02.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="ch10.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">Part II. Reference </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> Chapter 10. + Functional programming </td></tr></table></div></body></html>
\ No newline at end of file diff --git a/libs/msm/doc/HTML/ch10.html b/libs/msm/doc/HTML/ch10.html new file mode 100644 index 0000000000..2b93d5f5a7 --- /dev/null +++ b/libs/msm/doc/HTML/ch10.html @@ -0,0 +1,100 @@ +<html><head> + <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> + <title>Chapter 10. Functional programming</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="pt02.html" title="Part II. Reference"><link rel="prev" href="ch09.html" title="Chapter 9. eUML operators and basic helpers"><link rel="next" href="re01.html" title="Common headers"></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">Chapter 10. + Functional programming </th></tr><tr><td width="20%" align="left"><a accesskey="p" href="ch09.html">Prev</a> </td><th width="60%" align="center">Part II. Reference</th><td width="20%" align="right"> <a accesskey="n" href="re01.html">Next</a></td></tr></table><hr></div><div class="chapter" title="Chapter 10. Functional programming"><div class="titlepage"><div><div><h2 class="title"><a name="d0e3822"></a>Chapter 10. + <span class="command"><strong><a name="eUML-STL-all"></a></strong></span>Functional programming </h2></div></div></div><p>To use these functions, you need to include: </p><p><code class="code">#include <msm/front/euml/stl.hpp></code></p><p>or the specified header in the following tables.</p><p>The following tables list the supported STL algorithms: </p><p> + <span class="command"><strong><a name="eUML-STL-querying"></a></strong></span> + </p><div class="table"><a name="d0e3840"></a><p class="title"><b>Table 10.1. STL algorithms</b></p><div class="table-contents"><table summary="STL algorithms" border="1"><colgroup><col><col></colgroup><thead><tr><th>STL algorithms in querying.hpp</th><th>Functor</th></tr></thead><tbody><tr><td>find_(first, last, value)</td><td>Find_</td></tr><tr><td>find_if_(first, last, value)</td><td>FindIf_</td></tr><tr><td>lower_bound_(first, last, value [,opᵃ])</td><td>LowerBound_</td></tr><tr><td>upper_bound_(first, last, value [,opᵃ])</td><td>UpperBound_</td></tr><tr><td>equal_range_(first, last, value [,opᵃ])</td><td>EqualRange_</td></tr><tr><td>binary_search_(first, last, value [,opᵃ])</td><td>BinarySearch_</td></tr><tr><td>min_element_(first, last[,opᵃ])</td><td>MinElement_</td></tr><tr><td>max_element_(first, last[,opᵃ])</td><td>MaxElement_</td></tr><tr><td>adjacent_find_(first, last[,opᵃ])</td><td>AdjacentFind_</td></tr><tr><td>find_end_( first1, last1, first2, last2 [,op ᵃ])</td><td>FindEnd_</td></tr><tr><td>find_first_of_( first1, last1, first2, last2 [,op ᵃ])</td><td>FindFirstOf_</td></tr><tr><td>equal_( first1, last1, first2 [,op ᵃ])</td><td>Equal_</td></tr><tr><td>search_( first1, last1, first2, last2 [,op ᵃ])</td><td>Search_</td></tr><tr><td>includes_( first1, last1, first2, last2 [,op ᵃ])</td><td>Includes_</td></tr><tr><td>lexicographical_compare_ ( first1, last1, first2, last2 [,op + ᵃ]) </td><td>LexicographicalCompare_</td></tr><tr><td>count_(first, last, value [,size])</td><td>Count_</td></tr><tr><td>count_if_(first, last, op ᵃ [,size])</td><td>CountIf_</td></tr><tr><td>distance_(first, last)</td><td>Distance_</td></tr><tr><td>mismatch _( first1, last1, first2 [,op ᵃ])</td><td>Mismatch_</td></tr></tbody></table></div></div><p><br class="table-break"> + </p><p> + <span class="command"><strong><a name="eUML-STL-iteration"></a></strong></span> + </p><div class="table"><a name="d0e3953"></a><p class="title"><b>Table 10.2. STL algorithms</b></p><div class="table-contents"><table summary="STL algorithms" border="1"><colgroup><col><col></colgroup><thead><tr><th>STL algorithms in iteration.hpp</th><th>Functor</th></tr></thead><tbody><tr><td>for_each_(first,last, unary opᵃ)</td><td>ForEach_</td></tr><tr><td>accumulate_first, last, init [,opᵃ])</td><td>Accumulate_</td></tr></tbody></table></div></div><p><br class="table-break"> + </p><p> + <span class="command"><strong><a name="eUML-STL-transformation"></a></strong></span> + </p><div class="table"><a name="d0e3981"></a><p class="title"><b>Table 10.3. STL algorithms</b></p><div class="table-contents"><table summary="STL algorithms" border="1"><colgroup><col><col></colgroup><thead><tr><th>STL algorithms in transformation.hpp</th><th>Functor</th></tr></thead><tbody><tr><td>copy_(first, last, result)</td><td>Copy_</td></tr><tr><td>copy_backward_(first, last, result)</td><td>CopyBackward_</td></tr><tr><td>reverse_(first, last)</td><td>Reverse_</td></tr><tr><td>reverse_copy_(first, last , result)</td><td>ReverseCopy_</td></tr><tr><td>remove_(first, last, value)</td><td>Remove_</td></tr><tr><td>remove_if_(first, last , opᵃ)</td><td>RemoveIf_</td></tr><tr><td>remove_copy_(first, last , output, value)</td><td>RemoveCopy_</td></tr><tr><td>remove_copy_if_(first, last, output, opᵃ)</td><td>RemoveCopyIf_</td></tr><tr><td>fill_(first, last, value)</td><td>Fill_</td></tr><tr><td>fill_n_(first, size, value)ᵇ</td><td>FillN_</td></tr><tr><td>generate_(first, last, generatorᵃ)</td><td>Generate_</td></tr><tr><td>generate_(first, size, generatorᵃ)ᵇ</td><td>GenerateN_</td></tr><tr><td>unique_(first, last [,opᵃ])</td><td>Unique_</td></tr><tr><td>unique_copy_(first, last, output [,opᵃ])</td><td>UniqueCopy_</td></tr><tr><td>random_shuffle_(first, last [,opᵃ])</td><td>RandomShuffle_</td></tr><tr><td>rotate_copy_(first, middle, last, output)</td><td>RotateCopy_</td></tr><tr><td>partition_ (first, last [,opᵃ])</td><td>Partition_</td></tr><tr><td>stable_partition_ (first, last [,opᵃ])</td><td>StablePartition_</td></tr><tr><td>stable_sort_(first, last [,opᵃ])</td><td>StableSort_</td></tr><tr><td>sort_(first, last [,opᵃ])</td><td>Sort_</td></tr><tr><td>partial_sort_(first, middle, last [,opᵃ])</td><td>PartialSort_</td></tr><tr><td>partial_sort_copy_ (first, last, res_first, res_last [,opᵃ]) </td><td>PartialSortCopy_</td></tr><tr><td>nth_element_(first, nth, last [,opᵃ])</td><td>NthElement_</td></tr><tr><td>merge_( first1, last1, first2, last2, output [,op ᵃ])</td><td>Merge_</td></tr><tr><td>inplace_merge_(first, middle, last [,opᵃ])</td><td>InplaceMerge_</td></tr><tr><td>set_union_(first1, last1, first2, last2, output [,op + ᵃ])</td><td>SetUnion_</td></tr><tr><td>push_heap_(first, last [,op ᵃ])</td><td>PushHeap_</td></tr><tr><td>pop_heap_(first, last [,op ᵃ])</td><td>PopHeap_</td></tr><tr><td>make_heap_(first, last [,op ᵃ])</td><td>MakeHeap_</td></tr><tr><td>sort_heap_(first, last [,op ᵃ])</td><td>SortHeap_</td></tr><tr><td>next_permutation_(first, last [,op ᵃ])</td><td>NextPermutation_</td></tr><tr><td>prev_permutation_(first, last [,op ᵃ])</td><td>PrevPermutation_</td></tr><tr><td>inner_product_(first1, last1, first2, init [,op1ᵃ] [,op2ᵃ]) </td><td>InnerProduct_</td></tr><tr><td>partial_sum_(first, last, output [,opᵃ])</td><td>PartialSum_</td></tr><tr><td>adjacent_difference_(first, last, output [,opᵃ])</td><td>AdjacentDifference_</td></tr><tr><td>replace_(first, last, old_value, new_value)</td><td>Replace_</td></tr><tr><td>replace_if_(first, last, opᵃ, new_value)</td><td>ReplaceIf_</td></tr><tr><td>replace_copy_(first, last, result, old_value, + new_value)</td><td>ReplaceCopy_</td></tr><tr><td>replace_copy_if_(first, last, result, opᵃ, new_value)</td><td>ReplaceCopyIf_</td></tr><tr><td>rotate_(first, middle, last)ᵇ</td><td>Rotate_</td></tr></tbody></table></div></div><p><br class="table-break"> + </p><p> + <span class="command"><strong><a name="eUML-STL-container"></a></strong></span> + </p><div class="table"><a name="d0e4199"></a><p class="title"><b>Table 10.4. STL container methods</b></p><div class="table-contents"><table summary="STL container methods" border="1"><colgroup><col><col></colgroup><thead><tr><th>STL container methods(common) in container.hpp</th><th>Functor</th></tr></thead><tbody><tr><td>container::reference front_(container)</td><td>Front_</td></tr><tr><td>container::reference back_(container)</td><td>Back_</td></tr><tr><td>container::iterator begin_(container)</td><td>Begin_</td></tr><tr><td>container::iterator end_(container)</td><td>End_</td></tr><tr><td>container::reverse_iterator rbegin_(container)</td><td>RBegin_</td></tr><tr><td>container::reverse_iterator rend_(container)</td><td>REnd_</td></tr><tr><td>void push_back_(container, value)</td><td>Push_Back_</td></tr><tr><td>void pop_back_(container, value)</td><td>Pop_Back_</td></tr><tr><td>void push_front_(container, value)</td><td>Push_Front_</td></tr><tr><td>void pop_front_(container, value)</td><td>Pop_Front_</td></tr><tr><td>void clear_(container)</td><td>Clear_</td></tr><tr><td>size_type capacity_(container)</td><td>Capacity_</td></tr><tr><td>size_type size_(container)</td><td>Size_</td></tr><tr><td>size_type max_size_(container)</td><td>Max_Size_</td></tr><tr><td>void reserve_(container, value)</td><td>Reserve _</td></tr><tr><td>void resize_(container, value)</td><td>Resize _</td></tr><tr><td>iterator insert_(container, pos, value)</td><td>Insert_</td></tr><tr><td>void insert_( container , pos, first, last)</td><td>Insert_</td></tr><tr><td>void insert_( container , pos, number, value)</td><td>Insert_</td></tr><tr><td>void swap_( container , other_container)</td><td>Swap_</td></tr><tr><td>void erase_( container , pos)</td><td>Erase_</td></tr><tr><td>void erase_( container , first, last) </td><td>Erase_</td></tr><tr><td>bool empty_( container)</td><td>Empty_</td></tr></tbody></table></div></div><p><br class="table-break"> + </p><p> + </p><div class="table"><a name="d0e4330"></a><p class="title"><b>Table 10.5. STL list methods</b></p><div class="table-contents"><table summary="STL list methods" border="1"><colgroup><col><col></colgroup><thead><tr><th>std::list methods in container.hpp</th><th>Functor</th></tr></thead><tbody><tr><td>void list_remove_(container, value)</td><td>ListRemove_</td></tr><tr><td>void list_remove_if_(container, opᵃ)</td><td>ListRemove_If_</td></tr><tr><td>void list_merge_(container, other_list)</td><td>ListMerge_</td></tr><tr><td>void list_merge_(container, other_list, opᵃ)</td><td>ListMerge_</td></tr><tr><td>void splice_(container, iterator, other_list)</td><td>Splice_</td></tr><tr><td>void splice_(container, iterator, other_list, + iterator)</td><td>Splice_</td></tr><tr><td>void splice_(container, iterator, other_list, first, + last)</td><td>Splice_</td></tr><tr><td>void list_reverse_(container)</td><td>ListReverse_</td></tr><tr><td>void list_unique_(container)</td><td>ListUnique_</td></tr><tr><td>void list_unique_(container, opᵃ)</td><td>ListUnique_</td></tr><tr><td>void list_sort_(container)</td><td>ListSort_</td></tr><tr><td>void list_sort_(container, opᵃ)</td><td>ListSort_</td></tr></tbody></table></div></div><p><br class="table-break"> + </p><p> + </p><div class="table"><a name="d0e4406"></a><p class="title"><b>Table 10.6. STL associative container methods </b></p><div class="table-contents"><table summary="STL associative container methods " border="1"><colgroup><col><col></colgroup><thead><tr><th>Associative container methods in container.hpp</th><th>Functor</th></tr></thead><tbody><tr><td>iterator insert_(container, pos, value)</td><td>Insert_</td></tr><tr><td>void insert_( container , first, last)</td><td>Insert_</td></tr><tr><td>pair<iterator, bool> insert_( container , value)</td><td>Insert_</td></tr><tr><td>void associative_erase_( container , pos)</td><td>Associative_Erase_</td></tr><tr><td>void associative_erase_( container , first, last)</td><td>Associative_Erase_</td></tr><tr><td>size_type associative_erase_( container , key)</td><td>Associative_Erase_</td></tr><tr><td>iterator associative_find_( container , key)</td><td>Associative_Find_</td></tr><tr><td>size_type associative_count_( container , key)</td><td>AssociativeCount_</td></tr><tr><td>iterator associative_lower_bound_( container , key)</td><td>Associative_Lower_Bound_</td></tr><tr><td>iterator associative_upper_bound_( container , key)</td><td>Associative_Upper_Bound_</td></tr><tr><td>pair<iterator, iterator> associative_equal_range_( + container , key)</td><td>Associative_Equal_Range_</td></tr></tbody></table></div></div><p><br class="table-break"> + </p><p> + </p><div class="table"><a name="d0e4477"></a><p class="title"><b>Table 10.7. STL pair</b></p><div class="table-contents"><table summary="STL pair" border="1"><colgroup><col><col></colgroup><thead><tr><th>std::pair in container.hpp</th><th>Functor</th></tr></thead><tbody><tr><td>first_type first_(pair<T1, T2>)</td><td>First_</td></tr><tr><td>second_type second_(pair<T1, T2>)</td><td>Second_</td></tr></tbody></table></div></div><p><br class="table-break"> + </p><p> + </p><div class="table"><a name="d0e4503"></a><p class="title"><b>Table 10.8. STL string</b></p><div class="table-contents"><table summary="STL string" border="1"><colgroup><col><col><col></colgroup><thead><tr><th>STL string method</th><th>std::string method in container.hpp</th><th>Functor</th></tr></thead><tbody><tr><td>substr (size_type pos, size_type size)</td><td>string substr_(container, pos, length)</td><td>Substr_</td></tr><tr><td>int compare(string)</td><td>int string_compare_(container, another_string)</td><td>StringCompare_</td></tr><tr><td>int compare(char*)</td><td>int string_compare_(container, another_string)</td><td>StringCompare_</td></tr><tr><td>int compare(size_type pos, size_type size, string)</td><td>int string_compare_(container, pos, size, + another_string)</td><td>StringCompare_</td></tr><tr><td>int compare (size_type pos, size_type size, string, size_type + length)</td><td>int string_compare_(container, pos, size, another_string, + length)</td><td>StringCompare_</td></tr><tr><td>string& append(const string&)</td><td>string& append_(container, another_string)</td><td>Append_</td></tr><tr><td>string& append (charT*)</td><td>string& append_(container, another_string)</td><td>Append_</td></tr><tr><td>string& append (string , size_type pos, size_type + size)</td><td>string& append_(container, other_string, pos, + size)</td><td>Append_</td></tr><tr><td>string& append (charT*, size_type size)</td><td>string& append_(container, another_string, + length)</td><td>Append_</td></tr><tr><td>string& append (size_type size, charT)</td><td>string& append_(container, size, char)</td><td>Append_</td></tr><tr><td>string& append (iterator begin, iterator end)</td><td>string& append_(container, begin, end)</td><td>Append_</td></tr><tr><td>string& insert (size_type pos, charT*)</td><td>string& string_insert_(container, pos, + other_string)</td><td>StringInsert_</td></tr><tr><td>string& insert(size_type pos, charT*,size_type n)</td><td>string& string_insert_(container, pos, other_string, + n)</td><td>StringInsert_</td></tr><tr><td>string& insert(size_type pos,size_type n, charT + c)</td><td>string& string_insert_(container, pos, n, c)</td><td>StringInsert_</td></tr><tr><td>string& insert (size_type pos, const string&)</td><td>string& string_insert_(container, pos, + other_string)</td><td>StringInsert_</td></tr><tr><td>string& insert (size_type pos, const string&, + size_type pos1, size_type n)</td><td>string& string_insert_(container, pos, other_string, + pos1, n)</td><td>StringInsert_</td></tr><tr><td>string& erase(size_type pos=0, size_type n=npos)</td><td>string& string_erase_(container, pos, n)</td><td>StringErase_</td></tr><tr><td>string& assign(const string&)</td><td>string& string_assign_(container, another_string)</td><td>StringAssign_</td></tr><tr><td>string& assign(const charT*)</td><td>string& string_assign_(container, another_string)</td><td>StringAssign_</td></tr><tr><td>string& assign(const string&, size_type pos, + size_type n)</td><td>string& string_assign_(container, another_string, pos, + n)</td><td>StringAssign_</td></tr><tr><td>string& assign(const charT*, size_type n)</td><td>string& string_assign_(container, another_string, + n)</td><td>StringAssign_</td></tr><tr><td>string& assign(size_type n, charT c)</td><td>string& string_assign_(container, n, c)</td><td>StringAssign_</td></tr><tr><td>string& assign(iterator first, iterator last)</td><td>string& string_assign_(container, first, last)</td><td>StringAssign_</td></tr><tr><td>string& replace(size_type pos, size_type n, const + string&)</td><td>string& string_replace_(container, pos, n, + another_string)</td><td>StringReplace_</td></tr><tr><td>string& replace(size_type pos, size_type n, const charT*, + size_type n1)</td><td>string& string_replace_(container, pos, n, + another_string, n1)</td><td>StringReplace_</td></tr><tr><td>string& replace(size_type pos, size_type n, const + charT*)</td><td>string& string_replace_(container, pos, n, + another_string)</td><td>StringReplace_</td></tr><tr><td>string& replace(size_type pos, size_type n, size_type n1, + charT c)</td><td>string& string_replace_(container, pos, n, n1, c)</td><td>StringReplace_</td></tr><tr><td>string& replace(iterator first, iterator last, const + string&)</td><td>string& string_replace_(container, first, last, + another_string)</td><td>StringReplace_</td></tr><tr><td>string& replace(iterator first, iterator last, const + charT*, size_type n)</td><td>string& string_replace_(container, first, last, + another_string, n)</td><td>StringReplace_</td></tr><tr><td>string& replace(iterator first, iterator last, const + charT*)</td><td>string& string_replace_(container, first, last, + another_string)</td><td>StringReplace_</td></tr><tr><td>string& replace(iterator first, iterator last, size_type + n, charT c)</td><td>string& string_replace_(container, first, last, n, + c)</td><td>StringReplace_</td></tr><tr><td>string& replace(iterator first, iterator last, iterator + f, iterator l)</td><td>string& string_replace_(container, first, last, f, + l)</td><td>StringReplace_</td></tr><tr><td>const charT* c_str()</td><td>const charT* c_str_(container)</td><td>CStr_</td></tr><tr><td>const charT* data()</td><td>const charT* string_data_(container)</td><td>StringData_</td></tr><tr><td>size_type copy(charT* buf, size_type n, size_type pos = + 0)</td><td>size_type string_copy_(container, buf, n, pos); size_type + string_copy_(container, buf, n) </td><td>StringCopy_</td></tr><tr><td>size_type find(charT* s, size_type pos, size_type n)</td><td>size_type string_find_(container, s, pos, n)</td><td>StringFind_</td></tr><tr><td>size_type find(charT* s, size_type pos=0)</td><td>size_type string_find_(container, s, pos); size_type + string_find_(container, s) </td><td>StringFind_</td></tr><tr><td>size_type find(const string& s, size_type pos=0)</td><td>size_type string_find_(container, s, pos) size_type + string_find_(container, s) </td><td>StringFind_</td></tr><tr><td>size_type find(charT c, size_type pos=0)</td><td>size_type string_find_(container, c, pos) size_type + string_find_(container, c) </td><td>StringFind_</td></tr><tr><td>size_type rfind(charT* s, size_type pos, size_type n)</td><td>size_type string_rfind_(container, s, pos, n)</td><td>StringRFind_</td></tr><tr><td>size_type rfind(charT* s, size_type pos=npos)</td><td>size_type string_rfind_(container, s, pos); size_type + string_rfind_(container, s) </td><td>StringRFind_</td></tr><tr><td>size_type rfind(const string& s, size_type + pos=npos)</td><td>size_type string_rfind_(container, s, pos); size_type + string_rfind_(container, s) </td><td>StringRFind_</td></tr><tr><td>size_type rfind(charT c, size_type pos=npos)</td><td>size_type string_rfind_(container, c, pos) size_type + string_rfind_(container, c) </td><td>StringRFind_</td></tr><tr><td>size_type find_first_of(charT* s, size_type pos, size_type + n)</td><td>size_type find_first_of_(container, s, pos, n)</td><td>StringFindFirstOf_</td></tr><tr><td>size_type find_first_of (charT* s, size_type pos=0)</td><td>size_type find_first_of_(container, s, pos); size_type + find_first_of_(container, s) </td><td>StringFindFirstOf_</td></tr><tr><td>size_type find_first_of (const string& s, size_type + pos=0)</td><td>size_type find_first_of_(container, s, pos); size_type + find_first_of_(container, s) </td><td>StringFindFirstOf_</td></tr><tr><td>size_type find_first_of (charT c, size_type pos=0)</td><td>size_type find_first_of_(container, c, pos) size_type + find_first_of_(container, c) </td><td>StringFindFirstOf_</td></tr><tr><td>size_type find_first_not_of(charT* s, size_type pos, + size_type n)</td><td>size_type find_first_not_of_(container, s, pos, n)</td><td>StringFindFirstNotOf_</td></tr><tr><td>size_type find_first_not_of (charT* s, size_type + pos=0)</td><td>size_type find_first_not_of_(container, s, pos); size_type + find_first_not_of_(container, s) </td><td>StringFindFirstNotOf_</td></tr><tr><td>size_type find_first_not_of (const string& s, size_type + pos=0)</td><td>size_type find_first_not_of_(container, s, pos); size_type + find_first_not_of_(container, s) </td><td>StringFindFirstNotOf_</td></tr><tr><td>size_type find_first_not_of (charT c, size_type + pos=0)</td><td>size_type find_first_not_of_(container, c, pos); size_type + find_first_not_of_(container, c) </td><td>StringFindFirstNotOf_</td></tr><tr><td>size_type find_last_of(charT* s, size_type pos, size_type + n)</td><td>size_type find_last_of_(container, s, pos, n)</td><td>StringFindLastOf_</td></tr><tr><td>size_type find_last_of (charT* s, size_type pos=npos)</td><td>size_type find_last_of_(container, s, pos); size_type + find_last_of_(container, s) </td><td>StringFindLastOf_</td></tr><tr><td>size_type find_last_of (const string& s, size_type + pos=npos)</td><td>size_type find_last_of_(container, s, pos); size_type + find_last_of_(container, s) </td><td>StringFindLastOf_</td></tr><tr><td>size_type find_last_of (charT c, size_type pos=npos)</td><td>size_type find_last_of_(container, c, pos); size_type + find_last_of_(container, c) </td><td>StringFindLastOf_</td></tr><tr><td>size_type find_last_not_of(charT* s, size_type pos, size_type + n)</td><td>size_type find_last_not_of_(container, s, pos, n)</td><td>StringFindLastNotOf_</td></tr><tr><td>size_type find_last_not_of (charT* s, size_type + pos=npos)</td><td>size_type find_last_not_of_(container, s, pos); size_type + find_last_of_(container, s) </td><td>StringFindLastNotOf_</td></tr><tr><td>size_type find_last_not_of (const string& s, size_type + pos=npos)</td><td>size_type find_last_not_of_(container, s, pos); size_type + find_last_not_of_(container, s) </td><td>StringFindLastNotOf_</td></tr><tr><td>size_type find_last_not_of (charT c, size_type + pos=npos)</td><td>size_type find_last_not_of_(container, c, pos); size_type + find_last_not_of_(container, c) </td><td>StringFindLastNotOf_</td></tr></tbody></table></div></div><p><br class="table-break"> + </p><p><span class="underline">Notes</span>: </p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>ᵃ: algorithms requiring a predicate need to make them eUML compatible + by wrapping them inside a Predicate_ functor. For example, + std::less<int> => Predicate_<std::less<int> >()</p></li><li class="listitem"><p>ᵇ: If using the SGI STL implementation, these functors use the SGI + return value</p></li></ul></div><p> + </p></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="ch09.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="pt02.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="re01.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">Chapter 9. eUML operators and basic helpers </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> Common headers</td></tr></table></div></body></html>
\ No newline at end of file diff --git a/libs/msm/doc/HTML/examples/ActivateStateBeforeTransitionEuml.cpp b/libs/msm/doc/HTML/examples/ActivateStateBeforeTransitionEuml.cpp new file mode 100644 index 0000000000..fc69b777bc --- /dev/null +++ b/libs/msm/doc/HTML/examples/ActivateStateBeforeTransitionEuml.cpp @@ -0,0 +1,144 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <iostream> + +#include <boost/msm/back/state_machine.hpp> +#include <boost/msm/front/state_machine_def.hpp> +#include <boost/msm/front/euml/euml.hpp> + +using namespace std; +namespace msm = boost::msm; +namespace mpl = boost::mpl; +using namespace boost::msm::front::euml; + + +namespace // Concrete FSM implementation +{ + // events + BOOST_MSM_EUML_EVENT(connect) + BOOST_MSM_EUML_EVENT(disconnect) + + // flag + BOOST_MSM_EUML_FLAG(is_connected) + + BOOST_MSM_EUML_ACTION(SignalConnect) + { + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& fsm,SourceState& ,TargetState& ) + { + // by default, this would be wrong (shows false) + cout << "SignalConnect. Connected? " + << std::boolalpha + << fsm.template is_flag_active<BOOST_MSM_EUML_FLAG_NAME(is_connected)>() << endl; + } + }; + BOOST_MSM_EUML_ACTION(SignalDisconnect) + { + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& fsm,SourceState& ,TargetState& ) + { + // by default, this would be wrong (shows true) + cout << "SignalDisconnect. Connected? " + << std::boolalpha + << fsm.template is_flag_active<BOOST_MSM_EUML_FLAG_NAME(is_connected)>() + << endl; + } + }; + + // The list of FSM states + BOOST_MSM_EUML_ACTION(Connected_Entry) + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: Connected" << std::endl; + } + }; + BOOST_MSM_EUML_ACTION(Connected_Exit) + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "leaving: Connected" << std::endl; + } + }; + BOOST_MSM_EUML_STATE(( Connected_Entry,Connected_Exit, + attributes_ << no_attributes_, + configure_<< is_connected ),Connected) + + BOOST_MSM_EUML_ACTION(Disconnected_Entry) + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: Disconnected" << std::endl; + } + }; + BOOST_MSM_EUML_ACTION(Disconnected_Exit) + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "leaving: Disconnected" << std::endl; + } + }; + BOOST_MSM_EUML_STATE(( Disconnected_Entry,Disconnected_Exit ),Disconnected) + + // replaces the old transition table + BOOST_MSM_EUML_TRANSITION_TABLE(( + Disconnected == Connected + disconnect / SignalDisconnect , + Connected == Disconnected + connect / SignalConnect + // +------------------------------------------------------------------------------+ + ),transition_table) + + BOOST_MSM_EUML_ACTION(Log_No_Transition) + { + template <class FSM,class Event> + void operator()(Event const& e,FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + + // create a state machine "on the fly" + BOOST_MSM_EUML_DECLARE_STATE_MACHINE(( transition_table, //STT + init_ << Disconnected, // Init State + no_action, // Entry + no_action, // Exit + attributes_ << no_attributes_, // Attributes + configure_ << switch_active_before_transition, // configuration + Log_No_Transition // no_transition handler + ), + Connection_) //fsm name + + typedef msm::back::state_machine<Connection_> Connection; + + void test() + { + Connection connection; + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + connection.start(); + // signal a connection + connection.process_event(connect); + // signal a disconnection + connection.process_event(disconnect); + connection.stop(); + } +} + +int main() +{ + test(); + return 0; +} + + diff --git a/libs/msm/doc/HTML/examples/ActiveStateSetBeforeTransition.cpp b/libs/msm/doc/HTML/examples/ActiveStateSetBeforeTransition.cpp new file mode 100644 index 0000000000..769d7702f1 --- /dev/null +++ b/libs/msm/doc/HTML/examples/ActiveStateSetBeforeTransition.cpp @@ -0,0 +1,119 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> + +//front-end +#include <boost/msm/front/state_machine_def.hpp> +// functors +#include <boost/msm/front/functor_row.hpp> +#include <boost/msm/front/euml/common.hpp> + + +using namespace std; +namespace msm = boost::msm; +namespace mpl = boost::mpl; +using namespace msm::front; + +namespace // Concrete FSM implementation +{ + // events + struct disconnect {}; + struct connect {}; + + // flag + struct is_connected{}; + // front-end: define the FSM structure + struct Connection_ : public msm::front::state_machine_def<Connection_> + { + // when a transition is about to be taken, we already update our currently active state(s) + typedef msm::active_state_switch_before_transition active_state_switch_policy; + + // The list of FSM states + struct Connected : public msm::front::state<> + { + // in this state, we are connected + typedef mpl::vector1<is_connected> flag_list; + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Connected" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Connected" << std::endl;} + }; + struct Disconnected : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) {std::cout << "entering: Disconnected" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Disconnected" << std::endl;} + }; + + // transition actions + struct SignalConnect + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const&, FSM& fsm,SourceState& ,TargetState& ) + { + // by default, this would be wrong (shows false) + cout << "SignalConnect. Connected? " << std::boolalpha << fsm.template is_flag_active<is_connected>() << endl; + } + }; + struct SignalDisconnect + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const&, FSM& fsm,SourceState& ,TargetState& ) + { + // by default, this would be wrong (shows true) + cout << "SignalDisconnect. Connected? " << std::boolalpha << fsm.template is_flag_active<is_connected>() << endl; + } + }; + + // the initial state of the player SM. Must be defined + typedef Disconnected initial_state; + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +--------------+-------------+--------------+---------------------------+----------------------+ + Row < Connected , disconnect , Disconnected , SignalDisconnect , none >, + Row < Disconnected , connect , Connected , SignalConnect , none > + // +--------------+-------------+--------------+---------------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + // Pick a back-end + typedef msm::back::state_machine<Connection_> Connection; + + + void test() + { + Connection connection; + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + connection.start(); + // signal a connection + connection.process_event(connect()); + // signal a disconnection + connection.process_event(disconnect()); + connection.stop(); + } +} + +int main() +{ + test(); + return 0; +} diff --git a/libs/msm/doc/HTML/examples/AnonymousTutorial.cpp b/libs/msm/doc/HTML/examples/AnonymousTutorial.cpp new file mode 100644 index 0000000000..557f90b1d3 --- /dev/null +++ b/libs/msm/doc/HTML/examples/AnonymousTutorial.cpp @@ -0,0 +1,133 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> + +namespace msm = boost::msm; +namespace mpl = boost::mpl; +using namespace boost::msm::front; + +namespace +{ + // events + struct event1 {}; + + + // front-end: define the FSM structure + struct my_machine_ : public msm::front::state_machine_def<my_machine_> + { + // The list of FSM states + struct State1 : public msm::front::state<> + { + // every (optional) entry/exit methods get the event passed. + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: State1" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: State1" << std::endl;} + }; + struct State2 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) {std::cout << "entering: State2" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: State2" << std::endl;} + }; + + struct State3 : public msm::front::state<> + { + // when stopped, the CD is loaded + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) {std::cout << "entering: State3" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: State3" << std::endl;} + }; + + struct State4 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: State4" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: State4" << std::endl;} + }; + + // the initial state of the player SM. Must be defined + typedef State1 initial_state; + + // transition actions + void State2ToState3(none const&) { std::cout << "my_machine::State2ToState3\n"; } + void State3ToState4(none const&) { std::cout << "my_machine::State3ToState4\n"; } + // guard conditions + bool always_true(none const& evt) + { + std::cout << "always_true" << std::endl; + return true; + } + bool always_false(none const& evt) + { + std::cout << "always_false" << std::endl; + return false; + } + + typedef my_machine_ p; // makes transition table cleaner + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + _row < State1 , none , State2 >, + a_row < State2 , none , State3 , &p::State2ToState3 >, + // +---------+-------------+---------+---------------------+----------------------+ + row < State3 , none , State4 , &p::State3ToState4 , &p::always_true >, + g_row < State3 , none , State4 , &p::always_false >, + _row < State4 , event1 , State1 > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + // Pick a back-end + typedef msm::back::state_machine<my_machine_> my_machine; + + // + // Testing utilities. + // + static char const* const state_names[] = { "State1", "State2", "State3", "State4" }; + void pstate(my_machine const& p) + { + std::cout << " -> " << state_names[p.current_state()[0]] << std::endl; + } + + void test() + { + my_machine p; + + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + // in this case it will also immediately trigger all anonymous transitions + p.start(); + // this event will bring us back to the initial state and thus, a new "loop" will be started + p.process_event(event1()); + + } +} + +int main() +{ + test(); + return 0; +} diff --git a/libs/msm/doc/HTML/examples/AnonymousTutorialEuml.cpp b/libs/msm/doc/HTML/examples/AnonymousTutorialEuml.cpp new file mode 100644 index 0000000000..2ad0c5b3c3 --- /dev/null +++ b/libs/msm/doc/HTML/examples/AnonymousTutorialEuml.cpp @@ -0,0 +1,175 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +#include <boost/msm/front/euml/euml.hpp> + +namespace msm = boost::msm; +using namespace boost::msm::front::euml; + +namespace +{ + // events + BOOST_MSM_EUML_EVENT(event1) + + BOOST_MSM_EUML_ACTION(State1_Entry) + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: State1" << std::endl; + } + }; + BOOST_MSM_EUML_ACTION(State1_Exit) + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "leaving: State1" << std::endl; + } + }; + BOOST_MSM_EUML_ACTION(State2_Entry) + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: State2" << std::endl; + } + }; + BOOST_MSM_EUML_ACTION(State2_Exit) + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "leaving: State2" << std::endl; + } + }; + BOOST_MSM_EUML_ACTION(State3_Entry) + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: State3" << std::endl; + } + }; + BOOST_MSM_EUML_ACTION(State3_Exit) + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "leaving: State3" << std::endl; + } + }; + BOOST_MSM_EUML_ACTION(State4_Entry) + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: State4" << std::endl; + } + }; + BOOST_MSM_EUML_ACTION(State4_Exit) + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "leaving: State4" << std::endl; + } + }; + + // The list of FSM states + BOOST_MSM_EUML_STATE(( State1_Entry,State1_Exit ),State1) + BOOST_MSM_EUML_STATE(( State2_Entry,State2_Exit ),State2) + BOOST_MSM_EUML_STATE(( State3_Entry,State3_Exit ),State3) + BOOST_MSM_EUML_STATE(( State4_Entry,State4_Exit ),State4) + + // transition actions + BOOST_MSM_EUML_ACTION(State2ToState3) + { + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM&,SourceState& ,TargetState& ) + { + std::cout << "my_machine::State2ToState3" << std::endl; + } + }; + BOOST_MSM_EUML_ACTION(State3ToState4) + { + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM&,SourceState& ,TargetState& ) + { + std::cout << "my_machine::State3ToState4" << std::endl; + } + }; + // guard conditions + BOOST_MSM_EUML_ACTION(always_true) + { + template <class FSM,class EVT,class SourceState,class TargetState> + bool operator()(EVT const& evt,FSM&,SourceState& ,TargetState& ) + { + std::cout << "always_true" << std::endl; + return true; + } + }; + BOOST_MSM_EUML_ACTION(always_false) + { + template <class FSM,class EVT,class SourceState,class TargetState> + bool operator()(EVT const& evt,FSM&,SourceState& ,TargetState& ) + { + std::cout << "always_false" << std::endl; + return false; + } + }; + // replaces the old transition table + BOOST_MSM_EUML_TRANSITION_TABLE(( + State2 == State1 , + State3 == State2 / State2ToState3, + State4 == State3 [always_true] / State3ToState4, + State4 == State3 [always_false], + State1 == State4 + event1 + // +------------------------------------------------------------------------------+ + ),transition_table) + + // create a state machine "on the fly" + BOOST_MSM_EUML_DECLARE_STATE_MACHINE(( transition_table, //STT + init_ << State1 // Init State + ), + my_machine_) //fsm name + + // Pick a back-end + typedef msm::back::state_machine<my_machine_> my_machine; + + // + // Testing utilities. + // + static char const* const state_names[] = { "State1", "State2", "State3", "State4" }; + void pstate(my_machine const& p) + { + std::cout << " -> " << state_names[p.current_state()[0]] << std::endl; + } + + void test() + { + my_machine p; + + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + // in this case it will also immediately trigger all anonymous transitions + p.start(); + // this event will bring us back to the initial state and thus, a new "loop" will be started + p.process_event(event1); + } +} + +int main() +{ + test(); + return 0; +} diff --git a/libs/msm/doc/HTML/examples/AnonymousTutorialWithFunctors.cpp b/libs/msm/doc/HTML/examples/AnonymousTutorialWithFunctors.cpp new file mode 100644 index 0000000000..4bcd7ad37c --- /dev/null +++ b/libs/msm/doc/HTML/examples/AnonymousTutorialWithFunctors.cpp @@ -0,0 +1,158 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> +// functors +#include <boost/msm/front/functor_row.hpp> +#include <boost/msm/front/euml/common.hpp> + +namespace msm = boost::msm; +namespace mpl = boost::mpl; +using namespace boost::msm::front; + +namespace +{ + // events + struct event1 {}; + + + // front-end: define the FSM structure + struct my_machine_ : public msm::front::state_machine_def<my_machine_> + { + // The list of FSM states + struct State1 : public msm::front::state<> + { + // every (optional) entry/exit methods get the event passed. + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: State1" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: State1" << std::endl;} + }; + struct State2 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) {std::cout << "entering: State2" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: State2" << std::endl;} + }; + + struct State3 : public msm::front::state<> + { + // when stopped, the CD is loaded + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) {std::cout << "entering: State3" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: State3" << std::endl;} + }; + + struct State4 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: State4" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: State4" << std::endl;} + }; + + // the initial state of the player SM. Must be defined + typedef State1 initial_state; + + // transition actions + struct State2ToState3 + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + std::cout << "my_machine::State2ToState3" << std::endl; + } + }; + struct State3ToState4 + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + std::cout << "my_machine::State3ToState4" << std::endl; + } + }; + // guard conditions + struct always_true + { + template <class EVT,class FSM,class SourceState,class TargetState> + bool operator()(EVT const& evt,FSM& fsm,SourceState& src,TargetState& tgt) + { + std::cout << "always_true" << std::endl; + return true; + } + }; + struct always_false + { + template <class EVT,class FSM,class SourceState,class TargetState> + bool operator()(EVT const& evt,FSM& fsm,SourceState& src,TargetState& tgt) + { + std::cout << "always_false" << std::endl; + return true; + } + }; + + typedef my_machine_ p; // makes transition table cleaner + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + Row < State1 , none , State2 >, + Row < State2 , none , State3 , State2ToState3 >, + Row < State3 , none , State4 , none , always_false >, + // +---------+-------------+---------+---------------------+----------------------+ + Row < State3 , none , State4 , State3ToState4 , always_true >, + Row < State4 , event1 , State1 > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + // Pick a back-end + typedef msm::back::state_machine<my_machine_> my_machine; + + // + // Testing utilities. + // + static char const* const state_names[] = { "State1", "State2", "State3", "State4" }; + void pstate(my_machine const& p) + { + std::cout << " -> " << state_names[p.current_state()[0]] << std::endl; + } + + void test() + { + my_machine p; + + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + // in this case it will also immediately trigger all anonymous transitions + p.start(); + // this event will bring us back to the initial state and thus, a new "loop" will be started + p.process_event(event1()); + + } +} + +int main() +{ + test(); + return 0; +} diff --git a/libs/msm/doc/HTML/examples/BoostCon09Full.cpp b/libs/msm/doc/HTML/examples/BoostCon09Full.cpp new file mode 100644 index 0000000000..f4a2d7b047 --- /dev/null +++ b/libs/msm/doc/HTML/examples/BoostCon09Full.cpp @@ -0,0 +1,370 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <vector> +#include <iostream> +#include <boost/mpl/vector/vector50.hpp> +#include <boost/msm/back/state_machine.hpp> +#include <boost/msm/front/state_machine_def.hpp> +#include <boost/msm/back/tools.hpp> + +namespace msm = boost::msm; +namespace mpl = boost::mpl; + +namespace // Concrete FSM implementation +{ + // events + struct play {}; + struct end_pause {}; + struct stop {}; + struct pause {}; + struct open_close {}; + struct NextSong {}; + // event which every other event can convert to + struct AllSongsPlayed + { + template <class Event> + AllSongsPlayed(Event const&){} + }; + struct PreviousSong {}; + struct error_found {}; + struct end_error {}; + + // Flags. Allow information about a property of the current state + struct CDLoaded {}; + struct FirstSongPlaying {}; + + // A "complicated" event type that carries some data. + enum DiskTypeEnum + { + DISK_CD=0, + DISK_DVD=1 + }; + struct cd_detected + { + cd_detected(DiskTypeEnum diskType): disc_type(diskType) {} + DiskTypeEnum disc_type; + }; + + // Concrete FSM implementation + struct player_ : public msm::front::state_machine_def<player_> + { + // The list of FSM states + struct Empty : public msm::front::state<> + { + typedef mpl::vector<play> deferred_events; + // every (optional) entry/exit methods get the event packed as boost::any. Not useful very often. + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Empty" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Empty" << std::endl;} + }; + struct Open : public msm::front::state<> + { + typedef mpl::vector1<CDLoaded> flag_list; + typedef mpl::vector<play> deferred_events; + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Open" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Open" << std::endl;} + }; + // a state needing a pointer to the containing state machine + // and using for this the non-default policy + // if policy used, set_sm_ptr is needed + struct Stopped : public msm::front::state<default_base_state,msm::front::sm_ptr> + { + // when stopped, the CD is loaded + typedef mpl::vector1<CDLoaded> flag_list; + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Stopped" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Stopped" << std::endl;} + void set_sm_ptr(player_* pl){m_player=pl;} + player_* m_player; + }; + // the player state machine contains a state which is himself a state machine + // it demonstrates Shallow History: if the state gets activated with end_pause + // then it will remember the last active state and reactivate it + // also possible: AlwaysHistory, the last active state will always be reactivated + // or NoHistory, always restart from the initial state + struct Playing_ : public msm::front::state_machine_def<Playing_> + { + // when playing, the CD is loaded and we are in either pause or playing (duh) + typedef mpl::vector<CDLoaded> flag_list; + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Playing" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Playing" << std::endl;} + + // The list of FSM states + struct Song1 : public msm::front::state<> + { + typedef mpl::vector1<FirstSongPlaying> flag_list; + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: First song" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: First Song" << std::endl;} + }; + struct Song2 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: Second song" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: Second Song" << std::endl;} + }; + struct Song3 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: Third song" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: Third Song" << std::endl;} + }; + struct CDFinished : public msm::front::exit_pseudo_state<AllSongsPlayed> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Playing::CDFinished" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Playing::CDFinished" << std::endl;} + }; + // the initial state. Must be defined + typedef Song1 initial_state; + // transition actions + void start_next_song(NextSong const&) { std::cout << "Playing::start_next_song\n"; } + void start_prev_song(PreviousSong const&) { std::cout << "Playing::start_prev_song\n"; } + void all_songs_played(NextSong const&) { std::cout << "Playing::all_songs_played\n"; } + // guard conditions + + typedef Playing_ pl; // makes transition table cleaner + // Transition table for Playing + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+---------------+------------+---------------------+----------------------+ + a_row < Song1 , NextSong , Song2 , &pl::start_next_song >, + a_row < Song2 , PreviousSong, Song1 , &pl::start_prev_song >, + a_row < Song2 , NextSong , Song3 , &pl::start_next_song >, + a_row < Song3 , PreviousSong, Song2 , &pl::start_prev_song >, + a_row < Song3 , NextSong , CDFinished , &pl::all_songs_played > + // +---------+---------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + typedef msm::back::state_machine<Playing_,msm::back::ShallowHistory<mpl::vector<end_pause> > > Playing; + + // the player state machine contains a state which is himself a state machine (2 of them, Playing and Paused) + struct Paused : public msm::front::state<> + { + typedef mpl::vector<CDLoaded> flag_list; + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Paused" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Paused" << std::endl;} + }; + + struct AllOk : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: AllOk" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: AllOk" << std::endl;} + }; + struct ErrorMode : //public msm::front::terminate_state<> + public msm::front::interrupt_state<end_error> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: ErrorMode" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: ErrorMode" << std::endl;} + }; + + // the initial state of the player SM. Must be defined + typedef mpl::vector<Empty,AllOk> initial_state; + + // transition actions + void start_playback(play const&) { std::cout << "player::start_playback\n"; } + void open_drawer(open_close const&) { std::cout << "player::open_drawer\n"; } + void close_drawer(open_close const&) { std::cout << "player::close_drawer\n"; } + void store_cd_info(cd_detected const&) + { + std::cout << "player::store_cd_info\n"; + // generate another event to test the queue + //cd.m_player.process_event(play()); + } + void stop_playback(stop const&) { std::cout << "player::stop_playback\n"; } + void end_playback (AllSongsPlayed const&) { std::cout << "player::end_playback\n"; } + void pause_playback(pause const&) { std::cout << "player::pause_playback\n"; } + void resume_playback(end_pause const&) { std::cout << "player::resume_playback\n"; } + void stop_and_open(open_close const&) { std::cout << "player::stop_and_open\n"; } + void stopped_again(stop const&){std::cout << "player::stopped_again\n";} + void report_error(error_found const&) {std::cout << "player::report_error\n";} + void report_end_error(end_error const&) {std::cout << "player::report_end_error\n";} + // guard conditions + bool good_disk_format(cd_detected const& evt) + { + // to test a guard condition, let's say we understand only CDs, not DVD + if (evt.disc_type != DISK_CD) + { + std::cout << "wrong disk, sorry" << std::endl; + return false; + } + return true; + } + + typedef player_ p; // makes transition table cleaner + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +-------------+---------------+---------+---------------------+----------------------+ + a_row < Stopped , play , Playing , &p::start_playback >, + a_row < Stopped , open_close , Open , &p::open_drawer >, + a_row < Stopped , stop , Stopped , &p::stopped_again >, + // +-------------+---------------+---------+---------------------+----------------------+ + a_row < Open , open_close , Empty , &p::close_drawer >, + // +-------------+---------------+---------+---------------------+----------------------+ + a_row < Empty , open_close , Open , &p::open_drawer >, + row < Empty , cd_detected , Stopped , &p::store_cd_info ,&p::good_disk_format >, + // +-------------+---------------+---------+---------------------+----------------------+ + a_row < Playing , stop , Stopped , &p::stop_playback >, + a_row < Playing , pause , Paused , &p::pause_playback >, + a_row < Playing , open_close , Open , &p::stop_and_open >, + a_row < Playing::exit_pt< + Playing_::CDFinished> , AllSongsPlayed, Stopped , &p::end_playback >, + // +-------------+---------------+---------+---------------------+----------------------+ + a_row < Paused , end_pause , Playing , &p::resume_playback >, + a_row < Paused , stop , Stopped , &p::stop_playback >, + a_row < Paused , open_close , Open , &p::stop_and_open >, + // +-------------+---------------+---------+---------------------+----------------------+ + a_row < AllOk , error_found ,ErrorMode, &p::report_error >, + a_row < ErrorMode ,end_error ,AllOk , &p::report_end_error > + // +-------------+---------------+---------+---------------------+----------------------+ + > {}; + + + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + typedef msm::back::state_machine<player_> player; + + // + // Testing utilities. + // + + void pstate(player const& p) + { + typedef player::stt Stt; + typedef msm::back::generate_state_set<Stt>::type all_states; + static char const* state_names[mpl::size<all_states>::value]; + // fill the names of the states defined in the state machine + mpl::for_each<all_states,boost::msm::wrap<mpl::placeholders::_1> > + (msm::back::fill_state_names<Stt>(state_names)); + + for (unsigned int i=0;i<player::nr_regions::value;++i) + { + std::cout << " -> " << state_names[p.current_state()[i]] << std::endl; + } + } + + void test() + { + player p; + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + p.start(); + std::cout << "CDLoaded active:" << std::boolalpha << p.is_flag_active<CDLoaded>() << std::endl; //=> false (no CD yet) + + // test deferred event + // deferred in Empty and Open, will be handled only after event cd_detected + std::cout << "play is not handled in the current state but is marked as delayed" << std::endl; + p.process_event(play()); pstate(p); + std::cout << "cd_detected will cause play to be handled also" << std::endl; + // will be rejected, wrong disk type + p.process_event(cd_detected(DISK_DVD)); pstate(p); + // will be accepted, wrong disk type + p.process_event(cd_detected(DISK_CD)); pstate(p); + + std::cout << "FirstSong active:" << std::boolalpha << p.is_flag_active<FirstSongPlaying>() << std::endl; //=> true + p.process_event(NextSong());pstate(p); + // We are now in second song, Flag inactive + std::cout << "FirstSong active:" << std::boolalpha << p.is_flag_active<FirstSongPlaying>() << std::endl;//=> false + p.process_event(NextSong());pstate(p); + // 2nd song active + p.process_event(PreviousSong());pstate(p); + // Pause + p.process_event(pause()); pstate(p); + // go back to Playing + // but end_pause is an event activating the History + // => keep the last active State (SecondSong) + p.process_event(end_pause()); pstate(p); + // force an exit by listening all the songs + p.process_event(NextSong()); + p.process_event(NextSong());pstate(p); + + std::cout << "CDLoaded active:" << std::boolalpha << p.is_flag_active<CDLoaded>() << std::endl;//=> true + std::cout << "FirstSong active:" << std::boolalpha << p.is_flag_active<FirstSongPlaying>() << std::endl;//=> false + + // go back to Playing + // but play is not leading to Shallow History => do not remember the last active State (SecondSong) + // and activate again FirstSong and LightOn + p.process_event(play()); pstate(p); + p.process_event(error_found()); pstate(p); + + // try generating more events + std::cout << "Trying to generate another event" << std::endl; // will not work, fsm is terminated or interrupted + p.process_event(NextSong());pstate(p); + + std::cout << "Trying to end the error" << std::endl; // will work only if ErrorMode is interrupt state + p.process_event(end_error());pstate(p); + std::cout << "Trying to generate another event" << std::endl; // will work only if ErrorMode is interrupt state + p.process_event(NextSong());pstate(p); + + // the states and events of the higher level FSM (player) + typedef player::stt Stt; + typedef msm::back::generate_state_set<Stt>::type simple_states; + + std::cout << "the state list:" << std::endl; + mpl::for_each<simple_states,boost::msm::wrap<mpl::placeholders::_1> >(msm::back::display_type ()); + + std::cout << "the event list:" << std::endl; + typedef msm::back::generate_event_set<Stt>::type event_list; + mpl::for_each<event_list,boost::msm::wrap<mpl::placeholders::_1> >(msm::back::display_type ()); + std::cout << std::endl; + + // the states and events recursively searched + typedef msm::back::recursive_get_transition_table<player>::type recursive_stt; + + std::cout << "the state list (including sub-SMs):" << std::endl; + + typedef msm::back::generate_state_set<recursive_stt>::type all_states; + mpl::for_each<all_states,boost::msm::wrap<mpl::placeholders::_1> >(msm::back::display_type ()); + + std::cout << "the event list (including sub-SMs):" << std::endl; + typedef msm::back::generate_event_set<recursive_stt>::type all_events; + mpl::for_each<all_events,boost::msm::wrap<mpl::placeholders::_1> >(msm::back::display_type ()); + + } +} + +int main() +{ + test(); + return 0; +} + + diff --git a/libs/msm/doc/HTML/examples/CompilerStressTestEuml.cpp b/libs/msm/doc/HTML/examples/CompilerStressTestEuml.cpp new file mode 100644 index 0000000000..7bde7f5487 --- /dev/null +++ b/libs/msm/doc/HTML/examples/CompilerStressTestEuml.cpp @@ -0,0 +1,446 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <vector> +#include <list> +#include <set> +#include <map> +#include <iostream> + +// we need more than the default 10 states +#define FUSION_MAX_VECTOR_SIZE 15 + +#include <boost/msm/back/state_machine.hpp> +#include <boost/msm/front/euml/euml.hpp> +#include <boost/msm/front/euml/stl.hpp> + +using namespace std; +using namespace boost::msm::front::euml; +namespace msm = boost::msm; + +// how long the timer will ring when countdown elapsed. +#define RINGING_TIME 5 + +namespace // Concrete FSM implementation +{ + // flag + BOOST_MSM_EUML_FLAG(SomeFlag) + + // declares attributes with type and name. Can be used anywhere after + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(std::string,m_song) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(int,m_song_id) + + // declare that a type inheriting from OneSongDef will get these 2 attributes + BOOST_MSM_EUML_ATTRIBUTES((attributes_ << m_song << m_song_id ), OneSongDef) + // events + // this event is done "manually", not using any predefined macro + struct OneSong_impl : euml_event<OneSong_impl>,OneSongDef + { + OneSong_impl(){} + OneSong_impl(const string& asong) + { + get_attribute(m_song)=asong; + get_attribute(m_song_id)=1; + } + OneSong_impl(const OneSong_impl& asong) + { + get_attribute(m_song)=asong.get_attribute(m_song); + get_attribute(m_song_id)=1; + } + const string& get_data() const {return get_attribute(m_song);} + }; + // declare an instance for use in the transition table + OneSong_impl const OneSong; + + struct SongComparator : euml_action<SongComparator> + { + bool operator()(const OneSong_impl& lhs,const OneSong_impl& rhs)const + { + return lhs.get_data() == rhs.get_data(); + } + }; + struct SongLessComparator : euml_action<SongLessComparator> + { + bool operator()(const OneSong_impl& lhs,const OneSong_impl& rhs)const + { + return lhs.get_data() < rhs.get_data(); + } + }; + struct Comparator + { + template <class T> + bool operator()(const T& lhs,const T& rhs)const + { + return lhs < rhs; + } + }; + struct RemoveDummy + { + bool operator()(const OneSong_impl& lhs)const + { + return (lhs.get_attribute(m_song).compare(std::string("She-Dummy. Remove this one"))==0 ); + } + }; + template <int val> + struct LookFor + { + template <class T> + bool operator()(const T& lhs)const + { + return lhs == val; + } + }; + template <int val> + struct LessThan + { + template <class T> + bool operator()(const T& lhs)const + { + return lhs < val; + } + }; + BOOST_MSM_EUML_ACTION(SongDeleter) + { + bool operator()(const OneSong_impl& lhs)const + { + return lhs.get_data() == "Twist and Shout"; + } + }; + struct Generator + { + int operator()()const + { + return 1; + } + }; + struct Print + { + template <class T> + void operator()(const T& lhs)const + { + std::cout << "Song:" << lhs.get_data() << endl; + } + }; + BOOST_MSM_EUML_ATTRIBUTES((attributes_ << m_song ), NotFoundDef) + // declare an event instance called NotFound with the defined attributes + // these attributes can then be referenced anywhere (stt, state behaviors) + BOOST_MSM_EUML_EVENT_WITH_ATTRIBUTES(NotFound,NotFoundDef) + + BOOST_MSM_EUML_ATTRIBUTES((attributes_ << m_song ), FoundDef) + struct Found_impl : euml_event<Found_impl>,FoundDef + { + Found_impl(){} + Found_impl (const string& data) + { + get_attribute(m_song)=data; + } + int foo()const {std::cout << "foo()" << std::endl; return 0;} + int foo(int i)const {std::cout << "foo(int):" << i << std::endl; return 1;} + int foo(int i,int j)const {std::cout << "foo(int,int):" << i <<"," << j << std::endl; return 2;} + + }; + Found_impl const Found; + // some functions to call + // this macro creates a functor and an eUML function wrapper. Now, foo_ can be used anywhere + BOOST_MSM_EUML_METHOD(FoundFoo_ , foo , foo_ , int , int ) + + template <class T> + int do_print(T& t ) {std::cout << "print(T):" << typeid(T).name() << std::endl;return 1;} + BOOST_MSM_EUML_FUNCTION(PrintState_ , do_print , print_ , int , int ) + + BOOST_MSM_EUML_EVENT(Done) + + // Concrete FSM implementation + struct some_base + { + int foobar()const {std::cout << "foobar()" << std::endl; return 0;} + int foobar(int i)const {std::cout << "foobar(int):" << i << std::endl; return 1;} + int foobar(int i,int j)const {std::cout << "foobar(int,int):" << i <<"," << j << std::endl; return 2;} + }; + // some functions to call + BOOST_MSM_EUML_METHOD(FooBar_ , foobar , foobar_ , int , int ) + + // fsm attributes + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(vector<OneSong_impl>,m_src_container) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(list<OneSong_impl>,m_tgt_container) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(list<int>,m_var2) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(list<int>,m_var3) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(set<int>,m_var4) + typedef std::map<int,int> int_int_map; + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(int_int_map,m_var5) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(std::string,m_var6) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(std::string,m_var7) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(vector<int>,m_var8) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(vector<int>,m_var9) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(int,m_var10) + + // The list of FSM states + BOOST_MSM_EUML_STATE(( ( insert_(fsm_(m_tgt_container),end_(fsm_(m_tgt_container)), + append_(event_(m_song),fsm_(m_var7)) ),//foo_(event_,Int_<0>()) , + //foo_(event_,Int_<0>(),Int_<1>()),print_(state_), + process_(Done/*,fsm_*/),if_then_(true_,true_) ), + no_action + ),Insert) + + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(std::string,m_letters) + BOOST_MSM_EUML_STATE(( if_then_else_( (string_find_(event_(m_song),state_(m_letters),Size_t_<0>()) != Npos_<string>()&& + string_find_(event_(m_song),Char_<'S'>(),Size_t_<0>()) != Npos_<string>()&& + string_find_first_of_(event_(m_song),Char_<'S'>()) == Size_t_<0>() && + string_compare_(event_(m_song),Int_<0>(),size_(event_(m_song)),event_(m_song)) == Int_<0>() + //&& is_flag_(SomeFlag(),fsm_()) + //&& ( event_(m_song_id) == Int_<1>()) + //&& string_find_(event_(m_song),String_<mpl::string<'Sh','e'> >()) + // != Npos_<string>() + + ), + process2_(Found, + //string_insert_(event_(m_song),Size_t_<0>(),fsm_(m_var6)) ), + string_replace_( + string_assign_( + string_erase_( + string_insert_( + substr_(event_(m_song),Size_t_<1>()), + Size_t_<0>(), + Size_t_<1>(), + Char_<'S'>()), + Size_t_<0>(), + Size_t_<1>() ), + event_(m_song) ), + Size_t_<0>(), + Size_t_<1>(), + c_str_(fsm_(m_var6)) + /*Size_t_<1>(), + Char_<'s'>()*/ ) ), + process2_(NotFound,event_(m_song),fsm_) ) , + no_action, + attributes_ << m_letters, + configure_<< SomeFlag ),StringFind) + + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(vector<OneSong_impl>::iterator,m_src_it) + BOOST_MSM_EUML_STATE(( if_then_( (state_(m_src_it) != end_(fsm_(m_src_container)) && + //associative_find_(fsm_(m_var4),Int_<9>()) != end_(fsm_(m_var4))&& + //associative_count_(fsm_(m_var4),Int_<9>()) == Size_t_<1>() && + //*associative_upper_bound_(fsm_(m_var4),Int_<8>()) == Int_<9>()&& + //*associative_lower_bound_(fsm_(m_var4),Int_<9>()) == Int_<9>() && + //second_(associative_equal_range_(fsm_(m_var4),Int_<8>())) == associative_upper_bound_(fsm_(m_var4),Int_<8>()) && + //first_(associative_equal_range_(fsm_(m_var4),Int_<8>())) == associative_lower_bound_(fsm_(m_var4),Int_<8>())&& + //second_(*associative_lower_bound_(fsm_(m_var5),Int_<0>())) == Int_<0>() && //map => pair as return + //find_if_(begin_(fsm_(m_var4)),end_(fsm_(m_var4)),Predicate_<LookFor<8> >()) != end_(fsm_(m_var4))&& + //*lower_bound_(begin_(fsm_(m_var4)),end_(fsm_(m_var4)),Int_<9>(),Predicate_<std::less<int> >()) == Int_<9>()&& + //*upper_bound_(begin_(fsm_(m_var4)),end_(fsm_(m_var4)),Int_<8>(),Predicate_<std::less<int> >()) == Int_<9>() && + //second_(equal_range_(begin_(fsm_(m_var4)),end_(fsm_(m_var4)),Int_<8>())) + // == upper_bound_(begin_(fsm_(m_var4)),end_(fsm_(m_var4)),Int_<8>()) && + //first_(equal_range_(begin_(fsm_(m_var4)),end_(fsm_(m_var4)),Int_<9>(),Predicate_<std::less<int> >())) + // == lower_bound_(begin_(fsm_(m_var4)),end_(fsm_(m_var4)),Int_<9>(),Predicate_<std::less<int> >())&& + //binary_search_(begin_(fsm_(m_var4)),end_(fsm_(m_var4)),Int_<9>(),Predicate_<std::less<int> >())&& + //binary_search_(begin_(fsm_(m_var4)),end_(fsm_(m_var4)),Int_<9>())&& + //count_(begin_(fsm_(m_var4)),end_(fsm_(m_var4)),Int_<9>()) == Int_<1>()&& + //count_if_(begin_(fsm_(m_var4)),end_(fsm_(m_var4)),Predicate_<LookFor<9> >()) == Int_<1>()&& + //distance_(begin_(fsm_(m_var4)),end_(fsm_(m_var4))) == Int_<2>()&& + //*min_element_(begin_(fsm_(m_var4)),end_(fsm_(m_var4)),Predicate_<std::less<int> >()) == Int_<8>()&& + //*max_element_(begin_(fsm_(m_var4)),end_(fsm_(m_var4)),Predicate_<std::less<int> >()) == Int_<9>()&& + //adjacent_find_(begin_(fsm_(m_var4)),end_(fsm_(m_var4))) == end_(fsm_(m_var4))&& + //*find_end_(begin_(fsm_(m_var8)),end_(fsm_(m_var8)),begin_(fsm_(m_var9)),end_(fsm_(m_var9))) + // == Int_<1>()&& + //*find_first_of_(begin_(fsm_(m_var8)),end_(fsm_(m_var8)),begin_(fsm_(m_var9)),end_(fsm_(m_var9))) + // == Int_<1>()&& + //equal_(begin_(fsm_(m_var9)),end_(fsm_(m_var9)),begin_(fsm_(m_var8)))&& + //*search_(begin_(fsm_(m_var8)),end_(fsm_(m_var8)),begin_(fsm_(m_var9)),end_(fsm_(m_var9))) + // == Int_<1>()&& + //includes_(begin_(fsm_(m_var8)),end_(fsm_(m_var8)),begin_(fsm_(m_var9)),end_(fsm_(m_var9)))&& + //!lexicographical_compare_(begin_(fsm_(m_var8)),end_(fsm_(m_var8)), + // begin_(fsm_(m_var9)),end_(fsm_(m_var9)))&& + //first_(mismatch_(begin_(fsm_(m_var9)),end_(fsm_(m_var9)),begin_(fsm_(m_var8)))) + // == end_(fsm_(m_var9)) && + accumulate_(begin_(fsm_(m_var9)),end_(fsm_(m_var9)),Int_<1>(), + Predicate_<std::plus<int> >()) == Int_<1>() + ), + (process2_(OneSong,*(state_(m_src_it)++))/*,foobar_(fsm_,Int_<0>())*/ ) ), + no_action, + attributes_ << m_src_it + , configure_<< SomeFlag ),Foreach) + + + // replaces the old transition table + BOOST_MSM_EUML_TRANSITION_TABLE(( + StringFind == Foreach + OneSong[if_then_else_(true_,true_,true_)], + Insert == StringFind + Found / (if_then_(true_,no_action)), + Foreach == StringFind + NotFound , + Foreach == Insert + Done + // +------------------------------------------------------------------------------+ + ),transition_table ) + + BOOST_MSM_EUML_ACTION(Log_No_Transition) + { + template <class FSM,class Event> + void operator()(Event const& e,FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + // create a state machine "on the fly" + BOOST_MSM_EUML_DECLARE_STATE_MACHINE(( transition_table, //STT + init_ << Foreach, // Init + //insert_(state_(m_var4),begin_(state_(m_var2)),end_(state_(m_var2))), + (insert_(state_(m_var4),Int_<5>()),insert_(state_(m_var4),Int_<6>()),insert_(state_(m_var4),Int_<7>()), + insert_(state_(m_var4),Int_<8>()),insert_(state_(m_var4),Int_<9>()), + associative_erase_(state_(m_var4),Int_<6>()),associative_erase_(state_(m_var4),begin_(state_(m_var4))), + associative_erase_(state_(m_var4),begin_(state_(m_var4)),++begin_(state_(m_var4))), + insert_(state_(m_var2),begin_(state_(m_var2)),begin_(state_(m_var3)),end_(state_(m_var3))), + state_(m_var5)[Int_<0>()]=Int_<0>(),state_(m_var5)[Int_<1>()]=Int_<1>() + ,attribute_(substate_(Foreach,fsm_),m_src_it) + = begin_(fsm_(m_src_container)) + //,fill_(begin_(state_(m_var9)),end_(state_(m_var9)),Int_<0>()) + //,fill_n_(begin_(state_(m_var9)),Size_t_<2>(),Int_<0>()) + //,transform_(begin_(state_(m_var4)),end_(state_(m_var4)),begin_(state_(m_var2)),begin_(state_(m_var4)), + // Predicate_<std::plus<int> >()) + //,process_(Done,fsm_(),fsm_) + //,process_(Done,fsm_) + //,fsm_ + //,foobar_(state_,Int_<0>(),Int_<1>()) + //,nth_element_(begin_(state_(m_var9)),++begin_(state_(m_var9)),end_(state_(m_var9)),Predicate_<std::less<int> >()) + //,partial_sort_(begin_(state_(m_var9)),end_(state_(m_var9)),end_(state_(m_var9)),Predicate_<std::less<int> >()) + //,partial_sort_copy_(begin_(state_(m_var9)),end_(state_(m_var9)),begin_(state_(m_var9)),end_(state_(m_var9)),Predicate_<std::less<int> >()) + //,list_sort_(state_(m_var2)) + //,sort_(begin_(state_(m_var9)),end_(state_(m_var9)),Predicate_<std::less<int> >()) + //,inner_product_(begin_(state_(m_var9)),end_(state_(m_var9)),begin_(state_(m_var9)),Int_<1>()) + //,replace_copy_(begin_(state_(m_var4)),end_(state_(m_var4)),begin_(state_(m_var4)),Int_<8>(),Int_<7>()) + //,replace_copy_if_(begin_(state_(m_var4)),end_(state_(m_var4)),begin_(state_(m_var4)),Predicate_<LookFor<9> >(),Int_<8>()) + //,replace_(begin_(state_(m_var4)),end_(state_(m_var4)),Int_<8>(),Int_<7>()) + //,replace_if_(begin_(state_(m_var4)),end_(state_(m_var4)),Predicate_<LookFor<9> >(),Int_<8>()) + //,adjacent_difference_(begin_(state_(m_var9)),end_(state_(m_var9)),begin_(state_(m_var9))) + //,partial_sum_(begin_(state_(m_var9)),end_(state_(m_var9)),begin_(state_(m_var9))) + //,inner_product_(begin_(state_(m_var9)),end_(state_(m_var9)),begin_(state_(m_var9)),Int_<1>()) + //,next_permutation_(begin_(state_(m_var9)),end_(state_(m_var9)),Predicate_<std::less<int> >()) + //,prev_permutation_(begin_(state_(m_var9)),end_(state_(m_var9)),Predicate_<std::less<int> >()) + //,set_union_(begin_(state_(m_var9)),end_(state_(m_var9)),begin_(state_(m_var9)),end_(state_(m_var9)),begin_(state_(m_var9))) + //,inplace_merge_(begin_(state_(m_var9)),end_(state_(m_var9)),end_(state_(m_var9)),Predicate_<std::less<int> >()) + //,merge_(begin_(state_(m_var9)),end_(state_(m_var9)),begin_(state_(m_var9)),end_(state_(m_var9)) + // ,begin_(state_(m_var9)),Predicate_<std::less<int> >()) + //,stable_sort_(begin_(state_(m_var9)),end_(state_(m_var9)),Predicate_<std::less<int> >()) + //,partition_(begin_(state_(m_var2)),end_(state_(m_var2)),Predicate_<LessThan<3> >()) + //,stable_partition_(begin_(state_(m_var2)),end_(state_(m_var2)),Predicate_<LessThan<3> >()) + //,rotate_copy_(begin_(state_(m_var2)),++begin_(state_(m_var2)),end_(state_(m_var2)),begin_(state_(m_var2))) + //,rotate_(begin_(state_(m_var2)),++begin_(state_(m_var2)),end_(state_(m_var2))) + //,unique_(begin_(state_(m_var2)),end_(state_(m_var2))) + //,unique_copy_(begin_(state_(m_var2)),end_(state_(m_var2)),begin_(state_(m_var2))) + //,random_shuffle_(begin_(state_(m_var9)),end_(state_(m_var9))) + //,generate_(begin_(state_(m_var9)),end_(state_(m_var9)),Predicate_<Generator>()) + //,generate_n_(begin_(state_(m_var9)),Int_<2>(),Predicate_<Generator>()) + //,reverse_copy_(begin_(state_(m_var2)),end_(state_(m_var2)),begin_(state_(m_var2))) + //erase_(state_(m_src_container), + // remove_if_(begin_(state_(m_src_container)),end_(state_(m_src_container)), + // Predicate_<RemoveDummy>()), + // end_(state_(m_src_container))), + //list_remove_(state_(m_var2),Int_<3>()), + //remove_copy_if_(begin_(state_(m_var9)),end_(state_(m_var9)),back_inserter_(state_(m_var2)), + // Predicate_<LookFor<2> >() ) + //for_each_(begin_(state_(m_src_container)),end_(state_m_src_container()), + // Predicate_<Print>() ), + //copy_(begin_(state_(m_var9)),end_(state_(m_var9)),inserter_(state_(m_var2),end_(state_(m_var2)))), + //reverse_(begin_(state_(m_var2)),end_(state_(m_var2))) + ), + //no_action, // Entry + //splice_(state_(m_var2),begin_(state_(m_var2)),state_(m_var3),begin_(state_(m_var3)),end_(state_(m_var3))), + //(list_remove_(state_(m_var2),Int_<3>()),list_merge_(state_(m_var2),state_(m_var3),Comparator())),//no_action, // Entry + no_action, // Exit + attributes_ << m_src_container // song list + << m_tgt_container // result + << m_var2 + << m_var3 + << m_var4 + << m_var5 + << m_var6 + << m_var7 + << m_var8 + << m_var9 + << m_var10, + configure_<< no_configure_, + Log_No_Transition + ),iPodSearch_helper) + + struct iPodSearch_ : public iPodSearch_helper, public some_base + { + }; + + + // choice of back-end + typedef msm::back::state_machine<iPodSearch_> iPodSearch; + + void test() + { + iPodSearch search; + // fill our song list + //search.get_attribute<m_src_container>().push_back(OneSong("She-Dummy. Remove this one")); + search.get_attribute(m_src_container).push_back(OneSong_impl("Let it be")); + search.get_attribute(m_src_container).push_back(OneSong_impl("Yellow submarine")); + search.get_attribute(m_src_container).push_back(OneSong_impl("Twist and Shout")); + search.get_attribute(m_src_container).push_back(OneSong_impl("She Loves You")); + + search.get_attribute(m_var2).push_back(1); + search.get_attribute(m_var2).push_back(3); + search.get_attribute(m_var2).push_back(4); + + search.get_attribute(m_var3).push_back(2); + search.get_attribute(m_var3).push_back(4); + + search.get_attribute(m_var6) = "S"; + search.get_attribute(m_var7) = "- Some text"; + + search.get_attribute(m_var8).push_back(1); + search.get_attribute(m_var8).push_back(2); + search.get_attribute(m_var8).push_back(3); + search.get_attribute(m_var8).push_back(4); + + search.get_attribute(m_var9).push_back(1); + search.get_attribute(m_var9).push_back(2); + + + // look for "She Loves You" using the first letters + // BOOST_MSM_EUML_STATE_NAME returns the name of the event type of which StringFind is an instance + search.get_state<BOOST_MSM_EUML_STATE_NAME(StringFind)&>().get_attribute(m_letters)="Sh"; + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + search.start(); + // display all the songs + for (list<OneSong_impl>::const_iterator it = search.get_attribute(m_tgt_container).begin(); + it != search.get_attribute(m_tgt_container).end();++it) + { + cout << "candidate song:" << (*it).get_data() << endl; + } + for (list<int>::const_iterator iti = search.get_attribute(m_var2).begin(); + iti != search.get_attribute(m_var2).end();++iti) + { + cout << "int in attribute m_var2:" << (*iti) << endl; + } + for (set<int>::const_iterator its = search.get_attribute(m_var4).begin(); + its != search.get_attribute(m_var4).end();++its) + { + cout << "int in attribute m_var4:" << (*its) << endl; + } + cout << "search using more letters" << endl; + // look for "She Loves You" using more letters + search.get_state<BOOST_MSM_EUML_STATE_NAME(StringFind)&>().get_attribute(m_letters)="She"; + search.get_attribute(m_tgt_container).clear(); + search.start(); + // display all the songs + for (list<OneSong_impl>::const_iterator it = search.get_attribute(m_tgt_container).begin(); + it != search.get_attribute(m_tgt_container).end();++it) + { + cout << "candidate song:" << (*it).get_data() << endl; + } + } +} + +int main() +{ + test(); + return 0; +} diff --git a/libs/msm/doc/HTML/examples/CompositeTutorial.cpp b/libs/msm/doc/HTML/examples/CompositeTutorial.cpp new file mode 100644 index 0000000000..b0ee192eec --- /dev/null +++ b/libs/msm/doc/HTML/examples/CompositeTutorial.cpp @@ -0,0 +1,244 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> + +namespace msm = boost::msm; +namespace mpl = boost::mpl; + +namespace // Concrete FSM implementation +{ + // events + struct play {}; + struct end_pause {}; + struct stop {}; + struct pause {}; + struct open_close {}; + struct NextSong {}; + struct PreviousSong {}; + + // A "complicated" event type that carries some data. + struct cd_detected + { + cd_detected(std::string name) + : name(name) + {} + + std::string name; + }; + + // front-end: define the FSM structure + struct player_ : public msm::front::state_machine_def<player_> + { + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) {std::cout << "entering: Player" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Player" << std::endl;} + + // The list of FSM states + struct Empty : public msm::front::state<> + { + // every (optional) entry/exit methods get the event passed. + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Empty" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Empty" << std::endl;} + }; + struct Open : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Open" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Open" << std::endl;} + }; + + struct Stopped : public msm::front::state<> + { + // when stopped, the CD is loaded + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Stopped" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Stopped" << std::endl;} + }; + + // the player state machine contains a state which is himself a state machine + // as you see, no need to declare it anywhere so Playing can be developed separately + // by another team in another module. For simplicity I just declare it inside player + struct Playing_ : public msm::front::state_machine_def<Playing_> + { + // when playing, the CD is loaded and we are in either pause or playing (duh) + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Playing" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Playing" << std::endl;} + + // The list of FSM states + struct Song1 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: First song" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: First Song" << std::endl;} + + }; + struct Song2 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: Second song" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: Second Song" << std::endl;} + }; + struct Song3 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: Third song" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: Third Song" << std::endl;} + }; + // the initial state. Must be defined + typedef Song1 initial_state; + // transition actions + void start_next_song(NextSong const&) { std::cout << "Playing::start_next_song\n"; } + void start_prev_song(PreviousSong const&) { std::cout << "Playing::start_prev_song\n"; } + // guard conditions + + typedef Playing_ pl; // makes transition table cleaner + // Transition table for Playing + struct transition_table : mpl::vector4< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Song1 , NextSong , Song2 , &pl::start_next_song >, + a_row < Song2 , PreviousSong, Song1 , &pl::start_prev_song >, + a_row < Song2 , NextSong , Song3 , &pl::start_next_song >, + a_row < Song3 , PreviousSong, Song2 , &pl::start_prev_song > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + // back-end + typedef msm::back::state_machine<Playing_> Playing; + + + // state not defining any entry or exit + struct Paused : public msm::front::state<> + { + }; + + // the initial state of the player SM. Must be defined + typedef Empty initial_state; + + // transition actions + void start_playback(play const&) { std::cout << "player::start_playback\n"; } + void open_drawer(open_close const&) { std::cout << "player::open_drawer\n"; } + void close_drawer(open_close const&) { std::cout << "player::close_drawer\n"; } + void store_cd_info(cd_detected const& cd) {std::cout << "player::store_cd_info\n";} + void stop_playback(stop const&) { std::cout << "player::stop_playback\n"; } + void pause_playback(pause const&) { std::cout << "player::pause_playback\n"; } + void resume_playback(end_pause const&) { std::cout << "player::resume_playback\n"; } + void stop_and_open(open_close const&) { std::cout << "player::stop_and_open\n"; } + void stopped_again(stop const&){std::cout << "player::stopped_again\n";} + // guard conditions + + typedef player_ p; // makes transition table cleaner + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Stopped , play , Playing , &p::start_playback >, + a_row < Stopped , open_close , Open , &p::open_drawer >, + a_row < Stopped , stop , Stopped , &p::stopped_again >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Open , open_close , Empty , &p::close_drawer >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Empty , open_close , Open , &p::open_drawer >, + a_row < Empty , cd_detected , Stopped , &p::store_cd_info >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Playing , stop , Stopped , &p::stop_playback >, + a_row < Playing , pause , Paused , &p::pause_playback >, + a_row < Playing , open_close , Open , &p::stop_and_open >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Paused , end_pause , Playing , &p::resume_playback >, + a_row < Paused , stop , Stopped , &p::stop_playback >, + a_row < Paused , open_close , Open , &p::stop_and_open > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + + }; + // Pick a back-end + typedef msm::back::state_machine<player_> player; + + // + // Testing utilities. + // + static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" }; + + void pstate(player const& p) + { + std::cout << " -> " << state_names[p.current_state()[0]] << std::endl; + } + + void test() + { + player p; + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + p.start(); + // go to Open, call on_exit on Empty, then action, then on_entry on Open + p.process_event(open_close()); pstate(p); + p.process_event(open_close()); pstate(p); + p.process_event(cd_detected("louie, louie")); + p.process_event(play()); + + // at this point, Play is active + // make transition happen inside it. Player has no idea about this event but it's ok. + p.process_event(NextSong());pstate(p); //2nd song active + p.process_event(NextSong());pstate(p);//3rd song active + p.process_event(PreviousSong());pstate(p);//2nd song active + + p.process_event(pause()); pstate(p); + // go back to Playing + // as you see, it starts back from the original state + p.process_event(end_pause()); pstate(p); + p.process_event(pause()); pstate(p); + p.process_event(stop()); pstate(p); + // event leading to the same state + p.process_event(stop()); pstate(p); + // stop the fsm (call on_exit's, including the submachines) + p.process_event(play()); + std::cout << "stop fsm" << std::endl; + p.stop(); + std::cout << "restart fsm" << std::endl; + p.start(); + } +} + +int main() +{ + test(); + return 0; +} diff --git a/libs/msm/doc/HTML/examples/CompositeTutorialEuml.cpp b/libs/msm/doc/HTML/examples/CompositeTutorialEuml.cpp new file mode 100644 index 0000000000..516d3dbd50 --- /dev/null +++ b/libs/msm/doc/HTML/examples/CompositeTutorialEuml.cpp @@ -0,0 +1,182 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <vector> +#include <iostream> + +#include <boost/msm/back/state_machine.hpp> +#include <boost/msm/front/euml/euml.hpp> + +using namespace std; +using namespace boost::msm::front::euml; +namespace msm = boost::msm; + +// entry/exit/action/guard logging functors +#include "logging_functors.h" + +namespace // Concrete FSM implementation +{ + // events + BOOST_MSM_EUML_EVENT(play) + BOOST_MSM_EUML_EVENT(end_pause) + BOOST_MSM_EUML_EVENT(stop) + BOOST_MSM_EUML_EVENT(pause) + BOOST_MSM_EUML_EVENT(open_close) + BOOST_MSM_EUML_EVENT(next_song) + BOOST_MSM_EUML_EVENT(previous_song) + BOOST_MSM_EUML_EVENT(region2_evt) + + // A "complicated" event type that carries some data. + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(std::string,cd_name) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(DiskTypeEnum,cd_type) + BOOST_MSM_EUML_ATTRIBUTES((attributes_ << cd_name << cd_type ), cd_detected_attributes) + BOOST_MSM_EUML_EVENT_WITH_ATTRIBUTES(cd_detected,cd_detected_attributes) + + // Concrete FSM implementation + + // The list of FSM states + // state not defining any entry or exit + BOOST_MSM_EUML_STATE((),Paused) + BOOST_MSM_EUML_STATE(( Empty_Entry,Empty_Exit ),Empty) + BOOST_MSM_EUML_STATE(( Open_Entry,Open_Exit ),Open) + BOOST_MSM_EUML_STATE(( Stopped_Entry,Stopped_Exit ),Stopped) + + + // Playing is now a state machine itself. + + // It has 5 substates + BOOST_MSM_EUML_STATE(( Song1_Entry,Song1_Exit ),Song1) + BOOST_MSM_EUML_STATE(( Song2_Entry,Song2_Exit ),Song2) + BOOST_MSM_EUML_STATE(( Song3_Entry,Song3_Exit ),Song3) + BOOST_MSM_EUML_STATE(( Region2State1_Entry,Region2State1_Exit ),Region2State1) + BOOST_MSM_EUML_STATE(( Region2State2_Entry,Region2State2_Exit ),Region2State2) + + // Playing has a transition table + BOOST_MSM_EUML_TRANSITION_TABLE(( + // +------------------------------------------------------------------------------+ + Song2 == Song1 + next_song / start_next_song, + Song1 == Song2 + previous_song / start_prev_song, + Song3 == Song2 + next_song / start_next_song, + Song2 == Song3 + previous_song / start_prev_song, + Region2State2 == Region2State1 + region2_evt + // +------------------------------------------------------------------------------+ + ),playing_transition_table ) + + BOOST_MSM_EUML_DECLARE_STATE_MACHINE( (playing_transition_table, //STT + init_ << Song1 << Region2State1 // Init State + ),Playing_) + // choice of back-end + typedef msm::back::state_machine<Playing_> Playing_type; + Playing_type const Playing; + + // guard conditions + BOOST_MSM_EUML_ACTION(good_disk_format) + { + template <class FSM,class EVT,class SourceState,class TargetState> + bool operator()(EVT const& evt,FSM&,SourceState& ,TargetState& ) + { + // to test a guard condition, let's say we understand only CDs, not DVD + if (evt.get_attribute(cd_type)!=DISK_CD) + { + std::cout << "wrong disk, sorry" << std::endl; + // just for logging, does not block any transition + return true; + } + std::cout << "good disk" << std::endl; + return true; + } + }; + // replaces the old transition table + BOOST_MSM_EUML_TRANSITION_TABLE(( + Playing == Stopped + play / start_playback , + Playing == Paused + end_pause / resume_playback, + // +------------------------------------------------------------------------------+ + Empty == Open + open_close / close_drawer, + // +------------------------------------------------------------------------------+ + Open == Empty + open_close / open_drawer, + Open == Paused + open_close / stop_and_open, + Open == Stopped + open_close / open_drawer, + Open == Playing + open_close / stop_and_open, + // +------------------------------------------------------------------------------+ + Paused == Playing + pause / pause_playback, + // +------------------------------------------------------------------------------+ + Stopped == Playing + stop / stop_playback, + Stopped == Paused + stop / stop_playback, + Stopped == Empty + cd_detected [good_disk_format &&(event_(cd_type)==Int_<DISK_CD>())] + / (store_cd_info,process_(play)), + Stopped == Stopped + stop + // +------------------------------------------------------------------------------+ + ),transition_table) + + // create a state machine "on the fly" + BOOST_MSM_EUML_DECLARE_STATE_MACHINE(( transition_table, //STT + init_ << Empty, // Init State + no_action, // Entry + no_action, // Exit + attributes_ << no_attributes_, // Attributes + configure_ << no_configure_, // configuration + Log_No_Transition // no_transition handler + ), + player_) //fsm name + + // choice of back-end + typedef msm::back::state_machine<player_> player; + + // + // Testing utilities. + // + static char const* const state_names[] = { "Stopped", "Paused", "Open", "Empty", "Playing" }; + void pstate(player const& p) + { + std::cout << " -> " << state_names[p.current_state()[0]] << std::endl; + } + + void test() + { + player p; + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + p.start(); + // note that we write open_close and not open_close(), like usual. Both are possible with eUML, but + // you now have less to type. + // go to Open, call on_exit on Empty, then action, then on_entry on Open + p.process_event(open_close); pstate(p); + p.process_event(open_close); pstate(p); + // will be rejected, wrong disk type + p.process_event( + cd_detected("louie, louie",DISK_DVD)); pstate(p); + p.process_event( + cd_detected("louie, louie",DISK_CD)); pstate(p); + // no need to call play() as the previous event does it in its action method + //p.process_event(play); + + // make transition happen inside it. Player has no idea about this event but it's ok. + p.process_event(next_song);pstate(p); //2nd song active + p.process_event(next_song);pstate(p);//3rd song active + p.process_event(previous_song);pstate(p);//2nd song active + + // at this point, Play is active + p.process_event(pause); pstate(p); + + // go back to Playing + p.process_event(end_pause); pstate(p); + p.process_event(pause); pstate(p); + p.process_event(stop); pstate(p); + + // event leading to the same state + // no action method called as none is defined in the transition table + p.process_event(stop); pstate(p); + } +} + +int main() +{ + test(); + return 0; +} diff --git a/libs/msm/doc/HTML/examples/CompositeTutorialWithEumlTable.cpp b/libs/msm/doc/HTML/examples/CompositeTutorialWithEumlTable.cpp new file mode 100644 index 0000000000..7783d2cdf6 --- /dev/null +++ b/libs/msm/doc/HTML/examples/CompositeTutorialWithEumlTable.cpp @@ -0,0 +1,213 @@ +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> +#include <boost/msm/front/euml/euml.hpp> + +namespace msm = boost::msm; +namespace mpl = boost::mpl; +using namespace std; + +// entry/exit/action/guard logging functors +#include "logging_functors.h" + +namespace +{ + // events + struct play_impl : msm::front::euml::euml_event<play_impl> {}; + struct end_pause_impl : msm::front::euml::euml_event<end_pause_impl>{}; + struct stop_impl : msm::front::euml::euml_event<stop_impl>{}; + struct pause_impl : msm::front::euml::euml_event<pause_impl>{}; + struct open_close_impl : msm::front::euml::euml_event<open_close_impl>{}; + struct cd_detected_impl : msm::front::euml::euml_event<cd_detected_impl>{}; + struct next_song_impl : msm::front::euml::euml_event<next_song_impl>{}; + struct previous_song_impl : msm::front::euml::euml_event<previous_song_impl>{}; + + // define some dummy instances for use in the transition table + // it is also possible to default-construct them instead: + // struct play {}; + // inside the table: play() + play_impl play; + end_pause_impl end_pause; + stop_impl stop; + pause_impl pause; + open_close_impl open_close; + cd_detected_impl cd_detected; + next_song_impl next_song; + previous_song_impl previous_song; + + // The list of FSM states + // they have to be declared outside of the front-end only to make VC happy :( + // note: gcc would have no problem + struct Empty_impl : public msm::front::state<> , public msm::front::euml::euml_state<Empty_impl> + { + // every (optional) entry/exit methods get the event passed. + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Empty" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Empty" << std::endl;} + }; + struct Open_impl : public msm::front::state<> , public msm::front::euml::euml_state<Open_impl> + { + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) {std::cout << "entering: Open" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Open" << std::endl;} + }; + + struct Stopped_impl : public msm::front::state<> , public msm::front::euml::euml_state<Stopped_impl> + { + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) {std::cout << "entering: Stopped" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Stopped" << std::endl;} + }; + + // Playing Submachine front-end + struct Playing_ : public msm::front::state_machine_def<Playing_> + { + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) {std::cout << "entering: Playing" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Playing" << std::endl;} + + struct Song1_ : public msm::front::state<> , public msm::front::euml::euml_state<Song1_> + { + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) {std::cout << "entering: Song1" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Song1" << std::endl;} + }; + struct Song2_ : public msm::front::state<> , public msm::front::euml::euml_state<Song2_> + { + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) {std::cout << "entering: Song2" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Song2" << std::endl;} + }; + struct Song3_ : public msm::front::state<> , public msm::front::euml::euml_state<Song3_> + { + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) {std::cout << "entering: Song3" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Song3" << std::endl;} + }; + // to make the transition table more readable + // VC seems to need them static + static Song1_ Song1; + static Song2_ Song2; + static Song3_ Song3; + typedef Song1_ initial_state; + + // Playing has a transition table + BOOST_MSM_EUML_DECLARE_TRANSITION_TABLE(( + // +------------------------------------------------------------------------------+ + Song2 == Song1 + next_song / start_next_song, + Song1 == Song2 + previous_song / start_prev_song, + Song3 == Song2 + next_song / start_next_song, + Song2 == Song3 + previous_song / start_prev_song + // +------------------------------------------------------------------------------+ + ),transition_table ) + + }; + // Playing Submachine back-end + typedef boost::msm::back::state_machine<Playing_> Playing_helper; + struct Playing_impl : public Playing_helper, + public msm::front::euml::euml_state<Playing_helper> + { + }; + + // state not defining any entry or exit + struct Paused_impl : public msm::front::state<> , public msm::front::euml::euml_state<Paused_impl> + { + }; + //to make the transition table more readable + Empty_impl const Empty; + Open_impl const Open; + Stopped_impl const Stopped; + Playing_impl const Playing; + Paused_impl const Paused; + + // front-end: define the FSM structure + struct player_ : public msm::front::state_machine_def<player_> + { + + // the initial state of the player SM. Must be defined + typedef Empty_impl initial_state; + + // Transition table for player + // replaces the old transition table + BOOST_MSM_EUML_DECLARE_TRANSITION_TABLE(( + Stopped + play / start_playback == Playing , + Stopped + open_close / open_drawer == Open , + Stopped + stop == Stopped , + // +------------------------------------------------------------------------------+ + Open + open_close / close_drawer == Empty , + // +------------------------------------------------------------------------------+ + Empty + open_close / open_drawer == Open , + Empty + cd_detected /(store_cd_info, + msm::front::euml::process_(play)) == Stopped , + // +------------------------------------------------------------------------------+ + Playing + stop / stop_playback == Stopped , + Playing + pause / pause_playback == Paused , + Playing + open_close / stop_and_open == Open , + // +------------------------------------------------------------------------------+ + Paused + end_pause / resume_playback == Playing , + Paused + stop / stop_playback == Stopped , + Paused + open_close / stop_and_open == Open + // +------------------------------------------------------------------------------+ + ),transition_table) + + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + // Pick a back-end + typedef msm::back::state_machine<player_> player; + + // + // Testing utilities. + // + static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" }; + void pstate(player const& p) + { + std::cout << " -> " << state_names[p.current_state()[0]] << std::endl; + } + + void test() + { + player p; + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + p.start(); + // go to Open, call on_exit on Empty, then action, then on_entry on Open + p.process_event(open_close); pstate(p); + p.process_event(open_close); pstate(p); + p.process_event(cd_detected); pstate(p); + + // at this point, Play is active + // make transition happen inside it. Player has no idea about this event but it's ok. + p.process_event(next_song);pstate(p); //2nd song active + p.process_event(next_song);pstate(p);//3rd song active + p.process_event(previous_song);pstate(p);//2nd song active + + p.process_event(pause); pstate(p); + // go back to Playing + p.process_event(end_pause); pstate(p); + p.process_event(pause); pstate(p); + p.process_event(stop); pstate(p); + // event leading to the same state + // no action method called as it is not present in the transition table + p.process_event(stop); pstate(p); + } +} + +int main() +{ + test(); + return 0; +} diff --git a/libs/msm/doc/HTML/examples/Constructor.cpp b/libs/msm/doc/HTML/examples/Constructor.cpp new file mode 100644 index 0000000000..8ae05412d7 --- /dev/null +++ b/libs/msm/doc/HTML/examples/Constructor.cpp @@ -0,0 +1,292 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> + +namespace msm = boost::msm; +namespace mpl = boost::mpl; + +struct SomeExternalContext +{ + SomeExternalContext(int b):bla(b){} + int bla; +}; + +namespace +{ + // events + struct play {}; + struct end_pause {}; + struct stop {}; + struct pause {}; + struct open_close {}; + struct NextSong {}; + struct PreviousSong {}; + + // A "complicated" event type that carries some data. + enum DiskTypeEnum + { + DISK_CD=0, + DISK_DVD=1 + }; + struct cd_detected + { + cd_detected(std::string name, DiskTypeEnum diskType) + : name(name), + disc_type(diskType) + {} + + std::string name; + DiskTypeEnum disc_type; + }; + + // front-end: define the FSM structure + struct player_ : public msm::front::state_machine_def<player_> + { + player_(SomeExternalContext& context,int someint) + :context_(context) + { + std::cout << "context value:" << context_.bla << " with value:" << someint << std::endl; + context.bla = 10; + } + + SomeExternalContext& context_; + + // The list of FSM states + struct Empty : public msm::front::state<> + { + int data_; + Empty():data_(0){} + Empty(int i):data_(i){} + // every (optional) entry/exit methods get the event passed. + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Empty" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Empty" << std::endl;} + }; + struct Open : public msm::front::state<> + { + int data_; + Open():data_(0){} + Open(int i):data_(i){} + + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) {std::cout << "entering: Open" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Open" << std::endl;} + }; + + // sm_ptr still supported but deprecated as functors are a much better way to do the same thing + struct Stopped : public msm::front::state<msm::front::default_base_state,msm::front::sm_ptr> + { + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) {std::cout << "entering: Stopped" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Stopped" << std::endl;} + void set_sm_ptr(player_* pl) + { + m_player=pl; + } + player_* m_player; + }; + + struct Playing_ : public msm::front::state_machine_def<Playing_> + { + // when playing, the CD is loaded and we are in either pause or playing (duh) + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Playing" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Playing" << std::endl;} + + // The list of FSM states + struct Song1 : public msm::front::state<> + { + int data_; + Song1():data_(0){} + Song1(int i):data_(i){} + + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: First song" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: First Song" << std::endl;} + + }; + struct Song2 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: Second song" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: Second Song" << std::endl;} + }; + struct Song3 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: Third song" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: Third Song" << std::endl;} + }; + // the initial state. Must be defined + typedef Song1 initial_state; + // transition actions + void start_next_song(NextSong const&) { std::cout << "Playing::start_next_song\n"; } + void start_prev_song(PreviousSong const&) { std::cout << "Playing::start_prev_song\n"; } + // guard conditions + + typedef Playing_ pl; // makes transition table cleaner + // Transition table for Playing + struct transition_table : mpl::vector4< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Song1 , NextSong , Song2 , &pl::start_next_song >, + a_row < Song2 , PreviousSong, Song1 , &pl::start_prev_song >, + a_row < Song2 , NextSong , Song3 , &pl::start_next_song >, + a_row < Song3 , PreviousSong, Song2 , &pl::start_prev_song > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + // back-end + typedef msm::back::state_machine<Playing_> Playing; + + // state not defining any entry or exit + struct Paused : public msm::front::state<> + { + }; + + // the initial state of the player SM. Must be defined + typedef Empty initial_state; + + // transition actions + void start_playback(play const&) { std::cout << "player::start_playback\n"; } + void open_drawer(open_close const&) { std::cout << "player::open_drawer\n"; } + void close_drawer(open_close const&) { std::cout << "player::close_drawer\n"; } + void store_cd_info(cd_detected const&) { std::cout << "player::store_cd_info\n"; } + void stop_playback(stop const&) { std::cout << "player::stop_playback\n"; } + void pause_playback(pause const&) { std::cout << "player::pause_playback\n"; } + void resume_playback(end_pause const&) { std::cout << "player::resume_playback\n"; } + void stop_and_open(open_close const&) { std::cout << "player::stop_and_open\n"; } + void stopped_again(stop const&) {std::cout << "player::stopped_again\n";} + // guard conditions + bool good_disk_format(cd_detected const& evt) + { + // to test a guard condition, let's say we understand only CDs, not DVD + if (evt.disc_type != DISK_CD) + { + std::cout << "wrong disk, sorry" << std::endl; + return false; + } + return true; + } + // used to show a transition conflict. This guard will simply deactivate one transition and thus + // solve the conflict + bool auto_start(cd_detected const&) + { + return false; + } + + typedef player_ p; // makes transition table cleaner + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Stopped , play , Playing , &p::start_playback >, + a_row < Stopped , open_close , Open , &p::open_drawer >, + _row < Stopped , stop , Stopped >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Open , open_close , Empty , &p::close_drawer >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Empty , open_close , Open , &p::open_drawer >, + row < Empty , cd_detected , Stopped , &p::store_cd_info ,&p::good_disk_format >, + row < Empty , cd_detected , Playing , &p::store_cd_info ,&p::auto_start >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Playing , stop , Stopped , &p::stop_playback >, + a_row < Playing , pause , Paused , &p::pause_playback >, + a_row < Playing , open_close , Open , &p::stop_and_open >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Paused , end_pause , Playing , &p::resume_playback >, + a_row < Paused , stop , Stopped , &p::stop_playback >, + a_row < Paused , open_close , Open , &p::stop_and_open > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + // Pick a back-end + typedef msm::back::state_machine<player_> player; + + // + // Testing utilities. + // + static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" }; + void pstate(player const& p) + { + std::cout << " -> " << state_names[p.current_state()[0]] << std::endl; + } + + void test() + { + SomeExternalContext ctx(3); + // example 1 + // create a player with an argument by reference and an int + // (go to the constructor front-end) + player p1(boost::ref(ctx),5); + + // example 2 + // create a player with a copy of an Empty state, an argument by reference and an int + // (last 2 go to the constructor front-end) + player p2(msm::back::states_ << player_::Empty(1),boost::ref(ctx),5); + std::cout << "Empty's data should be 1. Is: " << p2.get_state<player_::Empty&>().data_ << std::endl; + + std::cout << "Set a new Empty state" << std::endl; + p2.set_states(msm::back::states_ << player_::Empty(5)); + std::cout << "Empty's data should be 5. Is: " << p2.get_state<player_::Empty&>().data_ << std::endl; + + std::cout << "Set new Empty and Open states" << std::endl; + p2.set_states(msm::back::states_ << player_::Empty(7) << player_::Open(2)); + std::cout << "Empty's data should be 7. Is: " << p2.get_state<player_::Empty&>().data_ << std::endl; + std::cout << "Open's data should be 2. Is: " << p2.get_state<player_::Open&>().data_ << std::endl; + + // example 3 + // create a player with a copy of an Empty state, a copy of a Playing submachine + // (which is itself created by a copy of Song1) + // an argument by reference and an int + // (last 2 go to the constructor front-end) + player p(msm::back::states_ << player_::Empty(1) + << player_::Playing(msm::back::states_ << player_::Playing_::Song1(8)), + boost::ref(ctx),5); + + std::cout << "Song1's data should be 8. Is: " + << p.get_state<player_::Playing&>().get_state<player_::Playing_::Song1&>().data_ << std::endl; + + + } +} + +int main() +{ + test(); + return 0; +} diff --git a/libs/msm/doc/HTML/examples/DirectEntryEuml.cpp b/libs/msm/doc/HTML/examples/DirectEntryEuml.cpp new file mode 100644 index 0000000000..7cde090e8d --- /dev/null +++ b/libs/msm/doc/HTML/examples/DirectEntryEuml.cpp @@ -0,0 +1,386 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <vector> +#include <iostream> + +#include <boost/msm/back/state_machine.hpp> +#include <boost/msm/front/euml/euml.hpp> + +using namespace std; +using namespace boost::msm::front::euml; +namespace msm = boost::msm; +namespace mpl = boost::mpl; + + +namespace // Concrete FSM implementation +{ + // events + BOOST_MSM_EUML_EVENT(event1) + BOOST_MSM_EUML_EVENT(event2) + BOOST_MSM_EUML_EVENT(event3) + BOOST_MSM_EUML_EVENT(event4) + BOOST_MSM_EUML_EVENT(event5) + // if we need something special, like a template constructor, we cannot use the helper macros + struct event6_impl : euml_event<event6_impl> + { + event6_impl(){} + template <class Event> + event6_impl(Event const&){} + }; + event6_impl const event6; + + //Sub fsm state definition + BOOST_MSM_EUML_ACTION(SubState1_Entry) + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: SubFsm2::SubState1" << std::endl; + } + }; + BOOST_MSM_EUML_ACTION(SubState1_Exit) + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "leaving: SubFsm2::SubState1" << std::endl; + } + }; + BOOST_MSM_EUML_STATE(( SubState1_Entry,SubState1_Exit ),SubState1) + + BOOST_MSM_EUML_ACTION(SubState1b_Entry) + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: SubFsm2::SubState1b" << std::endl; + } + }; + BOOST_MSM_EUML_ACTION(SubState1b_Exit) + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "leaving: SubFsm2::SubState1b" << std::endl; + } + }; + BOOST_MSM_EUML_STATE(( SubState1b_Entry,SubState1b_Exit ),SubState1b) + + BOOST_MSM_EUML_ACTION(SubState1c_Entry) + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: SubFsm2::SubState1c" << std::endl; + } + }; + BOOST_MSM_EUML_ACTION(SubState1c_Exit) + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "leaving: SubFsm2::SubState1c" << std::endl; + } + }; + BOOST_MSM_EUML_STATE(( SubState1c_Entry,SubState1c_Exit ),SubState1c) + + BOOST_MSM_EUML_ACTION(SubState2_Entry) + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: SubFsm2::SubState2" << std::endl; + } + }; + BOOST_MSM_EUML_ACTION(SubState2_Exit) + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "leaving: SubFsm2::SubState2" << std::endl; + } + }; + BOOST_MSM_EUML_EXPLICIT_ENTRY_STATE(0,( SubState2_Entry,SubState2_Exit ),SubState2) + + BOOST_MSM_EUML_ACTION(SubState2b_Entry) + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: SubFsm2::SubState2b" << std::endl; + } + }; + BOOST_MSM_EUML_ACTION(SubState2b_Exit) + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "leaving: SubFsm2::SubState2b" << std::endl; + } + }; + BOOST_MSM_EUML_EXPLICIT_ENTRY_STATE(1,( SubState2b_Entry,SubState2b_Exit ),SubState2b) + + BOOST_MSM_EUML_ACTION(SubState2c_Entry) + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: SubFsm2::SubState2c" << std::endl; + } + }; + BOOST_MSM_EUML_ACTION(SubState2c_Exit) + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "leaving: SubFsm2::SubState2c" << std::endl; + } + }; + BOOST_MSM_EUML_EXPLICIT_ENTRY_STATE(2,( SubState2c_Entry,SubState2c_Exit ),SubState2c) + + BOOST_MSM_EUML_ACTION(PseudoEntry1_Entry) + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: SubFsm2::PseudoEntry1" << std::endl; + } + }; + BOOST_MSM_EUML_ACTION(PseudoEntry1_Exit) + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "leaving: SubFsm2::PseudoEntry1" << std::endl; + } + }; + BOOST_MSM_EUML_ENTRY_STATE(0,( PseudoEntry1_Entry,PseudoEntry1_Exit ),PseudoEntry1) + + BOOST_MSM_EUML_ACTION(SubState3_Entry) + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: SubFsm2::SubState3" << std::endl; + } + }; + BOOST_MSM_EUML_ACTION(SubState3_Exit) + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "leaving: SubFsm2::SubState3" << std::endl; + } + }; + BOOST_MSM_EUML_STATE(( SubState3_Entry,SubState3_Exit ),SubState3) + + BOOST_MSM_EUML_ACTION(SubState3b_Entry) + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: SubFsm2::SubState3b" << std::endl; + } + }; + BOOST_MSM_EUML_ACTION(SubState3b_Exit) + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "leaving: SubFsm2::SubState3b" << std::endl; + } + }; + BOOST_MSM_EUML_STATE(( SubState3b_Entry,SubState3b_Exit ),SubState3b) + + BOOST_MSM_EUML_ACTION(PseudoExit1_Entry) + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: SubFsm2::PseudoExit1" << std::endl; + } + }; + BOOST_MSM_EUML_ACTION(PseudoExit1_Exit) + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "leaving: SubFsm2::PseudoExit1" << std::endl; + } + }; + BOOST_MSM_EUML_EXIT_STATE(( event6,PseudoExit1_Entry,PseudoExit1_Exit ),PseudoExit1) + + // actions + BOOST_MSM_EUML_ACTION(entry_action) + { + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(FSM& ,EVT const& ,SourceState& ,TargetState& ) + { + cout << "calling entry_action" << endl; + } + }; + // SubFsm definition + BOOST_MSM_EUML_ACTION(SubFsm2_Entry) + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: SubFsm2" << std::endl; + } + }; + BOOST_MSM_EUML_ACTION(SubFsm2_Exit) + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "leaving: SubFsm2" << std::endl; + } + }; + BOOST_MSM_EUML_TRANSITION_TABLE(( + // +------------------------------------------------------------------------------+ + SubState3 == PseudoEntry1 + event4 / entry_action , + SubState1 == SubState2 + event6 , + PseudoExit1 == SubState3 + event5 + // +------------------------------------------------------------------------------+ + ), SubFsm2_transition_table) + + BOOST_MSM_EUML_DECLARE_STATE_MACHINE( (SubFsm2_transition_table, //STT + init_ << SubState1 << SubState1b << SubState1c, // Init State + SubFsm2_Entry, // Entry + SubFsm2_Exit + ),SubFsm2_def) + // inherit to add some typedef + struct SubFsm2_ : public SubFsm2_def + { + // these 2 states are not found in the transition table because they are accessed only through + // a fork, so we need to create them explicitly + typedef mpl::vector<BOOST_MSM_EUML_STATE_NAME(SubState2b), + BOOST_MSM_EUML_STATE_NAME(SubState2c)> explicit_creation; + }; + + // back-end + typedef msm::back::state_machine<SubFsm2_> SubFsm2_type; + SubFsm2_type const SubFsm2; + + // Fsm state definitions + BOOST_MSM_EUML_ACTION(State1_Entry) + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: State1" << std::endl; + } + }; + BOOST_MSM_EUML_ACTION(State1_Exit) + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "leaving: State1" << std::endl; + } + }; + BOOST_MSM_EUML_STATE(( State1_Entry,State1_Exit ),State1) + + BOOST_MSM_EUML_ACTION(State2_Entry) + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: State2" << std::endl; + } + }; + BOOST_MSM_EUML_ACTION(State2_Exit) + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "leaving: State2" << std::endl; + } + }; + BOOST_MSM_EUML_STATE(( State2_Entry,State2_Exit ),State2) + + // Fsm definition + BOOST_MSM_EUML_TRANSITION_TABLE(( + // +------------------------------------------------------------------------------+ + SubFsm2 == State1 + event1 , + explicit_(SubFsm2,SubState2) == State1 + event2, + (explicit_(SubFsm2,SubState2), + explicit_(SubFsm2,SubState2b), + explicit_(SubFsm2,SubState2c)) == State1 + event3 , + entry_pt_(SubFsm2,PseudoEntry1) == State1 + event4 , + State1 == SubFsm2 + event1 , + State2 == exit_pt_ + (SubFsm2,PseudoExit1) + event6 + // +------------------------------------------------------------------------------+ + ),transition_table ) + + + BOOST_MSM_EUML_ACTION(Log_No_Transition) + { + template <class Event,class FSM,class STATE> + void operator()(Event const& e,FSM&,STATE& ) + { + std::cout << "no transition in Fsm" + << " on event " << typeid(e).name() << std::endl; + } + }; + BOOST_MSM_EUML_DECLARE_STATE_MACHINE(( transition_table, //STT + init_ << State1, // Init State + no_action, // Entry + no_action, // Exit + attributes_ << no_attributes_, // Attributes + configure_ << no_configure_, // configuration + Log_No_Transition // no_transition handler + ), + Fsm_) //fsm name + + //back-end + typedef msm::back::state_machine<Fsm_> Fsm; + + // + // Testing utilities. + // + static char const* const state_names[] = { "State1", "SubFsm2","State2" }; + void pstate(Fsm const& p) + { + std::cout << " -> " << state_names[p.current_state()[0]] << std::endl; + } + + void test() + { + Fsm p; + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + p.start(); + std::cout << "Simply move in and out of the composite, activate init states" << std::endl; + p.process_event(event1); pstate(p); + p.process_event(event1); pstate(p); + std::cout << "Direct entry into SubFsm2::SubState2, then transition to SubState1 and back to State1" << std::endl; + p.process_event(event2); pstate(p); + p.process_event(event6); pstate(p); + p.process_event(event1); pstate(p); + std::cout << "processing fork to SubFsm2::SubState2, SubFsm2::SubState2b and SubFsm2::SubState2c" << std::endl; + p.process_event(event3); pstate(p); + p.process_event(event1); pstate(p); + std::cout << "processing entry pseudo state" << std::endl; + p.process_event(event4); pstate(p); + p.process_event(event1); pstate(p); + std::cout << "processing entry + exit pseudo state" << std::endl; + p.process_event(event4); pstate(p); + std::cout << "using exit pseudo state" << std::endl; + p.process_event(event5); pstate(p); + } +} + +int main() +{ + test(); + return 0; +} diff --git a/libs/msm/doc/HTML/examples/DirectEntryTutorial.cpp b/libs/msm/doc/HTML/examples/DirectEntryTutorial.cpp new file mode 100644 index 0000000000..35bf9fdd22 --- /dev/null +++ b/libs/msm/doc/HTML/examples/DirectEntryTutorial.cpp @@ -0,0 +1,221 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <vector> +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> + +namespace msm = boost::msm; +namespace mpl = boost::mpl; + +namespace +{ + // events + struct event1 {}; + struct event2 {}; + struct event3 {}; + struct event4 {}; + struct event5 {}; + struct event6 + { + event6(){} + template <class Event> + event6(Event const&){} + }; + // front-end: define the FSM structure + struct Fsm_ : public msm::front::state_machine_def<Fsm_> + { + // The list of FSM states + struct State1 : public msm::front::state<> + { + // every (optional) entry/exit methods get the event passed. + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: State1" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: State1" << std::endl;} + }; + struct State2 : public msm::front::state<> + { + // every (optional) entry/exit methods get the event passed. + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: State2" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: State2" << std::endl;} + }; + struct SubFsm2_ : public msm::front::state_machine_def<SubFsm2_> + { + typedef msm::back::state_machine<SubFsm2_> SubFsm2; + + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: SubFsm2" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: SubFsm2" << std::endl;} + + struct SubState1 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: SubFsm2::SubState1" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: SubFsm2::SubState1" << std::endl;} + }; + struct SubState1b : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: SubFsm2::SubState1b" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: SubFsm2::SubState1b" << std::endl;} + }; + struct SubState2 : public msm::front::state<> , public msm::front::explicit_entry<0> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: SubFsm2::SubState2" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: SubFsm2::SubState2" << std::endl;} + }; + struct SubState2b : public msm::front::state<> , public msm::front::explicit_entry<1> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: SubFsm2::SubState2b" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: SubFsm2::SubState2b" << std::endl;} + }; + // test with a pseudo entry + struct PseudoEntry1 : public msm::front::entry_pseudo_state<0> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: SubFsm2::PseudoEntry1" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: SubFsm2::PseudoEntry1" << std::endl;} + }; + struct SubState3 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: SubFsm2::SubState3" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: SubFsm2::SubState3" << std::endl;} + }; + struct SubState3b : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: SubFsm2::SubState3b" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: SubFsm2::SubState3b" << std::endl;} + }; + struct PseudoExit1 : public msm::front::exit_pseudo_state<event6> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: SubFsm2::PseudoExit1" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: SubFsm2::PseudoExit1" << std::endl;} + }; + // action methods + void entry_action(event4 const&) + { + std::cout << "calling entry_action" << std::endl; + } + // the initial state. Must be defined + typedef mpl::vector<SubState1,SubState1b> initial_state; + + typedef mpl::vector<SubState2b> explicit_creation; + + // Transition table for SubFsm2 + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +--------------+-------------+------------+------------------------+----------------------+ + a_row < PseudoEntry1 , event4 , SubState3 ,&SubFsm2_::entry_action >, + _row < SubState2 , event6 , SubState1 >, + _row < SubState3 , event5 , PseudoExit1 > + // +--------------+-------------+------------+------------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + typedef msm::back::state_machine<SubFsm2_> SubFsm2; + + // the initial state of the player SM. Must be defined + typedef State1 initial_state; + + // transition actions + // guard conditions + + // Transition table for Fsm + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------------------+--------+------------------------------------+-------+--------+ + _row < State1 , event1 , SubFsm2 >, + _row < State1 , event2 , SubFsm2::direct<SubFsm2_::SubState2> >, + _row < State1 , event3 , mpl::vector<SubFsm2::direct<SubFsm2_::SubState2>, + SubFsm2::direct<SubFsm2_::SubState2b> > >, + _row < State1 , event4 , SubFsm2::entry_pt + <SubFsm2_::PseudoEntry1> >, + // +---------------------+--------+------------------------------------+-------+--------+ + _row < SubFsm2 , event1 , State1 >, + _row < SubFsm2::exit_pt + <SubFsm2_::PseudoExit1>, event6 , State2 > + // +---------------------+--------+------------------------------------+-------+--------+ + > {}; + + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + typedef msm::back::state_machine<Fsm_> Fsm; + + // + // Testing utilities. + // + static char const* const state_names[] = { "State1", "SubFsm2","State2" }; + void pstate(Fsm const& p) + { + std::cout << " -> " << state_names[p.current_state()[0]] << std::endl; + } + + void test() + { + Fsm p; + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + p.start(); + std::cout << "Simply move in and out of the composite, activate init states" << std::endl; + p.process_event(event1()); pstate(p); + p.process_event(event1()); pstate(p); + std::cout << "Direct entry into SubFsm2::SubState2, then transition to SubState1 and back to State1" << std::endl; + p.process_event(event2()); pstate(p); + p.process_event(event6()); pstate(p); + p.process_event(event1()); pstate(p); + std::cout << "processing fork to SubFsm2::SubState2 and SubFsm2::SubState2b" << std::endl; + p.process_event(event3()); pstate(p); + p.process_event(event1()); pstate(p); + std::cout << "processing entry pseudo state" << std::endl; + p.process_event(event4()); pstate(p); + p.process_event(event1()); pstate(p); + std::cout << "processing entry + exit pseudo state" << std::endl; + p.process_event(event4()); pstate(p); + std::cout << "using exit pseudo state" << std::endl; + p.process_event(event5()); pstate(p); + } +} + +int main() +{ + test(); + return 0; +} diff --git a/libs/msm/doc/HTML/examples/EumlInternal.cpp b/libs/msm/doc/HTML/examples/EumlInternal.cpp new file mode 100644 index 0000000000..407740dbe0 --- /dev/null +++ b/libs/msm/doc/HTML/examples/EumlInternal.cpp @@ -0,0 +1,154 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <vector> +#include <iostream> + +#include <boost/msm/back/state_machine.hpp> +#include <boost/msm/front/euml/euml.hpp> + +using namespace std; +using namespace boost::msm::front::euml; +using namespace boost::msm::front; +namespace msm = boost::msm; + +// entry/exit/action/guard logging functors +#include "logging_functors.h" + +namespace // Concrete FSM implementation +{ + // events + BOOST_MSM_EUML_EVENT(play) + BOOST_MSM_EUML_EVENT(end_pause) + BOOST_MSM_EUML_EVENT(stop) + BOOST_MSM_EUML_EVENT(pause) + BOOST_MSM_EUML_EVENT(open_close) + BOOST_MSM_EUML_EVENT(internal_event) + + // A "complicated" event type that carries some data. + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(std::string,cd_name) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(DiskTypeEnum,cd_type) + BOOST_MSM_EUML_ATTRIBUTES((attributes_ << cd_name << cd_type ), cd_detected_attributes) + BOOST_MSM_EUML_EVENT_WITH_ATTRIBUTES(cd_detected,cd_detected_attributes) + + // Concrete FSM implementation + + // The list of FSM states + BOOST_MSM_EUML_STATE(( Empty_Entry,Empty_Exit ),Empty) + BOOST_MSM_EUML_STATE(( Open_Entry,Open_Exit ),Open) + BOOST_MSM_EUML_STATE(( Stopped_Entry,Stopped_Exit ),Stopped) + BOOST_MSM_EUML_STATE(( Playing_Entry,Playing_Exit ),Playing) + // state not needing any entry or exit + BOOST_MSM_EUML_STATE((),Paused) + + // guard conditions + BOOST_MSM_EUML_ACTION(good_disk_format) + { + template <class FSM,class EVT,class SourceState,class TargetState> + bool operator()(EVT const& evt,FSM&,SourceState& ,TargetState& ) + { + // to test a guard condition, let's say we understand only CDs, not DVD + if (evt.get_attribute(cd_type)!=DISK_CD) + { + std::cout << "wrong disk, sorry" << std::endl; + // just for logging, does not block any transition + return true; + } + std::cout << "good disk" << std::endl; + return true; + } + }; + // replaces the old transition table + BOOST_MSM_EUML_TRANSITION_TABLE(( + Playing == Stopped + play / start_playback , + Playing == Paused + end_pause / resume_playback, + // +------------------------------------------------------------------------------+ + Empty == Open + open_close / close_drawer, + // +------------------------------------------------------------------------------+ + Open == Empty + open_close / open_drawer, + Open == Paused + open_close / stop_and_open, + Open == Stopped + open_close / open_drawer, + Open == Playing + open_close / stop_and_open, + Open + open_close [internal_guard1] / internal_action1, + Open + open_close [internal_guard2] / internal_action2, + Open + internal_event / internal_action , + // +------------------------------------------------------------------------------+ + Paused == Playing + pause / pause_playback, + // +------------------------------------------------------------------------------+ + Stopped == Playing + stop / stop_playback, + Stopped == Paused + stop / stop_playback, + Stopped == Empty + cd_detected [good_disk_format&& + (event_(cd_type)==Int_<DISK_CD>())] + / (store_cd_info,process_(play)), + Stopped == Stopped + stop + // +------------------------------------------------------------------------------+ + ),transition_table) + + + // create a state machine "on the fly" + BOOST_MSM_EUML_DECLARE_STATE_MACHINE(( transition_table, //STT + init_ << Empty, // Init State + no_action, // Entry + no_action, // Exit + attributes_ << no_attributes_, // Attributes + configure_ << no_configure_, // configuration + Log_No_Transition // no_transition handler + ), + player_) //fsm name + // choice of back-end + typedef msm::back::state_machine<player_> player; + + // + // Testing utilities. + // + static char const* const state_names[] = { "Stopped", "Paused", "Open", "Empty", "Playing" }; + void pstate(player const& p) + { + std::cout << " -> " << state_names[p.current_state()[0]] << std::endl; + } + + void test() + { + player p; + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + p.start(); + // go to Open, call on_exit on Empty, then action, then on_entry on Open + p.process_event(open_close); pstate(p); + std::cout << "sending internal event (not rejected)" << std::endl; + p.process_event(internal_event); + std::cout << "sending open_close event. Conflict with internal transitions (rejecting event)" << std::endl; + p.process_event(open_close); pstate(p); + // will be rejected, wrong disk type + p.process_event( + cd_detected("louie, louie",DISK_DVD)); pstate(p); + p.process_event( + cd_detected("louie, louie",DISK_CD)); pstate(p); + // no need to call play() as the previous event does it in its action method + //p.process_event(play); + + // at this point, Play is active + p.process_event(pause); pstate(p); + // go back to Playing + p.process_event(end_pause); pstate(p); + p.process_event(pause); pstate(p); + p.process_event(stop); pstate(p); + // event leading to the same state + // no action method called as none is defined in the transition table + p.process_event(stop); pstate(p); + // test call to no_transition + p.process_event(pause); pstate(p); + } +} + +int main() +{ + test(); + return 0; +} diff --git a/libs/msm/doc/HTML/examples/EumlInternalDistributed.cpp b/libs/msm/doc/HTML/examples/EumlInternalDistributed.cpp new file mode 100644 index 0000000000..c7371dfc50 --- /dev/null +++ b/libs/msm/doc/HTML/examples/EumlInternalDistributed.cpp @@ -0,0 +1,218 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <vector> +#include <iostream> + +#include <boost/msm/back/state_machine.hpp> +#include <boost/msm/front/euml/euml.hpp> + +using namespace std; +using namespace boost::msm::front::euml; +using namespace boost::msm::front; +namespace msm = boost::msm; + +// entry/exit/action/guard logging functors +#include "logging_functors.h" + +namespace // Concrete FSM implementation +{ + // events + // note that unlike the SimpleTutorial, events must derive from euml_event. + BOOST_MSM_EUML_EVENT(play) + BOOST_MSM_EUML_EVENT(end_pause) + BOOST_MSM_EUML_EVENT(stop) + BOOST_MSM_EUML_EVENT(pause) + BOOST_MSM_EUML_EVENT(open_close) + BOOST_MSM_EUML_EVENT(internal_event) + BOOST_MSM_EUML_EVENT(next_song) + BOOST_MSM_EUML_EVENT(previous_song) + + // A "complicated" event type that carries some data. + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(std::string,cd_name) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(DiskTypeEnum,cd_type) + BOOST_MSM_EUML_ATTRIBUTES((attributes_ << cd_name << cd_type ), cd_detected_attributes) + BOOST_MSM_EUML_EVENT_WITH_ATTRIBUTES(cd_detected,cd_detected_attributes) + + // Concrete FSM implementation + + // The list of FSM states + BOOST_MSM_EUML_STATE(( Empty_Entry,Empty_Exit ),Empty) + + // we just declare a state type but do not create any instance as we want to inherit from this type + BOOST_MSM_EUML_DECLARE_STATE((Open_Entry,Open_Exit),Open_def) + // derive to be able to add an internal transition table + struct Open_impl : public Open_def + { + BOOST_MSM_EUML_DECLARE_INTERNAL_TRANSITION_TABLE(( + open_close [internal_guard1] / internal_action1 , + open_close [internal_guard2] / internal_action2 , + internal_event / internal_action + )) + }; + // declare an instance for the stt as we are manually declaring a state + Open_impl const Open; + BOOST_MSM_EUML_STATE(( Stopped_Entry,Stopped_Exit ),Stopped) + + // Playing is a state machine itself. + // It has 3 substates + BOOST_MSM_EUML_STATE(( Song1_Entry,Song1_Exit ),Song1) + BOOST_MSM_EUML_STATE(( Song2_Entry,Song2_Exit ),Song2) + BOOST_MSM_EUML_STATE(( Song3_Entry,Song3_Exit ),Song3) + + // Playing has a transition table + BOOST_MSM_EUML_TRANSITION_TABLE(( + // +------------------------------------------------------------------------------+ + Song2 == Song1 + next_song / start_next_song, + Song1 == Song2 + previous_song / start_prev_song, + Song3 == Song2 + next_song / start_next_song, + Song2 == Song3 + previous_song / start_prev_song + // +------------------------------------------------------------------------------+ + ),playing_transition_table ) + + BOOST_MSM_EUML_DECLARE_STATE_MACHINE( (playing_transition_table, //STT + init_ << Song1 // Init State + ),Playing_def) + + // some action for the internal transition + BOOST_MSM_EUML_ACTION(playing_internal_action) + { + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const&, FSM& ,SourceState& ,TargetState& ) + { + cout << "Playing::internal action" << endl; + } + }; + // derive to be able to add an internal transition table + struct Playing_ : public Playing_def + { + BOOST_MSM_EUML_DECLARE_INTERNAL_TRANSITION_TABLE(( + internal_event / playing_internal_action + )) + }; + // choice of back-end + typedef msm::back::state_machine<Playing_> Playing_type; + Playing_type const Playing; + + + // state not needing any entry or exit + BOOST_MSM_EUML_STATE((),Paused) + + // guard conditions + BOOST_MSM_EUML_ACTION(good_disk_format) + { + template <class FSM,class EVT,class SourceState,class TargetState> + bool operator()(EVT const& evt,FSM&,SourceState& ,TargetState& ) + { + // to test a guard condition, let's say we understand only CDs, not DVD + if (evt.get_attribute(cd_type)!=DISK_CD) + { + std::cout << "wrong disk, sorry" << std::endl; + // just for logging, does not block any transition + return true; + } + std::cout << "good disk" << std::endl; + return true; + } + }; + // replaces the old transition table + BOOST_MSM_EUML_TRANSITION_TABLE(( + Stopped + play / start_playback == Playing , + Stopped + open_close / open_drawer == Open , + Stopped + stop == Stopped , + // +------------------------------------------------------------------------------+ + Open + open_close / close_drawer == Empty , + // +------------------------------------------------------------------------------+ + Empty + open_close / open_drawer == Open , + Empty + cd_detected + [good_disk_format&&(event_(cd_type)==Int_<DISK_CD>())] + / (store_cd_info,process_(play)) + == Stopped , + // +------------------------------------------------------------------------------+ + Playing + stop / stop_playback == Stopped , + Playing + pause / pause_playback == Paused , + Playing + open_close / stop_and_open == Open , + // +------------------------------------------------------------------------------+ + Paused + end_pause / resume_playback == Playing , + Paused + stop / stop_playback == Stopped , + Paused + open_close / stop_and_open == Open + // +------------------------------------------------------------------------------+ + ),transition_table) + + + + // create a state machine "on the fly" + BOOST_MSM_EUML_DECLARE_STATE_MACHINE(( transition_table, //STT + init_ << Empty, // Init State + no_action, // Entry + no_action, // Exit + attributes_ << no_attributes_, // Attributes + configure_ << no_configure_, // configuration + Log_No_Transition // no_transition handler + ), + player_) //fsm name + + // choice of back-end + typedef msm::back::state_machine<player_> player; + + // + // Testing utilities. + // + static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" }; + void pstate(player const& p) + { + std::cout << " -> " << state_names[p.current_state()[0]] << std::endl; + } + + void test() + { + player p; + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + p.start(); + // go to Open, call on_exit on Empty, then action, then on_entry on Open + p.process_event(open_close); pstate(p); + std::cout << "sending internal event (not rejected)" << std::endl; + p.process_event(internal_event); + std::cout << "sending open_close event. Conflict with internal transitions (rejecting event)" << std::endl; + p.process_event(open_close); pstate(p); + // will be rejected, wrong disk type + p.process_event( + cd_detected("louie, louie",DISK_DVD)); pstate(p); + p.process_event( + cd_detected("louie, louie",DISK_CD)); pstate(p); + // no need to call play as the previous event does it in its action method + //p.process_event(play); + // at this point, Play is active + // make transition happen inside it. Player has no idea about this event but it's ok. + p.process_event(next_song);pstate(p); //2nd song active + p.process_event(next_song);pstate(p);//3rd song active + p.process_event(previous_song);pstate(p);//2nd song active + // event handled internally in Playing, without region checking + std::cout << "sending internal event (not rejected)" << std::endl; + p.process_event(internal_event); + + p.process_event(pause); pstate(p); + // go back to Playing + p.process_event(end_pause); pstate(p); + p.process_event(pause); pstate(p); + p.process_event(stop); pstate(p); + // event leading to the same state + // no action method called as none is defined in the transition table + p.process_event(stop); pstate(p); + // test call to no_transition + p.process_event(pause); pstate(p); + } +} + +int main() +{ + test(); + return 0; +} diff --git a/libs/msm/doc/HTML/examples/EumlSimple.cpp b/libs/msm/doc/HTML/examples/EumlSimple.cpp new file mode 100644 index 0000000000..38eebd04fb --- /dev/null +++ b/libs/msm/doc/HTML/examples/EumlSimple.cpp @@ -0,0 +1,222 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include "stdafx.h" + +#include <boost/msm/back/state_machine.hpp> +#include <boost/msm/front/state_machine_def.hpp> +#include <boost/msm/front/euml/euml.hpp> +#include <boost/msm/front/euml/stl.hpp> + +namespace msm = boost::msm; +namespace mpl = boost::mpl; +using namespace boost::msm::front::euml; + +#include <iostream> +#ifdef WIN32 +#include "windows.h" +#else +#include <sys/time.h> +#endif + +namespace // Concrete FSM implementation +{ + // events + BOOST_MSM_EUML_EVENT(play) + BOOST_MSM_EUML_EVENT(end_pause) + BOOST_MSM_EUML_EVENT(stop) + BOOST_MSM_EUML_EVENT(pause) + BOOST_MSM_EUML_EVENT(open_close) + BOOST_MSM_EUML_EVENT(cd_detected) + + BOOST_MSM_EUML_ACTION(start_playback) + { + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM&,SourceState& ,TargetState& ) + { + } + }; + BOOST_MSM_EUML_ACTION(open_drawer) + { + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM&,SourceState& ,TargetState& ) + { + } + }; + BOOST_MSM_EUML_ACTION(close_drawer) + { + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM&,SourceState& ,TargetState& ) + { + } + }; + BOOST_MSM_EUML_ACTION(store_cd_info) + { + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const&, FSM& fsm ,SourceState& ,TargetState& ) + { + } + }; + BOOST_MSM_EUML_ACTION(stop_playback) + { + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM&,SourceState& ,TargetState& ) + { + } + }; + BOOST_MSM_EUML_ACTION(pause_playback) + { + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM&,SourceState& ,TargetState& ) + { + } + }; + BOOST_MSM_EUML_ACTION(resume_playback) + { + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM&,SourceState& ,TargetState& ) + { + } + }; + BOOST_MSM_EUML_ACTION(stop_and_open) + { + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM&,SourceState& ,TargetState& ) + { + } + }; + BOOST_MSM_EUML_ACTION(stopped_again) + { + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM&,SourceState& ,TargetState& ) + { + } + }; + + // The list of FSM states + BOOST_MSM_EUML_STATE((),Empty) + BOOST_MSM_EUML_STATE((),Open) + BOOST_MSM_EUML_STATE((),Stopped) + BOOST_MSM_EUML_STATE((),Playing) + BOOST_MSM_EUML_STATE((),Paused) + + // replaces the old transition table + BOOST_MSM_EUML_TRANSITION_TABLE(( + Playing == Stopped + play / start_playback , + Playing == Paused + end_pause / resume_playback , + // +------------------------------------------------------------------------------+ + Empty == Open + open_close / close_drawer , + // +------------------------------------------------------------------------------+ + Open == Empty + open_close / open_drawer , + Open == Paused + open_close / stop_and_open , + Open == Stopped + open_close / open_drawer , + Open == Playing + open_close / stop_and_open , + // +------------------------------------------------------------------------------+ + Paused == Playing + pause / pause_playback , + // +------------------------------------------------------------------------------+ + Stopped == Playing + stop / stop_playback , + Stopped == Paused + stop / stop_playback , + Stopped == Empty + cd_detected / store_cd_info , + Stopped == Stopped + stop / stopped_again + // +------------------------------------------------------------------------------+ + ),transition_table) + + BOOST_MSM_EUML_ACTION(Log_No_Transition) + { + template <class FSM,class Event> + void operator()(Event const& e,FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + + // create a state machine "on the fly" + BOOST_MSM_EUML_DECLARE_STATE_MACHINE(( transition_table, //STT + init_ << Empty, // Init State + no_action, // Entry + no_action, // Exit + attributes_ << no_attributes_, // Attributes + configure_ << no_exception << no_msg_queue, // configuration + Log_No_Transition // no_transition handler + ), + player_) //fsm name + + typedef msm::back::state_machine<player_> player; + + // + // Testing utilities. + // + static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" }; + + void pstate(player const& p) + { + std::cout << " -> " << state_names[p.current_state()[0]] << std::endl; + } + +} + +#ifndef WIN32 +long mtime(struct timeval& tv1,struct timeval& tv2) +{ + return (tv2.tv_sec-tv1.tv_sec) *1000000 + ((tv2.tv_usec-tv1.tv_usec)); +} +#endif + + +int main() +{ + // for timing +#ifdef WIN32 + LARGE_INTEGER res; + ::QueryPerformanceFrequency(&res); + LARGE_INTEGER li,li2; +#else + struct timeval tv1,tv2; + gettimeofday(&tv1,NULL); +#endif + + player p2; + p2.start(); + // for timing +#ifdef WIN32 + ::QueryPerformanceCounter(&li); +#else + gettimeofday(&tv1,NULL); +#endif + for (int i=0;i<100;++i) + { + p2.process_event(open_close); + p2.process_event(open_close); + p2.process_event(cd_detected); + p2.process_event(play); + p2.process_event(pause); + // go back to Playing + p2.process_event(end_pause); + p2.process_event(pause); + p2.process_event(stop); + // event leading to the same state + p2.process_event(stop); + p2.process_event(open_close); + p2.process_event(open_close); + } +#ifdef WIN32 + ::QueryPerformanceCounter(&li2); +#else + gettimeofday(&tv2,NULL); +#endif +#ifdef WIN32 + std::cout << "msm took in s:" << (double)(li2.QuadPart-li.QuadPart)/res.QuadPart <<"\n" <<std::endl; +#else + std::cout << "msm took in us:" << mtime(tv1,tv2) <<"\n" <<std::endl; +#endif + return 0; +} + diff --git a/libs/msm/doc/HTML/examples/ExampleCircularBuffer.cpp b/libs/msm/doc/HTML/examples/ExampleCircularBuffer.cpp new file mode 100644 index 0000000000..68a5b0a38f --- /dev/null +++ b/libs/msm/doc/HTML/examples/ExampleCircularBuffer.cpp @@ -0,0 +1,299 @@ +#include <vector> +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +// header for support of circular_buffer +#include <boost/msm/back/queue_container_circular.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> +// functors +#include <boost/msm/front/functor_row.hpp> +#include <boost/msm/front/euml/common.hpp> +// for And_ operator +#include <boost/msm/front/euml/operator.hpp> + +using namespace std; +namespace msm = boost::msm; +namespace mpl = boost::mpl; +using namespace msm::front; +// for And_ operator +using namespace msm::front::euml; + +namespace // Concrete FSM implementation +{ + // events + struct play {}; + struct end_pause {}; + struct stop {}; + struct pause {}; + struct open_close {}; + + // A "complicated" event type that carries some data. + enum DiskTypeEnum + { + DISK_CD=0, + DISK_DVD=1 + }; + struct cd_detected + { + cd_detected(std::string name, DiskTypeEnum diskType) + : name(name), + disc_type(diskType) + {} + + std::string name; + DiskTypeEnum disc_type; + }; + + // front-end: define the FSM structure + struct player_ : public msm::front::state_machine_def<player_> + { + // The list of FSM states + struct Empty : public msm::front::state<> + { + // every (optional) entry/exit methods get the event passed. + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Empty" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Empty" << std::endl;} + }; + struct Open : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) {std::cout << "entering: Open" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Open" << std::endl;} + }; + + struct Stopped : public msm::front::state<> + { + // when stopped, the CD is loaded + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) {std::cout << "entering: Stopped" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Stopped" << std::endl;} + }; + + struct Playing : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Playing" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Playing" << std::endl;} + }; + + // state not defining any entry or exit + struct Paused : public msm::front::state<> + { + }; + + // the initial state of the player SM. Must be defined + typedef Empty initial_state; + + // transition actions + // as the functors are generic on events, fsm and source/target state, + // you can reuse them in another machine if you wish + struct TestFct + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const&, FSM&,SourceState& ,TargetState& ) + { + cout << "transition with event:" << typeid(EVT).name() << endl; + } + }; + struct start_playback + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + cout << "player::start_playback" << endl; + } + }; + struct open_drawer + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + cout << "player::open_drawer" << endl; + } + }; + struct close_drawer + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + cout << "player::close_drawer" << endl; + } + }; + struct store_cd_info + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const&,FSM& fsm ,SourceState& ,TargetState& ) + { + cout << "player::store_cd_info" << endl; + fsm.process_event(play()); + } + }; + struct stop_playback + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + cout << "player::stop_playback" << endl; + } + }; + struct pause_playback + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + cout << "player::pause_playback" << endl; + } + }; + struct resume_playback + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + cout << "player::resume_playback" << endl; + } + }; + struct stop_and_open + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + cout << "player::stop_and_open" << endl; + } + }; + struct stopped_again + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + cout << "player::stopped_again" << endl; + } + }; + // guard conditions + struct DummyGuard + { + template <class EVT,class FSM,class SourceState,class TargetState> + bool operator()(EVT const& evt,FSM& fsm,SourceState& src,TargetState& tgt) + { + return true; + } + }; + struct good_disk_format + { + template <class EVT,class FSM,class SourceState,class TargetState> + bool operator()(EVT const& evt ,FSM&,SourceState& ,TargetState& ) + { + // to test a guard condition, let's say we understand only CDs, not DVD + if (evt.disc_type != DISK_CD) + { + std::cout << "wrong disk, sorry" << std::endl; + return false; + } + return true; + } + }; + struct always_true + { + template <class EVT,class FSM,class SourceState,class TargetState> + bool operator()(EVT const& evt ,FSM&,SourceState& ,TargetState& ) + { + return true; + } + }; + // we want to define one row with the classic look. + bool auto_start(cd_detected const& evt) + { + return false; + } + + typedef player_ p; // makes transition table cleaner + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------------+----------------------+ + Row < Stopped , play , Playing , ActionSequence_ + <mpl::vector< + TestFct,start_playback> > + , DummyGuard >, + Row < Stopped , open_close , Open , open_drawer , none >, + Row < Stopped , stop , Stopped , none , none >, + // +---------+-------------+---------+---------------------------+----------------------+ + Row < Open , open_close , Empty , close_drawer , none >, + // +---------+-------------+---------+---------------------------+----------------------+ + Row < Empty , open_close , Open , open_drawer , none >, + Row < Empty , cd_detected , Stopped , store_cd_info , And_<good_disk_format, + always_true> >, + // we here also mix with some "classical row" + g_row < Empty , cd_detected , Playing , &p::auto_start >, + // +---------+-------------+---------+---------------------------+----------------------+ + Row < Playing , stop , Stopped , stop_playback , none >, + Row < Playing , pause , Paused , pause_playback , none >, + Row < Playing , open_close , Open , stop_and_open , none >, + // +---------+-------------+---------+---------------------------+----------------------+ + Row < Paused , end_pause , Playing , resume_playback , none >, + Row < Paused , stop , Stopped , stop_playback , none >, + Row < Paused , open_close , Open , stop_and_open , none > + // +---------+-------------+---------+---------------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + // Pick a back-end + typedef msm::back::state_machine<player_, msm::back::queue_container_circular> player; + + // + // Testing utilities. + // + static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" }; + void pstate(player const& p) + { + std::cout << " -> " << state_names[p.current_state()[0]] << std::endl; + } + + void test() + { + player p; + // we get the message queue and limit it to capacity 1 + // get_message_queue returns the queue container (in this case circular_buffer) + p.get_message_queue().set_capacity(1); + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + p.start(); + // go to Open, call on_exit on Empty, then action, then on_entry on Open + p.process_event(open_close()); pstate(p); + p.process_event(open_close()); pstate(p); + // will be rejected, wrong disk type + p.process_event( + cd_detected("louie, louie",DISK_DVD)); pstate(p); + p.process_event( + cd_detected("louie, louie",DISK_CD)); pstate(p); + // no need to call play() as the previous event does it in its action method + //p.process_event(play()); + + // at this point, Play is active + p.process_event(pause()); pstate(p); + // go back to Playing + p.process_event(end_pause()); pstate(p); + p.process_event(pause()); pstate(p); + p.process_event(stop()); pstate(p); + // event leading to the same state + // no action method called as it is not present in the transition table + p.process_event(stop()); pstate(p); + } +} + +int main() +{ + test(); + return 0; +} diff --git a/libs/msm/doc/HTML/examples/Flags.cpp b/libs/msm/doc/HTML/examples/Flags.cpp new file mode 100644 index 0000000000..5226bcc43a --- /dev/null +++ b/libs/msm/doc/HTML/examples/Flags.cpp @@ -0,0 +1,254 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> + +namespace msm = boost::msm; +namespace mpl = boost::mpl; + +namespace // Concrete FSM implementation +{ + // events + struct play {}; + struct end_pause {}; + struct stop {}; + struct pause {}; + struct open_close {}; + struct NextSong {}; + struct PreviousSong {}; + + // Flags. Allow information about a property of the current state + struct PlayingPaused{}; + struct CDLoaded {}; + struct FirstSongPlaying {}; + + // A "complicated" event type that carries some data. + struct cd_detected + { + cd_detected(std::string name) + : name(name) + {} + + std::string name; + }; + + // front-end: define the FSM structure + struct player_ : public msm::front::state_machine_def<player_> + { + // The list of FSM states + struct Empty : public msm::front::state<> + { + // every (optional) entry/exit methods get the event passed. + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Empty" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Empty" << std::endl;} + }; + struct Open : public msm::front::state<> + { + typedef mpl::vector1<CDLoaded> flag_list; + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Open" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Open" << std::endl;} + }; + + struct Stopped : public msm::front::state<> + { + // when stopped, the CD is loaded + typedef mpl::vector1<CDLoaded> flag_list; + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Stopped" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Stopped" << std::endl;} + }; + + // the player state machine contains a state which is himself a state machine + // as you see, no need to declare it anywhere so Playing can be developed separately + // by another team in another module. For simplicity I just declare it inside player + struct Playing_ : public msm::front::state_machine_def<Playing_> + { + // when playing, the CD is loaded and we are in either pause or playing (duh) + typedef mpl::vector2<PlayingPaused,CDLoaded> flag_list; + + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Playing" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Playing" << std::endl;} + + // The list of FSM states + struct Song1 : public msm::front::state<> + { + typedef mpl::vector1<FirstSongPlaying> flag_list; + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: First song" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: First Song" << std::endl;} + + }; + struct Song2 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: Second song" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: Second Song" << std::endl;} + }; + struct Song3 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: Third song" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: Third Song" << std::endl;} + }; + // the initial state. Must be defined + typedef Song1 initial_state; + // transition actions + void start_next_song(NextSong const&) { std::cout << "Playing::start_next_song\n"; } + void start_prev_song(PreviousSong const&) { std::cout << "Playing::start_prev_song\n"; } + // guard conditions + + typedef Playing_ pl; // makes transition table cleaner + // Transition table for Playing + struct transition_table : mpl::vector4< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Song1 , NextSong , Song2 , &pl::start_next_song >, + a_row < Song2 , PreviousSong, Song1 , &pl::start_prev_song >, + a_row < Song2 , NextSong , Song3 , &pl::start_next_song >, + a_row < Song3 , PreviousSong, Song2 , &pl::start_prev_song > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + // back-end + typedef msm::back::state_machine<Playing_> Playing; + + // state not defining any entry or exit + struct Paused : public msm::front::state<> + { + typedef mpl::vector2<PlayingPaused,CDLoaded> flag_list; + }; + + // the initial state of the player SM. Must be defined + typedef Empty initial_state; + + // transition actions + void start_playback(play const&) { std::cout << "player::start_playback\n"; } + void open_drawer(open_close const&) { std::cout << "player::open_drawer\n"; } + void close_drawer(open_close const&) { std::cout << "player::close_drawer\n"; } + void store_cd_info(cd_detected const& cd) {std::cout << "player::store_cd_info\n";} + void stop_playback(stop const&) { std::cout << "player::stop_playback\n"; } + void pause_playback(pause const&) { std::cout << "player::pause_playback\n"; } + void resume_playback(end_pause const&) { std::cout << "player::resume_playback\n"; } + void stop_and_open(open_close const&) { std::cout << "player::stop_and_open\n"; } + void stopped_again(stop const&){std::cout << "player::stopped_again\n";} + // guard conditions + + typedef player_ p; // makes transition table cleaner + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Stopped , play , Playing , &p::start_playback >, + a_row < Stopped , open_close , Open , &p::open_drawer >, + a_row < Stopped , stop , Stopped , &p::stopped_again >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Open , open_close , Empty , &p::close_drawer >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Empty , open_close , Open , &p::open_drawer >, + a_row < Empty , cd_detected , Stopped , &p::store_cd_info >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Playing , stop , Stopped , &p::stop_playback >, + a_row < Playing , pause , Paused , &p::pause_playback >, + a_row < Playing , open_close , Open , &p::stop_and_open >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Paused , end_pause , Playing , &p::resume_playback >, + a_row < Paused , stop , Stopped , &p::stop_playback >, + a_row < Paused , open_close , Open , &p::stop_and_open > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + // Pick a back-end + typedef msm::back::state_machine<player_> player; + + // + // Testing utilities. + // + static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" }; + + void pstate(player const& p) + { + std::cout << " -> " << state_names[p.current_state()[0]] << std::endl; + } + + void test() + { + player p; + + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + p.start(); + // tests some flags + std::cout << "CDLoaded active:" << std::boolalpha << p.is_flag_active<CDLoaded>() << std::endl; //=> false (no CD yet) + // go to Open, call on_exit on Empty, then action, then on_entry on Open + p.process_event(open_close()); pstate(p); + p.process_event(open_close()); pstate(p); + p.process_event(cd_detected("louie, louie")); + p.process_event(play()); + + // at this point, Play is active + std::cout << "PlayingPaused active:" << std::boolalpha << p.is_flag_active<PlayingPaused>() << std::endl;//=> true + std::cout << "FirstSong active:" << std::boolalpha << p.is_flag_active<FirstSongPlaying>() << std::endl;//=> true + + // make transition happen inside it. Player has no idea about this event but it's ok. + p.process_event(NextSong());pstate(p); //2nd song active + p.process_event(NextSong());pstate(p);//3rd song active + p.process_event(PreviousSong());pstate(p);//2nd song active + std::cout << "FirstSong active:" << std::boolalpha << p.is_flag_active<FirstSongPlaying>() << std::endl;//=> false + + std::cout << "PlayingPaused active:" << std::boolalpha << p.is_flag_active<PlayingPaused>() << std::endl;//=> true + p.process_event(pause()); pstate(p); + std::cout << "PlayingPaused active:" << std::boolalpha << p.is_flag_active<PlayingPaused>() << std::endl;//=> true + // go back to Playing + // as you see, it starts back from the original state + p.process_event(end_pause()); pstate(p); + p.process_event(pause()); pstate(p); + p.process_event(stop()); pstate(p); + std::cout << "PlayingPaused active:" << std::boolalpha << p.is_flag_active<PlayingPaused>() << std::endl;//=> false + std::cout << "CDLoaded active:" << std::boolalpha << p.is_flag_active<CDLoaded>() << std::endl;//=> true + + // event leading to the same state + p.process_event(stop()); pstate(p); + } +} + +int main() +{ + test(); + return 0; +} diff --git a/libs/msm/doc/HTML/examples/FsmAsPtr.cpp b/libs/msm/doc/HTML/examples/FsmAsPtr.cpp new file mode 100644 index 0000000000..594c331386 --- /dev/null +++ b/libs/msm/doc/HTML/examples/FsmAsPtr.cpp @@ -0,0 +1,169 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include "FsmAsPtr.h" + +#include <boost/msm/back/state_machine.hpp> +#include <boost/msm/front/euml/euml.hpp> + +// cpp: using directives are okay +using namespace std; +using namespace boost::msm::front::euml; +namespace msm = boost::msm; + +// entry/exit/action/guard logging functors +#include "logging_functors.h" + +namespace +{ + // events + BOOST_MSM_EUML_EVENT(play) + BOOST_MSM_EUML_EVENT(end_pause) + BOOST_MSM_EUML_EVENT(stop) + BOOST_MSM_EUML_EVENT(pause) + BOOST_MSM_EUML_EVENT(open_close) + BOOST_MSM_EUML_EVENT(cd_detected) + + // Concrete FSM implementation + // The list of FSM states + // state not needing any entry or exit + BOOST_MSM_EUML_STATE((),Paused) + + // it is also possible to define a state which you can implement normally + // just make it a state, as usual, and also a grammar terminal, euml_state + struct Empty_impl : public msm::front::state<> , public euml_state<Empty_impl> + { + // this allows us to add some functions + void activate_empty() {std::cout << "switching to Empty " << std::endl;} + // standard entry behavior + template <class Event,class FSM> + void on_entry(Event const& evt,FSM& fsm) + { + std::cout << "entering: Empty" << std::endl; + } + template <class Event,class FSM> + void on_exit(Event const& evt,FSM& fsm) + { + std::cout << "leaving: Empty" << std::endl; + } + }; + //instance for use in the transition table + Empty_impl const Empty; + + // create a functor and a eUML function for the activate_empty method from Entry + BOOST_MSM_EUML_METHOD(ActivateEmpty_ , activate_empty , activate_empty_ , void , void ) + + // define more states + BOOST_MSM_EUML_STATE(( Open_Entry,Open_Exit ),Open) + BOOST_MSM_EUML_STATE(( Stopped_Entry,Stopped_Exit ),Stopped) + BOOST_MSM_EUML_STATE(( Playing_Entry,Playing_Exit ),Playing) + + // it is also possible to use a plain functor, with default-constructor in the transition table + struct start_play + { + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM&,SourceState& ,TargetState& ) + { + cout << "player::start_play" << endl; + } + }; + // replaces the old transition table + BOOST_MSM_EUML_TRANSITION_TABLE(( + Playing == Stopped + play / start_play() , + Playing == Paused + end_pause / resume_playback, + // +------------------------------------------------------------------------------+ + Empty == Open + open_close / (close_drawer,activate_empty_(target_)), + // +------------------------------------------------------------------------------+ + Open == Empty + open_close / open_drawer, + Open == Paused + open_close / stop_and_open, + Open == Stopped + open_close / open_drawer, + Open == Playing + open_close / stop_and_open, + // +------------------------------------------------------------------------------+ + Paused == Playing + pause / pause_playback, + // +------------------------------------------------------------------------------+ + Stopped == Playing + stop / stop_playback, + Stopped == Paused + stop / stop_playback, + Stopped == Empty + cd_detected / (store_cd_info,process_(play)), + Stopped == Stopped + stop + // +------------------------------------------------------------------------------+ + ),transition_table) + + // create a state machine "on the fly" + BOOST_MSM_EUML_DECLARE_STATE_MACHINE(( transition_table, //STT + init_ << Empty, // Init State + no_action, // Entry + no_action, // Exit + attributes_ << no_attributes_, // Attributes + configure_ << no_configure_, // configuration + Log_No_Transition // no_transition handler + ), + my_machine_impl_) //fsm name + + + // choice of back-end + typedef msm::back::state_machine<my_machine_impl_> my_machine_impl; +} +player::player() +: fsm_(new my_machine_impl) +{ + boost::static_pointer_cast<my_machine_impl>(fsm_)->start(); +} + +void player::do_play() +{ + boost::static_pointer_cast<my_machine_impl>(fsm_)->process_event(play); +} +void player::do_pause() +{ + boost::static_pointer_cast<my_machine_impl>(fsm_)->process_event(pause); +} +void player::do_open_close() +{ + boost::static_pointer_cast<my_machine_impl>(fsm_)->process_event(open_close); +} +void player::do_end_pause() +{ + boost::static_pointer_cast<my_machine_impl>(fsm_)->process_event(end_pause); +} +void player::do_stop() +{ + boost::static_pointer_cast<my_machine_impl>(fsm_)->process_event(stop); +} +void player::do_cd_detected() +{ + boost::static_pointer_cast<my_machine_impl>(fsm_)->process_event(cd_detected); +} + + +int main() +{ + player p; + // note that we write open_close and not open_close(), like usual. Both are possible with eUML, but + // you now have less to type. + // go to Open, call on_exit on Empty, then action, then on_entry on Open + p.do_open_close(); + p.do_open_close(); + p.do_cd_detected(); + // no need to call play as the previous event does it in its action method + + // at this point, Play is active + p.do_pause(); + // go back to Playing + p.do_end_pause(); + p.do_pause(); + p.do_stop(); + // event leading to the same state + // no action method called as none is defined in the transition table + p.do_stop(); + // test call to no_transition + p.do_pause(); + return 0; +} + diff --git a/libs/msm/doc/HTML/examples/FsmAsPtr.hpp b/libs/msm/doc/HTML/examples/FsmAsPtr.hpp new file mode 100644 index 0000000000..ef0376a42e --- /dev/null +++ b/libs/msm/doc/HTML/examples/FsmAsPtr.hpp @@ -0,0 +1,36 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef FSM_AS_PTR_H +#define FSM_AS_PTR_H + +#include <boost/shared_ptr.hpp> + +class player +{ +public: + player(); + virtual ~player(){} + + // public interface + void do_play(); + void do_pause(); + void do_open_close(); + void do_end_pause(); + void do_stop(); + void do_cd_detected(); + +private: + // my state machine, hidden + boost::shared_ptr<void> fsm_; + +}; + +#endif //FSM_AS_PTR_H diff --git a/libs/msm/doc/HTML/examples/History.cpp b/libs/msm/doc/HTML/examples/History.cpp new file mode 100644 index 0000000000..483e122e7f --- /dev/null +++ b/libs/msm/doc/HTML/examples/History.cpp @@ -0,0 +1,240 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> + +namespace msm = boost::msm; +namespace mpl = boost::mpl; + +namespace +{ + // events + struct play {}; + struct end_pause {}; + struct stop {}; + struct pause {}; + struct open_close {}; + struct NextSong {}; + struct PreviousSong {}; + + // A "complicated" event type that carries some data. + struct cd_detected + { + cd_detected(std::string name) + : name(name) + {} + + std::string name; + }; + + // front-end: define the FSM structure + struct player_ : public msm::front::state_machine_def<player_> + { + // The list of FSM states + struct Empty : public msm::front::state<> + { + // every (optional) entry/exit methods get the event passed + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Empty" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Empty" << std::endl;} + }; + struct Open : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Open" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Open" << std::endl;} + }; + + struct Stopped : public msm::front::state<> + { + // when stopped, the CD is loaded + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Stopped" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Stopped" << std::endl;} + }; + + struct Playing_ : public msm::front::state_machine_def<Playing_> + { + // when playing, the CD is loaded and we are in either pause or playing (duh) + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Playing" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Playing" << std::endl;} + + // The list of FSM states + struct Song1 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: First song" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: First Song" << std::endl;} + }; + struct Song2 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: Second song" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: Second Song" << std::endl;} + }; + struct Song3 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: Third song" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: Third Song" << std::endl;} + }; + // the initial state. Must be defined + typedef Song1 initial_state; + // transition actions + void start_next_song(NextSong const&) { std::cout << "Playing::start_next_song\n"; } + void start_prev_song(PreviousSong const&) { std::cout << "Playing::start_prev_song\n"; } + // guard conditions + + typedef Playing_ pl; // makes transition table cleaner + // Transition table for Playing + struct transition_table : mpl::vector4< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Song1 , NextSong , Song2 , &pl::start_next_song >, + a_row < Song2 , PreviousSong, Song1 , &pl::start_prev_song >, + a_row < Song2 , NextSong , Song3 , &pl::start_next_song >, + a_row < Song3 , PreviousSong, Song2 , &pl::start_prev_song > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + + }; + // back-end + // demonstrates Shallow History: if the state gets activated with end_pause + // then it will remember the last active state and reactivate it + // also possible: AlwaysHistory, the last active state will always be reactivated + // or NoHistory, always restart from the initial state + typedef msm::back::state_machine<Playing_,msm::back::ShallowHistory<mpl::vector<end_pause> > > Playing; + + // state not defining any entry or exit + struct Paused : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Paused" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Paused" << std::endl;} + }; + + // the initial state of the player SM. Must be defined + typedef Empty initial_state; + + // transition actions + void start_playback(play const&) { std::cout << "player::start_playback\n"; } + void open_drawer(open_close const&) { std::cout << "player::open_drawer\n"; } + void close_drawer(open_close const&) { std::cout << "player::close_drawer\n"; } + void store_cd_info(cd_detected const& cd) {std::cout << "player::store_cd_info\n";} + void stop_playback(stop const&) { std::cout << "player::stop_playback\n"; } + void pause_playback(pause const&) { std::cout << "player::pause_playback\n"; } + void resume_playback(end_pause const&) { std::cout << "player::resume_playback\n"; } + void stop_and_open(open_close const&) { std::cout << "player::stop_and_open\n"; } + void stopped_again(stop const&){std::cout << "player::stopped_again\n";} + // guard conditions + + typedef player_ p; // makes transition table cleaner + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Stopped , play , Playing , &p::start_playback >, + a_row < Stopped , open_close , Open , &p::open_drawer >, + a_row < Stopped , stop , Stopped , &p::stopped_again >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Open , open_close , Empty , &p::close_drawer >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Empty , open_close , Open , &p::open_drawer >, + a_row < Empty , cd_detected , Stopped , &p::store_cd_info >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Playing , stop , Stopped , &p::stop_playback >, + a_row < Playing , pause , Paused , &p::pause_playback >, + a_row < Playing , open_close , Open , &p::stop_and_open >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Paused , end_pause , Playing , &p::resume_playback >, + a_row < Paused , stop , Stopped , &p::stop_playback >, + a_row < Paused , open_close , Open , &p::stop_and_open > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + + }; + // Pick a back-end + typedef msm::back::state_machine<player_> player; + + // + // Testing utilities. + // + static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" }; + + void pstate(player const& p) + { + std::cout << " -> " << state_names[p.current_state()[0]] << std::endl; + } + + void test() + { + player p; + + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + p.start(); + // go to Open, call on_exit on Empty, then action, then on_entry on Open + p.process_event(open_close()); pstate(p); + p.process_event(open_close()); pstate(p); + p.process_event(cd_detected("louie, louie")); + p.process_event(play()); + + // at this point, Play is active + // make transition happen inside it. Player has no idea about this event but it's ok. + p.process_event(NextSong());pstate(p); //2nd song active + p.process_event(NextSong());pstate(p);//3rd song active + p.process_event(PreviousSong());pstate(p);//2nd song active + + p.process_event(pause()); pstate(p); + // go back to Playing + // as you see, remembers the original state as end_pause is an history trigger + p.process_event(end_pause()); pstate(p); + p.process_event(pause()); pstate(p); + p.process_event(stop()); pstate(p); + // event leading to the same state + p.process_event(stop()); pstate(p); + // play does not trigger shallow history => start back from 1st song + p.process_event(play()); pstate(p); + } +} + +int main() +{ + test(); + return 0; +} diff --git a/libs/msm/doc/HTML/examples/HistoryEuml.cpp b/libs/msm/doc/HTML/examples/HistoryEuml.cpp new file mode 100644 index 0000000000..15a68e2aa3 --- /dev/null +++ b/libs/msm/doc/HTML/examples/HistoryEuml.cpp @@ -0,0 +1,180 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <iostream> + +#include <boost/msm/back/state_machine.hpp> +#include <boost/msm/front/euml/euml.hpp> + +using namespace std; +using namespace boost::msm::front::euml; +namespace msm = boost::msm; +namespace mpl = boost::mpl; + +// entry/exit/action/guard logging functors +#include "logging_functors.h" + +namespace // Concrete FSM implementation +{ + // events + BOOST_MSM_EUML_EVENT(play) + BOOST_MSM_EUML_EVENT(end_pause) + BOOST_MSM_EUML_EVENT(stop) + BOOST_MSM_EUML_EVENT(pause) + BOOST_MSM_EUML_EVENT(open_close) + BOOST_MSM_EUML_EVENT(next_song) + BOOST_MSM_EUML_EVENT(previous_song) + + // A "complicated" event type that carries some data. + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(std::string,cd_name) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(DiskTypeEnum,cd_type) + BOOST_MSM_EUML_ATTRIBUTES((attributes_ << cd_name << cd_type ), cd_detected_attributes) + BOOST_MSM_EUML_EVENT_WITH_ATTRIBUTES(cd_detected,cd_detected_attributes) + + // Concrete FSM implementation + // The list of FSM states + // state not defining any entry or exit + BOOST_MSM_EUML_STATE((),Paused) + BOOST_MSM_EUML_STATE(( Empty_Entry,Empty_Exit ),Empty) + BOOST_MSM_EUML_STATE(( Open_Entry,Open_Exit ),Open) + BOOST_MSM_EUML_STATE(( Stopped_Entry,Stopped_Exit ),Stopped) + + + // Playing is now a state machine itself. + + // It has 3 substates + BOOST_MSM_EUML_STATE(( Song1_Entry,Song1_Exit ),Song1) + BOOST_MSM_EUML_STATE(( Song2_Entry,Song2_Exit ),Song2) + BOOST_MSM_EUML_STATE(( Song3_Entry,Song3_Exit ),Song3) + + + // Playing has a transition table + BOOST_MSM_EUML_TRANSITION_TABLE(( + // +------------------------------------------------------------------------------+ + Song2 == Song1 + next_song / start_next_song, + Song1 == Song2 + previous_song / start_prev_song, + Song3 == Song2 + next_song / start_next_song, + Song2 == Song3 + previous_song / start_prev_song + // +------------------------------------------------------------------------------+ + ),playing_transition_table ) + + // VC9 cannot compile the typedef with build_sm if one is also used for player + BOOST_MSM_EUML_DECLARE_STATE_MACHINE( (playing_transition_table, //STT + init_ << Song1 // Init State + ),Playing_) + // choice of back-end + typedef msm::back::state_machine<Playing_, + msm::back::ShallowHistory<mpl::vector<BOOST_MSM_EUML_EVENT_NAME(end_pause)> > > Playing_type; + Playing_type const Playing; + + // guard conditions + BOOST_MSM_EUML_ACTION(good_disk_format) + { + template <class FSM,class EVT,class SourceState,class TargetState> + bool operator()(EVT const& evt,FSM&,SourceState& ,TargetState& ) + { + // to test a guard condition, let's say we understand only CDs, not DVD + if (evt.get_attribute(cd_type)!=DISK_CD) + { + std::cout << "wrong disk, sorry" << std::endl; + // just for logging, does not block any transition + return true; + } + std::cout << "good disk" << std::endl; + return true; + } + }; + // replaces the old transition table + BOOST_MSM_EUML_TRANSITION_TABLE(( + Playing == Stopped + play / start_playback, + Playing == Paused + end_pause / resume_playback, + // +------------------------------------------------------------------------------+ + Empty == Open + open_close / close_drawer, + // +------------------------------------------------------------------------------+ + Open == Empty + open_close / open_drawer, + Open == Paused + open_close / stop_and_open, + Open == Stopped + open_close / open_drawer, + Open == Playing + open_close / stop_and_open, + // +------------------------------------------------------------------------------+ + Paused == Playing + pause / pause_playback, + // +------------------------------------------------------------------------------+ + Stopped == Playing + stop / stop_playback, + Stopped == Paused + stop / stop_playback, + Stopped == Empty + cd_detected [good_disk_format&& + (event_(cd_type)==Int_<DISK_CD>())] + / (store_cd_info,process_(play)), + Stopped == Stopped + stop + // +------------------------------------------------------------------------------+ + ),transition_table) + + // create a state machine "on the fly" + BOOST_MSM_EUML_DECLARE_STATE_MACHINE(( transition_table, //STT + init_ << Empty, // Init State + no_action, // Entry + no_action, // Exit + attributes_ << no_attributes_, // Attributes + configure_ << no_configure_, // configuration + Log_No_Transition // no_transition handler + ), + player_) //fsm name + + // choice of back-end + typedef msm::back::state_machine<player_> player; + + // + // Testing utilities. + // + static char const* const state_names[] = { "Stopped", "Paused", "Open", "Empty", "Playing" }; + void pstate(player const& p) + { + std::cout << " -> " << state_names[p.current_state()[0]] << std::endl; + } + + void test() + { + player p; + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + p.start(); + // go to Open, call on_exit on Empty, then action, then on_entry on Open + p.process_event(open_close); pstate(p); + p.process_event(open_close); pstate(p); + // will be rejected, wrong disk type + p.process_event( + cd_detected("louie, louie",DISK_DVD)); pstate(p); + p.process_event( + cd_detected("louie, louie",DISK_CD)); pstate(p); + // no need to call play as the previous event does it in its action method + //p.process_event(play); + + // make transition happen inside it. Player has no idea about this event but it's ok. + p.process_event(next_song);pstate(p); //2nd song active + p.process_event(next_song);pstate(p);//3rd song active + p.process_event(previous_song);pstate(p);//2nd song active + + // at this point, Play is active + p.process_event(pause); pstate(p); + // go back to Playing + // as you see, remembers the original state as end_pause is an history trigger + p.process_event(end_pause); pstate(p); + p.process_event(pause); pstate(p); + p.process_event(stop); pstate(p); + // event leading to the same state + // no action method called as none is defined in the transition table + p.process_event(stop); pstate(p); + // test call to no_transition + p.process_event(play); pstate(p); + } +} + +int main() +{ + test(); + return 0; +} diff --git a/libs/msm/doc/HTML/examples/MsmComposite.cpp b/libs/msm/doc/HTML/examples/MsmComposite.cpp new file mode 100644 index 0000000000..556aad42b3 --- /dev/null +++ b/libs/msm/doc/HTML/examples/MsmComposite.cpp @@ -0,0 +1,243 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <boost/msm/back/state_machine.hpp> +#include <boost/msm/front/state_machine_def.hpp> + +namespace msm = boost::msm; +namespace mpl = boost::mpl; + +#include <iostream> +#ifdef WIN32 +#include "windows.h" +#else +#include <sys/time.h> +#endif + +namespace test_fsm // Concrete FSM implementation +{ + // events + struct play {}; + struct end_pause {}; + struct stop {}; + struct pause {}; + struct open_close {}; + struct cd_detected{}; + struct NextSong {}; + struct PreviousSong {}; + + + // Concrete FSM implementation + struct player_ : public msm::front::state_machine_def<player_> + { + // no need for exception handling or message queue + typedef int no_exception_thrown; + typedef int no_message_queue; + + // The list of FSM states + struct Empty : public msm::front::state<> + { + // optional entry/exit methods + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {/*std::cout << "entering: Empty" << std::endl;*/} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {/*std::cout << "leaving: Empty" << std::endl;*/} + }; + struct Open : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {/*std::cout << "entering: Open" << std::endl;*/} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {/*std::cout << "leaving: Open" << std::endl;*/} + }; + + struct Stopped : public msm::front::state<> + { + // when stopped, the CD is loaded + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {/*std::cout << "entering: Stopped" << std::endl;*/} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {/*std::cout << "leaving: Stopped" << std::endl;*/} + }; + + struct Playing_ : public msm::front::state_machine_def<Playing_> + { + // no need for exception handling or message queue + typedef int no_exception_thrown; + typedef int no_message_queue; + + // The list of FSM states + struct Song1 : public msm::front::state<> {}; + struct Song2 : public msm::front::state<> {}; + struct Song3 : public msm::front::state<> {}; + // the initial state. Must be defined + typedef Song1 initial_state; + // transition actions + void start_next_song(NextSong const&) { /*std::cout << "Playing::start_next_song\n";*/ } + void start_prev_song(PreviousSong const&) { /*std::cout << "Playing::start_prev_song\n"; */} + // guard conditions + + typedef Playing_ pl; // makes transition table cleaner + // Transition table for Playing + struct transition_table : mpl::vector4< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Song1 , NextSong , Song2 , &pl::start_next_song >, + a_row < Song2 , PreviousSong, Song1 , &pl::start_prev_song >, + a_row < Song2 , NextSong , Song3 , &pl::start_next_song >, + a_row < Song3 , PreviousSong, Song2 , &pl::start_prev_song > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + typedef msm::back::state_machine<Playing_> Playing; + + // state not defining any entry or exit + struct Paused : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {/*std::cout << "entering: Paused" << std::endl;*/} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {/*std::cout << "leaving: Paused" << std::endl;*/} + }; + + // the initial state of the player SM. Must be defined + typedef Empty initial_state; + + // transition actions + void start_playback(play const&) { } + void open_drawer(open_close const&) { } + void close_drawer(open_close const&) { } + void store_cd_info(cd_detected const& cd) { } + void stop_playback(stop const&) { } + void pause_playback(pause const&) { } + void resume_playback(end_pause const&) { } + void stop_and_open(open_close const&) { } + void stopped_again(stop const&){} + // guard conditions + + typedef player_ p; // makes transition table cleaner + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Stopped , play , Playing , &p::start_playback >, + a_row < Stopped , open_close , Open , &p::open_drawer >, + a_row < Stopped , stop , Stopped , &p::stopped_again >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Open , open_close , Empty , &p::close_drawer >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Empty , open_close , Open , &p::open_drawer >, + a_row < Empty , cd_detected , Stopped , &p::store_cd_info >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Playing , stop , Stopped , &p::stop_playback >, + a_row < Playing , pause , Paused , &p::pause_playback >, + a_row < Playing , open_close , Open , &p::stop_and_open >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Paused , end_pause , Playing , &p::resume_playback >, + a_row < Paused , stop , Stopped , &p::stop_playback >, + a_row < Paused , open_close , Open , &p::stop_and_open > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + typedef msm::back::state_machine<player_> player; + + // + // Testing utilities. + // + static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" }; + + void pstate(player const& p) + { + std::cout << " -> " << state_names[p.current_state()[0]] << std::endl; + } + +} + +#ifndef WIN32 +long mtime(struct timeval& tv1,struct timeval& tv2) +{ + return (tv2.tv_sec-tv1.tv_sec) *1000000 + ((tv2.tv_usec-tv1.tv_usec)); +} +#endif + + +int main() +{ + // for timing +#ifdef WIN32 + LARGE_INTEGER res; + ::QueryPerformanceFrequency(&res); + LARGE_INTEGER li,li2; +#else + struct timeval tv1,tv2; + gettimeofday(&tv1,NULL); +#endif + + test_fsm::player p2; + p2.start(); + // for timing +#ifdef WIN32 + ::QueryPerformanceCounter(&li); +#else + gettimeofday(&tv1,NULL); +#endif + for (int i=0;i<100;++i) + { + p2.process_event(test_fsm::open_close()); + p2.process_event(test_fsm::open_close()); + p2.process_event(test_fsm::cd_detected()); + p2.process_event(test_fsm::play()); + for (int j=0;j<100;++j) + { + p2.process_event(test_fsm::NextSong()); + p2.process_event(test_fsm::NextSong()); + p2.process_event(test_fsm::PreviousSong()); + p2.process_event(test_fsm::PreviousSong()); + } + + p2.process_event(test_fsm::pause()); + // go back to Playing + p2.process_event(test_fsm::end_pause()); + p2.process_event(test_fsm::pause()); + p2.process_event(test_fsm::stop()); + // event leading to the same state + p2.process_event(test_fsm::stop()); + p2.process_event(test_fsm::open_close()); + p2.process_event(test_fsm::open_close()); + } +#ifdef WIN32 + ::QueryPerformanceCounter(&li2); +#else + gettimeofday(&tv2,NULL); +#endif +#ifdef WIN32 + std::cout << "msm took in s:" << (double)(li2.QuadPart-li.QuadPart)/res.QuadPart <<"\n" <<std::endl; +#else + std::cout << "msm took in us:" << mtime(tv1,tv2) <<"\n" <<std::endl; +#endif + return 0; +} + diff --git a/libs/msm/doc/HTML/examples/MsmSimple.cpp b/libs/msm/doc/HTML/examples/MsmSimple.cpp new file mode 100644 index 0000000000..f074a2fc4e --- /dev/null +++ b/libs/msm/doc/HTML/examples/MsmSimple.cpp @@ -0,0 +1,199 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <boost/msm/back/state_machine.hpp> +#include <boost/msm/front/state_machine_def.hpp> + +namespace msm = boost::msm; +namespace mpl = boost::mpl; + +#include <iostream> +#ifdef WIN32 +#include "windows.h" +#else +#include <sys/time.h> +#endif + +namespace test_fsm // Concrete FSM implementation +{ + // events + struct play {}; + struct end_pause {}; + struct stop {}; + struct pause {}; + struct open_close {}; + struct cd_detected{}; + + // Concrete FSM implementation + struct player_ : public msm::front::state_machine_def<player_> + { + // no need for exception handling or message queue + typedef int no_exception_thrown; + typedef int no_message_queue; + + // The list of FSM states + struct Empty : public msm::front::state<> + { + // optional entry/exit methods + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {/*std::cout << "entering: Empty" << std::endl;*/} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {/*std::cout << "leaving: Empty" << std::endl;*/} + }; + struct Open : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {/*std::cout << "entering: Open" << std::endl;*/} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {/*std::cout << "leaving: Open" << std::endl;*/} + }; + + struct Stopped : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {/*std::cout << "entering: Stopped" << std::endl;*/} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {/*std::cout << "leaving: Stopped" << std::endl;*/} + }; + + struct Playing : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {/*std::cout << "entering: Playing" << std::endl;*/} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {/*std::cout << "leaving: Playing" << std::endl;*/} + }; + + struct Paused : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {/*std::cout << "entering: Paused" << std::endl;*/} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {/*std::cout << "leaving: Paused" << std::endl;*/} + }; + + // the initial state of the player SM. Must be defined + typedef Empty initial_state; + // transition actions + void start_playback(play const&) { } + void open_drawer(open_close const&) { } + void close_drawer(open_close const&) { } + void store_cd_info(cd_detected const& cd) { } + void stop_playback(stop const&) { } + void pause_playback(pause const&) { } + void resume_playback(end_pause const&) { } + void stop_and_open(open_close const&) { } + void stopped_again(stop const&){} + // guard conditions + + typedef player_ p; // makes transition table cleaner + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + _row < Stopped , play , Playing >, + _row < Stopped , open_close , Open >, + _row < Stopped , stop , Stopped >, + // +---------+-------------+---------+---------------------+----------------------+ + _row < Open , open_close , Empty >, + // +---------+-------------+---------+---------------------+----------------------+ + _row < Empty , open_close , Open >, + _row < Empty , cd_detected , Stopped >, + // +---------+-------------+---------+---------------------+----------------------+ + _row < Playing , stop , Stopped >, + _row < Playing , pause , Paused >, + _row < Playing , open_close , Open >, + // +---------+-------------+---------+---------------------+----------------------+ + _row < Paused , end_pause , Playing >, + _row < Paused , stop , Stopped >, + _row < Paused , open_close , Open > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + typedef msm::back::state_machine<player_> player; + + // + // Testing utilities. + // + static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" }; + + void pstate(player const& p) + { + std::cout << " -> " << state_names[p.current_state()[0]] << std::endl; + } + +} + +#ifndef WIN32 +long mtime(struct timeval& tv1,struct timeval& tv2) +{ + return (tv2.tv_sec-tv1.tv_sec) *1000000 + ((tv2.tv_usec-tv1.tv_usec)); +} +#endif + + +int main() +{ + // for timing +#ifdef WIN32 + LARGE_INTEGER res; + ::QueryPerformanceFrequency(&res); + LARGE_INTEGER li,li2; +#else + struct timeval tv1,tv2; + gettimeofday(&tv1,NULL); +#endif + + test_fsm::player p2; + p2.start(); + // for timing +#ifdef WIN32 + ::QueryPerformanceCounter(&li); +#else + gettimeofday(&tv1,NULL); +#endif + for (int i=0;i<100;++i) + { + p2.process_event(test_fsm::open_close()); + p2.process_event(test_fsm::open_close()); + p2.process_event(test_fsm::cd_detected()); + p2.process_event(test_fsm::play()); + p2.process_event(test_fsm::pause()); + // go back to Playing + p2.process_event(test_fsm::end_pause()); + p2.process_event(test_fsm::pause()); + p2.process_event(test_fsm::stop()); + // event leading to the same state + p2.process_event(test_fsm::stop()); + p2.process_event(test_fsm::open_close()); + p2.process_event(test_fsm::open_close()); + } +#ifdef WIN32 + ::QueryPerformanceCounter(&li2); +#else + gettimeofday(&tv2,NULL); +#endif +#ifdef WIN32 + std::cout << "msm took in s:" << (double)(li2.QuadPart-li.QuadPart)/res.QuadPart <<"\n" <<std::endl; +#else + std::cout << "msm took in us:" << mtime(tv1,tv2) <<"\n" <<std::endl; +#endif + return 0; +} + diff --git a/libs/msm/doc/HTML/examples/MsmSimpleFunctors.cpp b/libs/msm/doc/HTML/examples/MsmSimpleFunctors.cpp new file mode 100644 index 0000000000..95c0aa650e --- /dev/null +++ b/libs/msm/doc/HTML/examples/MsmSimpleFunctors.cpp @@ -0,0 +1,254 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <boost/msm/back/state_machine.hpp> +#include <boost/msm/front/state_machine_def.hpp> +#include <boost/msm/front/functor_row.hpp> + +namespace msm = boost::msm; +namespace mpl = boost::mpl; +using namespace msm::front; + +#include <iostream> +#ifdef WIN32 +#include "windows.h" +#else +#include <sys/time.h> +#endif + +namespace test_fsm // Concrete FSM implementation +{ + // events + struct play {}; + struct end_pause {}; + struct stop {}; + struct pause {}; + struct open_close {}; + struct cd_detected{}; + + // Concrete FSM implementation + struct player_ : public msm::front::state_machine_def<player_> + { + // no need for exception handling or message queue + typedef int no_exception_thrown; + typedef int no_message_queue; + + // The list of FSM states + struct Empty : public msm::front::state<> + { + // optional entry/exit methods + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {/*std::cout << "entering: Empty" << std::endl;*/} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {/*std::cout << "leaving: Empty" << std::endl;*/} + }; + struct Open : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {/*std::cout << "entering: Open" << std::endl;*/} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {/*std::cout << "leaving: Open" << std::endl;*/} + }; + + struct Stopped : public msm::front::state<> + { + // when stopped, the CD is loaded + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {/*std::cout << "entering: Stopped" << std::endl;*/} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {/*std::cout << "leaving: Stopped" << std::endl;*/} + }; + + struct Playing : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {/*std::cout << "entering: Playing" << std::endl;*/} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {/*std::cout << "leaving: Playing" << std::endl;*/} + }; + + // state not defining any entry or exit + struct Paused : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {/*std::cout << "entering: Paused" << std::endl;*/} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {/*std::cout << "leaving: Paused" << std::endl;*/} + }; + + // the initial state of the player SM. Must be defined + typedef Empty initial_state; + // transition actions + struct start_playback + { + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM&,SourceState& ,TargetState& ) + { + } + }; + struct open_drawer + { + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM&,SourceState& ,TargetState& ) + { + } + }; + struct close_drawer + { + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM&,SourceState& ,TargetState& ) + { + } + }; + struct store_cd_info + { + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const&, FSM& fsm ,SourceState& ,TargetState& ) + { + } + }; + struct stop_playback + { + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM&,SourceState& ,TargetState& ) + { + } + }; + struct pause_playback + { + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM&,SourceState& ,TargetState& ) + { + } + }; + struct resume_playback + { + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM&,SourceState& ,TargetState& ) + { + } + }; + struct stop_and_open + { + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM&,SourceState& ,TargetState& ) + { + } + }; + struct stopped_again + { + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM&,SourceState& ,TargetState& ) + { + } + }; + // guard conditions + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + Row < Stopped , play , Playing , start_playback >, + Row < Stopped , open_close , Open , open_drawer >, + Row < Stopped , stop , Stopped , stopped_again >, + // +---------+-------------+---------+---------------------+----------------------+ + Row < Open , open_close , Empty , close_drawer >, + // +---------+-------------+---------+---------------------+----------------------+ + Row < Empty , open_close , Open , open_drawer >, + Row < Empty , cd_detected , Stopped , store_cd_info >, + // +---------+-------------+---------+---------------------+----------------------+ + Row < Playing , stop , Stopped , stop_playback >, + Row < Playing , pause , Paused , pause_playback >, + Row < Playing , open_close , Open , stop_and_open >, + // +---------+-------------+---------+---------------------+----------------------+ + Row < Paused , end_pause , Playing , resume_playback >, + Row < Paused , stop , Stopped , stop_playback >, + Row < Paused , open_close , Open , stop_and_open > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + typedef msm::back::state_machine<player_> player; + + // + // Testing utilities. + // + static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" }; + + void pstate(player const& p) + { + std::cout << " -> " << state_names[p.current_state()[0]] << std::endl; + } + +} + +#ifndef WIN32 +long mtime(struct timeval& tv1,struct timeval& tv2) +{ + return (tv2.tv_sec-tv1.tv_sec) *1000000 + ((tv2.tv_usec-tv1.tv_usec)); +} +#endif + + +int main() +{ + // for timing +#ifdef WIN32 + LARGE_INTEGER res; + ::QueryPerformanceFrequency(&res); + LARGE_INTEGER li,li2; +#else + struct timeval tv1,tv2; + gettimeofday(&tv1,NULL); +#endif + test_fsm::player p2; + p2.start(); + // for timing +#ifdef WIN32 + ::QueryPerformanceCounter(&li); +#else + gettimeofday(&tv1,NULL); +#endif + for (int i=0;i<100;++i) + { + p2.process_event(test_fsm::open_close()); + p2.process_event(test_fsm::open_close()); + p2.process_event(test_fsm::cd_detected()); + p2.process_event(test_fsm::play()); + p2.process_event(test_fsm::pause()); + // go back to Playing + p2.process_event(test_fsm::end_pause()); + p2.process_event(test_fsm::pause()); + p2.process_event(test_fsm::stop()); + // event leading to the same state + p2.process_event(test_fsm::stop()); + p2.process_event(test_fsm::open_close()); + p2.process_event(test_fsm::open_close()); + } +#ifdef WIN32 + ::QueryPerformanceCounter(&li2); +#else + gettimeofday(&tv2,NULL); +#endif +#ifdef WIN32 + std::cout << "msm took in s:" << (double)(li2.QuadPart-li.QuadPart)/res.QuadPart <<"\n" <<std::endl; +#else + std::cout << "msm took in us:" << mtime(tv1,tv2) <<"\n" <<std::endl; +#endif + return 0; +} + diff --git a/libs/msm/doc/HTML/examples/Orthogonal-deferred.cpp b/libs/msm/doc/HTML/examples/Orthogonal-deferred.cpp new file mode 100644 index 0000000000..facfa67c12 --- /dev/null +++ b/libs/msm/doc/HTML/examples/Orthogonal-deferred.cpp @@ -0,0 +1,314 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> + +namespace msm = boost::msm; +namespace mpl = boost::mpl; + +namespace +{ + // events + struct play {}; + struct end_pause {}; + struct stop {}; + struct pause {}; + struct open_close {}; + struct NextSong {}; + struct PreviousSong {}; + struct error_found {}; + struct end_error {}; + + // Flags. Allow information about a property of the current state + struct PlayingPaused{}; + struct CDLoaded {}; + struct FirstSongPlaying {}; + + // A "complicated" event type that carries some data. + struct cd_detected + { + cd_detected(std::string name) + : name(name) + {} + + std::string name; + }; + + // front-end: define the FSM structure + struct player_ : public msm::front::state_machine_def<player_> + { + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) + { + std::cout << "entering: Player" << std::endl; + } + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) + { + std::cout << "leaving: Player" << std::endl; + } + // The list of FSM states + struct Empty : public msm::front::state<> + { + // if the play event arrives in this state, defer it until a state handles it or + // rejects it + typedef mpl::vector<play> deferred_events; + // every (optional) entry/exit methods get the event passed. + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Empty" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Empty" << std::endl;} + }; + struct Open : public msm::front::state<> + { + // if the play event arrives in this state, defer it until a state handles it or + // rejects it + typedef mpl::vector<play> deferred_events; + typedef mpl::vector1<CDLoaded> flag_list; + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Open" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Open" << std::endl;} + }; + + struct Stopped : public msm::front::state<> + { + // when stopped, the CD is loaded + typedef mpl::vector1<CDLoaded> flag_list; + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Stopped" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Stopped" << std::endl;} + }; + + // the player state machine contains a state which is himself a state machine + // as you see, no need to declare it anywhere so Playing can be developed separately + // by another team in another module. For simplicity I just declare it inside player + struct Playing_ : public msm::front::state_machine_def<Playing_> + { + // when playing, the CD is loaded and we are in either pause or playing (duh) + typedef mpl::vector2<PlayingPaused,CDLoaded> flag_list; + + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Playing" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Playing" << std::endl;} + // The list of FSM states + struct Song1 : public msm::front::state<> + { + typedef mpl::vector1<FirstSongPlaying> flag_list; + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: First song" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: First Song" << std::endl;} + }; + struct Song2 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: Second song" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: Second Song" << std::endl;} + }; + struct Song3 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: Third song" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: Third Song" << std::endl;} + }; + // the initial state. Must be defined + typedef Song1 initial_state; + // transition actions + void start_next_song(NextSong const&) { std::cout << "Playing::start_next_song\n"; } + void start_prev_song(PreviousSong const&) { std::cout << "Playing::start_prev_song\n"; } + // guard conditions + + typedef Playing_ pl; // makes transition table cleaner + // Transition table for Playing + struct transition_table : mpl::vector4< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Song1 , NextSong , Song2 , &pl::start_next_song >, + a_row < Song2 , PreviousSong, Song1 , &pl::start_prev_song >, + a_row < Song2 , NextSong , Song3 , &pl::start_next_song >, + a_row < Song3 , PreviousSong, Song2 , &pl::start_prev_song > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + // back-end + typedef msm::back::state_machine<Playing_> Playing; + + // state not defining any entry or exit + struct Paused : public msm::front::state<> + { + typedef mpl::vector2<PlayingPaused,CDLoaded> flag_list; + }; + struct AllOk : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: AllOk" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: AllOk" << std::endl;} + }; + // this state is also made terminal so that all the events are blocked + struct ErrorMode : //public msm::front::terminate_state<> // ErrorMode terminates the state machine + public msm::front::interrupt_state<end_error> // ErroMode just interrupts. Will resume if + // the event end_error is generated + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: ErrorMode" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: ErrorMode" << std::endl;} + }; + // the initial state of the player SM. Must be defined + typedef mpl::vector<Empty,AllOk> initial_state; + + // transition actions + void start_playback(play const&) { std::cout << "player::start_playback\n"; } + void open_drawer(open_close const&) { std::cout << "player::open_drawer\n"; } + void close_drawer(open_close const&) { std::cout << "player::close_drawer\n"; } + void store_cd_info(cd_detected const& cd) {std::cout << "player::store_cd_info\n";} + void stop_playback(stop const&) { std::cout << "player::stop_playback\n"; } + void pause_playback(pause const&) { std::cout << "player::pause_playback\n"; } + void resume_playback(end_pause const&) { std::cout << "player::resume_playback\n"; } + void stop_and_open(open_close const&) { std::cout << "player::stop_and_open\n"; } + void stopped_again(stop const&){std::cout << "player::stopped_again\n";} + void report_error(error_found const&) {std::cout << "player::report_error\n";} + void report_end_error(end_error const&) {std::cout << "player::report_end_error\n";} + + // guard conditions + + typedef player_ p; // makes transition table cleaner + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Stopped , play , Playing , &p::start_playback >, + a_row < Stopped , open_close , Open , &p::open_drawer >, + a_row < Stopped , stop , Stopped , &p::stopped_again >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Open , open_close , Empty , &p::close_drawer >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Empty , open_close , Open , &p::open_drawer >, + a_row < Empty , cd_detected , Stopped , &p::store_cd_info >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Playing , stop , Stopped , &p::stop_playback >, + a_row < Playing , pause , Paused , &p::pause_playback >, + a_row < Playing , open_close , Open , &p::stop_and_open >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Paused , end_pause , Playing , &p::resume_playback >, + a_row < Paused , stop , Stopped , &p::stop_playback >, + a_row < Paused , open_close , Open , &p::stop_and_open >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < AllOk , error_found ,ErrorMode, &p::report_error >, + a_row <ErrorMode,end_error ,AllOk , &p::report_end_error > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + // Pick a back-end + typedef msm::back::state_machine<player_> player; + + // + // Testing utilities. + // + static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused","AllOk","ErrorMode" }; + + void pstate(player const& p) + { + // we have now several active states, which we show + for (unsigned int i=0;i<player::nr_regions::value;++i) + { + std::cout << " -> " << state_names[p.current_state()[i]] << std::endl; + } + } + + void test() + { + player p; + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + p.start(); + // test deferred event + // deferred in Empty and Open, will be handled only after event cd_detected + p.process_event(play()); + + // tests some flags + std::cout << "CDLoaded active:" << std::boolalpha << p.is_flag_active<CDLoaded>() << std::endl; //=> false (no CD yet) + // go to Open, call on_exit on Empty, then action, then on_entry on Open + p.process_event(open_close()); pstate(p); + p.process_event(open_close()); pstate(p); + p.process_event(cd_detected("louie, louie")); + + // at this point, Play is active (was deferred) + std::cout << "PlayingPaused active:" << std::boolalpha << p.is_flag_active<PlayingPaused>() << std::endl;//=> true + std::cout << "FirstSong active:" << std::boolalpha << p.is_flag_active<FirstSongPlaying>() << std::endl;//=> true + + // make transition happen inside it. Player has no idea about this event but it's ok. + p.process_event(NextSong());pstate(p); //2nd song active + p.process_event(NextSong());pstate(p);//3rd song active + p.process_event(PreviousSong());pstate(p);//2nd song active + std::cout << "FirstSong active:" << std::boolalpha << p.is_flag_active<FirstSongPlaying>() << std::endl;//=> false + + std::cout << "PlayingPaused active:" << std::boolalpha << p.is_flag_active<PlayingPaused>() << std::endl;//=> true + p.process_event(pause()); pstate(p); + std::cout << "PlayingPaused active:" << std::boolalpha << p.is_flag_active<PlayingPaused>() << std::endl;//=> true + // go back to Playing + // as you see, it starts back from the original state + p.process_event(end_pause()); pstate(p); + p.process_event(pause()); pstate(p); + p.process_event(stop()); pstate(p); + std::cout << "PlayingPaused active:" << std::boolalpha << p.is_flag_active<PlayingPaused>() << std::endl;//=> false + std::cout << "CDLoaded active:" << std::boolalpha << p.is_flag_active<CDLoaded>() << std::endl;//=> true + // by default, the flags are OR'ed but you can also use AND. Then the flag must be present in + // all of the active states + std::cout << "CDLoaded active with AND:" << std::boolalpha << p.is_flag_active<CDLoaded,player::Flag_AND>() << std::endl;//=> false + + // event leading to the same state + p.process_event(stop()); pstate(p); + + // event leading to a terminal/interrupt state + p.process_event(error_found()); pstate(p); + // try generating more events + std::cout << "Trying to generate another event" << std::endl; // will not work, fsm is terminated or interrupted + p.process_event(play());pstate(p); + std::cout << "Trying to end the error" << std::endl; // will work only if ErrorMode is interrupt state + p.process_event(end_error());pstate(p); + std::cout << "Trying to generate another event" << std::endl; // will work only if ErrorMode is interrupt state + p.process_event(play());pstate(p); + std::cout << "stop fsm" << std::endl; + p.stop(); + + } +} + +int main() +{ + test(); + return 0; +} diff --git a/libs/msm/doc/HTML/examples/Orthogonal-deferred2.cpp b/libs/msm/doc/HTML/examples/Orthogonal-deferred2.cpp new file mode 100644 index 0000000000..e5fa6c7cb3 --- /dev/null +++ b/libs/msm/doc/HTML/examples/Orthogonal-deferred2.cpp @@ -0,0 +1,303 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> +#include <boost/msm/front/functor_row.hpp> + +namespace msm = boost::msm; +namespace mpl = boost::mpl; +using namespace boost::msm::front; + +namespace +{ + // events + struct play {}; + struct end_pause {}; + struct stop {}; + struct pause {}; + struct open_close {}; + struct NextSong {}; + struct PreviousSong {}; + struct error_found {}; + struct end_error {}; + + // Flags. Allow information about a property of the current state + struct PlayingPaused{}; + struct CDLoaded {}; + struct FirstSongPlaying {}; + + // A "complicated" event type that carries some data. + struct cd_detected + { + cd_detected(std::string name) + : name(name) + {} + + std::string name; + }; + + // front-end: define the FSM structure + struct player_ : public msm::front::state_machine_def<player_> + { + // we want deferred events and no state requires deferred events (only the fsm in the + // transition table), so the fsm does. + typedef int activate_deferred_events; + // The list of FSM states + struct Empty : public msm::front::state<> + { + // every (optional) entry/exit methods get the event passed. + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Empty" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Empty" << std::endl;} + }; + struct Open : public msm::front::state<> + { + typedef mpl::vector1<CDLoaded> flag_list; + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Open" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Open" << std::endl;} + }; + + struct Stopped : public msm::front::state<> + { + // when stopped, the CD is loaded + typedef mpl::vector1<CDLoaded> flag_list; + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Stopped" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Stopped" << std::endl;} + }; + + // the player state machine contains a state which is himself a state machine + // as you see, no need to declare it anywhere so Playing can be developed separately + // by another team in another module. For simplicity I just declare it inside player + struct Playing_ : public msm::front::state_machine_def<Playing_> + { + // when playing, the CD is loaded and we are in either pause or playing (duh) + typedef mpl::vector2<PlayingPaused,CDLoaded> flag_list; + + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Playing" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Playing" << std::endl;} + // The list of FSM states + struct Song1 : public msm::front::state<> + { + typedef mpl::vector1<FirstSongPlaying> flag_list; + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: First song" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: First Song" << std::endl;} + }; + struct Song2 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: Second song" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: Second Song" << std::endl;} + }; + struct Song3 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: Third song" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: Third Song" << std::endl;} + }; + // the initial state. Must be defined + typedef Song1 initial_state; + // transition actions + void start_next_song(NextSong const&) { std::cout << "Playing::start_next_song\n"; } + void start_prev_song(PreviousSong const&) { std::cout << "Playing::start_prev_song\n"; } + // guard conditions + + typedef Playing_ pl; // makes transition table cleaner + // Transition table for Playing + struct transition_table : mpl::vector4< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Song1 , NextSong , Song2 , &pl::start_next_song >, + a_row < Song2 , PreviousSong, Song1 , &pl::start_prev_song >, + a_row < Song2 , NextSong , Song3 , &pl::start_next_song >, + a_row < Song3 , PreviousSong, Song2 , &pl::start_prev_song > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + // back-end + typedef msm::back::state_machine<Playing_> Playing; + + // state not defining any entry or exit + struct Paused : public msm::front::state<> + { + typedef mpl::vector2<PlayingPaused,CDLoaded> flag_list; + }; + struct AllOk : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: AllOk" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: AllOk" << std::endl;} + }; + // this state is also made terminal so that all the events are blocked + struct ErrorMode : //public msm::front::terminate_state<> // ErrorMode terminates the state machine + public msm::front::interrupt_state<end_error> // ErroMode just interrupts. Will resume if + // the event end_error is generated + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: ErrorMode" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: ErrorMode" << std::endl;} + }; + // the initial state of the player SM. Must be defined + typedef mpl::vector<Empty,AllOk> initial_state; + + // transition actions + void start_playback(play const&) { std::cout << "player::start_playback\n"; } + void open_drawer(open_close const&) { std::cout << "player::open_drawer\n"; } + void close_drawer(open_close const&) { std::cout << "player::close_drawer\n"; } + void store_cd_info(cd_detected const& cd) {std::cout << "player::store_cd_info\n";} + void stop_playback(stop const&) { std::cout << "player::stop_playback\n"; } + void pause_playback(pause const&) { std::cout << "player::pause_playback\n"; } + void resume_playback(end_pause const&) { std::cout << "player::resume_playback\n"; } + void stop_and_open(open_close const&) { std::cout << "player::stop_and_open\n"; } + void stopped_again(stop const&){std::cout << "player::stopped_again\n";} + void report_error(error_found const&) {std::cout << "player::report_error\n";} + void report_end_error(end_error const&) {std::cout << "player::report_end_error\n";} + + // guard conditions + + typedef player_ p; // makes transition table cleaner + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Stopped , play , Playing , &p::start_playback >, + a_row < Stopped , open_close , Open , &p::open_drawer >, + a_row < Stopped , stop , Stopped , &p::stopped_again >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Open , open_close , Empty , &p::close_drawer >, + Row < Open , play , none , Defer , none >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Empty , open_close , Open , &p::open_drawer >, + a_row < Empty , cd_detected , Stopped , &p::store_cd_info >, + Row < Empty , play , none , Defer , none >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Playing , stop , Stopped , &p::stop_playback >, + a_row < Playing , pause , Paused , &p::pause_playback >, + a_row < Playing , open_close , Open , &p::stop_and_open >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Paused , end_pause , Playing , &p::resume_playback >, + a_row < Paused , stop , Stopped , &p::stop_playback >, + a_row < Paused , open_close , Open , &p::stop_and_open >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < AllOk , error_found ,ErrorMode, &p::report_error >, + a_row <ErrorMode,end_error ,AllOk , &p::report_end_error > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + // Pick a back-end + typedef msm::back::state_machine<player_> player; + + // + // Testing utilities. + // + static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused","AllOk","ErrorMode" }; + + void pstate(player const& p) + { + // we have now several active states, which we show + for (unsigned int i=0;i<player::nr_regions::value;++i) + { + std::cout << " -> " << state_names[p.current_state()[i]] << std::endl; + } + } + + void test() + { + player p; + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + p.start(); + // test deferred event + // deferred in Empty and Open, will be handled only after event cd_detected + p.process_event(play()); + + // tests some flags + std::cout << "CDLoaded active:" << std::boolalpha << p.is_flag_active<CDLoaded>() << std::endl; //=> false (no CD yet) + // go to Open, call on_exit on Empty, then action, then on_entry on Open + p.process_event(open_close()); pstate(p); + p.process_event(open_close()); pstate(p); + p.process_event(cd_detected("louie, louie")); + + // at this point, Play is active (was deferred) + std::cout << "PlayingPaused active:" << std::boolalpha << p.is_flag_active<PlayingPaused>() << std::endl;//=> true + std::cout << "FirstSong active:" << std::boolalpha << p.is_flag_active<FirstSongPlaying>() << std::endl;//=> true + + // make transition happen inside it. Player has no idea about this event but it's ok. + p.process_event(NextSong());pstate(p); //2nd song active + p.process_event(NextSong());pstate(p);//3rd song active + p.process_event(PreviousSong());pstate(p);//2nd song active + std::cout << "FirstSong active:" << std::boolalpha << p.is_flag_active<FirstSongPlaying>() << std::endl;//=> false + + std::cout << "PlayingPaused active:" << std::boolalpha << p.is_flag_active<PlayingPaused>() << std::endl;//=> true + p.process_event(pause()); pstate(p); + std::cout << "PlayingPaused active:" << std::boolalpha << p.is_flag_active<PlayingPaused>() << std::endl;//=> true + // go back to Playing + // as you see, it starts back from the original state + p.process_event(end_pause()); pstate(p); + p.process_event(pause()); pstate(p); + p.process_event(stop()); pstate(p); + std::cout << "PlayingPaused active:" << std::boolalpha << p.is_flag_active<PlayingPaused>() << std::endl;//=> false + std::cout << "CDLoaded active:" << std::boolalpha << p.is_flag_active<CDLoaded>() << std::endl;//=> true + // by default, the flags are OR'ed but you can also use AND. Then the flag must be present in + // all of the active states + std::cout << "CDLoaded active with AND:" << std::boolalpha << p.is_flag_active<CDLoaded,player::Flag_AND>() << std::endl;//=> false + + // event leading to the same state + p.process_event(stop()); pstate(p); + + // event leading to a terminal/interrupt state + p.process_event(error_found()); pstate(p); + // try generating more events + std::cout << "Trying to generate another event" << std::endl; // will not work, fsm is terminated or interrupted + p.process_event(play());pstate(p); + std::cout << "Trying to end the error" << std::endl; // will work only if ErrorMode is interrupt state + p.process_event(end_error());pstate(p); + std::cout << "Trying to generate another event" << std::endl; // will work only if ErrorMode is interrupt state + p.process_event(play());pstate(p); + + } +} + +int main() +{ + test(); + return 0; +} diff --git a/libs/msm/doc/HTML/examples/OrthogonalDeferredEuml.cpp b/libs/msm/doc/HTML/examples/OrthogonalDeferredEuml.cpp new file mode 100644 index 0000000000..9b3553776b --- /dev/null +++ b/libs/msm/doc/HTML/examples/OrthogonalDeferredEuml.cpp @@ -0,0 +1,263 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <vector> +#include <iostream> + +#include <boost/msm/back/state_machine.hpp> +#include <boost/msm/front/euml/euml.hpp> + +using namespace std; +using namespace boost::msm::front::euml; +namespace msm = boost::msm; + +// entry/exit/action/guard logging functors +#include "logging_functors.h" + +namespace // Concrete FSM implementation +{ + // events + BOOST_MSM_EUML_EVENT(play) + BOOST_MSM_EUML_EVENT(end_pause) + BOOST_MSM_EUML_EVENT(stop) + BOOST_MSM_EUML_EVENT(pause) + BOOST_MSM_EUML_EVENT(open_close) + BOOST_MSM_EUML_EVENT(next_song) + BOOST_MSM_EUML_EVENT(previous_song) + BOOST_MSM_EUML_EVENT(end_error) + BOOST_MSM_EUML_EVENT(error_found) + + // A "complicated" event type that carries some data. + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(std::string,cd_name) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(DiskTypeEnum,cd_type) + BOOST_MSM_EUML_ATTRIBUTES((attributes_ << cd_name << cd_type ), cd_detected_attributes) + BOOST_MSM_EUML_EVENT_WITH_ATTRIBUTES(cd_detected,cd_detected_attributes) + + + // Flags. Allow information about a property of the current state + BOOST_MSM_EUML_FLAG(PlayingPaused) + BOOST_MSM_EUML_FLAG(CDLoaded) + BOOST_MSM_EUML_FLAG(FirstSongPlaying) + + // Concrete FSM implementation + + // The list of FSM states + + BOOST_MSM_EUML_STATE(( Empty_Entry, + Empty_Exit, + attributes_ << no_attributes_, + configure_ << play // defer play + ), + Empty) + + BOOST_MSM_EUML_STATE(( Open_Entry, + Open_Exit, + attributes_ << no_attributes_, + configure_<< CDLoaded << play // defer play, flag state with CDLoaded + ), + Open) + + BOOST_MSM_EUML_STATE(( Stopped_Entry, + Stopped_Exit, + attributes_ << no_attributes_, + configure_<< CDLoaded // flag state with CDLoaded + ), + Stopped) + + // state not defining any entry or exit + BOOST_MSM_EUML_STATE(( no_action, + no_action, + attributes_ << no_attributes_, + configure_<< PlayingPaused << CDLoaded // flag state with CDLoaded and PlayingPaused + ), + Paused) + + BOOST_MSM_EUML_STATE(( AllOk_Entry,AllOk_Exit ),AllOk) + + // a terminate state + //BOOST_MSM_EUML_TERMINATE_STATE(( ErrorMode_Entry,ErrorMode_Exit ),ErrorMode) + // or as an interrupt state + BOOST_MSM_EUML_INTERRUPT_STATE(( end_error,ErrorMode_Entry,ErrorMode_Exit ),ErrorMode) + + // Playing is now a state machine itself. + // It has 3 substates + BOOST_MSM_EUML_STATE(( Song1_Entry, + Song1_Exit, + attributes_ << no_attributes_, + configure_<< FirstSongPlaying ),Song1) + + BOOST_MSM_EUML_STATE(( Song2_Entry,Song2_Exit ),Song2) + BOOST_MSM_EUML_STATE(( Song3_Entry,Song3_Exit ),Song3) + + // Playing has a transition table + BOOST_MSM_EUML_TRANSITION_TABLE(( + // +------------------------------------------------------------------------------+ + Song2 == Song1 + next_song / start_next_song, + Song1 == Song2 + previous_song / start_prev_song, + Song3 == Song2 + next_song / start_next_song, + Song2 == Song3 + previous_song / start_prev_song + // +------------------------------------------------------------------------------+ + ),playing_transition_table ) + + BOOST_MSM_EUML_DECLARE_STATE_MACHINE( (playing_transition_table, //STT + init_ << Song1, // Init State + no_action, // entry + no_action, // exit + attributes_ << no_attributes_, //attributes + configure_<< PlayingPaused << CDLoaded //flags + ),Playing_) + // choice of back-end + typedef msm::back::state_machine<Playing_> Playing_type; + Playing_type const Playing; + + // guard conditions + BOOST_MSM_EUML_ACTION(good_disk_format) + { + template <class FSM,class EVT,class SourceState,class TargetState> + bool operator()(EVT const& evt,FSM&,SourceState& ,TargetState& ) + { + // to test a guard condition, let's say we understand only CDs, not DVD + if (evt.get_attribute(cd_type)!=DISK_CD) + { + std::cout << "wrong disk, sorry" << std::endl; + // just for logging, does not block any transition + return true; + } + std::cout << "good disk" << std::endl; + return true; + } + }; + // replaces the old transition table + BOOST_MSM_EUML_TRANSITION_TABLE(( + Playing == Stopped + play / start_playback , + Playing == Paused + end_pause / resume_playback, + // +------------------------------------------------------------------------------+ + Empty == Open + open_close / close_drawer, + // +------------------------------------------------------------------------------+ + Open == Empty + open_close / open_drawer, + Open == Paused + open_close / stop_and_open, + Open == Stopped + open_close / open_drawer, + Open == Playing + open_close / stop_and_open, + // +------------------------------------------------------------------------------+ + Paused == Playing + pause / pause_playback, + // +------------------------------------------------------------------------------+ + Stopped == Playing + stop / stop_playback, + Stopped == Paused + stop / stop_playback, + Stopped == Empty + cd_detected [good_disk_format&& + (event_(cd_type)==Int_<DISK_CD>())] + / (store_cd_info,process_(play)), + Stopped == Stopped + stop, + ErrorMode == AllOk + error_found / report_error, + AllOk == ErrorMode+ end_error / report_end_error + // +------------------------------------------------------------------------------+ + ),transition_table) + + // create a state machine "on the fly" + BOOST_MSM_EUML_DECLARE_STATE_MACHINE(( transition_table, //STT + init_ << Empty<< AllOk, // Init State + no_action, // Entry + no_action, // Exit + attributes_ << no_attributes_, // Attributes + configure_ << no_configure_, // configuration + Log_No_Transition // no_transition handler + ), + player_) //fsm name + + // choice of back-end + typedef msm::back::state_machine<player_> player; + + // + // Testing utilities. + // + static char const* const state_names[] = { "Stopped", "Paused", "Open", "Empty", "Playing","AllOk","ErrorMode" }; + void pstate(player const& p) + { + // we have now several active states, which we show + for (unsigned int i=0;i<player::nr_regions::value;++i) + { + std::cout << " -> " << state_names[p.current_state()[i]] << std::endl; + } + } + + void test() + { + player p; + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + p.start(); + + // tests some flags + std::cout << "CDLoaded active:" << std::boolalpha + << p.is_flag_active<BOOST_MSM_EUML_FLAG_NAME(CDLoaded)>() << std::endl; //=> false (no CD yet) + // go to Open, call on_exit on Empty, then action, then on_entry on Open + p.process_event(open_close); pstate(p); + p.process_event(open_close); pstate(p); + // will be rejected, wrong disk type + p.process_event( + cd_detected("louie, louie",DISK_DVD)); pstate(p); + p.process_event( + cd_detected("louie, louie",DISK_CD)); pstate(p); + // no need to call play as the previous event does it in its action method + //p.process_event(play); + + // at this point, Play is active + std::cout << "PlayingPaused active:" << std::boolalpha + << p.is_flag_active<BOOST_MSM_EUML_FLAG_NAME(PlayingPaused)>() << std::endl;//=> true + std::cout << "FirstSong active:" << std::boolalpha + << p.is_flag_active<BOOST_MSM_EUML_FLAG_NAME(FirstSongPlaying)>() << std::endl;//=> true + + // make transition happen inside it. Player has no idea about this event but it's ok. + p.process_event(next_song);pstate(p); //2nd song active + p.process_event(next_song);pstate(p);//3rd song active + p.process_event(previous_song);pstate(p);//2nd song active + std::cout << "FirstSong active:" << std::boolalpha + << p.is_flag_active<BOOST_MSM_EUML_FLAG_NAME(FirstSongPlaying)>() << std::endl;//=> false + std::cout << "PlayingPaused active:" << std::boolalpha + << p.is_flag_active<BOOST_MSM_EUML_FLAG_NAME(PlayingPaused)>() << std::endl;//=> true + + // at this point, Play is active + p.process_event(pause); pstate(p); + std::cout << "PlayingPaused active:" << std::boolalpha + << p.is_flag_active<BOOST_MSM_EUML_FLAG_NAME(PlayingPaused)>() << std::endl;//=> true + + // go back to Playing + p.process_event(end_pause); pstate(p); + p.process_event(pause); pstate(p); + p.process_event(stop); pstate(p); + std::cout << "PlayingPaused active:" << std::boolalpha + << p.is_flag_active<BOOST_MSM_EUML_FLAG_NAME(PlayingPaused)>() << std::endl;//=> false + std::cout << "CDLoaded active:" << std::boolalpha + << p.is_flag_active<BOOST_MSM_EUML_FLAG_NAME(CDLoaded)>() << std::endl;//=> true + // by default, the flags are OR'ed but you can also use AND. Then the flag must be present in + // all of the active states + std::cout << "CDLoaded active with AND:" << std::boolalpha + << p.is_flag_active<BOOST_MSM_EUML_FLAG_NAME(CDLoaded),player::Flag_AND>() << std::endl;//=> false + + // event leading to the same state + // no action method called as none is defined in the transition table + p.process_event(stop); pstate(p); + + // event leading to a terminal/interrupt state + p.process_event(error_found); pstate(p); + // try generating more events + std::cout << "Trying to generate another event" << std::endl; // will not work, fsm is terminated or interrupted + p.process_event(play);pstate(p); + std::cout << "Trying to end the error" << std::endl; // will work only if ErrorMode is interrupt state + p.process_event(end_error);pstate(p); + std::cout << "Trying to generate another event" << std::endl; // will work only if ErrorMode is interrupt state + p.process_event(play);pstate(p); + + } +} + +int main() +{ + test(); + return 0; +} diff --git a/libs/msm/doc/HTML/examples/OrthogonalDeferredEuml2.cpp b/libs/msm/doc/HTML/examples/OrthogonalDeferredEuml2.cpp new file mode 100644 index 0000000000..d0a80c69ae --- /dev/null +++ b/libs/msm/doc/HTML/examples/OrthogonalDeferredEuml2.cpp @@ -0,0 +1,251 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <vector> +#include <iostream> + +#include <boost/msm/back/state_machine.hpp> +#include <boost/msm/front/euml/euml.hpp> + +using namespace std; +using namespace boost::msm::front::euml; +namespace msm = boost::msm; + +// entry/exit/action/guard logging functors +#include "logging_functors.h" + +namespace // Concrete FSM implementation +{ + // events + BOOST_MSM_EUML_EVENT(play) + BOOST_MSM_EUML_EVENT(end_pause) + BOOST_MSM_EUML_EVENT(stop) + BOOST_MSM_EUML_EVENT(pause) + BOOST_MSM_EUML_EVENT(open_close) + BOOST_MSM_EUML_EVENT(next_song) + BOOST_MSM_EUML_EVENT(previous_song) + BOOST_MSM_EUML_EVENT(end_error) + BOOST_MSM_EUML_EVENT(error_found) + + // A "complicated" event type that carries some data. + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(std::string,cd_name) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(DiskTypeEnum,cd_type) + BOOST_MSM_EUML_ATTRIBUTES((attributes_ << cd_name << cd_type ), cd_detected_attributes) + BOOST_MSM_EUML_EVENT_WITH_ATTRIBUTES(cd_detected,cd_detected_attributes) + + // Flags. Allow information about a property of the current state + BOOST_MSM_EUML_FLAG(PlayingPaused) + BOOST_MSM_EUML_FLAG(CDLoaded) + BOOST_MSM_EUML_FLAG(FirstSongPlaying) + + // Concrete FSM implementation + // The list of FSM states + BOOST_MSM_EUML_STATE(( Empty_Entry, + Empty_Exit, + attributes_ << no_attributes_, + configure_ << no_configure_ + ), + Empty) + + BOOST_MSM_EUML_STATE(( Open_Entry, + Open_Exit, + attributes_ << no_attributes_, + configure_<< CDLoaded // flag state with CDLoaded + ), + Open) + + BOOST_MSM_EUML_STATE(( Stopped_Entry, + Stopped_Exit, + attributes_ << no_attributes_, + configure_<< CDLoaded // flag state with CDLoaded + ), + Stopped) + + // state not defining any entry or exit + BOOST_MSM_EUML_STATE(( no_action, + no_action, + attributes_ << no_attributes_, + configure_<< PlayingPaused << CDLoaded // flag state with CDLoaded and PlayingPaused + ), + Paused) + + BOOST_MSM_EUML_STATE(( AllOk_Entry,AllOk_Exit ),AllOk) + + // a terminate state + //BOOST_MSM_EUML_TERMINATE_STATE(( ErrorMode_Entry,ErrorMode_Exit ),ErrorMode) + // or as an interrupt state + BOOST_MSM_EUML_INTERRUPT_STATE(( end_error,ErrorMode_Entry,ErrorMode_Exit ),ErrorMode) + + // Playing is now a state machine itself. + + // It has 3 substates + BOOST_MSM_EUML_STATE(( Song1_Entry, + Song1_Exit, + attributes_ << no_attributes_, + configure_<< FirstSongPlaying ),Song1) + + BOOST_MSM_EUML_STATE(( Song2_Entry,Song2_Exit ),Song2) + BOOST_MSM_EUML_STATE(( Song3_Entry,Song3_Exit ),Song3) + + + // Playing has a transition table + BOOST_MSM_EUML_TRANSITION_TABLE(( + // +------------------------------------------------------------------------------+ + Song2 == Song1 + next_song / start_next_song, + Song1 == Song2 + previous_song / start_prev_song, + Song3 == Song2 + next_song / start_next_song, + Song2 == Song3 + previous_song / start_prev_song + // +------------------------------------------------------------------------------+ + ),playing_transition_table ) + + BOOST_MSM_EUML_DECLARE_STATE_MACHINE( (playing_transition_table, //STT + init_ << Song1, // Init State + no_action, // entry + no_action, // exit + attributes_ << no_attributes_, //attributes + configure_<< PlayingPaused << CDLoaded //flags + ),Playing_) + + // choice of back-end + typedef msm::back::state_machine<Playing_> Playing_type; + Playing_type const Playing; + + // guard conditions + BOOST_MSM_EUML_ACTION(good_disk_format) + { + template <class FSM,class EVT,class SourceState,class TargetState> + bool operator()(EVT const& evt,FSM&,SourceState& ,TargetState& ) + { + // to test a guard condition, let's say we understand only CDs, not DVD + if (evt.get_attribute(cd_type)!=DISK_CD) + { + std::cout << "wrong disk, sorry" << std::endl; + // just for logging, does not block any transition + return true; + } + std::cout << "good disk" << std::endl; + return true; + } + }; + // replaces the old transition table + BOOST_MSM_EUML_TRANSITION_TABLE(( + Playing == Stopped + play / start_playback , + Playing == Paused + end_pause / resume_playback , + // +------------------------------------------------------------------------------+ + Empty == Open + open_close / close_drawer, + // we now defer using the defer_ function. This will need deferred_events as config (see below) + Empty + play / defer_ , + // +------------------------------------------------------------------------------+ + Open == Empty + open_close / open_drawer , + Open == Paused + open_close / stop_and_open , + Open == Stopped + open_close / open_drawer , + Open == Playing + open_close / stop_and_open , + // we now defer using the defer_ function. This will need deferred_events as config (see below) + Open + play / defer_ , + // +------------------------------------------------------------------------------+ + Paused == Playing + pause / pause_playback , + // +------------------------------------------------------------------------------+ + Stopped == Playing + stop / stop_playback , + Stopped == Paused + stop / stop_playback , + Stopped == Empty + cd_detected [good_disk_format&& + (event_(cd_type)==Int_<DISK_CD>())] + / (store_cd_info,process_(play)), + Stopped == Stopped + stop , + ErrorMode == AllOk + error_found / report_error , + AllOk == ErrorMode+ end_error / report_end_error + // +------------------------------------------------------------------------------+ + ),transition_table) + + // create a state machine "on the fly" + BOOST_MSM_EUML_DECLARE_STATE_MACHINE(( transition_table, //STT + init_ << Empty << AllOk, // Init State + no_action, // Entry + no_action, // Exit + attributes_ << no_attributes_, // Attributes + configure_ << deferred_events, // configuration + Log_No_Transition // no_transition handler + ), + player_) //fsm name + + // choice of back-end + typedef msm::back::state_machine<player_> player; + + // + // Testing utilities. + // + static char const* const state_names[] = { "Stopped", "Paused", "Open", "Empty", "Playing","AllOk","ErrorMode" }; + void pstate(player const& p) + { + // we have now several active states, which we show + for (unsigned int i=0;i<player::nr_regions::value;++i) + { + std::cout << " -> " << state_names[p.current_state()[i]] << std::endl; + } + } + + void test() + { + player p; + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + p.start(); + + // tests some flags + std::cout << "CDLoaded active:" << std::boolalpha + << p.is_flag_active<BOOST_MSM_EUML_FLAG_NAME(CDLoaded)>() << std::endl; //=> false (no CD yet) + // go to Open, call on_exit on Empty, then action, then on_entry on Open + p.process_event(open_close); pstate(p); + p.process_event(open_close); pstate(p); + // will be rejected, wrong disk type + p.process_event( + cd_detected("louie, louie",DISK_DVD)); pstate(p); + p.process_event( + cd_detected("louie, louie",DISK_CD)); pstate(p); + // no need to call play as the previous event does it in its action method + //p.process_event(play); + + // at this point, Play is active + p.process_event(pause); pstate(p); + std::cout << "PlayingPaused active:" << std::boolalpha + << p.is_flag_active<BOOST_MSM_EUML_FLAG_NAME(PlayingPaused)>() << std::endl;//=> true + + // go back to Playing + p.process_event(end_pause); pstate(p); + p.process_event(pause); pstate(p); + p.process_event(stop); pstate(p); + std::cout << "PlayingPaused active:" << std::boolalpha + << p.is_flag_active<BOOST_MSM_EUML_FLAG_NAME(PlayingPaused)>() << std::endl;//=> false + std::cout << "CDLoaded active:" << std::boolalpha + << p.is_flag_active<BOOST_MSM_EUML_FLAG_NAME(CDLoaded)>() << std::endl;//=> true + // by default, the flags are OR'ed but you can also use AND. Then the flag must be present in + // all of the active states + std::cout << "CDLoaded active with AND:" << std::boolalpha + << p.is_flag_active<BOOST_MSM_EUML_FLAG_NAME(CDLoaded),player::Flag_AND>() << std::endl;//=> false + + // event leading to the same state + // no action method called as none is defined in the transition table + p.process_event(stop); pstate(p); + + // event leading to a terminal/interrupt state + p.process_event(error_found); pstate(p); + // try generating more events + std::cout << "Trying to generate another event" << std::endl; // will not work, fsm is terminated or interrupted + p.process_event(play);pstate(p); + std::cout << "Trying to end the error" << std::endl; // will work only if ErrorMode is interrupt state + p.process_event(end_error);pstate(p); + std::cout << "Trying to generate another event" << std::endl; // will work only if ErrorMode is interrupt state + p.process_event(play);pstate(p); + } +} + +int main() +{ + test(); + return 0; +} diff --git a/libs/msm/doc/HTML/examples/ParsingDigits.cpp b/libs/msm/doc/HTML/examples/ParsingDigits.cpp new file mode 100644 index 0000000000..6dadb4d53e --- /dev/null +++ b/libs/msm/doc/HTML/examples/ParsingDigits.cpp @@ -0,0 +1,479 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#define FUSION_MAX_VECTOR_SIZE 20 + +#include <boost/msm/back/state_machine.hpp> +#include "char_event_dispatcher.hpp" +#include <boost/msm/front/state_machine_def.hpp> +#include <boost/msm/front/functor_row.hpp> +#include <boost/timer.hpp> + +namespace msm = boost::msm; +namespace mpl = boost::mpl; +using namespace msm::front; + +#include <iostream> +#ifdef WIN32 +#include "windows.h" +#else +#include <sys/time.h> +#endif + +// events +struct end_sub {template <class Event> end_sub(Event const&){}}; +struct other_char {}; +struct default_char {}; +struct eos {}; + + +namespace test_fsm // Concrete FSM implementation +{ + // Concrete FSM implementation + struct parsing_ : public msm::front::state_machine_def<parsing_> + { + // no need for exception handling or message queue + typedef int no_exception_thrown; + typedef int no_message_queue; + + struct Waiting : public msm::front::state<> + { + // optional entry/exit methods + //template <class Event,class FSM> + //void on_entry(Event const&,FSM& ) {std::cout << "entering: Waiting" << std::endl;} + //template <class Event,class FSM> + //void on_exit(Event const&,FSM& ) {std::cout << "leaving: Waiting" << std::endl;} + }; + struct Digit1 : public msm::front::state<> + { + // optional entry/exit methods + //template <class Event,class FSM> + //void on_entry(Event const&,FSM& ) {std::cout << "entering: Digit1" << std::endl;} + //template <class Event,class FSM> + //void on_exit(Event const&,FSM& ) {std::cout << "leaving: Digit1" << std::endl;} + }; + struct Digit2 : public msm::front::state<> + { + // optional entry/exit methods + //template <class Event,class FSM> + //void on_entry(Event const&,FSM& ) {std::cout << "entering: Digit2" << std::endl;} + //template <class Event,class FSM> + //void on_exit(Event const&,FSM& ) {std::cout << "leaving: Digit2" << std::endl;} + }; + struct Digit3 : public msm::front::state<> + { + // optional entry/exit methods + //template <class Event,class FSM> + //void on_entry(Event const&,FSM& ) {std::cout << "entering: Digit3" << std::endl;} + //template <class Event,class FSM> + //void on_exit(Event const&,FSM& ) {std::cout << "leaving: Digit3" << std::endl;} + }; + struct Digit4 : public msm::front::state<> + { + // optional entry/exit methods + //template <class Event,class FSM> + //void on_entry(Event const&,FSM& ) {std::cout << "entering: Digit4" << std::endl;} + //template <class Event,class FSM> + //void on_exit(Event const&,FSM& ) {std::cout << "leaving: Digit4" << std::endl;} + }; + struct MinusChar1 : public msm::front::state<> + { + // optional entry/exit methods + //template <class Event,class FSM> + //void on_entry(Event const&,FSM& ) {std::cout << "entering: MinusChar1" << std::endl;} + //template <class Event,class FSM> + //void on_exit(Event const&,FSM& ) {std::cout << "leaving: MinusChar1" << std::endl;} + }; + struct Digit5 : public msm::front::state<> + { + // optional entry/exit methods + //template <class Event,class FSM> + //void on_entry(Event const&,FSM& ) {std::cout << "entering: Digit5" << std::endl;} + //template <class Event,class FSM> + //void on_exit(Event const&,FSM& ) {std::cout << "leaving: Digit5" << std::endl;} + }; + struct Digit6 : public msm::front::state<> + { + // optional entry/exit methods + //template <class Event,class FSM> + //void on_entry(Event const&,FSM& ) {std::cout << "entering: Digit6" << std::endl;} + //template <class Event,class FSM> + //void on_exit(Event const&,FSM& ) {std::cout << "leaving: Digit6" << std::endl;} + }; + struct Digit7 : public msm::front::state<> + { + // optional entry/exit methods + //template <class Event,class FSM> + //void on_entry(Event const&,FSM& ) {std::cout << "entering: Digit7" << std::endl;} + //template <class Event,class FSM> + //void on_exit(Event const&,FSM& ) {std::cout << "leaving: Digit7" << std::endl;} + }; + struct Digit8 : public msm::front::state<> + { + // optional entry/exit methods + //template <class Event,class FSM> + //void on_entry(Event const&,FSM& ) {std::cout << "entering: Digit8" << std::endl;} + //template <class Event,class FSM> + //void on_exit(Event const&,FSM& ) {std::cout << "leaving: Digit8" << std::endl;} + }; + struct MinusChar2 : public msm::front::state<> + { + // optional entry/exit methods + //template <class Event,class FSM> + //void on_entry(Event const&,FSM& ) {std::cout << "entering: MinusChar2" << std::endl;} + //template <class Event,class FSM> + //void on_exit(Event const&,FSM& ) {std::cout << "leaving: MinusChar2" << std::endl;} + }; + struct Digit9 : public msm::front::state<> + { + // optional entry/exit methods + //template <class Event,class FSM> + //void on_entry(Event const&,FSM& ) {std::cout << "entering: Digit9" << std::endl;} + //template <class Event,class FSM> + //void on_exit(Event const&,FSM& ) {std::cout << "leaving: Digit9" << std::endl;} + }; + struct Digit10 : public msm::front::state<> + { + // optional entry/exit methods + //template <class Event,class FSM> + //void on_entry(Event const&,FSM& ) {std::cout << "entering: Digit10" << std::endl;} + //template <class Event,class FSM> + //void on_exit(Event const&,FSM& ) {std::cout << "leaving: Digit10" << std::endl;} + }; + struct Digit11 : public msm::front::state<> + { + // optional entry/exit methods + //template <class Event,class FSM> + //void on_entry(Event const&,FSM& ) {std::cout << "entering: Digit11" << std::endl;} + //template <class Event,class FSM> + //void on_exit(Event const&,FSM& ) {std::cout << "leaving: Digit11" << std::endl;} + }; + struct Digit12 : public msm::front::state<> + { + // optional entry/exit methods + //template <class Event,class FSM> + //void on_entry(Event const&,FSM& ) {std::cout << "entering: Digit12" << std::endl;} + //template <class Event,class FSM> + //void on_exit(Event const&,FSM& ) {std::cout << "leaving: Digit12" << std::endl;} + }; + struct MinusChar3 : public msm::front::state<> + { + // optional entry/exit methods + //template <class Event,class FSM> + //void on_entry(Event const&,FSM& ) {std::cout << "entering: MinusChar3" << std::endl;} + //template <class Event,class FSM> + //void on_exit(Event const&,FSM& ) {std::cout << "leaving: MinusChar3" << std::endl;} + }; + struct Digit13 : public msm::front::state<> + { + // optional entry/exit methods + //template <class Event,class FSM> + //void on_entry(Event const&,FSM& ) {std::cout << "entering: Digit13" << std::endl;} + //template <class Event,class FSM> + //void on_exit(Event const&,FSM& ) {std::cout << "leaving: Digit13" << std::endl;} + }; + struct Digit14 : public msm::front::state<> + { + // optional entry/exit methods + //template <class Event,class FSM> + //void on_entry(Event const&,FSM& ) {std::cout << "entering: Digit14" << std::endl;} + //template <class Event,class FSM> + //void on_exit(Event const&,FSM& ) {std::cout << "leaving: Digit14" << std::endl;} + }; + struct Digit15 : public msm::front::state<> + { + // optional entry/exit methods + //template <class Event,class FSM> + //void on_entry(Event const&,FSM& ) {std::cout << "entering: Digit15" << std::endl;} + //template <class Event,class FSM> + //void on_exit(Event const&,FSM& ) {std::cout << "leaving: Digit15" << std::endl;} + }; + //struct Start : public msm::front::state<> {}; + struct Parsed : public msm::front::state<> {}; + //struct Failed : public msm::front::state<> {}; + + // the initial state of the player SM. Must be defined + typedef Waiting initial_state; + // transition actions + struct test_fct + { + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM&,SourceState& ,TargetState& ) + { + std::cout << "Parsed!" << std::endl; + } + }; + + // guard conditions + + + // Transition table for parsing_ + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +-------------+-------------------+---------+---------------------+----------------------+ + Row < Waiting , digit , Digit1 >, + Row < Digit1 , digit , Digit2 >, + Row < Digit2 , digit , Digit3 >, + Row < Digit3 , digit , Digit4 >, + Row < Digit4 , event_char<'-'> , MinusChar1 >, + Row < MinusChar1 , digit , Digit5 >, + Row < Digit5 , digit , Digit6 >, + Row < Digit6 , digit , Digit7 >, + Row < Digit7 , digit , Digit8 >, + Row < Digit8 , event_char<'-'> , MinusChar2 >, + Row < MinusChar2 , digit , Digit9 >, + Row < Digit9 , digit , Digit10 >, + Row < Digit10 , digit , Digit11 >, + Row < Digit11 , digit , Digit12 >, + Row < Digit12 , event_char<'-'> , MinusChar3 >, + Row < MinusChar3 , digit , Digit13 >, + Row < Digit13 , digit , Digit14 >, + Row < Digit14 , digit , Digit15 >, + Row < Digit15 , eos , Parsed >, + Row < Parsed , eos , Waiting > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + + + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + typedef msm::back::state_machine<parsing_> parsing; +} + +#ifndef WIN32 +long mtime(struct timeval& tv1,struct timeval& tv2) +{ + return (tv2.tv_sec-tv1.tv_sec) *1000000 + ((tv2.tv_usec-tv1.tv_usec)); +} +#endif + +// This declares the statically-initialized char_event_dispatcher instance. +template <class Fsm> +const msm::back::char_event_dispatcher<Fsm> +msm::back::char_event_dispatcher<Fsm>::instance; + +struct Parser +{ + Parser():p(){p.start();} + void new_char(char c) + { + typedef msm::back::char_event_dispatcher<test_fsm::parsing> table; + table::instance.process_event(p,c); + } + void finish_string(){p.process_event(eos());} + void reinit(){p.process_event(eos());} + test_fsm::parsing p; +}; + +void msm_match(const char* input) +{ + test_fsm::parsing p; + p.start(); + + int j=0; + while(input[j]) + //for (size_t j=0;j<len;++j) + { + switch (input[j]) + { + case '0': + p.process_event(char_0()); + break; + case '1': + p.process_event(char_1()); + break; + case '2': + p.process_event(char_2()); + break; + case '3': + p.process_event(char_3()); + break; + case '4': + p.process_event(char_4()); + break; + case '5': + p.process_event(char_5()); + break; + case '6': + p.process_event(char_6()); + break; + case '7': + p.process_event(char_7()); + break; + case '8': + p.process_event(char_8()); + break; + case '9': + p.process_event(char_9()); + break; + case '-': + p.process_event(event_char<'-'>()); + break; + default: + p.process_event(default_char()); + break; + } + ++j; + } + p.process_event(eos()); + p.process_event(eos()); +} + +double time_match(const char* text) +{ + boost::timer tim; + int iter = 1; + int counter, repeats; + double result = 0; + double run; + do + { + tim.restart(); + for(counter = 0; counter < iter; ++counter) + { + msm_match( text); + } + result = tim.elapsed(); + iter *= 2; + } while(result < 0.5); + iter /= 2; + + // repeat test and report least value for consistency: + for(repeats = 0; repeats < 10; ++repeats) + { + tim.restart(); + for(counter = 0; counter < iter; ++counter) + { + msm_match( text); + } + run = tim.elapsed(); + result = (std::min)(run, result); + } + return result / iter; +} +int main() +{ + // for timing +#ifdef WIN32 + LARGE_INTEGER res; + ::QueryPerformanceFrequency(&res); + LARGE_INTEGER li,li2; +#else + struct timeval tv1,tv2; + gettimeofday(&tv1,NULL); +#endif + + test_fsm::parsing p; + p.start(); + const char* input = "1234-5678-1234-456"; + size_t len = strlen(input); + // for timing +#ifdef WIN32 + ::QueryPerformanceCounter(&li); +#else + gettimeofday(&tv1,NULL); +#endif + for (int i=0;i<1000;++i) + { + int j=0; + while(input[j]) + //for (size_t j=0;j<len;++j) + { + switch (input[j]) + { + case '0': + p.process_event(char_0()); + break; + case '1': + p.process_event(char_1()); + break; + case '2': + p.process_event(char_2()); + break; + case '3': + p.process_event(char_3()); + break; + case '4': + p.process_event(char_4()); + break; + case '5': + p.process_event(char_5()); + break; + case '6': + p.process_event(char_6()); + break; + case '7': + p.process_event(char_7()); + break; + case '8': + p.process_event(char_8()); + break; + case '9': + p.process_event(char_9()); + break; + case '-': + p.process_event(event_char<'-'>()); + break; + default: + p.process_event(default_char()); + break; + } + ++j; + } + p.process_event(eos()); + p.process_event(eos()); + } +#ifdef WIN32 + ::QueryPerformanceCounter(&li2); +#else + gettimeofday(&tv2,NULL); +#endif +#ifdef WIN32 + std::cout << "msm(1) took in s:" << (double)(li2.QuadPart-li.QuadPart)/res.QuadPart <<"\n" <<std::endl; +#else + std::cout << "msm(1) took in us:" << mtime(tv1,tv2) <<"\n" <<std::endl; +#endif + + Parser parse; + // for timing +#ifdef WIN32 + ::QueryPerformanceCounter(&li); +#else + gettimeofday(&tv1,NULL); +#endif + for (int i=0;i<1000;++i) + { + for (size_t j=0;j<len;++j) + { + parse.new_char(input[j]); + } + parse.finish_string(); + parse.reinit(); + } +#ifdef WIN32 + ::QueryPerformanceCounter(&li2); +#else + gettimeofday(&tv2,NULL); +#endif +#ifdef WIN32 + std::cout << "msm(2) took in s:" << (double)(li2.QuadPart-li.QuadPart)/res.QuadPart <<"\n" <<std::endl; +#else + std::cout << "msm(2) took in us:" << mtime(tv1,tv2) <<"\n" <<std::endl; +#endif + std::cout << "msm(3) took in s:" << time_match(input) <<"\n" <<std::endl; + return 0; +} + diff --git a/libs/msm/doc/HTML/examples/SCComposite.cpp b/libs/msm/doc/HTML/examples/SCComposite.cpp new file mode 100644 index 0000000000..7a98fdabb6 --- /dev/null +++ b/libs/msm/doc/HTML/examples/SCComposite.cpp @@ -0,0 +1,207 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <boost/statechart/event.hpp> +#include <boost/statechart/state_machine.hpp> +#include <boost/statechart/simple_state.hpp> +#include <boost/statechart/transition.hpp> +#include "boost/mpl/list.hpp" + +#include <vector> + +#include <iostream> +#ifdef WIN32 +#include "windows.h" +#else +#include <sys/time.h> +#endif + +namespace sc = boost::statechart; +namespace mpl = boost::mpl; + +namespace test_sc +{ + + //events + struct play : sc::event< play > {}; + struct end_pause : sc::event< end_pause > {}; + struct stop : sc::event< stop > {}; + struct pause : sc::event< pause > {}; + struct open_close : sc::event< open_close > {}; + struct cd_detected : sc::event< cd_detected > {}; + struct NextSong: sc::event< NextSong > {}; + struct PreviousSong : sc::event< PreviousSong >{}; + + struct Empty; + struct Open; + struct Stopped; + struct Playing; + struct Paused; + // SM + struct player : sc::state_machine< player, Empty > + { + void open_drawer(open_close const&) { /*std::cout << "player::open_drawer\n";*/ } + void store_cd_info(cd_detected const& cd) {/*std::cout << "player::store_cd_info\n";*/ } + void close_drawer(open_close const&) { /*std::cout << "player::close_drawer\n";*/ } + void start_playback(play const&) { /*std::cout << "player::start_playback\n";*/ } + void stopped_again(stop const&) {/*std::cout << "player::stopped_again\n";*/} + void stop_playback(stop const&) { /*std::cout << "player::stop_playback\n";*/ } + void pause_playback(pause const&) { /*std::cout << "player::pause_playback\n"; */} + void stop_and_open(open_close const&) { /*std::cout << "player::stop_and_open\n";*/ } + void resume_playback(end_pause const&) { /*std::cout << "player::resume_playback\n";*/ } + }; + + struct Empty : sc::simple_state< Empty, player > + { + Empty() { /*std::cout << "entering Empty" << std::endl;*/ } // entry + ~Empty() { /*std::cout << "leaving Empty" << std::endl;*/ } // exit + typedef mpl::list< + sc::transition< open_close, Open, + player, &player::open_drawer >, + sc::transition< cd_detected, Stopped, + player, &player::store_cd_info > > reactions; + + }; + struct Open : sc::simple_state< Open, player > + { + Open() { /*std::cout << "entering Open" << std::endl;*/ } // entry + ~Open() { /*std::cout << "leaving Open" << std::endl;*/ } // exit + typedef sc::transition< open_close, Empty, + player, &player::close_drawer > reactions; + + }; + struct Stopped : sc::simple_state< Stopped, player > + { + Stopped() { /*std::cout << "entering Stopped" << std::endl;*/ } // entry + ~Stopped() { /*std::cout << "leaving Stopped" << std::endl;*/ } // exit + typedef mpl::list< + sc::transition< play, Playing, + player, &player::start_playback >, + sc::transition< open_close, Open, + player, &player::open_drawer >, + sc::transition< stop, Stopped, + player, &player::stopped_again > > reactions; + + }; + struct Song1; + struct Playing : sc::simple_state< Playing, player,Song1 > + { + Playing() { /*std::cout << "entering Playing" << std::endl;*/ } // entry + ~Playing() { /*std::cout << "leaving Playing" << std::endl;*/ } // exit + typedef mpl::list< + sc::transition< stop, Stopped, + player, &player::stop_playback >, + sc::transition< pause, Paused, + player, &player::pause_playback >, + sc::transition< open_close, Open, + player, &player::stop_and_open > > reactions; + void start_next_song(NextSong const&) { /*std::cout << "Playing::start_next_song\n";*/ } + void start_prev_song(PreviousSong const&) { /*std::cout << "Playing::start_prev_song\n";*/ } + }; + struct Song2; + struct Song1 : sc::simple_state< Song1, Playing > + { + Song1() { /*std::cout << "entering Song1" << std::endl;*/ } // entry + ~Song1() { /*std::cout << "leaving Song1" << std::endl;*/ } // exit + typedef sc::transition< NextSong, Song2, + Playing, &Playing::start_next_song > reactions; + }; + struct Song3; + struct Song2 : sc::simple_state< Song2, Playing > + { + Song2() { /*std::cout << "entering Song2" << std::endl;*/ } // entry + ~Song2() { /*std::cout << "leaving Song2" << std::endl;*/ } // exit + typedef mpl::list< + sc::transition< NextSong, Song3, + Playing, &Playing::start_next_song >, + sc::transition< PreviousSong, Song1, + Playing, &Playing::start_prev_song > > reactions; + }; + struct Song3 : sc::simple_state< Song3, Playing > + { + Song3() { /*std::cout << "entering Song3" << std::endl;*/ } // entry + ~Song3() { /*std::cout << "leaving Song3" << std::endl;*/ } // exit + typedef sc::transition< PreviousSong, Song2, + Playing, &Playing::start_prev_song > reactions; + }; + struct Paused : sc::simple_state< Paused, player > + { + Paused() { /*std::cout << "entering Paused" << std::endl;*/ } // entry + ~Paused() { /*std::cout << "leaving Paused" << std::endl;*/ } // exit + typedef mpl::list< + sc::transition< end_pause, Playing, + player, &player::resume_playback >, + sc::transition< stop, Stopped, + player, &player::stop_playback >, + sc::transition< open_close, Open, + player, &player::stop_and_open > > reactions; + }; +} + + +#ifndef WIN32 +long mtime(struct timeval& tv1,struct timeval& tv2) +{ + return (tv2.tv_sec-tv1.tv_sec) *1000000 + ((tv2.tv_usec-tv1.tv_usec)); +} +#endif + +int main() +{ + test_sc::player p; + p.initiate(); + // for timing +#ifdef WIN32 + LARGE_INTEGER res; + ::QueryPerformanceFrequency(&res); + LARGE_INTEGER li,li2; + ::QueryPerformanceCounter(&li); +#else + struct timeval tv1,tv2; + gettimeofday(&tv1,NULL); +#endif + + for (int i=0;i<100;++i) + { + p.process_event(test_sc::open_close()); + p.process_event(test_sc::open_close()); + p.process_event(test_sc::cd_detected()); + p.process_event(test_sc::play()); + for (int j=0;j<100;++j) + { + p.process_event(test_sc::NextSong()); + p.process_event(test_sc::NextSong()); + p.process_event(test_sc::PreviousSong()); + p.process_event(test_sc::PreviousSong()); + } + + p.process_event(test_sc::pause()); + // go back to Playing + p.process_event(test_sc::end_pause()); + p.process_event(test_sc::pause()); + p.process_event(test_sc::stop()); + // event leading to the same state + p.process_event(test_sc::stop()); + p.process_event(test_sc::open_close()); + p.process_event(test_sc::open_close()); + } +#ifdef WIN32 + ::QueryPerformanceCounter(&li2); +#else + gettimeofday(&tv2,NULL); +#endif +#ifdef WIN32 + std::cout << "sc took in s:" << (double)(li2.QuadPart-li.QuadPart)/res.QuadPart <<"\n" <<std::endl; +#else + std::cout << "sc took in us:" << mtime(tv1,tv2) <<"\n" <<std::endl; +#endif + return 0; +} + diff --git a/libs/msm/doc/HTML/examples/SCSimple.cpp b/libs/msm/doc/HTML/examples/SCSimple.cpp new file mode 100644 index 0000000000..0d1dddb6e2 --- /dev/null +++ b/libs/msm/doc/HTML/examples/SCSimple.cpp @@ -0,0 +1,169 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <boost/statechart/event.hpp> +#include <boost/statechart/state_machine.hpp> +#include <boost/statechart/simple_state.hpp> +#include <boost/statechart/transition.hpp> +#include "boost/mpl/list.hpp" + +#include <vector> + +#include <iostream> +#ifdef WIN32 +#include "windows.h" +#else +#include <sys/time.h> +#endif + +namespace sc = boost::statechart; +namespace mpl = boost::mpl; + +namespace test_sc +{ + + //events + struct play : sc::event< play > {}; + struct end_pause : sc::event< end_pause > {}; + struct stop : sc::event< stop > {}; + struct pause : sc::event< pause > {}; + struct open_close : sc::event< open_close > {}; + struct cd_detected : sc::event< cd_detected > {}; + + + struct Empty; + struct Open; + struct Stopped; + struct Playing; + struct Paused; + // SM + struct player : sc::state_machine< player, Empty > + { + void open_drawer(open_close const&) { /*std::cout << "player::open_drawer\n";*/ } + void store_cd_info(cd_detected const& cd) {/*std::cout << "player::store_cd_info\n";*/ } + void close_drawer(open_close const&) { /*std::cout << "player::close_drawer\n";*/ } + void start_playback(play const&) { /*std::cout << "player::start_playback\n";*/ } + void stopped_again(stop const&) {/*std::cout << "player::stopped_again\n";*/} + void stop_playback(stop const&) { /*std::cout << "player::stop_playback\n";*/ } + void pause_playback(pause const&) { /*std::cout << "player::pause_playback\n"; */} + void stop_and_open(open_close const&) { /*std::cout << "player::stop_and_open\n";*/ } + void resume_playback(end_pause const&) { /*std::cout << "player::resume_playback\n";*/ } + }; + + struct Empty : sc::simple_state< Empty, player > + { + Empty() { /*std::cout << "entering Empty" << std::endl;*/ } // entry + ~Empty() { /*std::cout << "leaving Empty" << std::endl;*/ } // exit + typedef mpl::list< + sc::transition< open_close, Open, + player, &player::open_drawer >, + sc::transition< cd_detected, Stopped, + player, &player::store_cd_info > > reactions; + + }; + struct Open : sc::simple_state< Open, player > + { + Open() { /*std::cout << "entering Open" << std::endl;*/ } // entry + ~Open() { /*std::cout << "leaving Open" << std::endl;*/ } // exit + typedef sc::transition< open_close, Empty, + player, &player::close_drawer > reactions; + + }; + struct Stopped : sc::simple_state< Stopped, player > + { + Stopped() { /*std::cout << "entering Stopped" << std::endl;*/ } // entry + ~Stopped() { /*std::cout << "leaving Stopped" << std::endl;*/ } // exit + typedef mpl::list< + sc::transition< play, Playing, + player, &player::start_playback >, + sc::transition< open_close, Open, + player, &player::open_drawer >, + sc::transition< stop, Stopped, + player, &player::stopped_again > > reactions; + + }; + struct Playing : sc::simple_state< Playing, player > + { + Playing() { /*std::cout << "entering Playing" << std::endl;*/ } // entry + ~Playing() { /*std::cout << "leaving Playing" << std::endl;*/ } // exit + typedef mpl::list< + sc::transition< stop, Stopped, + player, &player::stop_playback >, + sc::transition< pause, Paused, + player, &player::pause_playback >, + sc::transition< open_close, Open, + player, &player::stop_and_open > > reactions; + }; + struct Paused : sc::simple_state< Paused, player > + { + Paused() { /*std::cout << "entering Paused" << std::endl;*/ } // entry + ~Paused() { /*std::cout << "leaving Paused" << std::endl;*/ } // exit + typedef mpl::list< + sc::transition< end_pause, Playing, + player, &player::resume_playback >, + sc::transition< stop, Stopped, + player, &player::stop_playback >, + sc::transition< open_close, Open, + player, &player::stop_and_open > > reactions; + }; +} + + +#ifndef WIN32 +long mtime(struct timeval& tv1,struct timeval& tv2) +{ + return (tv2.tv_sec-tv1.tv_sec) *1000000 + ((tv2.tv_usec-tv1.tv_usec)); +} +#endif + + +int main() +{ + test_sc::player p; + p.initiate(); + // for timing +#ifdef WIN32 + LARGE_INTEGER res; + ::QueryPerformanceFrequency(&res); + LARGE_INTEGER li,li2; + ::QueryPerformanceCounter(&li); +#else + struct timeval tv1,tv2; + gettimeofday(&tv1,NULL); +#endif + for (int i=0;i<100;++i) + { + p.process_event(test_sc::open_close()); + p.process_event(test_sc::open_close()); + p.process_event(test_sc::cd_detected()); + p.process_event(test_sc::play()); + p.process_event(test_sc::pause()); + // go back to Playing + p.process_event(test_sc::end_pause()); + p.process_event(test_sc::pause()); + p.process_event(test_sc::stop()); + // event leading to the same state + p.process_event(test_sc::stop()); + p.process_event(test_sc::open_close()); + p.process_event(test_sc::open_close()); + } +#ifdef WIN32 + ::QueryPerformanceCounter(&li2); +#else + gettimeofday(&tv2,NULL); +#endif +#ifdef WIN32 + std::cout << "sc took in s:" << (double)(li2.QuadPart-li.QuadPart)/res.QuadPart <<"\n" <<std::endl; +#else + std::cout << "sc took in us:" << mtime(tv1,tv2) <<"\n" <<std::endl; +#endif + return 0; +} + diff --git a/libs/msm/doc/HTML/examples/SM-2Arg.cpp b/libs/msm/doc/HTML/examples/SM-2Arg.cpp new file mode 100644 index 0000000000..58efb7917a --- /dev/null +++ b/libs/msm/doc/HTML/examples/SM-2Arg.cpp @@ -0,0 +1,469 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <iostream> +#include <string> +#include "boost/mpl/vector/vector30.hpp" + +#include <boost/msm/back/state_machine.hpp> +#include <boost/msm/front/state_machine_def.hpp> +#include <boost/msm/back/tools.hpp> + +using namespace std; +namespace msm = boost::msm; + +namespace // Concrete FSM implementation +{ + // events + struct play {}; + struct end_pause {}; + struct stop {}; + struct pause {}; + struct open_close {}; + struct NextSong {}; + struct PreviousSong {}; + struct ThreeSec {}; + struct TenSec {}; + struct go_sleep {}; + struct error_found {}; + struct end_error {}; + + // Flags. Allow information about a property of the current state + struct PlayingPaused{}; + struct CDLoaded {}; + struct FirstSongPlaying {}; + + // A "complicated" event type that carries some data. + struct cd_detected + { + cd_detected(std::string name) + : name(name) + {} + + std::string name; + }; + // an easy visitor + struct SomeVisitor + { + template <class T> + void visit_state(T* astate,int i) + { + std::cout << "visiting state:" << typeid(*astate).name() + << " with data:" << i << std::endl; + } + }; + // overwrite of the base state (not default) + struct my_visitable_state + { + // signature of the accept function + typedef msm::back::args<void,SomeVisitor&,int> 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&,int) const {} + }; + + // Concrete FSM implementation + struct player_ : public msm::front::state_machine_def<player_,my_visitable_state> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: player" << std::endl;} + // The list of FSM states + struct Empty : public msm::front::state<my_visitable_state> + { + typedef mpl::vector<play> deferred_events; + // every (optional) entry/exit methods get the event packed as boost::any. Not useful very often. + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Empty" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Empty" << std::endl;} + void accept(SomeVisitor& vis,int i) const + { + vis.visit_state(this,i); + } + }; + struct Open : public msm::front::state<my_visitable_state> + { + typedef mpl::vector1<CDLoaded> flag_list; + typedef mpl::vector<play> deferred_events; + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Open" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Open" << std::endl;} + void accept(SomeVisitor& vis,int i) const + { + vis.visit_state(this,i); + } + }; + // a state needing a pointer to the containing state machine + // and using for this the non-default policy + // if policy used, set_sm_ptr is needed + struct Stopped : public msm::front::state<my_visitable_state> + { + // when stopped, the CD is loaded + typedef mpl::vector1<CDLoaded> flag_list; + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Stopped" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Stopped" << std::endl;} + }; + // the player state machine contains a state which is himself a state machine + // it demonstrates Shallow History: if the state gets activated with end_pause + // then it will remember the last active state and reactivate it + // also possible: AlwaysHistory, the last active state will always be reactivated + // or NoHistory, always restart from the initial state + struct Playing_ : public msm::front::state_machine_def<Playing_,my_visitable_state > + { + // when playing, the CD is loaded and we are in either pause or playing (duh) + typedef mpl::vector2<PlayingPaused,CDLoaded> flag_list; + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Playing" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Playing" << std::endl;} + void accept(SomeVisitor& vis,int i) const + { + // note that visiting will recursively visit sub-states + vis.visit_state(this,i); + } + // The list of FSM states + // the Playing state machine contains a state which is himself a state machine + // so we have a SM containing a SM containing a SM + struct Song1_ : public msm::front::state_machine_def<Song1_,my_visitable_state> + { + typedef mpl::vector1<FirstSongPlaying> flag_list; + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: First song" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: First Song" << std::endl;} + void accept(SomeVisitor& vis,int i) const + { + vis.visit_state(this,i); + } + struct LightOn : public msm::front::state<my_visitable_state> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: LightOn" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: LightOn" << std::endl;} + }; + struct LightOff : public msm::front::state<my_visitable_state> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: LightOff" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: LightOff" << std::endl;} + }; + // the initial state. Must be defined + typedef LightOn initial_state; + // transition actions + void turn_light_off(ThreeSec const&) { std::cout << "3s off::turn light off\n"; } + // guard conditions + + typedef Song1_ s; // makes transition table cleaner + // Transition table for Song1 + struct transition_table : mpl::vector1< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row < LightOn , ThreeSec , LightOff, &s::turn_light_off > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + typedef msm::back::state_machine<Song1_> Song1; + + struct Song2 : public msm::front::state<my_visitable_state> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: Second song" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: Second Song" << std::endl;} + }; + struct Song3 : public msm::front::state<my_visitable_state> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: Third song" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: Third Song" << std::endl;} + }; + // the initial state. Must be defined + typedef Song1 initial_state; + // transition actions + void start_next_song(NextSong const&) { std::cout << "Playing::start_next_song\n"; } + void start_prev_song(PreviousSong const&) { std::cout << "Playing::start_prev_song\n"; } + // guard conditions + + typedef Playing_ pl; // makes transition table cleaner + // Transition table for Playing + struct transition_table : mpl::vector4< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Song1 , NextSong , Song2 , &pl::start_next_song >, + a_row < Song2 , PreviousSong, Song1 , &pl::start_prev_song >, + a_row < Song2 , NextSong , Song3 , &pl::start_next_song >, + a_row < Song3 , PreviousSong, Song2 , &pl::start_prev_song > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + typedef msm::back::state_machine<Playing_,msm::back::ShallowHistory<mpl::vector<end_pause> > > Playing; + + // the player state machine contains a state which is himself a state machine (2 of them, Playing and Paused) + struct Paused_ : public msm::front::state_machine_def<Paused_,my_visitable_state> + { + typedef mpl::vector2<PlayingPaused,CDLoaded> flag_list; + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Paused" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Paused" << std::endl;} + + // The list of FSM states + struct StartBlinking : public msm::front::state<my_visitable_state> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: StartBlinking" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: StartBlinking" << std::endl;} + }; + struct StopBlinking : public msm::front::state<my_visitable_state> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: StopBlinking" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: StopBlinking" << std::endl;} + }; + // the initial state. Must be defined + typedef StartBlinking initial_state; + // transition actions + void start_blinking(TenSec const&) { std::cout << "Paused::start_blinking\n"; } + void stop_blinking(TenSec const&) { std::cout << "Paused::stop_blinking\n"; } + // guard conditions + + typedef Paused_ pa; // makes transition table cleaner + // Transition table + struct transition_table : mpl::vector2< + // Start Event Next Action Guard + // +---------------+-------------+--------------+---------------------+----------------------+ + a_row < StartBlinking , TenSec , StopBlinking , &pa::stop_blinking >, + a_row < StopBlinking , TenSec , StartBlinking , &pa::start_blinking > + // +---------------+-------------+---------------+--------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + typedef msm::back::state_machine<Paused_> Paused; + + struct SleepMode : public msm::front::state<my_visitable_state> + { + }; // dummy state just to test the automatic id generation + + struct AllOk : public msm::front::state<my_visitable_state> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: AllOk" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: AllOk" << std::endl;} + }; + struct ErrorMode : //public terminate_state<> + public msm::front::interrupt_state<end_error,my_visitable_state> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: ErrorMode" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: ErrorMode" << std::endl;} + }; + + // the initial state of the player SM. Must be defined + typedef mpl::vector<Empty,AllOk> initial_state; + //typedef Empty initial_state; // this is to have only one active state + + // transition actions + void start_playback(play const&) { std::cout << "player::start_playback\n"; } + void open_drawer(open_close const&) { std::cout << "player::open_drawer\n"; } + void close_drawer(open_close const&) { std::cout << "player::close_drawer\n"; } + void store_cd_info(cd_detected const&) + { + std::cout << "player::store_cd_info\n"; + // generate another event to test the queue + //process_event(play()); + } + void stop_playback(stop const&) { std::cout << "player::stop_playback\n"; } + void pause_playback(pause const&) { std::cout << "player::pause_playback\n"; } + void resume_playback(end_pause const&) { std::cout << "player::resume_playback\n"; } + void stop_and_open(open_close const&) { std::cout << "player::stop_and_open\n"; } + void stopped_again(stop const&){std::cout << "player::stopped_again\n";} + void start_sleep(go_sleep const&) { } + void report_error(error_found const&) {std::cout << "player::report_error\n";} + void report_end_error(end_error const&) {std::cout << "player::report_end_error\n";} + // guard conditions + + + typedef player_ p; // makes transition table cleaner + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Stopped , play , Playing , &p::start_playback >, + a_row < Stopped , open_close , Open , &p::open_drawer >, + a_row < Stopped , stop , Stopped , &p::stopped_again >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Open , open_close , Empty , &p::close_drawer >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Empty , open_close , Open , &p::open_drawer >, + a_row < Empty , cd_detected , Stopped , &p::store_cd_info >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Playing , stop , Stopped , &p::stop_playback >, + a_row < Playing , pause , Paused , &p::pause_playback >, + a_row < Playing , open_close , Open , &p::stop_and_open >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Paused , end_pause , Playing , &p::resume_playback >, + a_row < Paused , stop , Stopped , &p::stop_playback >, + a_row < Paused , open_close , Open , &p::stop_and_open >, + a_row < Paused , go_sleep ,SleepMode, &p::start_sleep >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < AllOk , error_found ,ErrorMode, &p::report_error >, + a_row <ErrorMode,end_error ,AllOk , &p::report_end_error > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + + // back-end + typedef msm::back::state_machine<player_> player; + + // + // Testing utilities. + // + + void pstate(player const& p) + { + static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused","AllOk","ErrorMode","SleepMode" }; + for (unsigned int i=0;i<player::nr_regions::value;++i) + { + std::cout << " -> " << state_names[p.current_state()[i]] << std::endl; + } + } + void test() + { + player p; + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + p.start(); + std::cout << "CDLoaded active:" << std::boolalpha << p.is_flag_active<CDLoaded>() << std::endl; //=> false (no CD yet) + + // test deferred event + // deferred in Empty and Open, will be handled only after event cd_detected + p.process_event(play()); + + // go to Open, call on_exit on Empty, then action, then on_entry on Open + p.process_event(open_close()); pstate(p); + // visiting Paused and AllOk, but only Paused cares + SomeVisitor vis; + p.visit_current_states(boost::ref(vis),1); + p.process_event(open_close()); pstate(p); + // visiting Empty and AllOk, but only Empty cares + p.visit_current_states(boost::ref(vis),2); + + + p.process_event(cd_detected("louie, louie")); + // no need to call play() as the previous event does it in its action method + //p.process_event(play()); + // at this point, Play is active, along FirstSong and LightOn + pstate(p); + // visiting Playing+Song1 and AllOk, but only Playing+Song1 care + p.visit_current_states(boost::ref(vis),3); + + std::cout << "PlayingPaused active:" << std::boolalpha << p.is_flag_active<PlayingPaused>() << std::endl;//=> true + // call on_exit on LightOn,FirstSong,Play like stated in the UML spec. + // and of course on_entry on Paused and StartBlinking + p.process_event(pause()); pstate(p); + std::cout << "PlayingPaused active:" << std::boolalpha << p.is_flag_active<PlayingPaused>() << std::endl;//=> true + // forward events to Paused + p.process_event(TenSec()); + p.process_event(TenSec()); + // go back to Playing + p.process_event(end_pause()); pstate(p); + std::cout << "FirstSong active:" << std::boolalpha << p.is_flag_active<FirstSongPlaying>() << std::endl; //=> true + p.process_event(ThreeSec()); pstate(p); + p.process_event(NextSong());pstate(p); + // We are now in second song, Flag inactive + std::cout << "FirstSong active:" << std::boolalpha << p.is_flag_active<FirstSongPlaying>() << std::endl;//=> false + // visiting Playing+Song2 and AllOk, but only Playing cares + p.visit_current_states(boost::ref(vis),4); + + p.process_event(NextSong());pstate(p); + // 2nd song active + p.process_event(PreviousSong());pstate(p); + // Pause + p.process_event(pause()); pstate(p); + // go back to Playing + // but end_pause is an event activating the History + // => keep the last active State (SecondSong) + p.process_event(end_pause()); pstate(p); + // test of an event from a state to itself. According to UML spec, call again exit/entry from Stopped + p.process_event(stop()); pstate(p); + p.process_event(stop()); pstate(p); + std::cout << "CDLoaded active:" << std::boolalpha << p.is_flag_active<CDLoaded>() << std::endl;//=> true + std::cout << "PlayingPaused active:" << std::boolalpha << p.is_flag_active<PlayingPaused>() << std::endl;//=> false + std::cout << "FirstSong active:" << std::boolalpha << p.is_flag_active<FirstSongPlaying>() << std::endl;//=> false + std::cout << "CDLoaded active with AND:" << std::boolalpha << p.is_flag_active<CDLoaded,player::Flag_AND>() << std::endl;//=> false + std::cout << "CDLoaded active with OR:" << std::boolalpha << p.is_flag_active<CDLoaded,player::Flag_OR>() << std::endl;//=> true + + // go back to Playing + // but play is not leading to Shallow History => do not remember the last active State (SecondSong) + // and activate again FirstSong and LightOn + p.process_event(play()); pstate(p); + p.process_event(error_found()); pstate(p); + + // try generating more events + std::cout << "Trying to generate another event" << std::endl; // will not work, fsm is terminated or interrupted + p.process_event(NextSong());pstate(p); + + std::cout << "Trying to end the error" << std::endl; // will work only if ErrorMode is interrupt state + p.process_event(end_error());pstate(p); + std::cout << "Trying to generate another event" << std::endl; // will work only if ErrorMode is interrupt state + p.process_event(NextSong());pstate(p); + + std::cout << "Simulate error. Event play is not valid" << std::endl; + p.process_event(play()); pstate(p); + } +} + +int main() +{ + test(); + return 0; +} + + diff --git a/libs/msm/doc/HTML/examples/Serialize.cpp b/libs/msm/doc/HTML/examples/Serialize.cpp new file mode 100644 index 0000000000..dc72c59eb5 --- /dev/null +++ b/libs/msm/doc/HTML/examples/Serialize.cpp @@ -0,0 +1,248 @@ +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> + +// include headers that implement a archive in simple text format +#include <boost/archive/text_oarchive.hpp> +#include <boost/archive/text_iarchive.hpp> +#include <boost/serialization/tracking.hpp> + +#include <fstream> + + +namespace msm = boost::msm; +namespace mpl = boost::mpl; + + +namespace +{ + // events + struct play {}; + struct end_pause {}; + struct stop {}; + struct pause {}; + struct open_close {}; + + // A "complicated" event type that carries some data. + enum DiskTypeEnum + { + DISK_CD=0, + DISK_DVD=1 + }; + struct cd_detected + { + cd_detected(std::string name, DiskTypeEnum diskType) + : name(name), + disc_type(diskType) + {} + + std::string name; + DiskTypeEnum disc_type; + }; + + // front-end: define the FSM structure + 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, ask for it + typedef int do_serialize; + // and provide a serialize + template<class Archive> + void serialize(Archive & ar, const unsigned int ) + { + ar & front_end_data; + } + // The list of FSM states + struct Empty : public msm::front::state<> + { + // we want Empty to be serialized + typedef int do_serialize; + template<class Archive> + void serialize(Archive & ar, const unsigned int ) + { + ar & some_dummy_data; + } + Empty():some_dummy_data(0){} + // every (optional) entry/exit methods get the event passed. + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Empty" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Empty" << std::endl;} + int some_dummy_data; + }; + struct Open : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) {std::cout << "entering: Open" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Open" << std::endl;} + }; + + // sm_ptr still supported but deprecated as functors are a much better way to do the same thing + struct Stopped : public msm::front::state<msm::front::default_base_state,msm::front::sm_ptr> + { + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) {std::cout << "entering: Stopped" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Stopped" << std::endl;} + void set_sm_ptr(player_* pl) + { + m_player=pl; + } + player_* m_player; + }; + + struct Playing : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Playing" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Playing" << std::endl;} + }; + + // state not defining any entry or exit + struct Paused : public msm::front::state<> + { + }; + + // the initial state of the player SM. Must be defined + typedef Empty initial_state; + + // transition actions + void start_playback(play const&) { std::cout << "player::start_playback\n"; } + void open_drawer(open_close const&) { std::cout << "player::open_drawer\n"; } + void close_drawer(open_close const&) { std::cout << "player::close_drawer\n"; } + void store_cd_info(cd_detected const&) { std::cout << "player::store_cd_info\n"; } + void stop_playback(stop const&) { std::cout << "player::stop_playback\n"; } + void pause_playback(pause const&) { std::cout << "player::pause_playback\n"; } + void resume_playback(end_pause const&) { std::cout << "player::resume_playback\n"; } + void stop_and_open(open_close const&) { std::cout << "player::stop_and_open\n"; } + void stopped_again(stop const&) {std::cout << "player::stopped_again\n";} + // guard conditions + bool good_disk_format(cd_detected const& evt) + { + // to test a guard condition, let's say we understand only CDs, not DVD + if (evt.disc_type != DISK_CD) + { + std::cout << "wrong disk, sorry" << std::endl; + return false; + } + return true; + } + // used to show a transition conflict. This guard will simply deactivate one transition and thus + // solve the conflict + bool auto_start(cd_detected const&) + { + return false; + } + + typedef player_ p; // makes transition table cleaner + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Stopped , play , Playing , &p::start_playback >, + a_row < Stopped , open_close , Open , &p::open_drawer >, + _row < Stopped , stop , Stopped >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Open , open_close , Empty , &p::close_drawer >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Empty , open_close , Open , &p::open_drawer >, + row < Empty , cd_detected , Stopped , &p::store_cd_info ,&p::good_disk_format >, + row < Empty , cd_detected , Playing , &p::store_cd_info ,&p::auto_start >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Playing , stop , Stopped , &p::stop_playback >, + a_row < Playing , pause , Paused , &p::pause_playback >, + a_row < Playing , open_close , Open , &p::stop_and_open >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Paused , end_pause , Playing , &p::resume_playback >, + a_row < Paused , stop , Stopped , &p::stop_playback >, + a_row < Paused , open_close , Open , &p::stop_and_open > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + // Pick a back-end + typedef msm::back::state_machine<player_> player; + + // + // Testing utilities. + // + static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" }; + void pstate(player const& p) + { + std::cout << " -> " << state_names[p.current_state()[0]] << std::endl; + } + + void test() + { + player p; + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + p.start(); + p.get_state<player_::Empty&>().some_dummy_data=3; + p.front_end_data=4; + + // go to Open, call on_exit on Empty, then action, then on_entry on Open + p.process_event(open_close()); pstate(p); + + std::ofstream ofs("fsm.txt"); + // save fsm to archive (current state is Open) + { + boost::archive::text_oarchive oa(ofs); + // write class instance to archive + oa << p; + } + // reload fsm in state Open + player p2; + { + // create and open an archive for input + std::ifstream ifs("fsm.txt"); + boost::archive::text_iarchive ia(ifs); + // read class state from archive + ia >> p2; + } + // we now use p2 as it was loaded + // check that we kept Empty's data value + std::cout << "Empty's data should be 3:" << p2.get_state<player_::Empty&>().some_dummy_data << std::endl; + std::cout << "front-end data should be 4:" << p2.front_end_data << std::endl; + + p2.process_event(open_close()); pstate(p2); + // will be rejected, wrong disk type + p2.process_event( + cd_detected("louie, louie",DISK_DVD)); pstate(p2); + p2.process_event( + cd_detected("louie, louie",DISK_CD)); pstate(p2); + p2.process_event(play()); + + // at this point, Play is active + p2.process_event(pause()); pstate(p2); + // go back to Playing + p2.process_event(end_pause()); pstate(p2); + p2.process_event(pause()); pstate(p2); + p2.process_event(stop()); pstate(p2); + // event leading to the same state + // no action method called as it is not present in the transition table + p2.process_event(stop()); pstate(p2); + } +} +// eliminate object tracking (even if serialized through a pointer) +// at the risk of a programming error creating duplicate objects. +// this is to get rid of warning because p is not const +BOOST_CLASS_TRACKING(player, boost::serialization::track_never) + +int main() +{ + test(); + return 0; +} diff --git a/libs/msm/doc/HTML/examples/SerializeCompositeAndHistory.cpp b/libs/msm/doc/HTML/examples/SerializeCompositeAndHistory.cpp new file mode 100644 index 0000000000..9699ab1e06 --- /dev/null +++ b/libs/msm/doc/HTML/examples/SerializeCompositeAndHistory.cpp @@ -0,0 +1,266 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> + +// include headers that implement a archive in simple text format +#include <boost/archive/text_oarchive.hpp> +#include <boost/archive/text_iarchive.hpp> +#include <boost/serialization/tracking.hpp> + +#include <fstream> + +namespace msm = boost::msm; +namespace mpl = boost::mpl; + +namespace +{ + // events + struct play {}; + struct end_pause {}; + struct stop {}; + struct pause {}; + struct open_close {}; + struct NextSong {}; + struct PreviousSong {}; + + // A "complicated" event type that carries some data. + struct cd_detected + { + cd_detected(std::string name) + : name(name) + {} + + std::string name; + }; + + // front-end: define the FSM structure + struct player_ : public msm::front::state_machine_def<player_> + { + // The list of FSM states + struct Empty : public msm::front::state<> + { + // every (optional) entry/exit methods get the event passed + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Empty" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Empty" << std::endl;} + }; + struct Open : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Open" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Open" << std::endl;} + }; + + struct Stopped : public msm::front::state<> + { + // when stopped, the CD is loaded + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Stopped" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Stopped" << std::endl;} + }; + + struct Playing_ : public msm::front::state_machine_def<Playing_> + { + // when playing, the CD is loaded and we are in either pause or playing (duh) + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Playing" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Playing" << std::endl;} + + // The list of FSM states + struct Song1 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: First song" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: First Song" << std::endl;} + }; + struct Song2 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: Second song" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: Second Song" << std::endl;} + }; + struct Song3 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: Third song" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: Third Song" << std::endl;} + }; + // the initial state. Must be defined + typedef Song1 initial_state; + // transition actions + void start_next_song(NextSong const&) { std::cout << "Playing::start_next_song\n"; } + void start_prev_song(PreviousSong const&) { std::cout << "Playing::start_prev_song\n"; } + // guard conditions + + typedef Playing_ pl; // makes transition table cleaner + // Transition table for Playing + struct transition_table : mpl::vector4< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Song1 , NextSong , Song2 , &pl::start_next_song >, + a_row < Song2 , PreviousSong, Song1 , &pl::start_prev_song >, + a_row < Song2 , NextSong , Song3 , &pl::start_next_song >, + a_row < Song3 , PreviousSong, Song2 , &pl::start_prev_song > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + + }; + // back-end + // demonstrates Shallow History: if the state gets activated with end_pause + // then it will remember the last active state and reactivate it + // also possible: AlwaysHistory, the last active state will always be reactivated + // or NoHistory, always restart from the initial state + typedef msm::back::state_machine<Playing_,msm::back::ShallowHistory<mpl::vector<end_pause> > > Playing; + + // state not defining any entry or exit + struct Paused : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Paused" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Paused" << std::endl;} + }; + + // the initial state of the player SM. Must be defined + typedef Empty initial_state; + + // transition actions + void start_playback(play const&) { std::cout << "player::start_playback\n"; } + void open_drawer(open_close const&) { std::cout << "player::open_drawer\n"; } + void close_drawer(open_close const&) { std::cout << "player::close_drawer\n"; } + void store_cd_info(cd_detected const& cd) {std::cout << "player::store_cd_info\n";} + void stop_playback(stop const&) { std::cout << "player::stop_playback\n"; } + void pause_playback(pause const&) { std::cout << "player::pause_playback\n"; } + void resume_playback(end_pause const&) { std::cout << "player::resume_playback\n"; } + void stop_and_open(open_close const&) { std::cout << "player::stop_and_open\n"; } + void stopped_again(stop const&){std::cout << "player::stopped_again\n";} + // guard conditions + + typedef player_ p; // makes transition table cleaner + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Stopped , play , Playing , &p::start_playback >, + a_row < Stopped , open_close , Open , &p::open_drawer >, + a_row < Stopped , stop , Stopped , &p::stopped_again >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Open , open_close , Empty , &p::close_drawer >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Empty , open_close , Open , &p::open_drawer >, + a_row < Empty , cd_detected , Stopped , &p::store_cd_info >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Playing , stop , Stopped , &p::stop_playback >, + a_row < Playing , pause , Paused , &p::pause_playback >, + a_row < Playing , open_close , Open , &p::stop_and_open >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Paused , end_pause , Playing , &p::resume_playback >, + a_row < Paused , stop , Stopped , &p::stop_playback >, + a_row < Paused , open_close , Open , &p::stop_and_open > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + + }; + // Pick a back-end + typedef msm::back::state_machine<player_> player; + + // + // Testing utilities. + // + static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" }; + + void pstate(player const& p) + { + std::cout << " -> " << state_names[p.current_state()[0]] << std::endl; + } + + void test() + { + player p; + + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + p.start(); + // go to Open, call on_exit on Empty, then action, then on_entry on Open + p.process_event(open_close()); pstate(p); + p.process_event(open_close()); pstate(p); + p.process_event(cd_detected("louie, louie")); + p.process_event(play()); + + // at this point, Play is active + // make transition happen inside it. Player has no idea about this event but it's ok. + p.process_event(NextSong());pstate(p); //2nd song active + p.process_event(NextSong());pstate(p);//3rd song active + p.process_event(PreviousSong());pstate(p);//2nd song active + p.process_event(pause()); pstate(p); + std::ofstream ofs("fsm.txt"); + // save fsm to archive (current state is Pause, Playing is in Song2) + { + boost::archive::text_oarchive oa(ofs); + // write class instance to archive + oa << p; + } + // reload fsm in state Open + player p2; + { + // create and open an archive for input + std::ifstream ifs("fsm.txt"); + boost::archive::text_iarchive ia(ifs); + // read class state from archive + ia >> p2; + } + // go back to Playing + // as you see, remembers the original state as end_pause is an history trigger + p2.process_event(end_pause()); pstate(p2); + p2.process_event(pause()); pstate(p2); + p2.process_event(stop()); pstate(p2); + // event leading to the same state + p2.process_event(stop()); pstate(p2); + // play does not trigger shallow history => start back from 1st song + p2.process_event(play()); pstate(p2); + } +} +// eliminate object tracking (even if serialized through a pointer) +// at the risk of a programming error creating duplicate objects. +// this is to get rid of warning because p is not const +BOOST_CLASS_TRACKING(player, boost::serialization::track_never) + +int main() +{ + test(); + return 0; +} diff --git a/libs/msm/doc/HTML/examples/SimplePhoenix.cpp b/libs/msm/doc/HTML/examples/SimplePhoenix.cpp new file mode 100644 index 0000000000..7df50096cb --- /dev/null +++ b/libs/msm/doc/HTML/examples/SimplePhoenix.cpp @@ -0,0 +1,258 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <vector> +#include <iostream> + +#include <boost/phoenix/phoenix.hpp> + +// add phoenix support in eUML +#define BOOST_MSM_EUML_PHOENIX_SUPPORT +#include <boost/msm/back/state_machine.hpp> +#include <boost/msm/front/euml/euml.hpp> + + +using namespace std; +using namespace boost::msm::front::euml; +namespace msm = boost::msm; +using namespace boost::phoenix; + +// entry/exit/action/guard logging functors +#include "logging_functors.h" + +namespace // Concrete FSM implementation +{ + // events + BOOST_MSM_EUML_EVENT(end_pause) + BOOST_MSM_EUML_EVENT(stop) + BOOST_MSM_EUML_EVENT(pause) + BOOST_MSM_EUML_EVENT(open_close) + struct play_event : boost::msm::front::euml::euml_event<play_event> + { + }; + play_event play; + + enum DiskTypeEnum + { + DISK_CD=0, + DISK_DVD=1 + }; + // A "complicated" event type that carries some data. + struct cd_detected_event : boost::msm::front::euml::euml_event<cd_detected_event> + { + cd_detected_event(){} + cd_detected_event(std::string const& name,DiskTypeEnum disk):cd_name(name),cd_type(disk){} + std::string cd_name; + DiskTypeEnum cd_type; + }; + // define an instance for a nicer transition table + cd_detected_event cd_detected; + + // Concrete FSM implementation + // The list of FSM states + // state not needing any entry or exit + BOOST_MSM_EUML_STATE((),Paused) + + // states with standard eUML actions + BOOST_MSM_EUML_STATE(( Stopped_Entry,Stopped_Exit ),Stopped) + BOOST_MSM_EUML_STATE(( Playing_Entry,Playing_Exit ),Playing) + + // a "standard" msm state + struct Empty_impl : public msm::front::state<> , public euml_state<Empty_impl> + { + // this allows us to add some functions + void foo() {std::cout << "Empty::foo " << std::endl;} + // standard entry behavior + template <class Event,class FSM> + void on_entry(Event const& evt,FSM& fsm) + { + std::cout << "entering: Empty" << std::endl; + } + template <class Event,class FSM> + void on_exit(Event const& evt,FSM& fsm) + { + std::cout << "leaving: Empty" << std::endl; + } + }; + //instance for use in the transition table + Empty_impl const Empty; + + // entry and exit actions as phoenix functions + struct open_entry_impl + { + typedef void result_type; + void operator()() + { + cout << "entering: Open" << endl; + } + }; + boost::phoenix::function<open_entry_impl> open_entry; + struct open_exit_impl + { + typedef void result_type; + void operator()() + { + cout << "leaving: Open" << endl; + } + }; + boost::phoenix::function<open_exit_impl> open_exit; + + // a state using phoenix for entry/exit actions + BOOST_MSM_EUML_STATE(( open_entry(),open_exit() ),Open) + + // actions and guards using boost::phoenix + struct start_playback_impl + { + typedef void result_type; + void operator()() + { + cout << "calling: start_playback" << endl; + } + }; + boost::phoenix::function<start_playback_impl> start_playback; + + // a guard taking the event as argument + struct good_disk_format_impl + { + typedef bool result_type; + + template <class Event> + bool operator()(Event const& evt) + { + // to test a guard condition, let's say we understand only CDs, not DVD + if (evt.cd_type!=DISK_CD) + { + std::cout << "wrong disk, sorry" << std::endl; + return false; + } + std::cout << "good disk" << std::endl; + return true; + } + }; + boost::phoenix::function<good_disk_format_impl> good_disk_format; + + // a simple action + struct store_cd_info_impl + { + typedef void result_type; + void operator()() + { + cout << "calling: store_cd_info" << endl; + } + }; + boost::phoenix::function<store_cd_info_impl> store_cd_info; + + // an action taking the fsm as argument and sending it a new event + struct process_play_impl + { + typedef void result_type; + + template <class Fsm> + void operator()(Fsm& fsm) + { + cout << "queuing a play event" << endl; + fsm.process_event(play); + } + }; + // it is also possible to use BOOST_PHOENIX_ADAPT_CALLABLE to avoid defining a global variable + BOOST_PHOENIX_ADAPT_CALLABLE(process_play, process_play_impl, 1) + + + // transition table. Actions and guards are written as phoenix functions + BOOST_MSM_EUML_TRANSITION_TABLE(( + //an action without arguments + Playing == Stopped + play / start_playback() , + Playing == Paused + end_pause , + // +------------------------------------------------------------------------------+ + Empty == Open + open_close , + // +------------------------------------------------------------------------------+ + Open == Empty + open_close , + Open == Paused + open_close , + Open == Stopped + open_close , + Open == Playing + open_close , + // +------------------------------------------------------------------------------+ + Paused == Playing + pause , + // +------------------------------------------------------------------------------+ + Stopped == Playing + stop , + Stopped == Paused + stop , + // a guard taking the event as argument + // and an action made of a phoenix expression of 2 actions + // _event is a placeholder for the current event + // _fsm is a placeholder for the current state machine + Stopped == Empty + cd_detected [good_disk_format(_event)] + / (store_cd_info(),process_play(_fsm)), + Stopped == Stopped + stop + // +------------------------------------------------------------------------------+ + ),transition_table) + + // create a state machine "on the fly" + BOOST_MSM_EUML_DECLARE_STATE_MACHINE(( transition_table, //STT + init_ << Empty, // Init State + no_action, // Entry + no_action, // Exit + attributes_ << no_attributes_, // Attributes + configure_ << no_configure_, // configuration + Log_No_Transition // no_transition handler + ), + player_) //fsm name + + // or simply, if no no_transition handler needed: + //BOOST_MSM_EUML_DECLARE_STATE_MACHINE(( transition_table, //STT + // Empty // Init State + // ),player_) + + // choice of back-end + typedef msm::back::state_machine<player_> player; + + // + // Testing utilities. + // + static char const* const state_names[] = { "Stopped", "Paused", "Open", "Empty", "Playing" }; + void pstate(player const& p) + { + std::cout << " -> " << state_names[p.current_state()[0]] << std::endl; + } + + void test() + { + player p; + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + p.start(); + // go to Open, call on_exit on Empty, then action, then on_entry on Open + p.process_event(open_close); pstate(p); + p.process_event(open_close); pstate(p); + // will be rejected, wrong disk type + p.process_event( + cd_detected_event("louie, louie",DISK_DVD)); pstate(p); + p.process_event( + cd_detected_event("louie, louie",DISK_CD)); pstate(p); + // no need to call play as the previous event does it in its action method + //p.process_event(play); + + // at this point, Play is active + p.process_event(pause); pstate(p); + // go back to Playing + p.process_event(end_pause); pstate(p); + p.process_event(pause); pstate(p); + p.process_event(stop); pstate(p); + // event leading to the same state + // no action method called as none is defined in the transition table + p.process_event(stop); pstate(p); + // test call to no_transition + p.process_event(pause); pstate(p); + } +} + +int main() +{ + test(); + return 0; +} + diff --git a/libs/msm/doc/HTML/examples/SimpleTimer.cpp b/libs/msm/doc/HTML/examples/SimpleTimer.cpp new file mode 100644 index 0000000000..25a5aaf9f0 --- /dev/null +++ b/libs/msm/doc/HTML/examples/SimpleTimer.cpp @@ -0,0 +1,159 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <vector> +#include <iostream> + +#include <boost/msm/back/state_machine.hpp> +#include <boost/msm/front/euml/euml.hpp> + +using namespace std; +using namespace boost::msm::front::euml; +namespace msm = boost::msm; + +// how long the timer will ring when countdown elapsed. +#define RINGING_TIME 5 + +namespace // Concrete FSM implementation +{ + // events + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(int,m_timer) + BOOST_MSM_EUML_ATTRIBUTES((attributes_ << m_timer ), start_timer_attr) + BOOST_MSM_EUML_EVENT_WITH_ATTRIBUTES(start_timer,start_timer_attr) + + BOOST_MSM_EUML_EVENT(stop_timer) + + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(int,m_tick) + BOOST_MSM_EUML_ATTRIBUTES((attributes_ << m_tick ), tick_attr) + BOOST_MSM_EUML_EVENT_WITH_ATTRIBUTES(tick,tick_attr) + + BOOST_MSM_EUML_EVENT(start_ringing) + + // Concrete FSM implementation + + // The list of FSM states + BOOST_MSM_EUML_ACTION(Stopped_Entry) + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: Stopped" << std::endl; + } + }; + BOOST_MSM_EUML_STATE(( Stopped_Entry ),Stopped) + + BOOST_MSM_EUML_ACTION(Started_Entry) + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: Started" << std::endl; + } + }; + + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(int,m_counter) + BOOST_MSM_EUML_STATE(( Started_Entry, + no_action, + attributes_ << m_counter + ), + Started) + + BOOST_MSM_EUML_ACTION(Ringing_Entry) + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: Ringing" << std::endl; + } + }; + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(int,m_ringing_cpt) + BOOST_MSM_EUML_STATE(( Ringing_Entry, + no_action, + attributes_ << m_ringing_cpt + ), + Ringing) + + // external function + void do_ring(int ringing_time) {std::cout << "ringing " << ringing_time << " s" << std::endl;} + // create functor and eUML function + BOOST_MSM_EUML_FUNCTION(Ring_ , do_ring , ring_ , void , void ) + + // replaces the old transition table + BOOST_MSM_EUML_TRANSITION_TABLE(( + // +------------------------------------------------------------------------------+ + // When we start the countdown, the countdown value is not hardcoded but contained in the start_timer event. + // We copy this value into Started + Started == Stopped + start_timer /(target_(m_counter)= event_(m_timer)) , + Stopped == Started + stop_timer , + // internal transition + Started + tick + // we here use the message queue to move to Started when the countdown is finished + // to do this we put start_ringing into the message queue + / if_then_( (source_(m_counter) -= event_(m_tick) ) <= Int_<0>(), + process_(start_ringing) ) , + // when we start ringing, we give to the state its hard-coded ringing time. + Ringing == Started + start_ringing + / (target_(m_ringing_cpt) = Int_<RINGING_TIME>(), + // call the external do_ring function + ring_(Int_<RINGING_TIME>())) , + // to change a bit, we now do not use the message queue but a transition conflict to solve the same problem. + // When tick is fired, we have an internal transition Ringing -> Ringing, as long as Counter > 0 + Ringing + tick [ source_(m_ringing_cpt) - event_(m_tick) > Int_<0>() ] + /(target_(m_ringing_cpt) -= event_(m_tick) ) , + // And we move to Stopped when the counter is 0 + Stopped == Ringing + tick[source_(m_ringing_cpt)-event_(m_tick) <= Int_<0>()] , + // we let the user manually stop the ringing by pressing any button + Stopped == Ringing + stop_timer , + Stopped == Ringing + start_timer + // +------------------------------------------------------------------------------+ + ),transition_table) + + // create a state machine "on the fly" + BOOST_MSM_EUML_DECLARE_STATE_MACHINE(( transition_table, //STT + init_ << Stopped // Init State + ), + SimpleTimer_) //fsm name + + // choice of back-end + typedef msm::back::state_machine<SimpleTimer_> SimpleTimer; + + // + // Testing utilities. + // + static char const* const state_names[] = { "Stopped", "Started","Ringing" }; + void pstate(SimpleTimer const& p) + { + std::cout << " -> " << state_names[p.current_state()[0]] << std::endl; + } + + void test() + { + SimpleTimer p; + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + p.start(); + + p.process_event(start_timer(5));pstate(p); //timer set to 5 ticks + p.process_event(tick(2));pstate(p); + p.process_event(tick(1));pstate(p); + p.process_event(tick(1));pstate(p); + p.process_event(tick(1));pstate(p); + // we are now ringing, let it ring a bit + p.process_event(tick(2));pstate(p); + p.process_event(tick(1));pstate(p); + p.process_event(tick(1));pstate(p); + p.process_event(tick(1));pstate(p); + } +} + +int main() +{ + test(); + return 0; +} diff --git a/libs/msm/doc/HTML/examples/SimpleTutorial.cpp b/libs/msm/doc/HTML/examples/SimpleTutorial.cpp new file mode 100644 index 0000000000..4fd879820e --- /dev/null +++ b/libs/msm/doc/HTML/examples/SimpleTutorial.cpp @@ -0,0 +1,213 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> + +namespace msm = boost::msm; +namespace mpl = boost::mpl; + +namespace +{ + // events + struct play {}; + struct end_pause {}; + struct stop {}; + struct pause {}; + struct open_close {}; + + // A "complicated" event type that carries some data. + enum DiskTypeEnum + { + DISK_CD=0, + DISK_DVD=1 + }; + struct cd_detected + { + cd_detected(std::string name, DiskTypeEnum diskType) + : name(name), + disc_type(diskType) + {} + + std::string name; + DiskTypeEnum disc_type; + }; + + // front-end: define the FSM structure + struct player_ : public msm::front::state_machine_def<player_> + { + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) + { + std::cout << "entering: Player" << std::endl; + } + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) + { + std::cout << "leaving: Player" << std::endl; + } + + // The list of FSM states + struct Empty : public msm::front::state<> + { + // every (optional) entry/exit methods get the event passed. + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Empty" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Empty" << std::endl;} + }; + struct Open : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) {std::cout << "entering: Open" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Open" << std::endl;} + }; + + // sm_ptr still supported but deprecated as functors are a much better way to do the same thing + struct Stopped : public msm::front::state<msm::front::default_base_state,msm::front::sm_ptr> + { + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) {std::cout << "entering: Stopped" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Stopped" << std::endl;} + void set_sm_ptr(player_* pl) + { + m_player=pl; + } + player_* m_player; + }; + + struct Playing : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Playing" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Playing" << std::endl;} + }; + + // state not defining any entry or exit + struct Paused : public msm::front::state<> + { + }; + + // the initial state of the player SM. Must be defined + typedef Empty initial_state; + + // transition actions + void start_playback(play const&) { std::cout << "player::start_playback\n"; } + void open_drawer(open_close const&) { std::cout << "player::open_drawer\n"; } + void close_drawer(open_close const&) { std::cout << "player::close_drawer\n"; } + void store_cd_info(cd_detected const&) { std::cout << "player::store_cd_info\n"; } + void stop_playback(stop const&) { std::cout << "player::stop_playback\n"; } + void pause_playback(pause const&) { std::cout << "player::pause_playback\n"; } + void resume_playback(end_pause const&) { std::cout << "player::resume_playback\n"; } + void stop_and_open(open_close const&) { std::cout << "player::stop_and_open\n"; } + void stopped_again(stop const&) {std::cout << "player::stopped_again\n";} + // guard conditions + bool good_disk_format(cd_detected const& evt) + { + // to test a guard condition, let's say we understand only CDs, not DVD + if (evt.disc_type != DISK_CD) + { + std::cout << "wrong disk, sorry" << std::endl; + return false; + } + return true; + } + // used to show a transition conflict. This guard will simply deactivate one transition and thus + // solve the conflict + bool auto_start(cd_detected const&) + { + return false; + } + + typedef player_ p; // makes transition table cleaner + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Stopped , play , Playing , &p::start_playback >, + a_row < Stopped , open_close , Open , &p::open_drawer >, + _row < Stopped , stop , Stopped >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Open , open_close , Empty , &p::close_drawer >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Empty , open_close , Open , &p::open_drawer >, + row < Empty , cd_detected , Stopped , &p::store_cd_info ,&p::good_disk_format >, + row < Empty , cd_detected , Playing , &p::store_cd_info ,&p::auto_start >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Playing , stop , Stopped , &p::stop_playback >, + a_row < Playing , pause , Paused , &p::pause_playback >, + a_row < Playing , open_close , Open , &p::stop_and_open >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Paused , end_pause , Playing , &p::resume_playback >, + a_row < Paused , stop , Stopped , &p::stop_playback >, + a_row < Paused , open_close , Open , &p::stop_and_open > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + // Pick a back-end + typedef msm::back::state_machine<player_> player; + + // + // Testing utilities. + // + static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" }; + void pstate(player const& p) + { + std::cout << " -> " << state_names[p.current_state()[0]] << std::endl; + } + + void test() + { + player p; + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + p.start(); + // go to Open, call on_exit on Empty, then action, then on_entry on Open + p.process_event(open_close()); pstate(p); + p.process_event(open_close()); pstate(p); + // will be rejected, wrong disk type + p.process_event( + cd_detected("louie, louie",DISK_DVD)); pstate(p); + p.process_event( + cd_detected("louie, louie",DISK_CD)); pstate(p); + p.process_event(play()); + + // at this point, Play is active + p.process_event(pause()); pstate(p); + // go back to Playing + p.process_event(end_pause()); pstate(p); + p.process_event(pause()); pstate(p); + p.process_event(stop()); pstate(p); + // event leading to the same state + // no action method called as it is not present in the transition table + p.process_event(stop()); pstate(p); + std::cout << "stop fsm" << std::endl; + p.stop(); + } +} + +int main() +{ + test(); + return 0; +} diff --git a/libs/msm/doc/HTML/examples/SimpleTutorial2.cpp b/libs/msm/doc/HTML/examples/SimpleTutorial2.cpp new file mode 100644 index 0000000000..65d2c62ae6 --- /dev/null +++ b/libs/msm/doc/HTML/examples/SimpleTutorial2.cpp @@ -0,0 +1,227 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> +#include <boost/msm/front/row2.hpp> + +namespace msm = boost::msm; +namespace mpl = boost::mpl; +using namespace msm::front; + +namespace +{ + // events + struct play {}; + struct end_pause {}; + struct stop {}; + struct pause {}; + struct open_close {}; + + // A "complicated" event type that carries some data. + enum DiskTypeEnum + { + DISK_CD=0, + DISK_DVD=1 + }; + struct cd_detected + { + cd_detected(std::string name, DiskTypeEnum diskType) + : name(name), + disc_type(diskType) + {} + + std::string name; + DiskTypeEnum disc_type; + }; + + // front-end: define the FSM structure + struct player_ : public msm::front::state_machine_def<player_> + { + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) + { + std::cout << "entering: Player" << std::endl; + } + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) + { + std::cout << "leaving: Player" << std::endl; + } + + // The list of FSM states + struct Empty : public msm::front::state<> + { + // every (optional) entry/exit methods get the event passed. + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Empty" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Empty" << std::endl;} + void open_drawer(open_close const&) { std::cout << "Empty::open_drawer\n"; } + // actions for Empty's internal transitions + void internal_action(cd_detected const&){ std::cout << "Empty::internal action\n"; } + bool internal_guard(cd_detected const&) + { + std::cout << "Empty::internal guard\n"; + return false; + } + }; + struct Open : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) {std::cout << "entering: Open" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Open" << std::endl;} + void close_drawer(open_close const&) { std::cout << "Open::close_drawer\n"; } + void stop_and_open(open_close const&) { std::cout << "Open::stop_and_open\n"; } + }; + + // sm_ptr still supported but deprecated as functors are a much better way to do the same thing + struct Stopped : public msm::front::state<msm::front::default_base_state,msm::front::sm_ptr> + { + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) {std::cout << "entering: Stopped" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Stopped" << std::endl;} + void set_sm_ptr(player_* pl) + { + m_player=pl; + } + player_* m_player; + void start_playback(play const&) { std::cout << "Stopped::start_playback\n"; } + void stop_playback(stop const&) { std::cout << "Stopped::stop_playback\n"; } + }; + + struct Playing : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Playing" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Playing" << std::endl;} + // guard conditions + // used to show a transition conflict. This guard will simply deactivate one transition and thus + // solve the conflict + bool auto_start(cd_detected const&) + { + return false; + } + }; + + // state not defining any entry or exit + struct Paused : public msm::front::state<> + { + void pause_playback(pause const&) { std::cout << "Paused::pause_playback\n"; } + void resume_playback(end_pause const&) { std::cout << "Paused::resume_playback\n"; } + }; + + // action + void store_cd_info(cd_detected const&) { std::cout << "Player::store_cd_info\n"; } + + // guard + bool good_disk_format(cd_detected const& evt) + { + // to test a guard condition, let's say we understand only CDs, not DVD + if (evt.disc_type != DISK_CD) + { + std::cout << "wrong disk, sorry" << std::endl; + return false; + } + return true; + } + // the initial state of the player SM. Must be defined + typedef Empty initial_state; + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action/Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row2 < Stopped , play , Playing , Stopped , &Stopped::start_playback >, + a_row2 < Stopped , open_close , Open , Empty , &Empty::open_drawer >, + _row < Stopped , stop , Stopped >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row2 < Open , open_close , Empty , Open , &Open::close_drawer >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row2 < Empty , open_close , Open , Empty ,&Empty::open_drawer >, + row2 < Empty , cd_detected , Stopped , player_ ,&player_::store_cd_info + , player_ ,&player_::good_disk_format >, + row2 < Empty , cd_detected , Playing , player_ ,&player_::store_cd_info + , Playing ,&Playing::auto_start >, + // conflict with some internal rows + irow2 < Empty , cd_detected , Empty ,&Empty::internal_action + , Empty ,&Empty::internal_guard >, + g_irow2 < Empty , cd_detected , Empty ,&Empty::internal_guard >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row2 < Playing , stop , Stopped , Stopped ,&Stopped::stop_playback >, + a_row2 < Playing , pause , Paused , Paused ,&Paused::pause_playback >, + a_row2 < Playing , open_close , Open , Open ,&Open::stop_and_open >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row2 < Paused , end_pause , Playing , Paused ,&Paused::resume_playback >, + a_row2 < Paused , stop , Stopped , Stopped ,&Stopped::stop_playback >, + a_row2 < Paused , open_close , Open , Open ,&Open::stop_and_open > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + // Pick a back-end + typedef msm::back::state_machine<player_> player; + + // + // Testing utilities. + // + static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" }; + void pstate(player const& p) + { + std::cout << " -> " << state_names[p.current_state()[0]] << std::endl; + } + + void test() + { + player p; + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + p.start(); + // go to Open, call on_exit on Empty, then action, then on_entry on Open + p.process_event(open_close()); pstate(p); + p.process_event(open_close()); pstate(p); + // will be rejected, wrong disk type + p.process_event( + cd_detected("louie, louie",DISK_DVD)); pstate(p); + p.process_event( + cd_detected("louie, louie",DISK_CD)); pstate(p); + p.process_event(play()); + + // at this point, Play is active + p.process_event(pause()); pstate(p); + // go back to Playing + p.process_event(end_pause()); pstate(p); + p.process_event(pause()); pstate(p); + p.process_event(stop()); pstate(p); + // event leading to the same state + // no action method called as it is not present in the transition table + p.process_event(stop()); pstate(p); + std::cout << "stop fsm" << std::endl; + p.stop(); + + } +} + +int main() +{ + test(); + return 0; +} diff --git a/libs/msm/doc/HTML/examples/SimpleTutorialEuml.cpp b/libs/msm/doc/HTML/examples/SimpleTutorialEuml.cpp new file mode 100644 index 0000000000..b227258f60 --- /dev/null +++ b/libs/msm/doc/HTML/examples/SimpleTutorialEuml.cpp @@ -0,0 +1,187 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <vector> +#include <iostream> + +#include <boost/msm/back/state_machine.hpp> +#include <boost/msm/front/euml/euml.hpp> + +using namespace std; +using namespace boost::msm::front::euml; +namespace msm = boost::msm; + +// entry/exit/action/guard logging functors +#include "logging_functors.h" + +namespace // Concrete FSM implementation +{ + // events + BOOST_MSM_EUML_EVENT(play) + BOOST_MSM_EUML_EVENT(end_pause) + BOOST_MSM_EUML_EVENT(stop) + BOOST_MSM_EUML_EVENT(pause) + BOOST_MSM_EUML_EVENT(open_close) + + // A "complicated" event type that carries some data. + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(std::string,cd_name) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(DiskTypeEnum,cd_type) + BOOST_MSM_EUML_ATTRIBUTES((attributes_ << cd_name << cd_type ), cd_detected_attributes) + BOOST_MSM_EUML_EVENT_WITH_ATTRIBUTES(cd_detected,cd_detected_attributes) + + // Concrete FSM implementation + // The list of FSM states + // state not needing any entry or exit + BOOST_MSM_EUML_STATE((),Paused) + + // it is also possible to define a state which you can implement normally + // just make it a state, as usual, and also a grammar terminal, euml_state + struct Empty_impl : public msm::front::state<> , public euml_state<Empty_impl> + { + // this allows us to add some functions + void activate_empty() {std::cout << "switching to Empty " << std::endl;} + // standard entry behavior + template <class Event,class FSM> + void on_entry(Event const& evt,FSM& fsm) + { + std::cout << "entering: Empty" << std::endl; + } + template <class Event,class FSM> + void on_exit(Event const& evt,FSM& fsm) + { + std::cout << "leaving: Empty" << std::endl; + } + }; + //instance for use in the transition table + Empty_impl const Empty; + + // create a functor and a eUML function for the activate_empty method from Entry + BOOST_MSM_EUML_METHOD(ActivateEmpty_ , activate_empty , activate_empty_ , void , void ) + + // define more states + BOOST_MSM_EUML_STATE(( Open_Entry,Open_Exit ),Open) + BOOST_MSM_EUML_STATE(( Stopped_Entry,Stopped_Exit ),Stopped) + BOOST_MSM_EUML_STATE(( Playing_Entry,Playing_Exit ),Playing) + + + // guard conditions + BOOST_MSM_EUML_ACTION(good_disk_format) + { + template <class FSM,class EVT,class SourceState,class TargetState> + bool operator()(EVT const& evt,FSM&,SourceState& ,TargetState& ) + { + // to test a guard condition, let's say we understand only CDs, not DVD + if (evt.get_attribute(cd_type)!=DISK_CD) + { + std::cout << "wrong disk, sorry" << std::endl; + // just for logging, does not block any transition + return true; + } + std::cout << "good disk" << std::endl; + return true; + } + }; + // it is also possible to use a plain functor, with default-constructor in the transition table + struct start_play + { + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM&,SourceState& ,TargetState& ) + { + cout << "player::start_play" << endl; + } + }; + // replaces the old transition table + BOOST_MSM_EUML_TRANSITION_TABLE(( + Playing == Stopped + play / start_play() , + Playing == Paused + end_pause / resume_playback, + // +------------------------------------------------------------------------------+ + Empty == Open + open_close / (close_drawer,activate_empty_(target_)), + // +------------------------------------------------------------------------------+ + Open == Empty + open_close / open_drawer, + Open == Paused + open_close / stop_and_open, + Open == Stopped + open_close / open_drawer, + Open == Playing + open_close / stop_and_open, + // +------------------------------------------------------------------------------+ + Paused == Playing + pause / pause_playback, + // +------------------------------------------------------------------------------+ + Stopped == Playing + stop / stop_playback, + Stopped == Paused + stop / stop_playback, + Stopped == Empty + cd_detected [good_disk_format && + (event_(cd_type)==Int_<DISK_CD>())] + / (store_cd_info,process_(play)), + Stopped == Stopped + stop + // +------------------------------------------------------------------------------+ + ),transition_table) + + // create a state machine "on the fly" + BOOST_MSM_EUML_DECLARE_STATE_MACHINE(( transition_table, //STT + init_ << Empty, // Init State + no_action, // Entry + no_action, // Exit + attributes_ << no_attributes_, // Attributes + configure_ << no_configure_, // configuration + Log_No_Transition // no_transition handler + ), + player_) //fsm name + + // or simply, if no no_transition handler needed: + //BOOST_MSM_EUML_DECLARE_STATE_MACHINE(( transition_table, //STT + // Empty // Init State + // ),player_) + + // choice of back-end + typedef msm::back::state_machine<player_> player; + + // + // Testing utilities. + // + static char const* const state_names[] = { "Stopped", "Paused", "Open", "Empty", "Playing" }; + void pstate(player const& p) + { + std::cout << " -> " << state_names[p.current_state()[0]] << std::endl; + } + + void test() + { + player p; + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + p.start(); + // note that we write open_close and not open_close(), like usual. Both are possible with eUML, but + // you now have less to type. + // go to Open, call on_exit on Empty, then action, then on_entry on Open + p.process_event(open_close); pstate(p); + p.process_event(open_close); pstate(p); + // will be rejected, wrong disk type + p.process_event( + cd_detected("louie, louie",DISK_DVD)); pstate(p); + p.process_event( + cd_detected("louie, louie",DISK_CD)); pstate(p); + // no need to call play as the previous event does it in its action method + //p.process_event(play); + + // at this point, Play is active + p.process_event(pause); pstate(p); + // go back to Playing + p.process_event(end_pause); pstate(p); + p.process_event(pause); pstate(p); + p.process_event(stop); pstate(p); + // event leading to the same state + // no action method called as none is defined in the transition table + p.process_event(stop); pstate(p); + // test call to no_transition + p.process_event(pause); pstate(p); + } +} + +int main() +{ + test(); + return 0; +} diff --git a/libs/msm/doc/HTML/examples/SimpleTutorialEuml2.cpp b/libs/msm/doc/HTML/examples/SimpleTutorialEuml2.cpp new file mode 100644 index 0000000000..2768caea66 --- /dev/null +++ b/libs/msm/doc/HTML/examples/SimpleTutorialEuml2.cpp @@ -0,0 +1,149 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <vector> +#include <iostream> + +#include <boost/msm/back/state_machine.hpp> +#include <boost/msm/front/euml/euml.hpp> + +using namespace std; +using namespace boost::msm::front::euml; +namespace msm = boost::msm; + +// entry/exit/action/guard logging functors +#include "logging_functors.h" + +namespace // Concrete FSM implementation +{ + // events + // note that unlike the SimpleTutorial, events must derive from euml_event. + BOOST_MSM_EUML_EVENT(play) + BOOST_MSM_EUML_EVENT(end_pause) + BOOST_MSM_EUML_EVENT(stop) + BOOST_MSM_EUML_EVENT(pause) + BOOST_MSM_EUML_EVENT(open_close) + + // A "complicated" event type that carries some data. + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(std::string,cd_name) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(DiskTypeEnum,cd_type) + BOOST_MSM_EUML_ATTRIBUTES((attributes_ << cd_name << cd_type ), cd_detected_attributes) + BOOST_MSM_EUML_EVENT_WITH_ATTRIBUTES(cd_detected,cd_detected_attributes) + + // Concrete FSM implementation + + // The list of FSM states + + // state not needing any entry or exit + BOOST_MSM_EUML_STATE((),Paused) + BOOST_MSM_EUML_STATE(( Empty_Entry,Empty_Exit ),Empty) + BOOST_MSM_EUML_STATE(( Open_Entry,Open_Exit ),Open) + BOOST_MSM_EUML_STATE(( Stopped_Entry,Stopped_Exit ),Stopped) + BOOST_MSM_EUML_STATE(( Playing_Entry,Playing_Exit ),Playing) + + // guard conditions + BOOST_MSM_EUML_ACTION(good_disk_format) + { + template <class FSM,class EVT,class SourceState,class TargetState> + bool operator()(EVT const& evt,FSM&,SourceState& ,TargetState& ) + { + // to test a guard condition, let's say we understand only CDs, not DVD + if (evt.get_attribute(cd_type)!=DISK_CD) + { + std::cout << "wrong disk, sorry" << std::endl; + // just for logging, does not block any transition + return true; + } + std::cout << "good disk" << std::endl; + return true; + } + }; + // replaces the old transition table + BOOST_MSM_EUML_TRANSITION_TABLE(( + Stopped + play / start_playback == Playing , + Stopped + open_close / open_drawer == Open , + Stopped + stop == Stopped, + // +------------------------------------------------------------------------------+ + Open + open_close / close_drawer == Empty , + // +------------------------------------------------------------------------------+ + Empty + open_close / open_drawer == Open , + Empty + cd_detected + [good_disk_format &&(event_(cd_type)==Int_<DISK_CD>())] + / (store_cd_info,process_(play)) == Stopped , + // +------------------------------------------------------------------------------+ + Playing + stop / stop_playback == Stopped , + Playing + pause / pause_playback == Paused , + Playing + open_close / stop_and_open == Open , + // +------------------------------------------------------------------------------+ + Paused + end_pause / resume_playback == Playing , + Paused + stop / stop_playback == Stopped , + Paused + open_close / stop_and_open == Open + // +------------------------------------------------------------------------------+ + ),transition_table) + + // create a state machine "on the fly" + BOOST_MSM_EUML_DECLARE_STATE_MACHINE(( transition_table, //STT + init_ << Empty, // Init State + no_action, // Entry + no_action, // Exit + attributes_ << no_attributes_, // Attributes + configure_ << no_configure_, // configuration + Log_No_Transition // no_transition handler + ), + player_) //fsm name + + // choice of back-end + typedef msm::back::state_machine<player_> player; + + // + // Testing utilities. + // + static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" }; + void pstate(player const& p) + { + std::cout << " -> " << state_names[p.current_state()[0]] << std::endl; + } + + void test() + { + player p; + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + p.start(); + // go to Open, call on_exit on Empty, then action, then on_entry on Open + p.process_event(open_close); pstate(p); + p.process_event(open_close); pstate(p); + // will be rejected, wrong disk type + p.process_event( + cd_detected("louie, louie",DISK_DVD)); pstate(p); + p.process_event( + cd_detected("louie, louie",DISK_CD)); pstate(p); + // no need to call play as the previous event does it in its action method + //p.process_event(play); + + // at this point, Play is active + p.process_event(pause); pstate(p); + // go back to Playing + p.process_event(end_pause); pstate(p); + p.process_event(pause); pstate(p); + p.process_event(stop); pstate(p); + // event leading to the same state + // no action method called as none is defined in the transition table + p.process_event(stop); pstate(p); + // test call to no_transition + p.process_event(pause); pstate(p); + } +} + +int main() +{ + test(); + return 0; +} + diff --git a/libs/msm/doc/HTML/examples/SimpleTutorialInternal.cpp b/libs/msm/doc/HTML/examples/SimpleTutorialInternal.cpp new file mode 100644 index 0000000000..49b71df3d4 --- /dev/null +++ b/libs/msm/doc/HTML/examples/SimpleTutorialInternal.cpp @@ -0,0 +1,226 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> +#include <boost/msm/front/functor_row.hpp> + +namespace msm = boost::msm; +namespace mpl = boost::mpl; +using namespace msm::front; + +namespace +{ + // events + struct play {}; + struct end_pause {}; + struct stop {}; + struct pause {}; + struct open_close {}; + struct to_ignore {}; + + // A "complicated" event type that carries some data. + enum DiskTypeEnum + { + DISK_CD=0, + DISK_DVD=1 + }; + struct cd_detected + { + cd_detected(std::string name, DiskTypeEnum diskType) + : name(name), + disc_type(diskType) + {} + + std::string name; + DiskTypeEnum disc_type; + }; + + // front-end: define the FSM structure + struct player_ : public msm::front::state_machine_def<player_> + { + // The list of FSM states + struct Empty : public msm::front::state<> + { + // every (optional) entry/exit methods get the event passed. + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Empty" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Empty" << std::endl;} + }; + struct Open : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) {std::cout << "entering: Open" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Open" << std::endl;} + }; + + // sm_ptr still supported but deprecated as functors are a much better way to do the same thing + struct Stopped : public msm::front::state<msm::front::default_base_state,msm::front::sm_ptr> + { + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) {std::cout << "entering: Stopped" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Stopped" << std::endl;} + void set_sm_ptr(player_* pl) + { + m_player=pl; + } + player_* m_player; + }; + + struct Playing : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Playing" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Playing" << std::endl;} + }; + + // state not defining any entry or exit + struct Paused : public msm::front::state<> + { + }; + + // the initial state of the player SM. Must be defined + typedef Empty initial_state; + + // transition actions + void start_playback(play const&) { std::cout << "player::start_playback\n"; } + void open_drawer(open_close const&) { std::cout << "player::open_drawer\n"; } + void close_drawer(open_close const&) { std::cout << "player::close_drawer\n"; } + void store_cd_info(cd_detected const&) { std::cout << "player::store_cd_info\n"; } + void stop_playback(stop const&) { std::cout << "player::stop_playback\n"; } + void pause_playback(pause const&) { std::cout << "player::pause_playback\n"; } + void resume_playback(end_pause const&) { std::cout << "player::resume_playback\n"; } + void stop_and_open(open_close const&) { std::cout << "player::stop_and_open\n"; } + void stopped_again(stop const&) {std::cout << "player::stopped_again\n";} + // guard conditions + bool good_disk_format(cd_detected const& evt) + { + // to test a guard condition, let's say we understand only CDs, not DVD + if (evt.disc_type != DISK_CD) + { + std::cout << "wrong disk, sorry" << std::endl; + return false; + } + return true; + } + // transitions internal to Empty + void internal_action(cd_detected const&){ std::cout << "Empty::internal action\n"; } + bool internal_guard(cd_detected const&) + { + std::cout << "Empty::internal guard\n"; + return false; + } + struct internal_guard_fct + { + template <class EVT,class FSM,class SourceState,class TargetState> + bool operator()(EVT const& evt ,FSM&,SourceState& ,TargetState& ) + { + std::cout << "Empty::internal guard functor\n"; + return false; + } + }; + struct internal_action_fct + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + std::cout << "Empty::internal action functor" << std::endl; + } + }; + + typedef player_ p; // makes transition table cleaner + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Stopped , play , Playing , &p::start_playback >, + a_row < Stopped , open_close , Open , &p::open_drawer >, + _row < Stopped , stop , Stopped >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Open , open_close , Empty , &p::close_drawer >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Empty , open_close , Open , &p::open_drawer >, + row < Empty , cd_detected , Stopped , &p::store_cd_info ,&p::good_disk_format >, + irow < Empty , cd_detected , &p::internal_action ,&p::internal_guard >, + _irow < Empty , to_ignore >, + g_irow < Empty , cd_detected ,&p::internal_guard >, + Row < Empty , cd_detected , none , internal_action_fct ,internal_guard_fct >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Playing , stop , Stopped , &p::stop_playback >, + a_row < Playing , pause , Paused , &p::pause_playback >, + a_row < Playing , open_close , Open , &p::stop_and_open >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Paused , end_pause , Playing , &p::resume_playback >, + a_row < Paused , stop , Stopped , &p::stop_playback >, + a_row < Paused , open_close , Open , &p::stop_and_open > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + // Pick a back-end + typedef msm::back::state_machine<player_> player; + + // + // Testing utilities. + // + static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" }; + void pstate(player const& p) + { + std::cout << " -> " << state_names[p.current_state()[0]] << std::endl; + } + + void test() + { + player p; + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + p.start(); + // this event will be ignored and not call no_transition + p.process_event(to_ignore()); + // go to Open, call on_exit on Empty, then action, then on_entry on Open + p.process_event(open_close()); pstate(p); + p.process_event(open_close()); pstate(p); + // will be rejected, wrong disk type + p.process_event( + cd_detected("louie, louie",DISK_DVD)); pstate(p); + p.process_event( + cd_detected("louie, louie",DISK_CD)); pstate(p); + p.process_event(play()); + + // at this point, Play is active + p.process_event(pause()); pstate(p); + // go back to Playing + p.process_event(end_pause()); pstate(p); + p.process_event(pause()); pstate(p); + p.process_event(stop()); pstate(p); + // event leading to the same state + // no action method called as it is not present in the transition table + p.process_event(stop()); pstate(p); + } +} + +int main() +{ + test(); + return 0; +} diff --git a/libs/msm/doc/HTML/examples/SimpleTutorialInternal2.cpp b/libs/msm/doc/HTML/examples/SimpleTutorialInternal2.cpp new file mode 100644 index 0000000000..f123fdba92 --- /dev/null +++ b/libs/msm/doc/HTML/examples/SimpleTutorialInternal2.cpp @@ -0,0 +1,231 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> +#include <boost/msm/front/functor_row.hpp> + +namespace msm = boost::msm; +using namespace msm::front; + +namespace +{ + // events + struct play {}; + struct end_pause {}; + struct stop {}; + struct pause {}; + struct open_close {}; + + // A "complicated" event type that carries some data. + enum DiskTypeEnum + { + DISK_CD=0, + DISK_DVD=1 + }; + struct cd_detected + { + cd_detected(std::string name, DiskTypeEnum diskType) + : name(name), + disc_type(diskType) + {} + + std::string name; + DiskTypeEnum disc_type; + }; + + // front-end: define the FSM structure + struct player_ : public msm::front::state_machine_def<player_> + { + // actions for Empty's internal transitions + void internal_action(cd_detected const&){ std::cout << "Empty::internal action\n"; } + bool internal_guard(cd_detected const&) + { + std::cout << "Empty::internal guard\n"; + return false; + } + struct internal_guard_fct + { + template <class EVT,class FSM,class SourceState,class TargetState> + bool operator()(EVT const& evt ,FSM&,SourceState& ,TargetState& ) + { + std::cout << "Empty::internal guard functor\n"; + return false; + } + }; + struct internal_action_fct + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + std::cout << "Empty::internal action functor" << std::endl; + } + }; + + // The list of FSM states + struct Empty : public msm::front::state<> + { + // every (optional) entry/exit methods get the event passed. + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Empty" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Empty" << std::endl;} + + // Transition table for Empty + struct internal_transition_table : mpl::vector< + // Start Event Next Action Guard + Internal < cd_detected , internal_action_fct ,internal_guard_fct > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + }; + struct Open : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) {std::cout << "entering: Open" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Open" << std::endl;} + }; + + // sm_ptr still supported but deprecated as functors are a much better way to do the same thing + struct Stopped : public msm::front::state<msm::front::default_base_state,msm::front::sm_ptr> + { + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) {std::cout << "entering: Stopped" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Stopped" << std::endl;} + void set_sm_ptr(player_* pl) + { + m_player=pl; + } + player_* m_player; + }; + + struct Playing : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Playing" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Playing" << std::endl;} + }; + + // state not defining any entry or exit + struct Paused : public msm::front::state<> + { + }; + + // the initial state of the player SM. Must be defined + typedef Empty initial_state; + + // transition actions + void start_playback(play const&) { std::cout << "player::start_playback\n"; } + void open_drawer(open_close const&) { std::cout << "player::open_drawer\n"; } + void close_drawer(open_close const&) { std::cout << "player::close_drawer\n"; } + void store_cd_info(cd_detected const&) { std::cout << "player::store_cd_info\n"; } + void stop_playback(stop const&) { std::cout << "player::stop_playback\n"; } + void pause_playback(pause const&) { std::cout << "player::pause_playback\n"; } + void resume_playback(end_pause const&) { std::cout << "player::resume_playback\n"; } + void stop_and_open(open_close const&) { std::cout << "player::stop_and_open\n"; } + void stopped_again(stop const&) {std::cout << "player::stopped_again\n";} + // guard conditions + bool good_disk_format(cd_detected const& evt) + { + // to test a guard condition, let's say we understand only CDs, not DVD + if (evt.disc_type != DISK_CD) + { + std::cout << "wrong disk, sorry" << std::endl; + return false; + } + return true; + } + + typedef player_ p; // makes transition table cleaner + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Stopped , play , Playing , &p::start_playback >, + a_row < Stopped , open_close , Open , &p::open_drawer >, + _row < Stopped , stop , Stopped >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Open , open_close , Empty , &p::close_drawer >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Empty , open_close , Open , &p::open_drawer >, + // conflict between a normal and 2 internal transitions (irow/g_irow) + row < Empty , cd_detected , Stopped , &p::store_cd_info ,&p::good_disk_format >, + irow < Empty , cd_detected , &p::internal_action ,&p::internal_guard >, + g_irow < Empty , cd_detected ,&p::internal_guard >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Playing , stop , Stopped , &p::stop_playback >, + a_row < Playing , pause , Paused , &p::pause_playback >, + a_row < Playing , open_close , Open , &p::stop_and_open >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Paused , end_pause , Playing , &p::resume_playback >, + a_row < Paused , stop , Stopped , &p::stop_playback >, + a_row < Paused , open_close , Open , &p::stop_and_open > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + typedef int no_message_queue; + + }; + // Pick a back-end + typedef msm::back::state_machine<player_> player; + + // + // Testing utilities. + // + static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" }; + void pstate(player const& p) + { + std::cout << " -> " << state_names[p.current_state()[0]] << std::endl; + } + + void test() + { + player p; + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + p.start(); + // go to Open, call on_exit on Empty, then action, then on_entry on Open + p.process_event(open_close()); pstate(p); + p.process_event(open_close()); pstate(p); + // will be rejected, wrong disk type + p.process_event( + cd_detected("louie, louie",DISK_DVD)); pstate(p); + p.process_event( + cd_detected("louie, louie",DISK_CD)); pstate(p); + p.process_event(play()); + + // at this point, Play is active + p.process_event(pause()); pstate(p); + // go back to Playing + p.process_event(end_pause()); pstate(p); + p.process_event(pause()); pstate(p); + p.process_event(stop()); pstate(p); + // event leading to the same state + // no action method called as it is not present in the transition table + p.process_event(stop()); pstate(p); + } +} + +int main() +{ + test(); + return 0; +} diff --git a/libs/msm/doc/HTML/examples/SimpleTutorialInternalFunctors.cpp b/libs/msm/doc/HTML/examples/SimpleTutorialInternalFunctors.cpp new file mode 100644 index 0000000000..77f08da69c --- /dev/null +++ b/libs/msm/doc/HTML/examples/SimpleTutorialInternalFunctors.cpp @@ -0,0 +1,325 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> +#include <boost/msm/front/functor_row.hpp> +#include <boost/msm/front/euml/common.hpp> +// for And_ operator +#include <boost/msm/front/euml/operator.hpp> + +using namespace std; +namespace msm = boost::msm; +using namespace msm::front; +namespace mpl = boost::mpl; +// for And_ operator +using namespace msm::front::euml; + +namespace +{ + // events + struct play {}; + struct end_pause {}; + struct stop {}; + struct pause {}; + struct open_close {}; + + // A "complicated" event type that carries some data. + enum DiskTypeEnum + { + DISK_CD=0, + DISK_DVD=1 + }; + struct cd_detected + { + cd_detected(std::string name, DiskTypeEnum diskType) + : name(name), + disc_type(diskType) + {} + + std::string name; + DiskTypeEnum disc_type; + }; + + // front-end: define the FSM structure + struct player_ : public msm::front::state_machine_def<player_> + { + // The list of FSM states + struct Empty : public msm::front::state<> + { + // every (optional) entry/exit methods get the event passed. + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Empty" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Empty" << std::endl;} + struct internal_guard_fct + { + template <class EVT,class FSM,class SourceState,class TargetState> + bool operator()(EVT const& evt ,FSM&,SourceState& ,TargetState& ) + { + std::cout << "Empty::internal_transition_table guard\n"; + return false; + } + }; + struct internal_action_fct + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + std::cout << "Empty::internal_transition_table action" << std::endl; + } + }; + // Transition table for Empty + struct internal_transition_table : mpl::vector< + // Start Event Next Action Guard + Internal < cd_detected , internal_action_fct ,internal_guard_fct > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + }; + struct Open : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) {std::cout << "entering: Open" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Open" << std::endl;} + }; + + struct Stopped : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) {std::cout << "entering: Stopped" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Stopped" << std::endl;} + }; + + struct Playing : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Playing" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Playing" << std::endl;} + }; + + // state not defining any entry or exit + struct Paused : public msm::front::state<> + { + }; + + // the initial state of the player SM. Must be defined + typedef Empty initial_state; + + // transition actions + // as the functors are generic on events, fsm and source/target state, + // you can reuse them in another machine if you wish + struct TestFct + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const&, FSM&,SourceState& ,TargetState& ) + { + cout << "transition with event:" << typeid(EVT).name() << endl; + } + }; + struct start_playback + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + cout << "player::start_playback" << endl; + } + }; + struct open_drawer + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + cout << "player::open_drawer" << endl; + } + }; + struct close_drawer + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + cout << "player::close_drawer" << endl; + } + }; + struct store_cd_info + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const&,FSM& fsm ,SourceState& ,TargetState& ) + { + cout << "player::store_cd_info" << endl; + fsm.process_event(play()); + } + }; + struct stop_playback + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + cout << "player::stop_playback" << endl; + } + }; + struct pause_playback + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + cout << "player::pause_playback" << endl; + } + }; + struct resume_playback + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + cout << "player::resume_playback" << endl; + } + }; + struct stop_and_open + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + cout << "player::stop_and_open" << endl; + } + }; + struct stopped_again + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + cout << "player::stopped_again" << endl; + } + }; + // guard conditions + struct DummyGuard + { + template <class EVT,class FSM,class SourceState,class TargetState> + bool operator()(EVT const& evt,FSM& fsm,SourceState& src,TargetState& tgt) + { + return true; + } + }; + struct good_disk_format + { + template <class EVT,class FSM,class SourceState,class TargetState> + bool operator()(EVT const& evt ,FSM&,SourceState& ,TargetState& ) + { + // to test a guard condition, let's say we understand only CDs, not DVD + if (evt.disc_type != DISK_CD) + { + std::cout << "wrong disk, sorry" << std::endl; + return false; + } + return true; + } + }; + struct always_true + { + template <class EVT,class FSM,class SourceState,class TargetState> + bool operator()(EVT const& evt ,FSM&,SourceState& ,TargetState& ) + { + return true; + } + }; + struct internal_guard + { + template <class EVT,class FSM,class SourceState,class TargetState> + bool operator()(EVT const& evt ,FSM&,SourceState& ,TargetState& ) + { + std::cout << "Empty::internal guard\n"; + return false; + } + }; + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------------+----------------------+ + Row < Stopped , play , Playing , ActionSequence_ + <mpl::vector< + TestFct,start_playback> > + , DummyGuard >, + Row < Stopped , open_close , Open , open_drawer , none >, + Row < Stopped , stop , Stopped , none , none >, + // +---------+-------------+---------+---------------------------+----------------------+ + Row < Open , open_close , Empty , close_drawer , none >, + // +---------+-------------+---------+---------------------------+----------------------+ + Row < Empty , open_close , Open , open_drawer , none >, + Row < Empty , cd_detected , Stopped , store_cd_info , And_<good_disk_format, + always_true> >, + // internal transition inside the stt: none as Target + Row < Empty , cd_detected , none , none , internal_guard >, + // +---------+-------------+---------+---------------------------+----------------------+ + Row < Playing , stop , Stopped , stop_playback , none >, + Row < Playing , pause , Paused , pause_playback , none >, + Row < Playing , open_close , Open , stop_and_open , none >, + // +---------+-------------+---------+---------------------------+----------------------+ + Row < Paused , end_pause , Playing , resume_playback , none >, + Row < Paused , stop , Stopped , stop_playback , none >, + Row < Paused , open_close , Open , stop_and_open , none > + // +---------+-------------+---------+---------------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + // Pick a back-end + typedef msm::back::state_machine<player_> player; + + // + // Testing utilities. + // + static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" }; + void pstate(player const& p) + { + std::cout << " -> " << state_names[p.current_state()[0]] << std::endl; + } + + void test() + { + player p; + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + p.start(); + // go to Open, call on_exit on Empty, then action, then on_entry on Open + p.process_event(open_close()); pstate(p); + p.process_event(open_close()); pstate(p); + // will be rejected, wrong disk type + p.process_event( + cd_detected("louie, louie",DISK_DVD)); pstate(p); + p.process_event( + cd_detected("louie, louie",DISK_CD)); pstate(p); + // no need to call play() as the previous transition does it in its action method + //p.process_event(play()); + + // at this point, Play is active + p.process_event(pause()); pstate(p); + // go back to Playing + p.process_event(end_pause()); pstate(p); + p.process_event(pause()); pstate(p); + p.process_event(stop()); pstate(p); + // event leading to the same state + // no action method called as it is not present in the transition table + p.process_event(stop()); pstate(p); + } +} + +int main() +{ + test(); + return 0; +} diff --git a/libs/msm/doc/HTML/examples/SimpleTutorialWithEumlTable.cpp b/libs/msm/doc/HTML/examples/SimpleTutorialWithEumlTable.cpp new file mode 100644 index 0000000000..e67b1aba3f --- /dev/null +++ b/libs/msm/doc/HTML/examples/SimpleTutorialWithEumlTable.cpp @@ -0,0 +1,158 @@ +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> +#include <boost/msm/front/euml/euml.hpp> + +namespace msm = boost::msm; +namespace mpl = boost::mpl; +using namespace std; + +// entry/exit/action/guard logging functors +#include "logging_functors.h" + +namespace +{ + // events + struct play_impl : msm::front::euml::euml_event<play_impl> {}; + struct end_pause_impl : msm::front::euml::euml_event<end_pause_impl>{}; + struct stop_impl : msm::front::euml::euml_event<stop_impl>{}; + struct pause_impl : msm::front::euml::euml_event<pause_impl>{}; + struct open_close_impl : msm::front::euml::euml_event<open_close_impl>{}; + struct cd_detected_impl : msm::front::euml::euml_event<cd_detected_impl>{}; + + // define some dummy instances for use in the transition table + // it is also possible to default-construct them instead: + // struct play {}; + // inside the table: play() + play_impl play; + end_pause_impl end_pause; + stop_impl stop; + pause_impl pause; + open_close_impl open_close; + cd_detected_impl cd_detected; + + // The list of FSM states + // they have to be declared outside of the front-end only to make VC happy :( + // note: gcc would have no problem + struct Empty_impl : public msm::front::state<> , public msm::front::euml::euml_state<Empty_impl> + { + // every (optional) entry/exit methods get the event passed. + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Empty" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Empty" << std::endl;} + }; + struct Open_impl : public msm::front::state<> , public msm::front::euml::euml_state<Open_impl> + { + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) {std::cout << "entering: Open" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Open" << std::endl;} + }; + + struct Stopped_impl : public msm::front::state<> , public msm::front::euml::euml_state<Stopped_impl> + { + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) {std::cout << "entering: Stopped" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Stopped" << std::endl;} + }; + + struct Playing_impl : public msm::front::state<> , public msm::front::euml::euml_state<Playing_impl> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Playing" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Playing" << std::endl;} + }; + + // state not defining any entry or exit + struct Paused_impl : public msm::front::state<> , public msm::front::euml::euml_state<Paused_impl> + { + }; + //to make the transition table more readable + Empty_impl const Empty; + Open_impl const Open; + Stopped_impl const Stopped; + Playing_impl const Playing; + Paused_impl const Paused; + + // front-end: define the FSM structure + struct player_ : public msm::front::state_machine_def<player_> + { + + // the initial state of the player SM. Must be defined + typedef Empty_impl initial_state; + + // Transition table for player + // replaces the old transition table + BOOST_MSM_EUML_DECLARE_TRANSITION_TABLE(( + Stopped + play / start_playback == Playing , + Stopped + open_close / open_drawer == Open , + Stopped + stop == Stopped , + // +------------------------------------------------------------------------------+ + Open + open_close / close_drawer == Empty , + // +------------------------------------------------------------------------------+ + Empty + open_close / open_drawer == Open , + Empty + cd_detected /(store_cd_info, + msm::front::euml::process_(play)) == Stopped , + // +------------------------------------------------------------------------------+ + Playing + stop / stop_playback == Stopped , + Playing + pause / pause_playback == Paused , + Playing + open_close / stop_and_open == Open , + // +------------------------------------------------------------------------------+ + Paused + end_pause / resume_playback == Playing , + Paused + stop / stop_playback == Stopped , + Paused + open_close / stop_and_open == Open + // +------------------------------------------------------------------------------+ + ),transition_table) + + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + // Pick a back-end + typedef msm::back::state_machine<player_> player; + + // + // Testing utilities. + // + static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" }; + void pstate(player const& p) + { + std::cout << " -> " << state_names[p.current_state()[0]] << std::endl; + } + + void test() + { + player p; + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + p.start(); + // go to Open, call on_exit on Empty, then action, then on_entry on Open + p.process_event(open_close); pstate(p); + p.process_event(open_close); pstate(p); + p.process_event(cd_detected); pstate(p); + + // at this point, Play is active + p.process_event(pause); pstate(p); + // go back to Playing + p.process_event(end_pause); pstate(p); + p.process_event(pause); pstate(p); + p.process_event(stop); pstate(p); + // event leading to the same state + // no action method called as it is not present in the transition table + p.process_event(stop); pstate(p); + } +} + +int main() +{ + test(); + return 0; +} diff --git a/libs/msm/doc/HTML/examples/SimpleWithFunctors.cpp b/libs/msm/doc/HTML/examples/SimpleWithFunctors.cpp new file mode 100644 index 0000000000..fa86f19030 --- /dev/null +++ b/libs/msm/doc/HTML/examples/SimpleWithFunctors.cpp @@ -0,0 +1,318 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <vector> +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> +// functors +#include <boost/msm/front/functor_row.hpp> +#include <boost/msm/front/euml/common.hpp> +// for And_ operator +#include <boost/msm/front/euml/operator.hpp> + +using namespace std; +namespace msm = boost::msm; +namespace mpl = boost::mpl; +using namespace msm::front; +// for And_ operator +using namespace msm::front::euml; + +namespace // Concrete FSM implementation +{ + // events + struct play {}; + struct end_pause {}; + struct stop {}; + struct pause {}; + struct open_close {}; + + // A "complicated" event type that carries some data. + enum DiskTypeEnum + { + DISK_CD=0, + DISK_DVD=1 + }; + struct cd_detected + { + cd_detected(std::string name, DiskTypeEnum diskType) + : name(name), + disc_type(diskType) + {} + + std::string name; + DiskTypeEnum disc_type; + }; + + // front-end: define the FSM structure + struct player_ : public msm::front::state_machine_def<player_> + { + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) + { + std::cout << "entering: Player" << std::endl; + } + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) + { + std::cout << "leaving: Player" << std::endl; + } + + // The list of FSM states + struct Empty : public msm::front::state<> + { + // every (optional) entry/exit methods get the event passed. + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Empty" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Empty" << std::endl;} + }; + struct Open : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) {std::cout << "entering: Open" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Open" << std::endl;} + }; + + struct Stopped : public msm::front::state<> + { + // when stopped, the CD is loaded + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) {std::cout << "entering: Stopped" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Stopped" << std::endl;} + }; + + struct Playing : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Playing" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Playing" << std::endl;} + }; + + // state not defining any entry or exit + struct Paused : public msm::front::state<> + { + }; + + // the initial state of the player SM. Must be defined + typedef Empty initial_state; + + // transition actions + // as the functors are generic on events, fsm and source/target state, + // you can reuse them in another machine if you wish + struct TestFct + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const&, FSM&,SourceState& ,TargetState& ) + { + cout << "transition with event:" << typeid(EVT).name() << endl; + } + }; + struct start_playback + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + cout << "player::start_playback" << endl; + } + }; + struct open_drawer + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + cout << "player::open_drawer" << endl; + } + }; + struct close_drawer + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + cout << "player::close_drawer" << endl; + } + }; + struct store_cd_info + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const&,FSM& fsm ,SourceState& ,TargetState& ) + { + cout << "player::store_cd_info" << endl; + fsm.process_event(play()); + } + }; + struct stop_playback + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + cout << "player::stop_playback" << endl; + } + }; + struct pause_playback + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + cout << "player::pause_playback" << endl; + } + }; + struct resume_playback + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + cout << "player::resume_playback" << endl; + } + }; + struct stop_and_open + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + cout << "player::stop_and_open" << endl; + } + }; + struct stopped_again + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + cout << "player::stopped_again" << endl; + } + }; + // guard conditions + struct DummyGuard + { + template <class EVT,class FSM,class SourceState,class TargetState> + bool operator()(EVT const& evt,FSM& fsm,SourceState& src,TargetState& tgt) + { + return true; + } + }; + struct good_disk_format + { + template <class EVT,class FSM,class SourceState,class TargetState> + bool operator()(EVT const& evt ,FSM&,SourceState& ,TargetState& ) + { + // to test a guard condition, let's say we understand only CDs, not DVD + if (evt.disc_type != DISK_CD) + { + std::cout << "wrong disk, sorry" << std::endl; + return false; + } + return true; + } + }; + struct always_true + { + template <class EVT,class FSM,class SourceState,class TargetState> + bool operator()(EVT const& evt ,FSM&,SourceState& ,TargetState& ) + { + return true; + } + }; + // we want to define one row with the classic look. + bool auto_start(cd_detected const& evt) + { + return false; + } + + typedef player_ p; // makes transition table cleaner + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------------+----------------------+ + Row < Stopped , play , Playing , ActionSequence_ + <mpl::vector< + TestFct,start_playback> > + , DummyGuard >, + Row < Stopped , open_close , Open , open_drawer , none >, + Row < Stopped , stop , Stopped , none , none >, + // +---------+-------------+---------+---------------------------+----------------------+ + Row < Open , open_close , Empty , close_drawer , none >, + // +---------+-------------+---------+---------------------------+----------------------+ + Row < Empty , open_close , Open , open_drawer , none >, + Row < Empty , cd_detected , Stopped , store_cd_info , And_<good_disk_format, + always_true> >, + // we here also mix with some "classical row" + g_row < Empty , cd_detected , Playing , &p::auto_start >, + // +---------+-------------+---------+---------------------------+----------------------+ + Row < Playing , stop , Stopped , stop_playback , none >, + Row < Playing , pause , Paused , pause_playback , none >, + Row < Playing , open_close , Open , stop_and_open , none >, + // +---------+-------------+---------+---------------------------+----------------------+ + Row < Paused , end_pause , Playing , resume_playback , none >, + Row < Paused , stop , Stopped , stop_playback , none >, + Row < Paused , open_close , Open , stop_and_open , none > + // +---------+-------------+---------+---------------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + // Pick a back-end + typedef msm::back::state_machine<player_> player; + + // + // Testing utilities. + // + static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" }; + void pstate(player const& p) + { + std::cout << " -> " << state_names[p.current_state()[0]] << std::endl; + } + + void test() + { + player p; + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + p.start(); + // go to Open, call on_exit on Empty, then action, then on_entry on Open + p.process_event(open_close()); pstate(p); + p.process_event(open_close()); pstate(p); + // will be rejected, wrong disk type + p.process_event( + cd_detected("louie, louie",DISK_DVD)); pstate(p); + p.process_event( + cd_detected("louie, louie",DISK_CD)); pstate(p); + // no need to call play() as the previous event does it in its action method + //p.process_event(play()); + + // at this point, Play is active + p.process_event(pause()); pstate(p); + // go back to Playing + p.process_event(end_pause()); pstate(p); + p.process_event(pause()); pstate(p); + p.process_event(stop()); pstate(p); + // event leading to the same state + // no action method called as it is not present in the transition table + p.process_event(stop()); pstate(p); + std::cout << "stop fsm" << std::endl; + p.stop(); + + } +} + +int main() +{ + test(); + return 0; +} diff --git a/libs/msm/doc/HTML/examples/SimpleWithFunctors2.cpp b/libs/msm/doc/HTML/examples/SimpleWithFunctors2.cpp new file mode 100644 index 0000000000..4ca3dc9fad --- /dev/null +++ b/libs/msm/doc/HTML/examples/SimpleWithFunctors2.cpp @@ -0,0 +1,320 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <vector> +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> +// functors +#include <boost/msm/front/functor_row.hpp> +#include <boost/msm/front/euml/common.hpp> +// for And_ operator +#include <boost/msm/front/euml/operator.hpp> +// for func_state and func_state_machine +#include <boost/msm/front/euml/state_grammar.hpp> + +using namespace std; +namespace msm = boost::msm; +namespace mpl = boost::mpl; +using namespace msm::front; +// for And_ operator +using namespace msm::front::euml; + +namespace // Concrete FSM implementation +{ + // events + struct play {}; + struct end_pause {}; + struct stop {}; + struct pause {}; + struct open_close {}; + + // A "complicated" event type that carries some data. + enum DiskTypeEnum + { + DISK_CD=0, + DISK_DVD=1 + }; + struct cd_detected + { + cd_detected(std::string name, DiskTypeEnum diskType) + : name(name), + disc_type(diskType) + {} + + std::string name; + DiskTypeEnum disc_type; + }; + + // front-end: define the FSM structure + struct player_ : public msm::front::state_machine_def<player_> + { + // The list of FSM states + // entry and exit functors for Empty + struct Empty_Entry + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: Empty" << std::endl; + } + }; + struct Empty_Exit + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "leaving: Empty" << std::endl; + } + }; + // definition of Empty + struct Empty_tag {}; + typedef msm::front::euml::func_state<Empty_tag,Empty_Entry,Empty_Exit> Empty; + + struct Open_Entry + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: Open" << std::endl; + } + }; + struct Open_Exit + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "leaving: Open" << std::endl; + } + }; + struct Open_tag {}; + typedef msm::front::euml::func_state<Open_tag,Open_Entry,Open_Exit> Open; + + // states without entry/exit actions (can be declared as functor state, just without functors ;-) ) + struct Stopped_tag {}; + typedef msm::front::euml::func_state<Stopped_tag> Stopped; + + struct Playing_tag {}; + typedef msm::front::euml::func_state<Playing_tag> Playing; + + // state not defining any entry or exit (declared as simple state. Equivalent) + struct Paused : public msm::front::state<> + { + }; + + // the initial state of the player SM. Must be defined + typedef Empty initial_state; + + // transition actions + // as the functors are generic on events, fsm and source/target state, + // you can reuse them in another machine if you wish + struct TestFct + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const&, FSM&,SourceState& ,TargetState& ) + { + cout << "transition with event:" << typeid(EVT).name() << endl; + } + }; + struct start_playback + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + cout << "player::start_playback" << endl; + } + }; + struct open_drawer + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + cout << "player::open_drawer" << endl; + } + }; + struct close_drawer + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + cout << "player::close_drawer" << endl; + } + }; + struct store_cd_info + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const&,FSM& fsm ,SourceState& ,TargetState& ) + { + cout << "player::store_cd_info" << endl; + fsm.process_event(play()); + } + }; + struct stop_playback + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + cout << "player::stop_playback" << endl; + } + }; + struct pause_playback + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + cout << "player::pause_playback" << endl; + } + }; + struct resume_playback + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + cout << "player::resume_playback" << endl; + } + }; + struct stop_and_open + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + cout << "player::stop_and_open" << endl; + } + }; + struct stopped_again + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + cout << "player::stopped_again" << endl; + } + }; + // guard conditions + struct DummyGuard + { + template <class EVT,class FSM,class SourceState,class TargetState> + bool operator()(EVT const& evt,FSM& fsm,SourceState& src,TargetState& tgt) + { + return true; + } + }; + struct good_disk_format + { + template <class EVT,class FSM,class SourceState,class TargetState> + bool operator()(EVT const& evt ,FSM&,SourceState& ,TargetState& ) + { + // to test a guard condition, let's say we understand only CDs, not DVD + if (evt.disc_type != DISK_CD) + { + std::cout << "wrong disk, sorry" << std::endl; + return false; + } + return true; + } + }; + struct always_true + { + template <class EVT,class FSM,class SourceState,class TargetState> + bool operator()(EVT const& evt ,FSM&,SourceState& ,TargetState& ) + { + return true; + } + }; + // we want to define one row with the classic look. + bool auto_start(cd_detected const& evt) + { + return false; + } + + typedef player_ p; // makes transition table cleaner + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------------+----------------------+ + Row < Stopped , play , Playing , ActionSequence_ + <mpl::vector< + TestFct,start_playback> > + , DummyGuard >, + Row < Stopped , open_close , Open , open_drawer , none >, + Row < Stopped , stop , Stopped , none , none >, + // +---------+-------------+---------+---------------------------+----------------------+ + Row < Open , open_close , Empty , close_drawer , none >, + // +---------+-------------+---------+---------------------------+----------------------+ + Row < Empty , open_close , Open , open_drawer , none >, + Row < Empty , cd_detected , Stopped , store_cd_info , And_<good_disk_format, + always_true> >, + // we here also mix with some "classical row" + g_row < Empty , cd_detected , Playing , &p::auto_start >, + // +---------+-------------+---------+---------------------------+----------------------+ + Row < Playing , stop , Stopped , stop_playback , none >, + Row < Playing , pause , Paused , pause_playback , none >, + Row < Playing , open_close , Open , stop_and_open , none >, + // +---------+-------------+---------+---------------------------+----------------------+ + Row < Paused , end_pause , Playing , resume_playback , none >, + Row < Paused , stop , Stopped , stop_playback , none >, + Row < Paused , open_close , Open , stop_and_open , none > + // +---------+-------------+---------+---------------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + // Pick a back-end + typedef msm::back::state_machine<player_> player; + + // + // Testing utilities. + // + static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" }; + void pstate(player const& p) + { + std::cout << " -> " << state_names[p.current_state()[0]] << std::endl; + } + + void test() + { + player p; + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + p.start(); + // go to Open, call on_exit on Empty, then action, then on_entry on Open + p.process_event(open_close()); pstate(p); + p.process_event(open_close()); pstate(p); + // will be rejected, wrong disk type + p.process_event( + cd_detected("louie, louie",DISK_DVD)); pstate(p); + p.process_event( + cd_detected("louie, louie",DISK_CD)); pstate(p); + // no need to call play() as the previous event does it in its action method + //p.process_event(play()); + + // at this point, Play is active + p.process_event(pause()); pstate(p); + // go back to Playing + p.process_event(end_pause()); pstate(p); + p.process_event(pause()); pstate(p); + p.process_event(stop()); pstate(p); + // event leading to the same state + // no action method called as it is not present in the transition table + p.process_event(stop()); pstate(p); + } +} + +int main() +{ + test(); + return 0; +} diff --git a/libs/msm/doc/HTML/examples/SimpleWithFunctors3.cpp b/libs/msm/doc/HTML/examples/SimpleWithFunctors3.cpp new file mode 100644 index 0000000000..6d654e79d6 --- /dev/null +++ b/libs/msm/doc/HTML/examples/SimpleWithFunctors3.cpp @@ -0,0 +1,307 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <vector> +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> +// functors +#include <boost/msm/front/functor_row.hpp> +#include <boost/msm/front/euml/common.hpp> +// for And_ operator +#include <boost/msm/front/euml/operator.hpp> +// for func_state and func_state_machine +#include <boost/msm/front/euml/state_grammar.hpp> + +using namespace std; +namespace msm = boost::msm; +namespace mpl = boost::mpl; +using namespace msm::front; +// for And_ operator +using namespace msm::front::euml; + +namespace // Concrete FSM implementation +{ + // events + struct play {}; + struct end_pause {}; + struct stop {}; + struct pause {}; + struct open_close {}; + + // A "complicated" event type that carries some data. + enum DiskTypeEnum + { + DISK_CD=0, + DISK_DVD=1 + }; + struct cd_detected + { + cd_detected(std::string name, DiskTypeEnum diskType) + : name(name), + disc_type(diskType) + {} + + std::string name; + DiskTypeEnum disc_type; + }; + + + // The list of FSM states + // entry and exit functors for Empty + struct Empty_Entry + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: Empty" << std::endl; + } + }; + struct Empty_Exit + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "leaving: Empty" << std::endl; + } + }; + // definition of Empty + struct Empty_tag {}; + typedef msm::front::euml::func_state<Empty_tag,Empty_Entry,Empty_Exit> Empty; + + struct Open_Entry + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: Open" << std::endl; + } + }; + struct Open_Exit + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "leaving: Open" << std::endl; + } + }; + struct Open_tag {}; + typedef msm::front::euml::func_state<Open_tag,Open_Entry,Open_Exit> Open; + + // states without entry/exit actions (can be declared as functor state, just without functors ;-) ) + struct Stopped_tag {}; + typedef msm::front::euml::func_state<Stopped_tag> Stopped; + + struct Playing_tag {}; + typedef msm::front::euml::func_state<Playing_tag> Playing; + + // state not defining any entry or exit (declared as simple state. Equivalent) + struct Paused_tag {}; + typedef msm::front::euml::func_state<Paused_tag> Paused; + + // the initial state of the player SM. Must be defined + typedef Empty initial_state; + + // transition actions + // as the functors are generic on events, fsm and source/target state, + // you can reuse them in another machine if you wish + struct TestFct + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const&, FSM&,SourceState& ,TargetState& ) + { + cout << "transition with event:" << typeid(EVT).name() << endl; + } + }; + struct start_playback + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + cout << "player::start_playback" << endl; + } + }; + struct open_drawer + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + cout << "player::open_drawer" << endl; + } + }; + struct close_drawer + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + cout << "player::close_drawer" << endl; + } + }; + struct store_cd_info + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const&,FSM& fsm ,SourceState& ,TargetState& ) + { + cout << "player::store_cd_info" << endl; + fsm.process_event(play()); + } + }; + struct stop_playback + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + cout << "player::stop_playback" << endl; + } + }; + struct pause_playback + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + cout << "player::pause_playback" << endl; + } + }; + struct resume_playback + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + cout << "player::resume_playback" << endl; + } + }; + struct stop_and_open + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + cout << "player::stop_and_open" << endl; + } + }; + struct stopped_again + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + cout << "player::stopped_again" << endl; + } + }; + // guard conditions + struct DummyGuard + { + template <class EVT,class FSM,class SourceState,class TargetState> + bool operator()(EVT const& evt,FSM& fsm,SourceState& src,TargetState& tgt) + { + return true; + } + }; + struct good_disk_format + { + template <class EVT,class FSM,class SourceState,class TargetState> + bool operator()(EVT const& evt ,FSM&,SourceState& ,TargetState& ) + { + // to test a guard condition, let's say we understand only CDs, not DVD + if (evt.disc_type != DISK_CD) + { + std::cout << "wrong disk, sorry" << std::endl; + return false; + } + return true; + } + }; + struct always_true + { + template <class EVT,class FSM,class SourceState,class TargetState> + bool operator()(EVT const& evt ,FSM&,SourceState& ,TargetState& ) + { + return true; + } + }; + + // Transition table for player + struct player_transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------------+----------------------+ + Row < Stopped , play , Playing , ActionSequence_ + <mpl::vector< + TestFct,start_playback> > + , DummyGuard >, + Row < Stopped , open_close , Open , open_drawer , none >, + Row < Stopped , stop , Stopped , none , none >, + // +---------+-------------+---------+---------------------------+----------------------+ + Row < Open , open_close , Empty , close_drawer , none >, + // +---------+-------------+---------+---------------------------+----------------------+ + Row < Empty , open_close , Open , open_drawer , none >, + Row < Empty , cd_detected , Stopped , store_cd_info , And_<good_disk_format, + always_true> >, + // +---------+-------------+---------+---------------------------+----------------------+ + Row < Playing , stop , Stopped , stop_playback , none >, + Row < Playing , pause , Paused , pause_playback , none >, + Row < Playing , open_close , Open , stop_and_open , none >, + // +---------+-------------+---------+---------------------------+----------------------+ + Row < Paused , end_pause , Playing , resume_playback , none >, + Row < Paused , stop , Stopped , stop_playback , none >, + Row < Paused , open_close , Open , stop_and_open , none > + // +---------+-------------+---------+---------------------------+----------------------+ + > {}; + // fsm definition + struct player_tag {}; + typedef msm::front::euml::func_state_machine<Playing_tag, + // transition table + player_transition_table, + //Initial state + Empty> player_; + // Pick a back-end + typedef msm::back::state_machine<player_> player; + + // + // Testing utilities. + // + static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" }; + void pstate(player const& p) + { + std::cout << " -> " << state_names[p.current_state()[0]] << std::endl; + } + + void test() + { + player p; + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + p.start(); + // go to Open, call on_exit on Empty, then action, then on_entry on Open + p.process_event(open_close()); pstate(p); + p.process_event(open_close()); pstate(p); + // will be rejected, wrong disk type + p.process_event( + cd_detected("louie, louie",DISK_DVD)); pstate(p); + p.process_event( + cd_detected("louie, louie",DISK_CD)); pstate(p); + // no need to call play() as the previous event does it in its action method + //p.process_event(play()); + + // at this point, Play is active + p.process_event(pause()); pstate(p); + // go back to Playing + p.process_event(end_pause()); pstate(p); + p.process_event(pause()); pstate(p); + p.process_event(stop()); pstate(p); + // event leading to the same state + // no action method called as it is not present in the transition table + p.process_event(stop()); pstate(p); + } +} + +int main() +{ + test(); + return 0; +} diff --git a/libs/msm/doc/HTML/examples/TestErrorOrthogonality.cpp b/libs/msm/doc/HTML/examples/TestErrorOrthogonality.cpp new file mode 100644 index 0000000000..fd67e75055 --- /dev/null +++ b/libs/msm/doc/HTML/examples/TestErrorOrthogonality.cpp @@ -0,0 +1,119 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <iostream> + +// back-end +#include <boost/msm/back/state_machine.hpp> +#include <boost/msm/back/mpl_graph_fsm_check.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> + +namespace msm = boost::msm; +namespace mpl = boost::mpl; +using namespace msm::back; +namespace +{ + // events + struct play {}; + struct end_pause {}; + struct stop {}; + struct pause {}; + struct open_close {}; + struct cd_detected{}; + struct error_found {}; + + // front-end: define the FSM structure + struct player_ : public msm::front::state_machine_def<player_> + { + // The list of FSM states + struct Empty : public msm::front::state<> + { + }; + struct Open : public msm::front::state<> + { + }; + + // sm_ptr still supported but deprecated as functors are a much better way to do the same thing + struct Stopped : public msm::front::state<> + { + }; + + struct Playing : public msm::front::state<> + { + }; + + // state not defining any entry or exit + struct Paused : public msm::front::state<> + { + }; + struct AllOk : public msm::front::state<> + { + }; + struct ErrorMode : public msm::front::state<> + { + }; + struct State1 : public msm::front::state<> + { + }; + struct State2 : public msm::front::state<> + { + }; + // the initial state of the player SM. Must be defined + typedef mpl::vector<Empty,AllOk> initial_state; + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + // adding this line makes non-reachable states and should cause a static assert + //_row < State1 , open_close , State2 >, + // adding this line makes non-orthogonal regions and should cause a static assert + //_row < Paused , error_found , ErrorMode >, + _row < Stopped , play , Playing >, + _row < Stopped , open_close , Open >, + _row < Stopped , stop , Stopped >, + // +---------+-------------+---------+---------------------+----------------------+ + _row < Open , open_close , Empty >, + // +---------+-------------+---------+---------------------+----------------------+ + _row < Empty , open_close , Open >, + _row < Empty , cd_detected , Stopped >, + _row < Empty , cd_detected , Playing >, + // +---------+-------------+---------+---------------------+----------------------+ + _row < Playing , stop , Stopped >, + _row < Playing , pause , Paused >, + _row < Playing , open_close , Open >, + // +---------+-------------+---------+---------------------+----------------------+ + _row < Paused , end_pause , Playing >, + _row < Paused , stop , Stopped >, + _row < Paused , open_close , Open >, + _row < AllOk , error_found , ErrorMode > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + } + }; + // Pick a back-end + typedef msm::back::state_machine<player_,msm::back::mpl_graph_fsm_check> player; + + void test() + { + player p; + } +} + +int main() +{ + test(); + return 0; +} diff --git a/libs/msm/doc/HTML/examples/TestInternal.cpp b/libs/msm/doc/HTML/examples/TestInternal.cpp new file mode 100644 index 0000000000..ea8f35c5a9 --- /dev/null +++ b/libs/msm/doc/HTML/examples/TestInternal.cpp @@ -0,0 +1,337 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <iostream> +#include "boost/mpl/vector/vector30.hpp" + +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> +#include <boost/msm/front/functor_row.hpp> +#include <boost/msm/front/internal_row.hpp> + +namespace msm = boost::msm; +namespace mpl = boost::mpl; +using namespace msm::front; + +namespace +{ + // events + struct play {}; + struct end_pause {}; + struct stop {}; + struct pause {}; + struct internal_event {}; + struct open_close {}; + struct NextSong {}; + struct PreviousSong {}; + struct to_ignore {}; + + // A "complicated" event type that carries some data. + enum DiskTypeEnum + { + DISK_CD=0, + DISK_DVD=1 + }; + struct cd_detected + { + cd_detected(std::string name, DiskTypeEnum diskType) + : name(name), + disc_type(diskType) + {} + + std::string name; + DiskTypeEnum disc_type; + }; + + // front-end: define the FSM structure + struct player_ : public msm::front::state_machine_def<player_> + { + // transition actions + void start_playback(play const&) { std::cout << "player::start_playback\n"; } + void open_drawer(open_close const&) { std::cout << "player::open_drawer\n"; } + void close_drawer(open_close const&) { std::cout << "player::close_drawer\n"; } + void store_cd_info(cd_detected const&) { std::cout << "player::store_cd_info\n"; } + void stop_playback(stop const&) { std::cout << "player::stop_playback\n"; } + void pause_playback(pause const&) { std::cout << "player::pause_playback\n"; } + void resume_playback(end_pause const&) { std::cout << "player::resume_playback\n"; } + void stop_and_open(open_close const&) { std::cout << "player::stop_and_open\n"; } + void stopped_again(stop const&) {std::cout << "player::stopped_again\n";} + // guard conditions + bool good_disk_format(cd_detected const& evt) + { + // to test a guard condition, let's say we understand only CDs, not DVD + if (evt.disc_type != DISK_CD) + { + std::cout << "wrong disk, sorry" << std::endl; + return false; + } + return true; + } + // transitions internal to Empty + void internal_action(cd_detected const&){ std::cout << "Empty::internal action\n"; } + bool internal_guard(cd_detected const&) + { + std::cout << "Empty::internal guard\n"; + return false; + } + void internal_action(internal_event const&){ std::cout << "Playing::internal action\n"; } + bool internal_guard(internal_event const&) + { + std::cout << "Playing::internal guard\n"; + return false; + } + + // The list of FSM states + struct Empty : public msm::front::state<> + { + // every (optional) entry/exit methods get the event passed. + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Empty" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Empty" << std::endl;} + + struct internal_guard_fct + { + template <class EVT,class FSM,class SourceState,class TargetState> + bool operator()(EVT const& evt ,FSM&,SourceState& ,TargetState& ) + { + std::cout << "Empty::internal guard functor\n"; + return false; + } + }; + struct internal_action_fct + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + std::cout << "Empty::internal action functor" << std::endl; + } + }; + void internal_action(to_ignore const&) { std::cout << "Empty::(almost)ignoring event\n"; } + // Transition table for Empty + struct internal_transition_table : mpl::vector< + // Start Event Next Action Guard + Row < Empty , cd_detected , none , internal_action_fct ,internal_guard_fct >, + Internal < cd_detected , internal_action_fct ,internal_guard_fct >, + a_internal< to_ignore , Empty,&Empty::internal_action > + // +---------+-------------+----------+------------------------+----------------------+ + > {}; + }; + struct Open : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) {std::cout << "entering: Open" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Open" << std::endl;} + }; + + struct Stopped : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) {std::cout << "entering: Stopped" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Stopped" << std::endl;} + }; + + struct Playing_ : public msm::front::state_machine_def<Playing_> + { + // when playing, the CD is loaded and we are in either pause or playing (duh) + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Playing" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Playing" << std::endl;} + + // The list of FSM states + struct Song1 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: First song" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: First Song" << std::endl;} + + }; + struct Song2 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: Second song" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: Second Song" << std::endl;} + }; + struct Song3 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: Third song" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: Third Song" << std::endl;} + }; + // the initial state. Must be defined + typedef Song1 initial_state; + // transition actions + void start_next_song(NextSong const&){std::cout << "Playing: start_next_song" << std::endl;} + void start_prev_song(PreviousSong const&){std::cout << "Playing: start_prev_song" << std::endl;} + // guard conditions + struct playing_internal_guard + { + template <class EVT,class FSM,class SourceState,class TargetState> + bool operator()(EVT const& evt ,FSM&,SourceState& ,TargetState& ) + { + std::cout << "Playing::internal guard fct\n"; + return true; + } + }; + struct playing_false_guard + { + template <class EVT,class FSM,class SourceState,class TargetState> + bool operator()(EVT const& evt ,FSM&,SourceState& ,TargetState& ) + { + std::cout << "Playing::false guard\n"; + return false; + } + }; + struct playing_internal_fct + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& evt ,FSM&,SourceState& ,TargetState& ) + { + std::cout << "Playing::internal fct\n"; + } + }; + typedef Playing_ pl; // makes transition table cleaner + // Transition table for Playing + struct transition_table : mpl::vector4< + // Start Event Next Action Guard + // +---------+---------------+---------+---------------------+----------------------+ + a_row < Song1 , NextSong , Song2 , &pl::start_next_song >, + a_row < Song2 , PreviousSong , Song1 , &pl::start_prev_song >, + a_row < Song2 , NextSong , Song3 , &pl::start_next_song >, + a_row < Song3 , PreviousSong , Song2 , &pl::start_prev_song > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Internal transition table for Playing + // +---------+----------------+---------+---------------------+-----------------------+ + struct internal_transition_table : mpl::vector< + // normal internal transition + // Start Event Next Action Guard + Internal < internal_event , playing_internal_fct,playing_internal_guard >, + // conflict between internal and the external defined above + Internal < PreviousSong , playing_internal_fct,playing_false_guard >, + internal < internal_event , player_,&player_::internal_action, + player_,&player_::internal_guard > + // +---------+----------------+---------+---------------------+-----------------------+ + > {}; + + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + // back-end + typedef msm::back::state_machine<Playing_> Playing; + + // state not defining any entry or exit + struct Paused : public msm::front::state<> + { + }; + + // the initial state of the player SM. Must be defined + typedef Empty initial_state; + + typedef player_ p; // makes transition table cleaner + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Stopped , play , Playing , &p::start_playback >, + a_row < Stopped , open_close , Open , &p::open_drawer >, + _row < Stopped , stop , Stopped >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Open , open_close , Empty , &p::close_drawer >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Empty , open_close , Open , &p::open_drawer >, + // conflict between a normal and 2 internal transitions (irow/g_irow) + // + a state-defined internals defined 2 ways (see Empty) + row < Empty , cd_detected , Stopped , &p::store_cd_info ,&p::good_disk_format >, + irow < Empty , cd_detected , &p::internal_action ,&p::internal_guard >, + g_irow < Empty , cd_detected ,&p::internal_guard >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Playing , stop , Stopped , &p::stop_playback >, + a_row < Playing , pause , Paused , &p::pause_playback >, + a_row < Playing , open_close , Open , &p::stop_and_open >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Paused , end_pause , Playing , &p::resume_playback >, + a_row < Paused , stop , Stopped , &p::stop_playback >, + a_row < Paused , open_close , Open , &p::stop_and_open > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + // Pick a back-end + typedef msm::back::state_machine<player_> player; + + // + // Testing utilities. + // + static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" }; + void pstate(player const& p) + { + std::cout << " -> " << state_names[p.current_state()[0]] << std::endl; + } + + void test() + { + player p; + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + p.start(); + // this event will be ignored and not call no_transition + p.process_event(to_ignore()); + // go to Open, call on_exit on Empty, then action, then on_entry on Open + p.process_event(open_close()); pstate(p); + p.process_event(open_close()); pstate(p); + // will be rejected, wrong disk type + p.process_event( + cd_detected("louie, louie",DISK_DVD)); pstate(p); + p.process_event( + cd_detected("louie, louie",DISK_CD)); pstate(p); + p.process_event(play()); + p.process_event(NextSong()); + std::cout << "sending an internal event" << std::endl; + p.process_event(internal_event()); + std::cout << "conflict between the internal and normal transition. Internal is tried last" << std::endl; + p.process_event(PreviousSong()); + + // at this point, Play is active + p.process_event(pause()); pstate(p); + // go back to Playing + p.process_event(end_pause()); pstate(p); + p.process_event(pause()); pstate(p); + p.process_event(stop()); pstate(p); + // event leading to the same state + // no action method called as it is not present in the transition table + p.process_event(stop()); pstate(p); + } +} + +int main() +{ + test(); + return 0; +} diff --git a/libs/msm/doc/HTML/examples/Visitor.cpp b/libs/msm/doc/HTML/examples/Visitor.cpp new file mode 100644 index 0000000000..03f2954060 --- /dev/null +++ b/libs/msm/doc/HTML/examples/Visitor.cpp @@ -0,0 +1,368 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <iostream> +#include <string> +#include "boost/mpl/vector/vector30.hpp" + +#include <boost/msm/back/state_machine.hpp> +#include <boost/msm/front/state_machine_def.hpp> +#include <boost/msm/back/tools.hpp> + +using namespace std; +namespace msm = boost::msm; +namespace mpl = boost::mpl; + +namespace // Concrete FSM implementation +{ + // events + struct play {}; + struct end_pause {}; + struct stop {}; + struct pause {}; + struct open_close {}; + struct NextSong {}; + struct PreviousSong {}; + struct ThreeSec {}; + struct TenSec {}; + struct error_found {}; + struct end_error {}; + struct cd_detected {}; + + // a simple visitor + struct SomeVisitor + { + template <class T> + void visit_state(T* astate,int i) + { + std::cout << "visiting state:" << typeid(*astate).name() + << " with data:" << i << std::endl; + } + }; + // base state for all states of ths fsm, to make them visitable + struct my_visitable_state + { + // signature of the accept function + typedef msm::back::args<void,SomeVisitor&,int> 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&,int) const {} + // or if you want all states to be visited, provide an implementation + /* + void accept(SomeVisitor& vis,int i) const + { + vis.visit_state(this,i); + } + */ + }; + + // Concrete FSM implementation + struct player_ : public msm::front::state_machine_def<player_,my_visitable_state> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: player" << std::endl;} + // The list of FSM states + struct Empty : public msm::front::state<my_visitable_state> + { + typedef mpl::vector<play> deferred_events; + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Empty" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Empty" << std::endl;} + // this state wants to be visited + void accept(SomeVisitor& vis,int i) const + { + vis.visit_state(this,i); + } + }; + struct Open : public msm::front::state<my_visitable_state> + { + typedef mpl::vector<play> deferred_events; + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Open" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Open" << std::endl;} + // this state wants to be visited + void accept(SomeVisitor& vis,int i) const + { + vis.visit_state(this,i); + } + }; + struct Stopped : public msm::front::state<my_visitable_state> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Stopped" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Stopped" << std::endl;} + // this state wants to be visited + void accept(SomeVisitor& vis,int i) const + { + // note that visiting will recursively visit sub-states + vis.visit_state(this,i); + } + }; + + struct Playing_ : public msm::front::state_machine_def<Playing_,my_visitable_state > + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Playing" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Playing" << std::endl;} + void accept(SomeVisitor& vis,int i) const + { + // note that visiting will recursively visit sub-states + vis.visit_state(this,i); + } + // The list of FSM states + // the Playing state machine contains a state which is himself a state machine + // so we have a SM containing a SM containing a SM + struct Song1_ : public msm::front::state_machine_def<Song1_,my_visitable_state> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: First song" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: First Song" << std::endl;} + void accept(SomeVisitor& vis,int i) const + { + vis.visit_state(this,i); + } + struct LightOn : public msm::front::state<my_visitable_state> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: LightOn" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: LightOn" << std::endl;} + void accept(SomeVisitor& vis,int i) const + { + // note that visiting will recursively visit sub-states + vis.visit_state(this,i); + } + }; + struct LightOff : public msm::front::state<my_visitable_state> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: LightOff" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: LightOff" << std::endl;} + }; + // the initial state. Must be defined + typedef LightOn initial_state; + // transition actions + void turn_light_off(ThreeSec const&) { std::cout << "3s off::turn light off\n"; } + // guard conditions + + typedef Song1_ s; // makes transition table cleaner + // Transition table for Song1 + struct transition_table : mpl::vector1< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row < LightOn , ThreeSec , LightOff, &s::turn_light_off > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + typedef msm::back::state_machine<Song1_> Song1; + + struct Song2 : public msm::front::state<my_visitable_state> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: Second song" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: Second Song" << std::endl;} + }; + struct Song3 : public msm::front::state<my_visitable_state> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: Third song" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: Third Song" << std::endl;} + }; + // the initial state. Must be defined + typedef Song1 initial_state; + // transition actions + void start_next_song(NextSong const&) { std::cout << "Playing::start_next_song\n"; } + void start_prev_song(PreviousSong const&) { std::cout << "Playing::start_prev_song\n"; } + // guard conditions + + typedef Playing_ pl; // makes transition table cleaner + // Transition table for Playing + struct transition_table : mpl::vector4< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Song1 , NextSong , Song2 , &pl::start_next_song >, + a_row < Song2 , PreviousSong, Song1 , &pl::start_prev_song >, + a_row < Song2 , NextSong , Song3 , &pl::start_next_song >, + a_row < Song3 , PreviousSong, Song2 , &pl::start_prev_song > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + typedef msm::back::state_machine<Playing_,msm::back::ShallowHistory<mpl::vector<end_pause> > > Playing; + + struct Paused : public msm::front::state<my_visitable_state> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Paused" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Paused" << std::endl;} + }; + + struct AllOk : public msm::front::state<my_visitable_state> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: AllOk" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: AllOk" << std::endl;} + }; + struct ErrorMode : + public msm::front::interrupt_state<end_error,my_visitable_state> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: ErrorMode" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: ErrorMode" << std::endl;} + void accept(SomeVisitor& vis,int i) const + { + vis.visit_state(this,i); + } + }; + + // the initial state of the player SM. Must be defined + typedef mpl::vector<Empty,AllOk> initial_state; + + // transition actions + void start_playback(play const&) { std::cout << "player::start_playback\n"; } + void open_drawer(open_close const&) { std::cout << "player::open_drawer\n"; } + void close_drawer(open_close const&) { std::cout << "player::close_drawer\n"; } + void store_cd_info(cd_detected const&) + { + std::cout << "player::store_cd_info\n"; + } + void stop_playback(stop const&) { std::cout << "player::stop_playback\n"; } + void pause_playback(pause const&) { std::cout << "player::pause_playback\n"; } + void resume_playback(end_pause const&) { std::cout << "player::resume_playback\n"; } + void stop_and_open(open_close const&) { std::cout << "player::stop_and_open\n"; } + void stopped_again(stop const&) {std::cout << "player::stopped_again\n";} + void report_error(error_found const&) {std::cout << "player::report_error\n";} + void report_end_error(end_error const&) {std::cout << "player::report_end_error\n";} + // guard conditions + + + typedef player_ p; // makes transition table cleaner + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Stopped , play , Playing , &p::start_playback >, + a_row < Stopped , open_close , Open , &p::open_drawer >, + a_row < Stopped , stop , Stopped , &p::stopped_again >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Open , open_close , Empty , &p::close_drawer >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Empty , open_close , Open , &p::open_drawer >, + a_row < Empty , cd_detected , Stopped , &p::store_cd_info >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Playing , stop , Stopped , &p::stop_playback >, + a_row < Playing , pause , Paused , &p::pause_playback >, + a_row < Playing , open_close , Open , &p::stop_and_open >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Paused , end_pause , Playing , &p::resume_playback >, + a_row < Paused , stop , Stopped , &p::stop_playback >, + a_row < Paused , open_close , Open , &p::stop_and_open >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < AllOk , error_found ,ErrorMode, &p::report_error >, + a_row <ErrorMode,end_error ,AllOk , &p::report_end_error > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + + // back-end + typedef msm::back::state_machine<player_> player; + + // + // Testing utilities. + // + + void pstate(player const& p) + { + static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused","AllOk","ErrorMode","SleepMode" }; + for (unsigned int i=0;i<player::nr_regions::value;++i) + { + std::cout << " -> " << state_names[p.current_state()[i]] << std::endl; + } + } + void test() + { + player p; + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + p.start(); + + // test deferred event + // deferred in Empty and Open, will be handled only after event cd_detected + p.process_event(play()); + + // go to Open, call on_exit on Empty, then action, then on_entry on Open + p.process_event(open_close()); pstate(p); + // visiting Paused and AllOk, but only Paused cares + SomeVisitor vis; + p.visit_current_states(boost::ref(vis),1); + p.process_event(open_close()); pstate(p); + // visiting Empty and AllOk, but only Empty cares + p.visit_current_states(boost::ref(vis),2); + + p.process_event(cd_detected()); + // no need to call play() as the previous event does it in its action method + //p.process_event(play()); + // at this point, Play is active, along FirstSong and LightOn + pstate(p); + // visiting Playing+Song1+LightOn and AllOk, but only Playing+Song1+LightOn care + p.visit_current_states(boost::ref(vis),3); + + // Stop will be active + p.process_event(stop()); pstate(p); + + // visiting when both regions have an active state who wants to be visited + p.process_event(error_found()); + p.visit_current_states(boost::ref(vis),5); + + + } +} + +int main() +{ + test(); + return 0; +} + + diff --git a/libs/msm/doc/HTML/examples/char_event_dispatcher.hpp b/libs/msm/doc/HTML/examples/char_event_dispatcher.hpp new file mode 100644 index 0000000000..023bb0d5ec --- /dev/null +++ b/libs/msm/doc/HTML/examples/char_event_dispatcher.hpp @@ -0,0 +1,92 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_MSM_CHAR_EVENT_DISPATCHER_HPP +#define BOOST_MSM_CHAR_EVENT_DISPATCHER_HPP + +#include <boost/msm/back/common_types.hpp> + +struct digit {}; +struct char_0 : public digit {}; +struct char_1 : public digit {}; +struct char_2 : public digit {}; +struct char_3 : public digit {}; +struct char_4 : public digit {}; +struct char_5 : public digit {}; +struct char_6 : public digit {}; +struct char_7 : public digit {}; +struct char_8 : public digit {}; +struct char_9 : public digit {}; +struct minus_char {}; +template <char c> +struct event_char{}; +template <> +struct event_char<'0'> : public digit{}; +template <> +struct event_char<'1'> : public digit{}; +template <> +struct event_char<'2'> : public digit{}; +template <> +struct event_char<'3'> : public digit{}; +template <> +struct event_char<'4'> : public digit{}; +template <> +struct event_char<'5'> : public digit{}; +template <> +struct event_char<'6'> : public digit{}; +template <> +struct event_char<'7'> : public digit{}; +template <> +struct event_char<'8'> : public digit{}; +template <> +struct event_char<'9'> : public digit{}; + +namespace boost { namespace msm { namespace back { + + +template <class Fsm> +struct char_event_dispatcher +{ + template <char c> + struct dispatch_event_helper + { + static execute_return apply(Fsm& fsm) + { + return fsm.process_event(event_char<c>()); + } + }; + char_event_dispatcher() + { + entries[0x30]=&dispatch_event_helper<'0'>::apply; + entries[0x31]=&dispatch_event_helper<'1'>::apply; + entries[0x32]=&dispatch_event_helper<'2'>::apply; + entries[0x33]=&dispatch_event_helper<'3'>::apply; + entries[0x34]=&dispatch_event_helper<'4'>::apply; + entries[0x35]=&dispatch_event_helper<'5'>::apply; + entries[0x36]=&dispatch_event_helper<'6'>::apply; + entries[0x37]=&dispatch_event_helper<'7'>::apply; + entries[0x38]=&dispatch_event_helper<'8'>::apply; + entries[0x39]=&dispatch_event_helper<'9'>::apply; + entries[0x2D]=&dispatch_event_helper<'-'>::apply; + entries[0x2B]=&dispatch_event_helper<'+'>::apply; + } + execute_return process_event(Fsm& fsm,char c) const + { + return entries[c](fsm); + } + + // The singleton instance. + static const char_event_dispatcher instance; + typedef execute_return (*cell)(Fsm&); + cell entries[255]; +}; + +}}} // boost::msm::back +#endif //BOOST_MSM_CHAR_EVENT_DISPATCHER_HPP diff --git a/libs/msm/doc/HTML/examples/distributed_table/DistributedTable.cpp b/libs/msm/doc/HTML/examples/distributed_table/DistributedTable.cpp new file mode 100644 index 0000000000..6a24e70fae --- /dev/null +++ b/libs/msm/doc/HTML/examples/distributed_table/DistributedTable.cpp @@ -0,0 +1,74 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <vector> +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> +// functors +#include <boost/msm/front/functor_row.hpp> +#include <boost/msm/front/euml/common.hpp> + +#include "Empty.hpp" +#include "Open.hpp" + +using namespace std; +namespace msm = boost::msm; +namespace mpl = boost::mpl; +using namespace msm::front; + + +// front-end: define the FSM structure +struct player_ : public msm::front::state_machine_def<player_> +{ + // the initial state of the player SM. Must be defined + typedef Empty initial_state; + typedef mpl::vector<Empty,Open> explicit_creation; + + typedef player_ p; // makes transition table cleaner + + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } +}; +// Pick a back-end +typedef msm::back::state_machine<player_> player; + +// +// Testing utilities. +// +static char const* const state_names[] = { "Empty", "Open" }; +void pstate(player const& p) +{ + std::cout << " -> " << state_names[p.current_state()[0]] << std::endl; +} + +void test() +{ + player p; + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + p.start(); + // go to Open, call on_exit on Empty, then action, then on_entry on Open + p.process_event(open_close()); pstate(p); + p.process_event(open_close()); pstate(p); +} + +int main() +{ + test(); + return 0; +} + diff --git a/libs/msm/doc/HTML/examples/distributed_table/Empty.cpp b/libs/msm/doc/HTML/examples/distributed_table/Empty.cpp new file mode 100644 index 0000000000..ecbb9a8208 --- /dev/null +++ b/libs/msm/doc/HTML/examples/distributed_table/Empty.cpp @@ -0,0 +1,17 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <iostream> +#include "Empty.hpp" + +void Empty::open_drawer(open_close const&) +{ + std::cout << "Empty::open_drawer\n"; +} diff --git a/libs/msm/doc/HTML/examples/distributed_table/Empty.hpp b/libs/msm/doc/HTML/examples/distributed_table/Empty.hpp new file mode 100644 index 0000000000..00dd0e5f65 --- /dev/null +++ b/libs/msm/doc/HTML/examples/distributed_table/Empty.hpp @@ -0,0 +1,43 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef EMPTY_HPP +#define EMPTY_HPP + +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> +#include <boost/msm/front/row2.hpp> + +#include "Events.hpp" + +struct Open; + +namespace msm = boost::msm; +namespace mpl = boost::mpl; + +struct Empty : public msm::front::state<> +{ + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Empty" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Empty" << std::endl;} + void open_drawer(open_close const&); + + struct internal_transition_table : mpl::vector< + // Start Event Next Action Guard + //+-------------+---------+-------------+---------+---------------------------+----------------------+ + msm::front::a_row2 < Empty , open_close , Open , Empty,&Empty::open_drawer > + //+-------------+---------+-------------+---------+---------------------------+----------------------+ + > {}; +}; + +#endif diff --git a/libs/msm/doc/HTML/examples/distributed_table/Events.hpp b/libs/msm/doc/HTML/examples/distributed_table/Events.hpp new file mode 100644 index 0000000000..023216bd7c --- /dev/null +++ b/libs/msm/doc/HTML/examples/distributed_table/Events.hpp @@ -0,0 +1,39 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef EVENTS_HPP +#define EVENTS_HPP + +// events +struct play {}; +struct end_pause {}; +struct stop {}; +struct pause {}; +struct open_close {}; + +// A "complicated" event type that carries some data. +enum DiskTypeEnum +{ + DISK_CD=0, + DISK_DVD=1 +}; +struct cd_detected +{ + cd_detected(std::string name, DiskTypeEnum diskType) + : name(name), + disc_type(diskType) + {} + + std::string name; + DiskTypeEnum disc_type; +}; + + +#endif diff --git a/libs/msm/doc/HTML/examples/distributed_table/Open.cpp b/libs/msm/doc/HTML/examples/distributed_table/Open.cpp new file mode 100644 index 0000000000..2d1b63aed6 --- /dev/null +++ b/libs/msm/doc/HTML/examples/distributed_table/Open.cpp @@ -0,0 +1,17 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <iostream> +#include "Open.hpp" + +void Open::close_drawer(open_close const&) +{ + std::cout << "Open::close_drawer\n"; +} diff --git a/libs/msm/doc/HTML/examples/distributed_table/Open.hpp b/libs/msm/doc/HTML/examples/distributed_table/Open.hpp new file mode 100644 index 0000000000..03a8e8f9d3 --- /dev/null +++ b/libs/msm/doc/HTML/examples/distributed_table/Open.hpp @@ -0,0 +1,44 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef OPEN_HPP +#define OPEN_HPP + +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> +#include <boost/msm/front/row2.hpp> + +#include "Events.hpp" + +struct Empty; + +namespace msm = boost::msm; +namespace mpl = boost::mpl; +using namespace msm::front; + +struct Open : public msm::front::state<> +{ + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) {std::cout << "entering: Open" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Open" << std::endl;} + void close_drawer(open_close const&); + + struct internal_transition_table : mpl::vector< + // Start Event Next Action Guard + //+-------------+---------+-------------+---------+---------------------------+----------------------+ + msm::front::a_row2 < Open , open_close , Empty , Open,&Open::close_drawer > + //+-------------+---------+-------------+---------+---------------------------+----------------------+ + > {}; +}; + +#endif diff --git a/libs/msm/doc/HTML/examples/iPodEuml.cpp b/libs/msm/doc/HTML/examples/iPodEuml.cpp new file mode 100644 index 0000000000..547da45202 --- /dev/null +++ b/libs/msm/doc/HTML/examples/iPodEuml.cpp @@ -0,0 +1,271 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <vector> +#include <set> +#include <string> +#include <iostream> +// we need more than the default 20 states +#define FUSION_MAX_VECTOR_SIZE 20 +// we need more than the default 20 transitions +#include "boost/mpl/vector/vector50.hpp" +#include <boost/msm/back/state_machine.hpp> +#include <boost/msm/front/euml/euml.hpp> + + +using namespace std; +using namespace boost::msm::front::euml; +namespace msm = boost::msm; + +// attribute names and types +BOOST_MSM_EUML_DECLARE_ATTRIBUTE(int,m_Selected) +BOOST_MSM_EUML_DECLARE_ATTRIBUTE(int,m_SongIndex) +BOOST_MSM_EUML_DECLARE_ATTRIBUTE(int,m_NumberOfSongs) +#include "ipod_functors.hpp" + + +namespace // Concrete FSM implementation +{ + //flags + BOOST_MSM_EUML_FLAG(MenuActive) + BOOST_MSM_EUML_FLAG(NoFastFwd) + // hardware-generated events + BOOST_MSM_EUML_EVENT(Hold) + BOOST_MSM_EUML_EVENT(NoHold) + BOOST_MSM_EUML_EVENT(SouthPressed) + BOOST_MSM_EUML_EVENT(SouthReleased) + BOOST_MSM_EUML_EVENT(MiddleButton) + BOOST_MSM_EUML_EVENT(EastPressed) + BOOST_MSM_EUML_EVENT(EastReleased) + BOOST_MSM_EUML_EVENT(Off) + BOOST_MSM_EUML_EVENT(MenuButton) + // internally defined events + BOOST_MSM_EUML_EVENT(PlayPause) + BOOST_MSM_EUML_EVENT(EndPlay) + struct CloseMenu_impl : euml_event<CloseMenu_impl> + { + CloseMenu_impl(){}//defined only for stt + template<class EVENT> + CloseMenu_impl(EVENT const &) {} + }; + CloseMenu_impl const CloseMenu; + BOOST_MSM_EUML_EVENT(OnOffTimer) + BOOST_MSM_EUML_EVENT(MenuMiddleButton) + BOOST_MSM_EUML_EVENT(SelectSong) + BOOST_MSM_EUML_EVENT(SongFinished) + + BOOST_MSM_EUML_ATTRIBUTES((attributes_ << m_Selected ), StartSongAttributes) + BOOST_MSM_EUML_EVENT_WITH_ATTRIBUTES(StartSong,StartSongAttributes) + BOOST_MSM_EUML_EVENT(PreviousSong) + BOOST_MSM_EUML_EVENT(NextSong) + BOOST_MSM_EUML_EVENT(ForwardTimer) + BOOST_MSM_EUML_EVENT(PlayingMiddleButton) + + // Concrete iPod implementation + // The list of iPod states + BOOST_MSM_EUML_STATE(( NotHolding_Entry ),NotHolding) + BOOST_MSM_EUML_INTERRUPT_STATE(( NoHold,Holding_Entry ),Holding) + BOOST_MSM_EUML_STATE(( NotPlaying_Entry ),NotPlaying) + BOOST_MSM_EUML_STATE(( NoMenuMode_Entry ),NoMenuMode) + BOOST_MSM_EUML_STATE(( NoOnOffButton_Entry ),NoOnOffButton) + BOOST_MSM_EUML_STATE(( OffDown_Entry ),OffDown) + BOOST_MSM_EUML_STATE(( PlayerOff_Entry ),PlayerOff) + BOOST_MSM_EUML_STATE(( CheckMiddleButton_Entry ),CheckMiddleButton) + + // Concrete PlayingMode_ implementation + // The list of PlayingMode_ states + BOOST_MSM_EUML_STATE(( Playing_Entry ),Playing) + BOOST_MSM_EUML_STATE(( WaitingForNextPrev_Entry ),WaitingForNextPrev) + BOOST_MSM_EUML_STATE(( Paused_Entry ),Paused) + BOOST_MSM_EUML_STATE(( WaitingForEnd_Entry ),WaitingForEnd) + BOOST_MSM_EUML_STATE(( NoForward_Entry ),NoForward) + BOOST_MSM_EUML_STATE(( ForwardPressed_Entry,ForwardPressed_Exit ),ForwardPressed) + BOOST_MSM_EUML_STATE(( FastForward_Entry,FastForward_Exit ),FastForward) + BOOST_MSM_EUML_STATE(( StdDisplay_Entry ),StdDisplay) + BOOST_MSM_EUML_STATE(( SetPosition_Entry ),SetPosition) + BOOST_MSM_EUML_STATE(( SetMark_Entry ),SetMark) + BOOST_MSM_EUML_EXIT_STATE(( EndPlay,PlayingExit_Entry ),PlayingExit) + + //stt + BOOST_MSM_EUML_TRANSITION_TABLE(( + // +------------------------------------------------------------------------------+ + Paused == Playing + PlayPause , + Paused == Playing + Off , + Playing == Playing + StartSong + / (if_then_(event_(m_Selected) > Int_<0>() && + event_(m_Selected) < fsm_(m_NumberOfSongs), + fsm_(m_SongIndex) = event_(m_Selected) ),show_selected_song) , + Playing == Playing + SongFinished + / (if_then_else_(++fsm_(m_SongIndex) <= fsm_(m_NumberOfSongs), /*if*/ + show_playing_song, /*then*/ + (fsm_(m_SongIndex)=Int_<1>(),process_(EndPlay))/*else*/ ) ) , + Playing == Paused + PlayPause , + Playing == Paused + StartSong + / (if_then_(event_(m_Selected) > Int_<0>() && + event_(m_Selected) < fsm_(m_NumberOfSongs), + fsm_(m_SongIndex) = event_(m_Selected) ),show_selected_song) , + WaitingForNextPrev == WaitingForNextPrev+ PreviousSong + /( if_then_else_(--fsm_(m_SongIndex) > Int_<0>(), /*if*/ + show_playing_song, /*then*/ + (fsm_(m_SongIndex)=Int_<1>(),process_(EndPlay)) /*else*/ ) ) , + WaitingForNextPrev == WaitingForNextPrev+ NextSong + / (if_then_else_(++fsm_(m_SongIndex) <= fsm_(m_NumberOfSongs), /*if*/ + show_playing_song, /*then*/ + (fsm_(m_SongIndex)=Int_<1>(),process_(EndPlay)) /*else*/ ) ), + + PlayingExit == WaitingForEnd + EndPlay , + ForwardPressed == NoForward + EastPressed [!is_flag_(NoFastFwd)] , + NoForward == ForwardPressed + EastReleased / process_(NextSong) , + FastForward == ForwardPressed + ForwardTimer / do_fast_forward , + FastForward == FastForward + ForwardTimer / do_fast_forward , + FastForward == NoForward + EastReleased , + SetPosition == StdDisplay + PlayingMiddleButton , + StdDisplay == SetPosition + StartSong , + SetMark == SetPosition + PlayingMiddleButton , + StdDisplay == SetMark + PlayingMiddleButton , + StdDisplay == SetMark + StartSong + // +------------------------------------------------------------------------------+ + ),playingmode_transition_table ) + + BOOST_MSM_EUML_DECLARE_STATE_MACHINE( (playingmode_transition_table, //STT + init_ << Playing << WaitingForNextPrev << WaitingForEnd + << NoForward << StdDisplay, // Init States + fsm_(m_NumberOfSongs)=Int_<5>(), // entry + no_action, // exit + attributes_ << m_SongIndex << m_NumberOfSongs, //attributes + configure_<< NoFastFwd // Flags, Deferred events, configuration + ),PlayingMode_) + + // choice of back-end + typedef msm::back::state_machine<PlayingMode_> PlayingMode_type; + PlayingMode_type const PlayingMode; + + // Concrete MenuMode_ implementation + // The list of MenuMode_ states + BOOST_MSM_EUML_STATE(( WaitingForSongChoice_Entry ),WaitingForSongChoice) + BOOST_MSM_EUML_STATE(( StartCurrentSong_Entry ),StartCurrentSong) + BOOST_MSM_EUML_EXIT_STATE(( CloseMenu,MenuExit_Entry ),MenuExit) + + //stt + BOOST_MSM_EUML_TRANSITION_TABLE(( + // +------------------------------------------------------------------------------+ + StartCurrentSong == WaitingForSongChoice + MenuMiddleButton , + MenuExit == StartCurrentSong + SelectSong + // +------------------------------------------------------------------------------+ + ),menumode_transition_table ) + + BOOST_MSM_EUML_DECLARE_STATE_MACHINE( (menumode_transition_table, //STT + init_ << WaitingForSongChoice, // Init States + no_action, // entry + no_action, // exit + attributes_ << no_attributes_, //attributes + configure_<< MenuActive // Flags, Deferred events, configuration + ),MenuMode_) + + typedef msm::back::state_machine<MenuMode_> MenuMode_type; + MenuMode_type const MenuMode; + + // iPod stt + BOOST_MSM_EUML_TRANSITION_TABLE(( + // +------------------------------------------------------------------------------+ + Holding == NotHolding + Hold , + NotHolding == Holding + NoHold , + PlayingMode == NotPlaying + PlayPause , + NotPlaying == exit_pt_(PlayingMode,PlayingExit) + EndPlay + / process_(MenuButton) , + MenuMode == NoMenuMode + MenuButton , + NoMenuMode == exit_pt_(MenuMode,MenuExit)+ CloseMenu + / process2_(StartSong,Int_<5>()) , + OffDown == NoOnOffButton + SouthPressed , + NoOnOffButton == OffDown + SouthReleased + / process_(PlayPause) , + PlayerOff == OffDown + OnOffTimer + / (show_player_off,process_(Off)) , + NoOnOffButton == PlayerOff + SouthPressed / show_player_on , + NoOnOffButton == PlayerOff + NoHold / show_player_on , + CheckMiddleButton == CheckMiddleButton + MiddleButton + [is_flag_(MenuActive)] / process_(PlayingMiddleButton) , + CheckMiddleButton == CheckMiddleButton + MiddleButton + [!is_flag_(MenuActive)] / process_(PlayingMiddleButton) + // +------------------------------------------------------------------------------+ + ),ipod_transition_table ) + + BOOST_MSM_EUML_DECLARE_STATE_MACHINE(( ipod_transition_table, //STT + init_ << NotHolding << NotPlaying << NoMenuMode + << NoOnOffButton << CheckMiddleButton + ), + iPod_) //fsm name + typedef msm::back::state_machine<iPod_> iPod; + + void test() + { + iPod sm; + sm.start(); + // we first press Hold + std::cout << "pressing hold" << std::endl; + sm.process_event(Hold); + // pressing a button is now ignored + std::cout << "pressing a button" << std::endl; + sm.process_event(SouthPressed); + // or even one contained in a submachine + sm.process_event(EastPressed); + // no more holding + std::cout << "no more holding, end interrupt event sent" << std::endl; + sm.process_event(NoHold); + std::cout << "pressing South button a short time" << std::endl; + sm.process_event(SouthPressed); + // we suppose a short pressing leading to playing a song + sm.process_event(SouthReleased); + // we move to the next song + std::cout << "we move to the next song" << std::endl; + sm.process_event(NextSong); + // then back to no song => exit from playing, menu active + std::cout << "we press twice the West button (simulated)=> end of playing" << std::endl; + sm.process_event(PreviousSong); + sm.process_event(PreviousSong); + // even in menu mode, pressing play will start playing the first song + std::cout << "pressing play/pause" << std::endl; + sm.process_event(SouthPressed); + sm.process_event(SouthReleased); + // of course pausing must be possible + std::cout << "pressing play/pause" << std::endl; + sm.process_event(SouthPressed); + sm.process_event(SouthReleased); + std::cout << "pressing play/pause" << std::endl; + sm.process_event(SouthPressed); + sm.process_event(SouthReleased); + // while playing, you can fast forward + std::cout << "pressing East button a long time" << std::endl; + sm.process_event(EastPressed); + // let's suppose the timer just fired + sm.process_event(ForwardTimer); + sm.process_event(ForwardTimer); + // end of fast forwarding + std::cout << "releasing East button" << std::endl; + sm.process_event(EastReleased); + // we now press the middle button to set playing at a given position + std::cout << "pressing Middle button, fast forwarding disabled" << std::endl; + sm.process_event(MiddleButton); + std::cout <<"pressing East button to fast forward" << std::endl; + sm.process_event(EastPressed); + // we switch off and on + std::cout <<"switch off player" << std::endl; + sm.process_event(SouthPressed); + sm.process_event(OnOffTimer); + std::cout <<"switch on player" << std::endl; + sm.process_event(SouthPressed); + } +} + +int main() +{ + test(); + return 0; +} diff --git a/libs/msm/doc/HTML/examples/iPodSearch.cpp b/libs/msm/doc/HTML/examples/iPodSearch.cpp new file mode 100644 index 0000000000..29aa41354f --- /dev/null +++ b/libs/msm/doc/HTML/examples/iPodSearch.cpp @@ -0,0 +1,208 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <set> +#include <string> +#include <iostream> +#include <boost/msm/back/state_machine.hpp> +#include <boost/msm/front/state_machine_def.hpp> + +using namespace std; +namespace msm = boost::msm; + +namespace // Concrete FSM implementation +{ + // events + struct OneSong + { + OneSong(string const& asong):m_Song(asong){} + const string& get_data() const {return m_Song;} + private: + string m_Song; + }; + template <class DATA> + struct NotFound + { + DATA get_data() const {return m_Data;} + DATA m_Data; + }; + template <class DATA> + struct Found + { + DATA get_data() const {return m_Data;} + DATA m_Data; + }; + struct Done {}; + + template <class Container,class BASE_TYPE,class FSMType> + struct Insert : public boost::msm::front::state<BASE_TYPE,boost::msm::front::sm_ptr> + { + template <class Event,class FSM> + void on_entry(Event const& evt,FSM& ) + { + //TODO other containers + if (m_Cont) + { + m_Cont->insert(evt.get_data()); + } + m_fsm->process_event(Done()); + } + void set_sm_ptr(FSMType* fsm){m_fsm=fsm;} + void set_container(Container* cont){m_Cont=cont;} + Container* m_Cont; + + private: + FSMType* m_fsm; + }; + template <class BASE_TYPE,class FSMType> + struct StringFind : public boost::msm::front::state<BASE_TYPE,boost::msm::front::sm_ptr> + { + template <class Event,class FSM> + void on_entry(Event const& evt,FSM& ) + { + //TODO other containers + // if the element in the event is found + if (evt.get_data().find(m_Cont) != std::string::npos ) + { + Found<std::string> res; + res.m_Data = evt.get_data(); + m_fsm->process_event(res); + } + // data not found + else + { + NotFound<std::string> res; + res.m_Data = evt.get_data(); + m_fsm->process_event(res); + } + } + void set_sm_ptr(FSMType* fsm){m_fsm=fsm;} + void set_container(const char* cont){m_Cont=cont;} + private: + std::string m_Cont; + FSMType* m_fsm; + }; + template <class EventType,class Container,class BASE_TYPE,class FSMType> + struct Foreach : public boost::msm::front::state<BASE_TYPE,boost::msm::front::sm_ptr> + { + template <class Event,class FSM> + void on_entry(Event const& evt,FSM& ) + { + //TODO other containers + if (!m_Cont.empty()) + { + typename Container::value_type next_event = *m_Cont.begin(); + m_Cont.erase(m_Cont.begin()); + m_fsm->process_event(EventType(next_event)); + } + } + void set_sm_ptr(FSMType* fsm){m_fsm=fsm;} + void set_container(Container* cont){m_Cont=*cont;} + + private: + Container m_Cont; + FSMType* m_fsm; + }; + + + // Concrete FSM implementation + struct iPodSearch_ : public msm::front::state_machine_def<iPodSearch_> + { + typedef msm::back::state_machine<iPodSearch_> iPodSearch; + // The list of FSM states + typedef std::set<std::string> Songset; + typedef Insert<Songset,boost::msm::front::default_base_state,iPodSearch> MyInsert; + typedef StringFind<boost::msm::front::default_base_state,iPodSearch> MyFind; + typedef Foreach<OneSong,Songset,boost::msm::front::default_base_state,iPodSearch> MyForeach; + + // the initial state of the player SM. Must be defined + typedef MyForeach initial_state; + + // transition actions + + // guard conditions + + typedef iPodSearch_ fsm; // makes transition table cleaner + + // Transition table for player + struct transition_table : mpl::vector4< + // Start Event Next Action Guard + // +-----------+------------------+------------+---------------------+----------------------+ + _row < MyForeach , OneSong , MyFind >, + _row < MyFind , NotFound<string> , MyForeach >, + _row < MyFind , Found<string> , MyInsert >, + _row < MyInsert , Done , MyForeach > + // +-----------+------------------+------------+---------------------+----------------------+ + > {}; + iPodSearch_():m_AllSongs(),m_ResultSearch() + { + // add a few songs for testing + m_AllSongs.insert("Let it be"); + m_AllSongs.insert("Yellow submarine"); + m_AllSongs.insert("Twist and Shout"); + m_AllSongs.insert("She Loves You"); + } + template <class Event,class FSM> + void on_entry(Event const&,FSM& fsm) + { + fsm.template get_state<MyForeach&>().set_container(&m_AllSongs); + fsm.template get_state<MyInsert&>().set_container(&m_ResultSearch); + } + const Songset& get_result(){return m_ResultSearch;} + void reset_search(){m_ResultSearch.clear();} + + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + + private: + Songset m_AllSongs; + Songset m_ResultSearch; + }; + typedef msm::back::state_machine<iPodSearch_> iPodSearch; + + + void test() + { + iPodSearch search; + // look for "She Loves You" using the first letters + search.get_state<iPodSearch::MyFind*>()->set_container("Sh");// will find 2 songs + + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + search.start(); + // display all the songs + const iPodSearch::Songset& res = search.get_result(); + for (iPodSearch::Songset::const_iterator it = res.begin();it != res.end();++it) + { + cout << "candidate song:" << *it << endl; + } + cout << "search using more letters" << endl; + // look for "She Loves You" using more letters + search.reset_search(); + search.get_state<iPodSearch::MyFind*>()->set_container("She");// will find 1 song + search.start(); + const iPodSearch::Songset& res2 = search.get_result(); + for (iPodSearch::Songset::const_iterator it = res2.begin();it != res2.end();++it) + { + cout << "candidate song:" << *it << endl; + } + + } +} + +int main() +{ + test(); + return 0; +} diff --git a/libs/msm/doc/HTML/examples/iPodSearchEuml.cpp b/libs/msm/doc/HTML/examples/iPodSearchEuml.cpp new file mode 100644 index 0000000000..e792ff134a --- /dev/null +++ b/libs/msm/doc/HTML/examples/iPodSearchEuml.cpp @@ -0,0 +1,166 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +// same as iPodSearch.cpp but using eUML +// requires boost >= v1.40 because using mpl::string + +#include <vector> +#include <iostream> + +#include <boost/msm/back/state_machine.hpp> +#include <boost/msm/front/euml/euml.hpp> +#include <boost/msm/front/euml/stl.hpp> + +using namespace std; +using namespace boost::msm::front::euml; +namespace msm = boost::msm; +namespace mpl = boost::mpl; + +// how long the timer will ring when countdown elapsed. +#define RINGING_TIME 5 + +namespace // Concrete FSM implementation +{ + // events + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(std::string,m_song) + BOOST_MSM_EUML_ATTRIBUTES((attributes_ << m_song ), OneSongDef) + struct OneSong_impl : euml_event<OneSong_impl>,OneSongDef + { + OneSong_impl(){} + OneSong_impl(const string& asong) + { + get_attribute(m_song)=asong; + } + OneSong_impl(const char* asong) + { + get_attribute(m_song)=asong; + } + OneSong_impl(const OneSong_impl& asong) + { + get_attribute(m_song)=asong.get_attribute(m_song); + } + const string& get_data() const {return get_attribute(m_song);} + }; + OneSong_impl const OneSong; + + // attribute definitions + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(vector<OneSong_impl>,m_src_container) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(vector<OneSong_impl>,m_tgt_container) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(std::string,m_letters) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(vector<OneSong_impl>::iterator,m_src_it) + + // the same attribute name can be reused + BOOST_MSM_EUML_ATTRIBUTES((attributes_ << m_song ), NotFoundDef) + BOOST_MSM_EUML_EVENT_WITH_ATTRIBUTES(NotFound,NotFoundDef) + + BOOST_MSM_EUML_ATTRIBUTES((attributes_ << m_song ), FoundDef) + BOOST_MSM_EUML_EVENT_WITH_ATTRIBUTES(Found,FoundDef) + + BOOST_MSM_EUML_EVENT(Done) + + // Concrete FSM implementation + + // The list of FSM states + BOOST_MSM_EUML_STATE(( (push_back_(fsm_(m_tgt_container),event_(m_song)) + ,process_(Done)), + no_action ),Insert) + + BOOST_MSM_EUML_STATE(( if_then_else_( string_find_(event_(m_song),state_(m_letters)) != Npos_<string>() , + process2_(Found,event_(m_song)), + process2_(NotFound,event_(m_song)) ) , + no_action, + attributes_ << m_letters ),StringFind) + + BOOST_MSM_EUML_STATE(( if_then_( state_(m_src_it) != end_(fsm_(m_src_container)), + process2_(OneSong,*(state_(m_src_it)++)) ), + no_action, + attributes_ << m_src_it ),Foreach) + + // replaces the old transition table + BOOST_MSM_EUML_TRANSITION_TABLE(( + StringFind == Foreach + OneSong , + Insert == StringFind + Found , + Foreach == StringFind + NotFound , + Foreach == Insert + Done + // +------------------------------------------------------------------------------+ + ),transition_table ) + + BOOST_MSM_EUML_ACTION(Log_No_Transition) + { + template <class FSM,class Event> + void operator()(Event const& e,FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + // create a state machine "on the fly" + BOOST_MSM_EUML_DECLARE_STATE_MACHINE(( transition_table, //STT + init_ << Foreach, // Init + ( + clear_(fsm_(m_src_container)), //clear source + clear_(fsm_(m_tgt_container)), //clear results + push_back_(fsm_(m_src_container), + String_<mpl::string<'Let ','it ','be'> >()),//add a song + push_back_(fsm_(m_src_container), + String_<mpl::string<'Yell','ow s','ubma','rine'> >()),//add a song + push_back_(fsm_(m_src_container), + String_<mpl::string<'Twis','t an','d Sh','out'> >()),//add a song + push_back_(fsm_(m_src_container), + String_<mpl::string<'She ','love','s yo','u'> >()),//add a song + attribute_(substate_(Foreach()),m_src_it) + = begin_(fsm_(m_src_container)) //set the search begin + ), // Entry + no_action, // Exit + attributes_ << m_src_container // song list + << m_tgt_container, // result + configure_<< no_configure_, + Log_No_Transition + ), + iPodSearch_) //fsm name + + + // choice of back-end + typedef msm::back::state_machine<iPodSearch_> iPodSearch; + + void test() + { + iPodSearch search; + + // look for "She Loves You" using the first letters + search.get_state<BOOST_MSM_EUML_STATE_NAME(StringFind)&>().get_attribute(m_letters)="Sh";// will find 2 songs + + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + search.start(); + // display all the songs + for (vector<OneSong_impl>::const_iterator it = search.get_attribute(m_tgt_container).begin(); + it != search.get_attribute(m_tgt_container).end();++it) + { + cout << "candidate song:" << (*it).get_attribute(m_song) << endl; + } + + cout << "search using more letters" << endl; + // look for "She Loves You" using more letters + search.get_state<BOOST_MSM_EUML_STATE_NAME(StringFind)&>().get_attribute(m_letters)="She";// will find 1 song + search.start(); + // display all the songs + for (vector<OneSong_impl>::const_iterator it = search.get_attribute(m_tgt_container).begin(); + it != search.get_attribute(m_tgt_container).end();++it) + { + cout << "candidate song:" << (*it).get_attribute(m_song) << endl; + } + } +} + +int main() +{ + test(); + return 0; +} diff --git a/libs/msm/doc/HTML/examples/iPod_distributed/Events.hpp b/libs/msm/doc/HTML/examples/iPod_distributed/Events.hpp new file mode 100644 index 0000000000..ce06e9f35a --- /dev/null +++ b/libs/msm/doc/HTML/examples/iPod_distributed/Events.hpp @@ -0,0 +1,50 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef EVENTS_HPP +#define EVENTS_HPP + +//flags +struct MenuActive{}; +// hardware-generated events +struct Hold {}; +struct NoHold {}; +struct SouthPressed {}; +struct SouthReleased {}; +struct MiddleButton {}; +struct EastPressed{}; +struct EastReleased{}; +struct Off {}; +struct MenuButton {}; + +// internally used events +struct PlayPause {}; +struct EndPlay {}; +struct CloseMenu +{ + template<class EVENT> + CloseMenu(EVENT const &) {} +}; +struct OnOffTimer {}; +struct MenuMiddleButton {}; +struct SelectSong {}; +struct SongFinished {}; +struct StartSong +{ + StartSong (int song_index):m_Selected(song_index){} + int m_Selected; +}; +struct PreviousSong{}; +struct NextSong{}; +struct NextSongDerived : public NextSong{}; +struct ForwardTimer{}; +struct PlayingMiddleButton{}; + +#endif // EVENTS_HPP diff --git a/libs/msm/doc/HTML/examples/iPod_distributed/MenuMode.cpp b/libs/msm/doc/HTML/examples/iPod_distributed/MenuMode.cpp new file mode 100644 index 0000000000..e23f167995 --- /dev/null +++ b/libs/msm/doc/HTML/examples/iPod_distributed/MenuMode.cpp @@ -0,0 +1,12 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include "MenuMode.hpp" +BOOST_MSM_BACK_GENERATE_PROCESS_EVENT(MenuMode) diff --git a/libs/msm/doc/HTML/examples/iPod_distributed/MenuMode.hpp b/libs/msm/doc/HTML/examples/iPod_distributed/MenuMode.hpp new file mode 100644 index 0000000000..cad441cf37 --- /dev/null +++ b/libs/msm/doc/HTML/examples/iPod_distributed/MenuMode.hpp @@ -0,0 +1,62 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef MENU_MODE_HPP +#define MENU_MODE_HPP + +#include <iostream> +#include <boost/any.hpp> + +#include "Events.hpp" +#include <boost/msm/back/favor_compile_time.hpp> +#include <boost/msm/back/state_machine.hpp> +#include <boost/msm/front/state_machine_def.hpp> + +using namespace std; +namespace msm = boost::msm; + +struct MenuMode_ : public msm::front::state_machine_def<MenuMode_> +{ + typedef mpl::vector1<MenuActive> flag_list; + struct WaitingForSongChoice : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: MenuMode::WaitingForSongChoice" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: MenuMode::WaitingForSongChoice" << std::endl;} + }; + struct StartCurrentSong : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: MenuMode::StartCurrentSong" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: MenuMode::StartCurrentSong" << std::endl;} + }; + struct MenuExit : public msm::front::exit_pseudo_state<CloseMenu> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: MenuMode::WaitingForSongChoice" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: MenuMode::WaitingForSongChoice" << std::endl;} + }; + typedef WaitingForSongChoice initial_state; + typedef MenuMode_ fsm; // makes transition table cleaner + // Transition table for player + struct transition_table : mpl::vector2< + // Start Event Next Action Guard + // +---------------------+------------------+-------------------+---------------------+----------------------+ + _row < WaitingForSongChoice , MenuMiddleButton , StartCurrentSong >, + _row < StartCurrentSong , SelectSong , MenuExit > + // +---------------------+------------------+-------------------+---------------------+----------------------+ + > {}; +}; +typedef msm::back::state_machine<MenuMode_> MenuMode; + +#endif diff --git a/libs/msm/doc/HTML/examples/iPod_distributed/PlayingMode.cpp b/libs/msm/doc/HTML/examples/iPod_distributed/PlayingMode.cpp new file mode 100644 index 0000000000..e35019c424 --- /dev/null +++ b/libs/msm/doc/HTML/examples/iPod_distributed/PlayingMode.cpp @@ -0,0 +1,12 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include "PlayingMode.hpp" +BOOST_MSM_BACK_GENERATE_PROCESS_EVENT(PlayingMode) diff --git a/libs/msm/doc/HTML/examples/iPod_distributed/PlayingMode.hpp b/libs/msm/doc/HTML/examples/iPod_distributed/PlayingMode.hpp new file mode 100644 index 0000000000..1328041ae4 --- /dev/null +++ b/libs/msm/doc/HTML/examples/iPod_distributed/PlayingMode.hpp @@ -0,0 +1,251 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef PLAYING_MODE_HPP +#define PLAYING_MODE_HPP + +#include <iostream> +#include <boost/any.hpp> +#define FUSION_MAX_VECTOR_SIZE 20 + +#include "Events.hpp" +#include <boost/msm/back/favor_compile_time.hpp> +#include <boost/msm/back/state_machine.hpp> +#include <boost/msm/front/state_machine_def.hpp> +#include <boost/msm/front/euml/euml.hpp> + +using namespace std; +namespace msm = boost::msm; +namespace euml = boost::msm::front::euml; + +struct PlayingMode_ : public msm::front::state_machine_def<PlayingMode_> +{ + //flags + struct NoFastFwd{}; + + struct Playing : public msm::front::state<default_base_state,msm::front::sm_ptr> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) + { + std::cout << "starting: PlayingMode::Playing" << std::endl; + std::cout << "playing song:" << m_fsm->get_current_song() << std::endl; + } + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: PlayingMode::Playing" << std::endl;} + void set_sm_ptr(PlayingMode_* pl) + { + m_fsm = pl; + } + private: + PlayingMode_* m_fsm; + }; + struct Paused : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: PlayingMode::Paused" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: PlayingMode::Paused" << std::endl;} + }; + struct WaitingForNextPrev : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: PlayingMode::WaitingForNextPrev" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: PlayingMode::WaitingForNextPrev" << std::endl;} + }; + struct WaitingForEnd : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: PlayingMode::WaitingForEnd" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: PlayingMode::WaitingForEnd" << std::endl;} + }; + struct NoForward : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: PlayingMode::NoForward" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: PlayingMode::NoForward" << std::endl;} + }; + struct ForwardPressed : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) + { + std::cout << "starting: PlayingMode::ForwardPressed," << "start timer" << std::endl; + } + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) + { + std::cout << "finishing: PlayingMode::ForwardPressed," << "stop timer" << std::endl; + } + }; + struct FastForward : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) + { + std::cout << "starting: PlayingMode::FastForward," << "start timer" << std::endl; + } + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) + { + std::cout << "finishing: PlayingMode::FastForward," << "stop timer" << std::endl; + } + }; + struct StdDisplay : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: PlayingMode::StdDisplay" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: PlayingMode::StdDisplay" << std::endl;} + }; + struct SetPosition : public msm::front::state<> + { + typedef mpl::vector1<NoFastFwd> flag_list; + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: PlayingMode::SetPosition" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: PlayingMode::SetPosition" << std::endl;} + }; + struct SetMark : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: PlayingMode::SetMark" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: PlayingMode::SetMark" << std::endl;} + }; + struct PlayingExit : public msm::front::exit_pseudo_state<EndPlay> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: PlayingMode::PlayingExit" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: PlayingMode::PlayingExit" << std::endl;} + }; + // transition action methods + struct inc_song_counter : euml::euml_action<inc_song_counter> + { + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& fsm,SourceState& ,TargetState& ) + { + if (++fsm.m_SongIndex <= fsm.m_NumberOfSongs ) + { + std::cout << "playing song:" << fsm.m_SongIndex << std::endl; + } + else + { + // last song => end playing, next play will start at the beginning + fsm.m_SongIndex = 1; + fsm.process_event(EndPlay()); + } + } + }; + + void select_song(StartSong const& evt) + { + if ((evt.m_Selected>0) && (evt.m_Selected<=m_NumberOfSongs)) + { + m_SongIndex = evt.m_Selected; + std::cout << "selecting song:" << m_SongIndex << std::endl; + } + else + { + // play current song + std::cout << "selecting song:" << m_SongIndex << std::endl; + } + } + struct dec_song_counter : euml::euml_action<dec_song_counter> + { + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& fsm,SourceState& ,TargetState& ) + { + if (--fsm.m_SongIndex >0 ) + { + std::cout << "playing song:" << fsm.m_SongIndex << std::endl; + } + else + { + // before first song => end playing + fsm.m_SongIndex = 1; + fsm.process_event(EndPlay()); + } + } + }; + struct send_NextSong : euml::euml_action<send_NextSong> + { + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& fsm,SourceState& ,TargetState& ) + { + fsm.process_event(NextSong()); + } + }; + + void do_fast_forward(ForwardTimer const&) + { + std::cout << "moving song forward..." << std::endl; + } + + // transition guard methods + struct fast_fwd_ok : euml::euml_action<fast_fwd_ok> + { + template <class FSM,class EVT,class SourceState,class TargetState> + bool operator()(EVT const& ,FSM& fsm,SourceState& ,TargetState& ) + { + // guard accepts only if fast forward is possible (No SetPosition mode) + return !fsm.is_flag_active<NoFastFwd>(); + } + }; + // initial states / orthogonal zones + typedef mpl::vector5<Playing,WaitingForNextPrev,WaitingForEnd,NoForward,StdDisplay> + initial_state; + typedef PlayingMode_ fsm; // makes transition table cleaner + // Transition table for player + struct transition_table : mpl::vector19< + // Start Event Next Action Guard + // +--------------------+---------------------+--------------------+--------------------------+----------------------+ + _row < Playing , PlayPause , Paused >, + _row < Playing , Off , Paused >, + a_row < Playing , StartSong , Playing , &fsm::select_song >, + _row < Paused , PlayPause , Playing >, + msm::front::Row < Playing , SongFinished , Playing , inc_song_counter , msm::front::none >, + a_row < Paused , StartSong , Playing , &fsm::select_song >, + // +--------------------+---------------------+--------------------+--------------------------+----------------------+ + msm::front::Row < WaitingForNextPrev , PreviousSong , WaitingForNextPrev , dec_song_counter , msm::front::none >, + msm::front::Row < WaitingForNextPrev , NextSong , WaitingForNextPrev , inc_song_counter , msm::front::none >, + // +--------------------+---------------------+--------------------+--------------------------+----------------------+ + _row < WaitingForEnd , EndPlay , PlayingExit >, + // +--------------------+---------------------+--------------------+--------------------------+----------------------+ + msm::front::Row < NoForward , EastPressed , ForwardPressed , msm::front::none , fast_fwd_ok >, + msm::front::Row < ForwardPressed , EastReleased , NoForward , send_NextSong , msm::front::none >, + a_row < ForwardPressed , ForwardTimer , FastForward , &fsm::do_fast_forward >, + a_row < FastForward , ForwardTimer , FastForward , &fsm::do_fast_forward >, + _row < FastForward , EastReleased , NoForward >, + // +--------------------+---------------------+---------------------+--------------------------+----------------------+ + _row < StdDisplay , PlayingMiddleButton , SetPosition >, + _row < SetPosition , StartSong , StdDisplay >, + _row < SetPosition , PlayingMiddleButton , SetMark >, + _row < SetMark , PlayingMiddleButton , StdDisplay >, + _row < SetMark , StartSong , StdDisplay > + // +--------------------+---------------------+---------------------+--------------------------+----------------------+ + > {}; + PlayingMode_(): + m_SongIndex(1), + // for simplicity we decide there are 5 songs + m_NumberOfSongs(5){} + + int get_current_song(){return m_SongIndex;} + int m_SongIndex; + int m_NumberOfSongs; + +}; +typedef msm::back::state_machine<PlayingMode_> PlayingMode; + +#endif // PLAYING_MODE_HPP diff --git a/libs/msm/doc/HTML/examples/iPod_distributed/iPod.cpp b/libs/msm/doc/HTML/examples/iPod_distributed/iPod.cpp new file mode 100644 index 0000000000..d5b3b79945 --- /dev/null +++ b/libs/msm/doc/HTML/examples/iPod_distributed/iPod.cpp @@ -0,0 +1,243 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <vector> +#include <set> +#include <string> +#include <iostream> +#define FUSION_MAX_VECTOR_SIZE 20 + +#include "boost/mpl/vector/vector50.hpp" +#include <boost/msm/back/state_machine.hpp> +#include <boost/msm/front/state_machine_def.hpp> + +#include "Events.hpp" +#include "PlayingMode.hpp" +#include "MenuMode.hpp" + +using namespace std; +namespace msm = boost::msm; + +namespace // Concrete FSM implementation +{ + struct iPod_; + typedef msm::back::state_machine<iPod_, + ::boost::msm::back::favor_compile_time> iPod; + + // Concrete FSM implementation + struct iPod_ : public msm::front::state_machine_def<iPod_> + { + // The list of FSM states + struct NotHolding : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: NotHolding" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: NotHolding" << std::endl;} + }; + struct Holding : public msm::front::interrupt_state<NoHold> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: Holding" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: Holding" << std::endl;} + }; + struct NotPlaying : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: NotPlaying" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: NotPlaying" << std::endl;} + }; + struct NoMenuMode : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: NoMenuMode" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: NoMenuMode" << std::endl;} + }; + struct NoOnOffButton : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: NoOnOffButton" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: NoOnOffButton" << std::endl;} + }; + struct OffDown : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: OffDown, start timer" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: OffDown, end timer" << std::endl;} + }; + struct PlayerOff : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: PlayerOff" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: PlayerOff" << std::endl;} + }; + struct CheckMiddleButton : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: CheckMiddleButton" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: CheckMiddleButton" << std::endl;} + }; + + // the initial state of the player SM. Must be defined + typedef mpl::vector5<NotHolding,NotPlaying,NoMenuMode,NoOnOffButton,CheckMiddleButton> + initial_state; + // transition actions + void send_ActivateMenu(EndPlay const&) + { + std::cout << "leaving Playing" << std::endl; + // we need to activate the menu and simulate its button + (static_cast<iPod*>(this))->process_event(MenuButton()); + } + void send_StartSong(CloseMenu const&) + { + // we suppose the 5th song was selected + (static_cast<iPod*>(this))->process_event(StartSong(5)); + } + void send_PlayPause(SouthReleased const&) + { + // action using the message queue to generate another event + (static_cast<iPod*>(this))->process_event(PlayPause()); + } + void send_Off(OnOffTimer const&) + { + std::cout << "turning player off" << std::endl; + (static_cast<iPod*>(this))->process_event(Off()); + } + void send_PlayingMiddleButton(MiddleButton const&) + { + (static_cast<iPod*>(this))->process_event(PlayingMiddleButton()); + } + void send_MenuMiddleButton(MiddleButton const&) + { + (static_cast<iPod*>(this))->process_event(MenuMiddleButton()); + } + // guard conditions + bool is_menu(MiddleButton const&) + { + return (static_cast<iPod*>(this))->is_flag_active<MenuActive>(); + } + bool is_no_menu(MiddleButton const& evt) + { + return !is_menu(evt); + } + template <class EVENT> + void switch_on(EVENT const&) + { + std::cout << "turning player on" << std::endl; + } + typedef iPod_ fsm; // makes transition table cleaner + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +-------------------+---------------+-------------------+--------------------------------+----------------------+ + _row < NotHolding , Hold , Holding >, + _row < Holding , NoHold , NotHolding >, + // +-------------------+---------------+-------------------+--------------------------------+----------------------+ + _row < NotPlaying , PlayPause , PlayingMode >, + a_row < PlayingMode::exit_pt<PlayingMode_:: + PlayingExit> , EndPlay , NotPlaying , &fsm::send_ActivateMenu >, + // +-------------------+---------------+-------------------+--------------------------------+----------------------+ + _row < NoMenuMode , MenuButton , MenuMode >, + a_row < MenuMode::exit_pt<MenuMode_:: + MenuExit> , CloseMenu , NoMenuMode , &fsm::send_StartSong >, + // +-------------------+---------------+-------------------+--------------------------------+----------------------+ + _row < NoOnOffButton , SouthPressed , OffDown >, + a_row < OffDown , SouthReleased , NoOnOffButton , &fsm::send_PlayPause >, + a_row < OffDown , OnOffTimer , PlayerOff , &fsm::send_Off >, + a_row < PlayerOff , SouthPressed , NoOnOffButton , &fsm::switch_on >, + a_row < PlayerOff , NoHold , NoOnOffButton , &fsm::switch_on >, + // +-------------------+---------------+--------------------+--------------------------------+----------------------+ + row < CheckMiddleButton , MiddleButton , CheckMiddleButton , &fsm::send_PlayingMiddleButton , &fsm::is_menu >, + row < CheckMiddleButton , MiddleButton , CheckMiddleButton , &fsm::send_MenuMiddleButton , &fsm::is_no_menu > + // +-------------------+---------------+--------------------+--------------------------------+----------------------+ + > {}; + + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + + void test() + { + iPod sm; + sm.start(); + // we first press Hold + std::cout << "pressing hold" << std::endl; + sm.process_event(Hold()); + // pressing a button is now ignored + std::cout << "pressing a button" << std::endl; + sm.process_event(SouthPressed()); + // or even one contained in a submachine + sm.process_event(EastPressed()); + // no more holding + std::cout << "no more holding, end interrupt event sent" << std::endl; + sm.process_event(NoHold()); + std::cout << "pressing South button a short time" << std::endl; + sm.process_event(SouthPressed()); + // we suppose a short pressing leading to playing a song + sm.process_event(SouthReleased()); + // we move to the next song + std::cout << "we move to the next song" << std::endl; + sm.process_event(NextSong()); + // then back to no song => exit from playing, menu active + std::cout << "we press twice the West button (simulated)=> end of playing" << std::endl; + sm.process_event(PreviousSong()); + sm.process_event(PreviousSong()); + // even in menu mode, pressing play will start playing the first song + std::cout << "pressing play/pause" << std::endl; + sm.process_event(SouthPressed()); + sm.process_event(SouthReleased()); + // of course pausing must be possible + std::cout << "pressing play/pause" << std::endl; + sm.process_event(SouthPressed()); + sm.process_event(SouthReleased()); + std::cout << "pressing play/pause" << std::endl; + sm.process_event(SouthPressed()); + sm.process_event(SouthReleased()); + // while playing, you can fast forward + std::cout << "pressing East button a long time" << std::endl; + sm.process_event(EastPressed()); + // let's suppose the timer just fired + sm.process_event(ForwardTimer()); + sm.process_event(ForwardTimer()); + // end of fast forwarding + std::cout << "releasing East button" << std::endl; + sm.process_event(EastReleased()); + // we now press the middle button to set playing at a given position + std::cout << "pressing Middle button, fast forwarding disabled" << std::endl; + sm.process_event(MiddleButton()); + std::cout <<"pressing East button to fast forward" << std::endl; + sm.process_event(EastPressed()); + // we switch off and on + std::cout <<"switch off player" << std::endl; + sm.process_event(SouthPressed()); + sm.process_event(OnOffTimer()); + std::cout <<"switch on player" << std::endl; + sm.process_event(SouthPressed()); + } +} + +int main() +{ + test(); + return 0; +} diff --git a/libs/msm/doc/HTML/examples/ipod_functors.hpp b/libs/msm/doc/HTML/examples/ipod_functors.hpp new file mode 100644 index 0000000000..c49c3473dd --- /dev/null +++ b/libs/msm/doc/HTML/examples/ipod_functors.hpp @@ -0,0 +1,248 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef IPOD_FUNCTORS_HPP +#define IPOD_FUNCTORS_HPP +#include <boost/msm/front/euml/euml.hpp> + +BOOST_MSM_EUML_ACTION(NotHolding_Entry) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: NotHolding" << std::endl; + } +}; +BOOST_MSM_EUML_ACTION(Holding_Entry) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: Holding" << std::endl; + } +}; +BOOST_MSM_EUML_ACTION(NotPlaying_Entry) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: NotPlaying" << std::endl; + } +}; +BOOST_MSM_EUML_ACTION(NoMenuMode_Entry) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: NoMenuMode" << std::endl; + } +}; +BOOST_MSM_EUML_ACTION(NoOnOffButton_Entry) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: NoOnOffButton" << std::endl; + } +}; +BOOST_MSM_EUML_ACTION(OffDown_Entry) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: OffDown" << std::endl; + } +}; +BOOST_MSM_EUML_ACTION(PlayerOff_Entry) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: PlayerOff" << std::endl; + } +}; +BOOST_MSM_EUML_ACTION(CheckMiddleButton_Entry) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: CheckMiddleButton" << std::endl; + } +}; +BOOST_MSM_EUML_ACTION(Playing_Entry) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM& fsm,STATE& ) + { + std::cout << "entering: Playing" << std::endl; + std::cout << "playing song:" << fsm.get_attribute(m_SongIndex) << std::endl; + } +}; +BOOST_MSM_EUML_ACTION(WaitingForNextPrev_Entry) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: WaitingForNextPrev" << std::endl; + } +}; +BOOST_MSM_EUML_ACTION(Paused_Entry) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: Paused" << std::endl; + } +}; +BOOST_MSM_EUML_ACTION(WaitingForEnd_Entry) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: OffDown" << std::endl; + } +}; +BOOST_MSM_EUML_ACTION(NoForward_Entry) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: NoForward" << std::endl; + } +}; +BOOST_MSM_EUML_ACTION(ForwardPressed_Entry) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: ForwardPressed" << std::endl; + } +}; +BOOST_MSM_EUML_ACTION(ForwardPressed_Exit) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "leaving: ForwardPressed" << std::endl; + } +}; +BOOST_MSM_EUML_ACTION(FastForward_Entry) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: FastForward" << std::endl; + } +}; +BOOST_MSM_EUML_ACTION(FastForward_Exit) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "leaving: FastForward" << std::endl; + } +}; +BOOST_MSM_EUML_ACTION(StdDisplay_Entry) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: StdDisplay" << std::endl; + } +}; +BOOST_MSM_EUML_ACTION(SetPosition_Entry) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: SetPosition" << std::endl; + } +}; +BOOST_MSM_EUML_ACTION(SetMark_Entry) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: SetMark" << std::endl; + } +}; +BOOST_MSM_EUML_ACTION(PlayingExit_Entry) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: PlayingExit" << std::endl; + } +}; +BOOST_MSM_EUML_ACTION(WaitingForSongChoice_Entry) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: WaitingForSongChoice" << std::endl; + } +}; +BOOST_MSM_EUML_ACTION(StartCurrentSong_Entry) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: StartCurrentSong" << std::endl; + } +}; +BOOST_MSM_EUML_ACTION(MenuExit_Entry) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: MenuExit" << std::endl; + } +}; +BOOST_MSM_EUML_ACTION(show_selected_song) +{ + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& fsm,SourceState& ,TargetState& ) + { + std::cout << "selecting song:" << fsm.get_attribute(m_SongIndex) << std::endl; + } +}; +BOOST_MSM_EUML_ACTION(do_fast_forward) +{ + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& fsm,SourceState& ,TargetState& ) + { + std::cout << "moving song forward..." << std::endl; + } +}; +BOOST_MSM_EUML_ACTION(show_playing_song) +{ + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& fsm,SourceState& ,TargetState& ) + { + std::cout << "playing song:" << fsm.get_attribute(m_SongIndex) << std::endl; + } +}; +BOOST_MSM_EUML_ACTION(show_player_off) +{ + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& fsm,SourceState& ,TargetState& ) + { + std::cout << "turning player off" << std::endl; + } +}; +BOOST_MSM_EUML_ACTION(show_player_on) +{ + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& fsm,SourceState& ,TargetState& ) + { + std::cout << "turning player on" << std::endl; + } +}; +#endif diff --git a/libs/msm/doc/HTML/examples/logging_functors.h b/libs/msm/doc/HTML/examples/logging_functors.h new file mode 100644 index 0000000000..7e68cedb07 --- /dev/null +++ b/libs/msm/doc/HTML/examples/logging_functors.h @@ -0,0 +1,370 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef LOGGING_FUNCTORS +#define LOGGING_FUNCTORS + +BOOST_MSM_EUML_ACTION(Empty_Entry) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: Empty" << std::endl; + } +}; +BOOST_MSM_EUML_ACTION(Empty_Exit) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "leaving: Empty" << std::endl; + } +}; + +BOOST_MSM_EUML_ACTION(Open_Entry) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: Open" << std::endl; + } +}; +BOOST_MSM_EUML_ACTION(Open_Exit) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "leaving: Open" << std::endl; + } +}; + +BOOST_MSM_EUML_ACTION(Stopped_Entry) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: Stopped" << std::endl; + } +}; +BOOST_MSM_EUML_ACTION(Stopped_Exit) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "leaving: Stopped" << std::endl; + } +}; + +BOOST_MSM_EUML_ACTION(AllOk_Entry) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "starting: AllOk" << std::endl; + } +}; +BOOST_MSM_EUML_ACTION(AllOk_Exit) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "finishing: AllOk" << std::endl; + } +}; + +BOOST_MSM_EUML_ACTION(ErrorMode_Entry) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "starting: ErrorMode" << std::endl; + } +}; +BOOST_MSM_EUML_ACTION(ErrorMode_Exit) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "finishing: ErrorMode" << std::endl; + } +}; + +BOOST_MSM_EUML_ACTION(Playing_Entry) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: Playing" << std::endl; + } +}; +BOOST_MSM_EUML_ACTION(Playing_Exit) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "leaving: Playing" << std::endl; + } +}; + +BOOST_MSM_EUML_ACTION(Song1_Entry) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "starting: First song" << std::endl; + } +}; +BOOST_MSM_EUML_ACTION(Song1_Exit) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "finishing: First Song" << std::endl; + } +}; + +BOOST_MSM_EUML_ACTION(Song2_Entry) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "starting: Second song" << std::endl; + } +}; +BOOST_MSM_EUML_ACTION(Song2_Exit) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "finishing: Second Song" << std::endl; + } +}; + +BOOST_MSM_EUML_ACTION(Song3_Entry) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "starting: Third song" << std::endl; + } +}; +BOOST_MSM_EUML_ACTION(Song3_Exit) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "finishing: Third Song" << std::endl; + } +}; + +BOOST_MSM_EUML_ACTION(Region2State1_Entry) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "starting: Region2State1" << std::endl; + } +}; +BOOST_MSM_EUML_ACTION(Region2State1_Exit) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "finishing: Region2State1" << std::endl; + } +}; +BOOST_MSM_EUML_ACTION(Region2State2_Entry) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "starting: Region2State2" << std::endl; + } +}; +BOOST_MSM_EUML_ACTION(Region2State2_Exit) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "finishing: Region2State2" << std::endl; + } +}; +// transition actions for Playing +BOOST_MSM_EUML_ACTION(start_next_song) +{ + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM&,SourceState& ,TargetState& ) + { + std::cout << "Playing::start_next_song" << endl; + } +}; +BOOST_MSM_EUML_ACTION(start_prev_song) +{ + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM&,SourceState& ,TargetState& ) + { + std::cout << "Playing::start_prev_song" << endl; + } +}; + +// transition actions +BOOST_MSM_EUML_ACTION(start_playback) +{ + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM&,SourceState& ,TargetState& ) + { + cout << "player::start_playback" << endl; + } +}; +BOOST_MSM_EUML_ACTION(open_drawer) +{ + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM&,SourceState& ,TargetState& ) + { + cout << "player::open_drawer" << endl; + } +}; +BOOST_MSM_EUML_ACTION(close_drawer) +{ + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM&,SourceState& ,TargetState& ) + { + cout << "player::close_drawer" << endl; + } +}; +BOOST_MSM_EUML_ACTION(store_cd_info) +{ + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const&, FSM& ,SourceState& ,TargetState& ) + { + cout << "player::store_cd_info" << endl; + // it is now easy to use the message queue. + // alternatively to the proces_ in the transition table, we could write: + // fsm.process_event(play()); + } +}; +BOOST_MSM_EUML_ACTION(stop_playback) +{ + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM&,SourceState& ,TargetState& ) + { + cout << "player::stop_playback" << endl; + } +}; +BOOST_MSM_EUML_ACTION(pause_playback) +{ + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM&,SourceState& ,TargetState& ) + { + cout << "player::pause_playback" << endl; + } +}; +BOOST_MSM_EUML_ACTION(resume_playback) +{ + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM&,SourceState& ,TargetState& ) + { + cout << "player::resume_playback" << endl; + } +}; +BOOST_MSM_EUML_ACTION(stop_and_open) +{ + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM&,SourceState& ,TargetState& ) + { + cout << "player::stop_and_open" << endl; + } +}; +BOOST_MSM_EUML_ACTION(stopped_again) +{ + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM&,SourceState& ,TargetState& ) + { + cout << "player::stopped_again" << endl; + } +}; + +BOOST_MSM_EUML_ACTION(report_error) +{ + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM&,SourceState& ,TargetState& ) + { + cout << "player::report_error" << endl; + } +}; + +BOOST_MSM_EUML_ACTION(report_end_error) +{ + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM&,SourceState& ,TargetState& ) + { + cout << "player::report_end_error" << endl; + } +}; +BOOST_MSM_EUML_ACTION(internal_action1) +{ + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const&, FSM& ,SourceState& ,TargetState& ) + { + cout << "Open::internal action1" << endl; + } +}; +BOOST_MSM_EUML_ACTION(internal_action2) +{ + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const&, FSM& ,SourceState& ,TargetState& ) + { + cout << "Open::internal action2" << endl; + } +}; +BOOST_MSM_EUML_ACTION(internal_action) +{ + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const&, FSM& ,SourceState& ,TargetState& ) + { + cout << "Open::internal action" << endl; + } +}; +enum DiskTypeEnum +{ + DISK_CD=0, + DISK_DVD=1 +}; + +// Handler called when no_transition detected +BOOST_MSM_EUML_ACTION(Log_No_Transition) +{ + template <class FSM,class Event> + void operator()(Event const& e,FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } +}; + +BOOST_MSM_EUML_ACTION(internal_guard1) +{ + template <class FSM,class EVT,class SourceState,class TargetState> + bool operator()(EVT const&, FSM& ,SourceState& ,TargetState& ) + { + cout << "Open::internal guard1" << endl; + return false; + } +}; +BOOST_MSM_EUML_ACTION(internal_guard2) +{ + template <class FSM,class EVT,class SourceState,class TargetState> + bool operator()(EVT const&, FSM& ,SourceState& ,TargetState& ) + { + cout << "Open::internal guard2" << endl; + return false; + } +}; +#endif // LOGGING_FUNCTORS diff --git a/libs/msm/doc/HTML/footer.htm b/libs/msm/doc/HTML/footer.htm new file mode 100644 index 0000000000..b6448f91bc --- /dev/null +++ b/libs/msm/doc/HTML/footer.htm @@ -0,0 +1,14 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> + +<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"> +<body> + <p>© Copyright Christophe Henry, 2010</p> + <p>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">www.boost.org/LICENSE_1_0.txt</a>)</p> + +</body> +</html> + diff --git a/libs/msm/doc/HTML/index.html b/libs/msm/doc/HTML/index.html new file mode 100644 index 0000000000..1dae2bee4e --- /dev/null +++ b/libs/msm/doc/HTML/index.html @@ -0,0 +1,19 @@ +<html><head> + <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> + <title>Meta State Machine (MSM)</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="next" href="pr01.html" title="Preface"></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">Meta State Machine (MSM)</th></tr><tr><td width="20%" align="left"> </td><th width="60%" align="center"> </th><td width="20%" align="right"> <a accesskey="n" href="pr01.html">Next</a></td></tr></table><hr></div><div class="book" title="Meta State Machine (MSM)"><div class="titlepage"><div><div><h1 class="title"><a name="d0e2"></a>Meta State Machine (MSM)</h1></div><div><div class="author"><h3 class="author">Christophe Henry</h3><code class="email"><<a class="email" href="mailto:christophe.j.henry@googlemail.com">christophe.j.henry@googlemail.com</a>></code></div></div><div><p class="copyright">Copyright © 2008-2010 + <span> Distributed under the Boost Software License, Version 1.0. (See + accompanying file LICENSE_1_0.txt or copy at <a class="link" href="http://www.boost.org/LICENSE_1_0.txt" target="_top">http://www.boost.org/LICENSE_1_0.txt</a> ) </span> + </p></div></div><hr></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="preface"><a href="pr01.html">Preface</a></span></dt><dt><span class="part"><a href="pt01.html">I. User' guide</a></span></dt><dd><dl><dt><span class="chapter"><a href="ch01.html">1. Founding idea</a></span></dt><dt><span class="chapter"><a href="ch02.html">2. UML Short Guide</a></span></dt><dd><dl><dt><span class="sect1"><a href="ch02.html#d0e111">What are state machines?</a></span></dt><dt><span class="sect1"><a href="ch02s02.html">Concepts</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch02s02.html#d0e121">State machine, state, transition, event </a></span></dt><dt><span class="sect2"><a href="ch02s02.html#d0e151">Submachines, orthogonal regions, pseudostates </a></span></dt><dt><span class="sect2"><a href="ch02s02.html#d0e194"> + History </a></span></dt><dt><span class="sect2"><a href="ch02s02.html#d0e208">Completion transitions / anonymous + transitions</a></span></dt><dt><span class="sect2"><a href="ch02s02.html#d0e220"> Internal transitions </a></span></dt><dt><span class="sect2"><a href="ch02s02.html#d0e226"> + Conflicting transitions </a></span></dt></dl></dd><dt><span class="sect1"><a href="ch02s03.html">State machine glossary</a></span></dt></dl></dd><dt><span class="chapter"><a href="ch03.html">3. Tutorial</a></span></dt><dd><dl><dt><span class="sect1"><a href="ch03.html#d0e315">Design</a></span></dt><dt><span class="sect1"><a href="ch03s02.html">Basic front-end</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch03s02.html#d0e334">A simple example</a></span></dt><dt><span class="sect2"><a href="ch03s02.html#d0e348">Transition table</a></span></dt><dt><span class="sect2"><a href="ch03s02.html#d0e396">Defining states with entry/exit actions</a></span></dt><dt><span class="sect2"><a href="ch03s02.html#d0e409">What do you actually do inside actions / guards?</a></span></dt><dt><span class="sect2"><a href="ch03s02.html#d0e461">Defining a simple state machine</a></span></dt><dt><span class="sect2"><a href="ch03s02.html#d0e519">Defining a submachine</a></span></dt><dt><span class="sect2"><a href="ch03s02.html#d0e554">Orthogonal regions, terminate state, event deferring</a></span></dt><dt><span class="sect2"><a href="ch03s02.html#d0e645">History</a></span></dt><dt><span class="sect2"><a href="ch03s02.html#d0e690">Completion (anonymous) transitions</a></span></dt><dt><span class="sect2"><a href="ch03s02.html#d0e717">Internal transitions</a></span></dt><dt><span class="sect2"><a href="ch03s02.html#d0e819">more row types</a></span></dt><dt><span class="sect2"><a href="ch03s02.html#d0e852">Explicit entry / entry and exit pseudo-state / fork</a></span></dt><dt><span class="sect2"><a href="ch03s02.html#d0e1041">Flags</a></span></dt><dt><span class="sect2"><a href="ch03s02.html#d0e1095">Event Hierarchy</a></span></dt><dt><span class="sect2"><a href="ch03s02.html#d0e1116">Customizing a state machine / Getting more speed</a></span></dt><dt><span class="sect2"><a href="ch03s02.html#d0e1165">Choosing the initial event</a></span></dt><dt><span class="sect2"><a href="ch03s02.html#d0e1178"> Containing state machine (deprecated)</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch03s03.html">Functor front-end</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch03s03.html#d0e1217"> Transition table </a></span></dt><dt><span class="sect2"><a href="ch03s03.html#d0e1250">Defining states with entry/exit actions</a></span></dt><dt><span class="sect2"><a href="ch03s03.html#d0e1269">What do you actually do inside actions / guards (Part 2)?</a></span></dt><dt><span class="sect2"><a href="ch03s03.html#d0e1281">Defining a simple state machine</a></span></dt><dt><span class="sect2"><a href="ch03s03.html#d0e1289">Anonymous transitions</a></span></dt><dt><span class="sect2"><a href="ch03s03.html#d0e1315">Internal + transitions</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch03s04.html">eUML (experimental)</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch03s04.html#d0e1394">Transition table</a></span></dt><dt><span class="sect2"><a href="ch03s04.html#d0e1435">A simple example: rewriting only our transition table</a></span></dt><dt><span class="sect2"><a href="ch03s04.html#d0e1466">Defining events, actions and states with entry/exit actions</a></span></dt><dt><span class="sect2"><a href="ch03s04.html#d0e1579">Wrapping up a simple state machine and first complete examples</a></span></dt><dt><span class="sect2"><a href="ch03s04.html#d0e1627">Defining a submachine</a></span></dt><dt><span class="sect2"><a href="ch03s04.html#d0e1643"> + Attributes / Function call</a></span></dt><dt><span class="sect2"><a href="ch03s04.html#d0e1743">Orthogonal regions, flags, event deferring</a></span></dt><dt><span class="sect2"><a href="ch03s04.html#d0e1855"> + Customizing a state machine / Getting + more speed</a></span></dt><dt><span class="sect2"><a href="ch03s04.html#d0e1890">Completion / Anonymous transitions</a></span></dt><dt><span class="sect2"><a href="ch03s04.html#d0e1908">Internal transitions</a></span></dt><dt><span class="sect2"><a href="ch03s04.html#d0e1939">Other state types</a></span></dt><dt><span class="sect2"><a href="ch03s04.html#d0e2003">Helper functions</a></span></dt><dt><span class="sect2"><a href="ch03s04.html#d0e2106">Phoenix-like STL support</a></span></dt><dt><span class="sect2"><a href="ch03s04.html#d0e2159">Writing actions with Boost.Phoenix (in development)</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch03s05.html">Back-end</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch03s05.html#d0e2218">Creation </a></span></dt><dt><span class="sect2"><a href="ch03s05.html#d0e2227">Starting and stopping a state + machine</a></span></dt><dt><span class="sect2"><a href="ch03s05.html#d0e2246">Event dispatching</a></span></dt><dt><span class="sect2"><a href="ch03s05.html#d0e2255">Active state(s)</a></span></dt><dt><span class="sect2"><a href="ch03s05.html#d0e2269">Serialization</a></span></dt><dt><span class="sect2"><a href="ch03s05.html#d0e2322">Base state type </a></span></dt><dt><span class="sect2"><a href="ch03s05.html#d0e2348">Visitor</a></span></dt><dt><span class="sect2"><a href="ch03s05.html#d0e2391">Flags</a></span></dt><dt><span class="sect2"><a href="ch03s05.html#d0e2404">Getting a state</a></span></dt><dt><span class="sect2"><a href="ch03s05.html#d0e2417"> State machine constructor with arguments </a></span></dt><dt><span class="sect2"><a href="ch03s05.html#d0e2457">Trading run-time speed for + better compile-time / multi-TU compilation</a></span></dt><dt><span class="sect2"><a href="ch03s05.html#d0e2539">Compile-time state machine analysis </a></span></dt><dt><span class="sect2"><a href="ch03s05.html#d0e2583"> Enqueueing events for later + processing </a></span></dt><dt><span class="sect2"><a href="ch03s05.html#d0e2603"> Customizing the message queues </a></span></dt><dt><span class="sect2"><a href="ch03s05.html#d0e2618">Policy definition with Boost.Parameter </a></span></dt><dt><span class="sect2"><a href="ch03s05.html#d0e2626">Choosing when to switch active + states </a></span></dt></dl></dd></dl></dd><dt><span class="chapter"><a href="ch04.html">4. Performance / Compilers</a></span></dt><dd><dl><dt><span class="sect1"><a href="ch04.html#d0e2707">Speed</a></span></dt><dt><span class="sect1"><a href="ch04s02.html">Executable size</a></span></dt><dt><span class="sect1"><a href="ch04s03.html">Supported compilers</a></span></dt><dt><span class="sect1"><a href="ch04s04.html"> Limitations </a></span></dt><dt><span class="sect1"><a href="ch04s05.html"> Compilers corner </a></span></dt></dl></dd><dt><span class="chapter"><a href="ch05.html">5. Questions & Answers, tips</a></span></dt><dt><span class="chapter"><a href="ch06.html">6. Internals</a></span></dt><dd><dl><dt><span class="sect1"><a href="ch06.html#d0e2920">Backend: Run To Completion</a></span></dt><dt><span class="sect1"><a href="ch06s02.html">Frontend / Backend + interface</a></span></dt><dt><span class="sect1"><a href="ch06s03.html"> Generated state ids </a></span></dt><dt><span class="sect1"><a href="ch06s04.html">Metaprogramming tools</a></span></dt></dl></dd><dt><span class="chapter"><a href="ch07.html">7. Acknowledgements</a></span></dt><dd><dl><dt><span class="sect1"><a href="ch07.html#d0e3215">MSM v2</a></span></dt><dt><span class="sect1"><a href="ch07s02.html"> MSM v1</a></span></dt></dl></dd><dt><span class="chapter"><a href="ch08.html">8. Version history</a></span></dt><dd><dl><dt><span class="sect1"><a href="ch08.html#d0e3262">From V2.21 to V2.22 (Boost 1.48)</a></span></dt><dt><span class="sect1"><a href="ch08s02.html">From V2.20 to V2.21 (Boost 1.47)</a></span></dt><dt><span class="sect1"><a href="ch08s03.html">From V2.12 to V2.20 (Boost 1.46)</a></span></dt><dt><span class="sect1"><a href="ch08s04.html">From V2.10 to V2.12 (Boost 1.45)</a></span></dt><dt><span class="sect1"><a href="ch08s05.html">From V2.0 to V2.12 (Boost 1.44)</a></span></dt></dl></dd></dl></dd><dt><span class="part"><a href="pt02.html">II. Reference</a></span></dt><dd><dl><dt><span class="chapter"><a href="ch09.html">9. eUML operators and basic helpers</a></span></dt><dt><span class="chapter"><a href="ch10.html">10. + Functional programming </a></span></dt><dt><span class="refentrytitle"><a href="re01.html">Common headers</a></span><span class="refpurpose"> — The common types used by front- and back-ends</span></dt><dt><span class="refentrytitle"><a href="re02.html">Back-end</a></span><span class="refpurpose"> — The back-end headers</span></dt><dt><span class="refentrytitle"><a href="re03.html">Front-end</a></span><span class="refpurpose"> — The front-end headers</span></dt></dl></dd></dl></div><div class="list-of-tables"><p><b>List of Tables</b></p><dl><dt>9.1. <a href="ch09.html#d0e3485">Operators and state machine helpers</a></dt><dt>10.1. <a href="ch10.html#d0e3840">STL algorithms</a></dt><dt>10.2. <a href="ch10.html#d0e3953">STL algorithms</a></dt><dt>10.3. <a href="ch10.html#d0e3981">STL algorithms</a></dt><dt>10.4. <a href="ch10.html#d0e4199">STL container methods</a></dt><dt>10.5. <a href="ch10.html#d0e4330">STL list methods</a></dt><dt>10.6. <a href="ch10.html#d0e4406">STL associative container methods </a></dt><dt>10.7. <a href="ch10.html#d0e4477">STL pair</a></dt><dt>10.8. <a href="ch10.html#d0e4503">STL string</a></dt></dl></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"> </td><td width="20%" align="center"> </td><td width="40%" align="right"> <a accesskey="n" href="pr01.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top"> </td><td width="20%" align="center"> </td><td width="40%" align="right" valign="top"> Preface</td></tr></table></div></body></html>
\ No newline at end of file diff --git a/libs/msm/doc/HTML/pr01.html b/libs/msm/doc/HTML/pr01.html new file mode 100644 index 0000000000..62cf7a97d8 --- /dev/null +++ b/libs/msm/doc/HTML/pr01.html @@ -0,0 +1,61 @@ +<html><head> + <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> + <title>Preface</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="index.html" title="Meta State Machine (MSM)"><link rel="prev" href="index.html" title="Meta State Machine (MSM)"><link rel="next" href="pt01.html" title="Part I. User' guide"></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">Preface</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="index.html">Prev</a> </td><th width="60%" align="center"> </th><td width="20%" align="right"> <a accesskey="n" href="pt01.html">Next</a></td></tr></table><hr></div><div class="preface" title="Preface"><div class="titlepage"><div><div><h2 class="title"><a name="d0e22"></a>Preface</h2></div></div></div><p>MSM is a library allowing you to easily and quickly define state machines of very high + performance. From this point, two main questions usually quickly arise, so please allow + me to try answering them upfront.</p><p> + </p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>When do I need a state machine?</p><p>More often that you think. Very often, one defined a state machine + informally without even noticing it. For example, one declares inside a + class some boolean attribute, say to remember that a task has been + completed. Later the boolean actually needs a third value, so it becomes an + int. A few weeks, a second attribute is needed. Then a third. Soon, you find + yourself writing:</p><p><code class="code">void incoming_data(data)</code></p><p><code class="code">{</code></p><p><code class="code"> if (data == packet_3 && flag1 == work_done && flag2 + > step3)...</code></p><p><code class="code">}</code></p><p>This starts to look like event processing (contained inside data) if some + stage of the object life has been achieved (but is ugly).</p><p>This could be a protocol definition and it is a common use case for state + machines. Another common one is a user interface. The stage of the user's + interaction defines if some button is active, a functionality is available, + etc.</p><p>But there are many more use cases if you start looking. Actually, a whole + model-driven development method, Executable UML + (http://en.wikipedia.org/wiki/Executable_UML) specifies its complete dynamic + behavior using state machines. Class diagram, state machine diagrams, and an + action language are all you absolutely need in the Executable UML + world.</p></li><li class="listitem"><p>Another state machine library? What for?</p><p>True, there are many state machine libraries. This should already be an + indication that if you're not using any of them, you might be missing + something. Why should you use this one? Unfortunately, when looking for a + good state machine library, you usually pretty fast hit one or several of + the following snags:</p><div class="itemizedlist"><ul class="itemizedlist" type="circle"><li class="listitem"><p>speed: "state machines are slow" is usually the first + criticism you might hear. While it is often an excuse not to use + any and instead resort to dirty, hand-written implementations (I + mean, no, yours are not dirty of course, I'm talking about other + developers). MSM removes this often feeble excuse because it is + blazingly fast. Most hand-written implementations will be beaten + by MSM.</p></li><li class="listitem"><p>ease of use: good argument. If you used another library, you + are probably right. Many state machine definitions will look + similar to:</p><p><code class="code">state s1 = new State; // a state</code></p><p><code class="code">state s2 = new State; // another state</code></p><p><code class="code">event e = new Event; // event</code></p><p><code class="code">s1->addTransition(e,s2); // transition s1 -> + s2</code></p><p>The more transitions you have, the less readable it is. A long + time ago, there was not so much Java yet, and many electronic + systems were built with a state machine defined by a simple + transition table. You could easily see the whole structure and + immediately see if you forgot some transitions. Thanks to our + new OO techniques, this ease of use was gone. MSM gives you back + the transition table and reduces the noise to the + minimum.</p></li><li class="listitem"><p>expressiveness: MSM offers several front-ends and constantly + tries to improve state machine definition techniques. For + example, you can define a transition with eUML (one of MSM's + front-ends) as:</p><p><code class="code">state1 == state2 + event [condition] / + action</code></p><p>This is not simply syntactic sugar. Such a formalized, + readable structure allows easy communication with domain experts + of a software to be constructed. Having domain experts + understand your code will greatly reduce the number of + bugs.</p></li><li class="listitem"><p>model-driven-development: a common difficulty of a + model-driven development is the complexity of making a + round-trip (generating code from model and then model from + code). This is due to the fact that if a state machine structure + is hard for you to read, chances are that your parsing tool will + also have a hard time. MSM's syntax will hopefully help tool + writers.</p></li><li class="listitem"><p>features: most developers use only 20% of the richly defined + UML standard. Unfortunately, these are never the same 20% for + all. And so, very likely, one will need something from the + standard which is not implemented. MSM offers a very large part + of the standard, with more on the way.</p></li></ul></div><p>Let us not wait any longer, I hope you will enjoy MSM and have fun with + it!</p></li></ul></div><p> + </p></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="index.html">Prev</a> </td><td width="20%" align="center"> </td><td width="40%" align="right"> <a accesskey="n" href="pt01.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">Meta State Machine (MSM) </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> Part I. User' guide</td></tr></table></div></body></html>
\ No newline at end of file diff --git a/libs/msm/doc/HTML/pt01.html b/libs/msm/doc/HTML/pt01.html new file mode 100644 index 0000000000..bad3ad9ce7 --- /dev/null +++ b/libs/msm/doc/HTML/pt01.html @@ -0,0 +1,15 @@ +<html><head> + <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> + <title>Part I. User' guide</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="index.html" title="Meta State Machine (MSM)"><link rel="prev" href="pr01.html" title="Preface"><link rel="next" href="ch01.html" title="Chapter 1. Founding idea"></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">Part I. User' guide</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="pr01.html">Prev</a> </td><th width="60%" align="center"> </th><td width="20%" align="right"> <a accesskey="n" href="ch01.html">Next</a></td></tr></table><hr></div><div class="part" title="Part I. User' guide"><div class="titlepage"><div><div><h1 class="title"><a name="d0e96"></a>Part I. User' guide</h1></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="chapter"><a href="ch01.html">1. Founding idea</a></span></dt><dt><span class="chapter"><a href="ch02.html">2. UML Short Guide</a></span></dt><dd><dl><dt><span class="sect1"><a href="ch02.html#d0e111">What are state machines?</a></span></dt><dt><span class="sect1"><a href="ch02s02.html">Concepts</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch02s02.html#d0e121">State machine, state, transition, event </a></span></dt><dt><span class="sect2"><a href="ch02s02.html#d0e151">Submachines, orthogonal regions, pseudostates </a></span></dt><dt><span class="sect2"><a href="ch02s02.html#d0e194"> + History </a></span></dt><dt><span class="sect2"><a href="ch02s02.html#d0e208">Completion transitions / anonymous + transitions</a></span></dt><dt><span class="sect2"><a href="ch02s02.html#d0e220"> Internal transitions </a></span></dt><dt><span class="sect2"><a href="ch02s02.html#d0e226"> + Conflicting transitions </a></span></dt></dl></dd><dt><span class="sect1"><a href="ch02s03.html">State machine glossary</a></span></dt></dl></dd><dt><span class="chapter"><a href="ch03.html">3. Tutorial</a></span></dt><dd><dl><dt><span class="sect1"><a href="ch03.html#d0e315">Design</a></span></dt><dt><span class="sect1"><a href="ch03s02.html">Basic front-end</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch03s02.html#d0e334">A simple example</a></span></dt><dt><span class="sect2"><a href="ch03s02.html#d0e348">Transition table</a></span></dt><dt><span class="sect2"><a href="ch03s02.html#d0e396">Defining states with entry/exit actions</a></span></dt><dt><span class="sect2"><a href="ch03s02.html#d0e409">What do you actually do inside actions / guards?</a></span></dt><dt><span class="sect2"><a href="ch03s02.html#d0e461">Defining a simple state machine</a></span></dt><dt><span class="sect2"><a href="ch03s02.html#d0e519">Defining a submachine</a></span></dt><dt><span class="sect2"><a href="ch03s02.html#d0e554">Orthogonal regions, terminate state, event deferring</a></span></dt><dt><span class="sect2"><a href="ch03s02.html#d0e645">History</a></span></dt><dt><span class="sect2"><a href="ch03s02.html#d0e690">Completion (anonymous) transitions</a></span></dt><dt><span class="sect2"><a href="ch03s02.html#d0e717">Internal transitions</a></span></dt><dt><span class="sect2"><a href="ch03s02.html#d0e819">more row types</a></span></dt><dt><span class="sect2"><a href="ch03s02.html#d0e852">Explicit entry / entry and exit pseudo-state / fork</a></span></dt><dt><span class="sect2"><a href="ch03s02.html#d0e1041">Flags</a></span></dt><dt><span class="sect2"><a href="ch03s02.html#d0e1095">Event Hierarchy</a></span></dt><dt><span class="sect2"><a href="ch03s02.html#d0e1116">Customizing a state machine / Getting more speed</a></span></dt><dt><span class="sect2"><a href="ch03s02.html#d0e1165">Choosing the initial event</a></span></dt><dt><span class="sect2"><a href="ch03s02.html#d0e1178"> Containing state machine (deprecated)</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch03s03.html">Functor front-end</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch03s03.html#d0e1217"> Transition table </a></span></dt><dt><span class="sect2"><a href="ch03s03.html#d0e1250">Defining states with entry/exit actions</a></span></dt><dt><span class="sect2"><a href="ch03s03.html#d0e1269">What do you actually do inside actions / guards (Part 2)?</a></span></dt><dt><span class="sect2"><a href="ch03s03.html#d0e1281">Defining a simple state machine</a></span></dt><dt><span class="sect2"><a href="ch03s03.html#d0e1289">Anonymous transitions</a></span></dt><dt><span class="sect2"><a href="ch03s03.html#d0e1315">Internal + transitions</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch03s04.html">eUML (experimental)</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch03s04.html#d0e1394">Transition table</a></span></dt><dt><span class="sect2"><a href="ch03s04.html#d0e1435">A simple example: rewriting only our transition table</a></span></dt><dt><span class="sect2"><a href="ch03s04.html#d0e1466">Defining events, actions and states with entry/exit actions</a></span></dt><dt><span class="sect2"><a href="ch03s04.html#d0e1579">Wrapping up a simple state machine and first complete examples</a></span></dt><dt><span class="sect2"><a href="ch03s04.html#d0e1627">Defining a submachine</a></span></dt><dt><span class="sect2"><a href="ch03s04.html#d0e1643"> + Attributes / Function call</a></span></dt><dt><span class="sect2"><a href="ch03s04.html#d0e1743">Orthogonal regions, flags, event deferring</a></span></dt><dt><span class="sect2"><a href="ch03s04.html#d0e1855"> + Customizing a state machine / Getting + more speed</a></span></dt><dt><span class="sect2"><a href="ch03s04.html#d0e1890">Completion / Anonymous transitions</a></span></dt><dt><span class="sect2"><a href="ch03s04.html#d0e1908">Internal transitions</a></span></dt><dt><span class="sect2"><a href="ch03s04.html#d0e1939">Other state types</a></span></dt><dt><span class="sect2"><a href="ch03s04.html#d0e2003">Helper functions</a></span></dt><dt><span class="sect2"><a href="ch03s04.html#d0e2106">Phoenix-like STL support</a></span></dt><dt><span class="sect2"><a href="ch03s04.html#d0e2159">Writing actions with Boost.Phoenix (in development)</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch03s05.html">Back-end</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch03s05.html#d0e2218">Creation </a></span></dt><dt><span class="sect2"><a href="ch03s05.html#d0e2227">Starting and stopping a state + machine</a></span></dt><dt><span class="sect2"><a href="ch03s05.html#d0e2246">Event dispatching</a></span></dt><dt><span class="sect2"><a href="ch03s05.html#d0e2255">Active state(s)</a></span></dt><dt><span class="sect2"><a href="ch03s05.html#d0e2269">Serialization</a></span></dt><dt><span class="sect2"><a href="ch03s05.html#d0e2322">Base state type </a></span></dt><dt><span class="sect2"><a href="ch03s05.html#d0e2348">Visitor</a></span></dt><dt><span class="sect2"><a href="ch03s05.html#d0e2391">Flags</a></span></dt><dt><span class="sect2"><a href="ch03s05.html#d0e2404">Getting a state</a></span></dt><dt><span class="sect2"><a href="ch03s05.html#d0e2417"> State machine constructor with arguments </a></span></dt><dt><span class="sect2"><a href="ch03s05.html#d0e2457">Trading run-time speed for + better compile-time / multi-TU compilation</a></span></dt><dt><span class="sect2"><a href="ch03s05.html#d0e2539">Compile-time state machine analysis </a></span></dt><dt><span class="sect2"><a href="ch03s05.html#d0e2583"> Enqueueing events for later + processing </a></span></dt><dt><span class="sect2"><a href="ch03s05.html#d0e2603"> Customizing the message queues </a></span></dt><dt><span class="sect2"><a href="ch03s05.html#d0e2618">Policy definition with Boost.Parameter </a></span></dt><dt><span class="sect2"><a href="ch03s05.html#d0e2626">Choosing when to switch active + states </a></span></dt></dl></dd></dl></dd><dt><span class="chapter"><a href="ch04.html">4. Performance / Compilers</a></span></dt><dd><dl><dt><span class="sect1"><a href="ch04.html#d0e2707">Speed</a></span></dt><dt><span class="sect1"><a href="ch04s02.html">Executable size</a></span></dt><dt><span class="sect1"><a href="ch04s03.html">Supported compilers</a></span></dt><dt><span class="sect1"><a href="ch04s04.html"> Limitations </a></span></dt><dt><span class="sect1"><a href="ch04s05.html"> Compilers corner </a></span></dt></dl></dd><dt><span class="chapter"><a href="ch05.html">5. Questions & Answers, tips</a></span></dt><dt><span class="chapter"><a href="ch06.html">6. Internals</a></span></dt><dd><dl><dt><span class="sect1"><a href="ch06.html#d0e2920">Backend: Run To Completion</a></span></dt><dt><span class="sect1"><a href="ch06s02.html">Frontend / Backend + interface</a></span></dt><dt><span class="sect1"><a href="ch06s03.html"> Generated state ids </a></span></dt><dt><span class="sect1"><a href="ch06s04.html">Metaprogramming tools</a></span></dt></dl></dd><dt><span class="chapter"><a href="ch07.html">7. Acknowledgements</a></span></dt><dd><dl><dt><span class="sect1"><a href="ch07.html#d0e3215">MSM v2</a></span></dt><dt><span class="sect1"><a href="ch07s02.html"> MSM v1</a></span></dt></dl></dd><dt><span class="chapter"><a href="ch08.html">8. Version history</a></span></dt><dd><dl><dt><span class="sect1"><a href="ch08.html#d0e3262">From V2.21 to V2.22 (Boost 1.48)</a></span></dt><dt><span class="sect1"><a href="ch08s02.html">From V2.20 to V2.21 (Boost 1.47)</a></span></dt><dt><span class="sect1"><a href="ch08s03.html">From V2.12 to V2.20 (Boost 1.46)</a></span></dt><dt><span class="sect1"><a href="ch08s04.html">From V2.10 to V2.12 (Boost 1.45)</a></span></dt><dt><span class="sect1"><a href="ch08s05.html">From V2.0 to V2.12 (Boost 1.44)</a></span></dt></dl></dd></dl></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="pr01.html">Prev</a> </td><td width="20%" align="center"> </td><td width="40%" align="right"> <a accesskey="n" href="ch01.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">Preface </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> Chapter 1. Founding idea</td></tr></table></div></body></html>
\ No newline at end of file diff --git a/libs/msm/doc/HTML/pt02.html b/libs/msm/doc/HTML/pt02.html new file mode 100644 index 0000000000..1d39f71806 --- /dev/null +++ b/libs/msm/doc/HTML/pt02.html @@ -0,0 +1,4 @@ +<html><head> + <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> + <title>Part II. Reference</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="index.html" title="Meta State Machine (MSM)"><link rel="prev" href="ch08s05.html" title="From V2.0 to V2.12 (Boost 1.44)"><link rel="next" href="ch09.html" title="Chapter 9. eUML operators and basic helpers"></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">Part II. Reference</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="ch08s05.html">Prev</a> </td><th width="60%" align="center"> </th><td width="20%" align="right"> <a accesskey="n" href="ch09.html">Next</a></td></tr></table><hr></div><div class="part" title="Part II. Reference"><div class="titlepage"><div><div><h1 class="title"><a name="d0e3474"></a>Part II. <span class="command"><strong><a name="Reference-begin"></a></strong></span>Reference</h1></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="chapter"><a href="ch09.html">9. eUML operators and basic helpers</a></span></dt><dt><span class="chapter"><a href="ch10.html">10. + Functional programming </a></span></dt><dt><span class="refentrytitle"><a href="re01.html">Common headers</a></span><span class="refpurpose"> — The common types used by front- and back-ends</span></dt><dt><span class="refentrytitle"><a href="re02.html">Back-end</a></span><span class="refpurpose"> — The back-end headers</span></dt><dt><span class="refentrytitle"><a href="re03.html">Front-end</a></span><span class="refpurpose"> — The front-end headers</span></dt></dl></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="ch08s05.html">Prev</a> </td><td width="20%" align="center"> </td><td width="40%" align="right"> <a accesskey="n" href="ch09.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">From V2.0 to V2.12 (Boost 1.44) </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> Chapter 9. eUML operators and basic helpers</td></tr></table></div></body></html>
\ No newline at end of file diff --git a/libs/msm/doc/HTML/re01.html b/libs/msm/doc/HTML/re01.html new file mode 100644 index 0000000000..25aa3162b6 --- /dev/null +++ b/libs/msm/doc/HTML/re01.html @@ -0,0 +1,8 @@ +<html><head> + <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> + <title>Common headers</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="pt02.html" title="Part II. Reference"><link rel="prev" href="ch10.html" title="Chapter 10. Functional programming"><link rel="next" href="re02.html" title="Back-end"></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">Common headers</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="ch10.html">Prev</a> </td><th width="60%" align="center">Part II. Reference</th><td width="20%" align="right"> <a accesskey="n" href="re02.html">Next</a></td></tr></table><hr></div><div class="refentry" title="Common headers"><a name="d0e4945"></a><div class="titlepage"></div><div class="refnamediv"><h2>Name</h2><p>Common headers — The common types used by front- and back-ends</p></div><div class="refsect1" title="msm/common.hpp"><a name="d0e4951"></a><h2>msm/common.hpp</h2><p>This header provides one type, wrap, which is an empty type whose only reason + to exist is to be cheap to construct, so that it can be used with mpl::for_each, + as shown in the Metaprogramming book, chapter 9.</p><pre class="classsynopsis"> <span class="ooclass"><span class="classname">template <class Dummy> wrap{};</span></span> {<br>}</pre></div><div class="refsect1" title="msm/row_tags.hpp"><a name="d0e4960"></a><h2>msm/row_tags.hpp</h2><p>This header contains the row type tags which front-ends can support partially + or totally. Please see the <span class="command"><strong><a class="command" href="ch06s02.html#internals-front-back-interface">Internals</a></strong></span> section for a description of the different + types.</p></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="ch10.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="pt02.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="re02.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">Chapter 10. + Functional programming </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> Back-end</td></tr></table></div></body></html>
\ No newline at end of file diff --git a/libs/msm/doc/HTML/re02.html b/libs/msm/doc/HTML/re02.html new file mode 100644 index 0000000000..087a566b65 --- /dev/null +++ b/libs/msm/doc/HTML/re02.html @@ -0,0 +1,113 @@ +<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="pt02.html" title="Part II. Reference"><link rel="prev" href="re01.html" title="Common headers"><link rel="next" href="re03.html" title="Front-end"></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="re01.html">Prev</a> </td><th width="60%" align="center">Part II. Reference</th><td width="20%" align="right"> <a accesskey="n" href="re03.html">Next</a></td></tr></table><hr></div><div class="refentry" title="Back-end"><a name="d0e4968"></a><div class="titlepage"></div><div class="refnamediv"><h2>Name</h2><p>Back-end — The back-end headers</p></div><div class="refsect1" title="msm/back/state_machine.hpp"><a name="d0e4974"></a><h2>msm/back/state_machine.hpp</h2><p> This header provides one type, state_machine, MSM's state machine engine + implementation.</p><pre class="classsynopsis"> <span class="ooclass"><span class="classname">template <class Derived,class HistoryPolicy=NoHistory,class + CompilePolicy=favor_runtime_speed> state_machine</span></span> {<br>}</pre><div class="refsect2" title="Template arguments"><a name="d0e4983"></a><h3> Template arguments </h3><div class="refsect3" title="Derived"><a name="d0e4986"></a><h4> Derived </h4><p>The name of the front-end state machine definition. All three + front-ends are possible.</p></div><div class="refsect3" title="HistoryPolicy"><a name="d0e4991"></a><h4> HistoryPolicy </h4><p>The desired history. This can be: AlwaysHistory, NoHistory, + ShallowHistory. Default is NoHistory.</p></div><div class="refsect3" title="CompilePolicy"><a name="d0e4996"></a><h4> CompilePolicy </h4><p>The trade-off performance / compile-time. There are two predefined + policies, favor_runtime_speed and favor_compile_time. Default is + favor_runtime_speed, best performance, longer compile-time. See <a class="link" href="ch03s05.html#backend-tradeof-rt-ct">the backend</a>.</p></div></div><div class="refsect2" title="methods"><a name="d0e5004"></a><h3> methods </h3><div class="refsect3" title="start"><a name="d0e5007"></a><h4>start</h4><p> The start methods must be called before any call to process_event. It + activates the entry action of the initial state(s). This allows you to + choose when a state machine can start. See <a class="link" href="ch03s05.html#backend-start">backend</a>.</p><code class="methodsynopsis"><span class="methodname">void start</span>(<span class="methodparam"></span>);</code></div><div class="refsect3" title="process_event"><a name="d0e5020"></a><h4>process_event</h4><p>The event processing method implements the double-dispatch. Each call + to this function with a new event type instantiates a new dispatch + algorithm and increases compile-time.</p><code class="methodsynopsis"><span class="methodname">template <class Event> HandledEnum + process_event</span>(<span class="methodparam">Event const&</span>);</code></div><div class="refsect3" title="current_state"><a name="d0e5031"></a><h4>current_state</h4><p>Returns the ids of currently active states. You will typically need it + only for debugging or logging purposes.</p><code class="methodsynopsis"><span class="methodname">const int* current_state const</span>(<span class="methodparam"></span>);</code></div><div class="refsect3" title="get_state_by_id"><a name="d0e5041"></a><h4>get_state_by_id</h4><p>Returns the state whose id is given. As all states of a concrete state + machine share a common base state, the return value is a base state. If + the id corresponds to no state, a null pointer is returned.</p><code class="methodsynopsis"><span class="methodname">const BaseState* get_state_by_id const</span>(<span class="methodparam">int id</span>);</code></div><div class="refsect3" title="is_contained"><a name="d0e5052"></a><h4>is_contained</h4><p>Helper returning true if the state machine is contained as a + submachine of another state machine.</p><code class="methodsynopsis"><span class="methodname">bool is_contained const</span>(<span class="methodparam"></span>);</code></div><div class="refsect3" title="get_state"><a name="d0e5062"></a><h4>get_state</h4><p>Returns the required state of the state machine as a pointer. A + compile error will occur if the state is not to be found in the state + machine.</p><code class="methodsynopsis"><span class="methodname">template <class State> State* get_state</span>(<span class="methodparam"></span>);</code></div><div class="refsect3" title="get_state"><a name="d0e5072"></a><h4>get_state</h4><p>Returns the required state of the state machine as a reference. A + compile error will occur if the state is not to be found in the state + machine.</p><code class="methodsynopsis"><span class="methodname">template <class State> State& get_state</span>(<span class="methodparam"></span>);</code></div><div class="refsect3" title="is_flag_active"><a name="d0e5082"></a><h4>is_flag_active</h4><p>Returns true if the given flag is currently active. A flag is active + if the active state of one region is tagged with this flag (using OR as + BinaryOp) or active states of <span class="underline">all</span> + regions (using AND as BinaryOp)</p><code class="methodsynopsis"><span class="methodname">template <class Flag,class BinaryOp> bool + is_flag_active</span>(<span class="methodparam"></span>);</code></div><div class="refsect3" title="is_flag_active"><a name="d0e5095"></a><h4>is_flag_active</h4><p>Returns true if the given flag is currently active. A flag is active + if the active state of one region is tagged with this flag.</p><code class="methodsynopsis"><span class="methodname">template <class Flag> bool is_flag_active</span>(<span class="methodparam"></span>);</code></div><div class="refsect3" title="visit_current_states"><a name="d0e5105"></a><h4>visit_current_states</h4><p>Visits all active states and their substates. A state is visited using + the <code class="code">accept</code> method without argument. The base class of all + states must provide an <code class="code">accept_sig</code> type.</p><code class="methodsynopsis"><span class="methodname">void visit_current_states</span>(<span class="methodparam"></span>);</code></div><div class="refsect3" title="visit_current_states"><a name="d0e5121"></a><h4>visit_current_states</h4><p>Visits all active states and their substates. A state is visited using + the <code class="code">accept</code> method with arguments. The base class of all + states must provide an <code class="code">accept_sig</code> type defining the + signature and thus the number and type of the parameters.</p><code class="methodsynopsis"><span class="methodname">void visit_current_states</span>(<span class="methodparam">any-type param1, any-type param2,...</span>);</code></div><div class="refsect3" title="defer_event"><a name="d0e5138"></a><h4>defer_event</h4><p> Defers the provided event. This method can be called only if at least + one state defers an event or if the state machine provides the + <code class="code">activate_deferred_events</code>(see <a class="link" href="examples/Orthogonal-deferred2.cpp" target="_top">example</a>) type + either directly or using the deferred_events configuration of eUML + (<code class="code">configure_ << deferred_events</code>)</p><code class="methodsynopsis"><span class="methodname">template <class Event> void defer_event</span>(<span class="methodparam">Event const&</span>);</code></div></div><div class="refsect2" title="Types"><a name="d0e5158"></a><h3>Types</h3><div class="refsect3" title="nr_regions"><a name="d0e5161"></a><h4>nr_regions </h4><p>The number of orthogonal regions contained in the state machine</p></div><div class="refsect3" title="entry_pt"><a name="d0e5166"></a><h4>entry_pt</h4><p>This nested type provides the necessary typedef for entry point + pseudostates. + <code class="code">state_machine<...>::entry_pt<state_name></code> is a + transition's valid target inside the containing state machine's + transition table.</p><pre class="classsynopsis"> <span class="ooclass"><span class="classname">entry_pt</span></span> {<br>}</pre></div><div class="refsect3" title="exit_pt"><a name="d0e5178"></a><h4>exit_pt</h4><p>This nested type provides the necessary typedef for exit point + pseudostates. <code class="code">state_machine<...>::exit_pt<state_name></code> + is a transition's valid source inside the containing state machine's + transition table.</p><pre class="classsynopsis"> <span class="ooclass"><span class="classname">exit_pt</span></span> {<br>}</pre></div><div class="refsect3" title="direct"><a name="d0e5190"></a><h4>direct</h4><p>This nested type provides the necessary typedef for an explicit entry + inside a submachine. + <code class="code">state_machine<...>::direct<state_name></code> is a + transition's valid target inside the containing state machine's + transition table.</p><pre class="classsynopsis"> <span class="ooclass"><span class="classname">direct</span></span> {<br>}</pre></div><div class="refsect3" title="stt"><a name="d0e5202"></a><h4>stt</h4><p>Calling state_machine<frontend>::stt returns a mpl::vector + containing the transition table of the state machine. This type can then + be used with generate_state_set or generate_event_set.</p></div></div></div><div class="refsect1" title="args.hpp"><a name="d0e5207"></a><h2>args.hpp</h2><p>This header provides one type, args. which provides the necessary types for a + visitor implementation.</p></div><div class="refsect1" title="msm/back/history_policies.hpp"><a name="d0e5212"></a><h2><span class="command"><strong><a name="history-interface"></a></strong></span>msm/back/history_policies.hpp</h2><p>This header provides the out-of-the-box history policies supported by MSM. + There are 3 such policies.</p><div class="refsect2" title="Every history policy must implement the following methods:"><a name="d0e5218"></a><h3>Every history policy must implement the following methods: </h3><div class="refsect3" title="set_initial_states"><a name="d0e5221"></a><h4> set_initial_states </h4><p> This method is called by msm::back::state_machine when constructed. + It gives the policy a chance to save the ids of all initial states + (passed as array).</p><div class="funcsynopsis"><table border="0" summary="Function synopsis" cellspacing="0" cellpadding="0" class="funcprototype-table"><tr><td><code class="funcdef">void set_initial_states(</code></td><td><code>)</code>;</td><td> </td></tr></table><div class="paramdef-list"><code> + <code>(</code>int* const<code>)</code> + </code>;</div><div class="funcprototype-spacer"> </div></div></div><div class="refsect3" title="history_exit"><a name="d0e5235"></a><h4> history_exit </h4><p>This method is called by msm::back::state_machine when the submachine + is exited. It gives the policy a chance to remember the ids of the last + active substates of this submachine (passed as array).</p><div class="funcsynopsis"><table border="0" summary="Function synopsis" cellspacing="0" cellpadding="0" class="funcprototype-table"><tr><td><code class="funcdef">void history_exit(</code></td><td><code>)</code>;</td><td> </td></tr></table><div class="paramdef-list"><code> + <code>(</code>int* const<code>)</code> + </code>;</div><div class="funcprototype-spacer"> </div></div></div><div class="refsect3" title="history_entry"><a name="d0e5249"></a><h4> history_entry </h4><p>This method is called by msm::back::state_machine when the submachine + is entered. It gives the policy a chance to set the active states + according to the policy's aim. The policy gets as parameter the event + which activated the submachine and returns an array of active states + ids.</p><div class="funcsynopsis"><table border="0" summary="Function synopsis" cellspacing="0" cellpadding="0" class="funcprototype-table"><tr><td><code class="funcdef">template <class Event> int* const history_exit(</code></td><td><code>)</code>;</td><td> </td></tr></table><div class="paramdef-list"><code> + <code>(</code>Event const&<code>)</code> + </code>;</div><div class="funcprototype-spacer"> </div></div></div></div><div class="refsect2" title="Out-of-the-box policies:"><a name="d0e5263"></a><h3>Out-of-the-box policies: </h3><div class="refsect3" title="NoHistory"><a name="d0e5266"></a><h4>NoHistory</h4><p>This policy is the default used by state_machine. No active state of a + submachine is remembered and at every new activation of the submachine, + the initial state(s) are activated. </p></div><div class="refsect3" title="AlwaysHistory"><a name="d0e5271"></a><h4>AlwaysHistory</h4><p>This policy is a non-UML-standard extension. The active state(s) of a + submachine is (are) always remembered at every new activation of the + submachine. </p></div><div class="refsect3" title="ShallowHistory"><a name="d0e5276"></a><h4>ShallowHistory</h4><p>This policy activates the active state(s) of a submachine if the event + is found in the policy's event list. </p></div></div></div><div class="refsect1" title="msm/back/default_compile_policy.hpp"><a name="d0e5281"></a><h2>msm/back/default_compile_policy.hpp</h2><p>This header contains the definition of favor_runtime_speed. This policy has + two settings:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Submachines dispatch faster because their transitions are added + into their containing machine's transition table instead of simply + forwarding events.</p></li><li class="listitem"><p>It solves transition conflicts at compile-time</p></li></ul></div></div><div class="refsect1" title="msm/back/favor_compile_time.hpp"><a name="d0e5293"></a><h2>msm/back/favor_compile_time.hpp</h2><p>This header contains the definition of favor_compile_time. This policy has two settings:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Submachines dispatch is slower because all events, even those with + no dispatch chance, are forwarded to submachines. In exchange, no + row is added into the containing machine's transition table, which + reduces compile-time.</p></li><li class="listitem"><p>It solves transition conflicts at run-time.</p></li></ul></div></div><div class="refsect1" title="msm/back/metafunctions.hpp"><a name="d0e5305"></a><h2>msm/back/metafunctions.hpp </h2><p>This header contains metafunctions for use by the library. Three metafunctions + can be useful for the user:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p><code class="code">generate_state_set< stt ></code>: generates the list of + all states referenced by the transition table stt. If stt is a + recursive table (generated by + <code class="code">recursive_get_transition_table</code>), the metafunction + finds recursively all states of the submachines. A non-recursive + table can be obtained with some_backend_fsm::stt.</p></li><li class="listitem"><p><code class="code">generate_event_set< stt></code>: generates the list of + all events referenced by the transition table stt. If stt is a + recursive table (generated by + <code class="code">recursive_get_transition_table</code>), the metafunction + finds recursively all events of the submachines. A non-recursive + table can be obtained with some_backend_fsm::stt.</p></li><li class="listitem"><p><code class="code">recursive_get_transition_table<fsm></code>: recursively + extends the transition table of the state machine fsm with tables + from the submachines.</p></li></ul></div></div><div class="refsect1" title="msm/back/tools.hpp"><a name="d0e5332"></a><h2>msm/back/tools.hpp </h2><p> This header contains a few metaprogramming tools to get some information out + of a state machine.</p><div class="refsect2" title="fill_state_names"><a name="d0e5337"></a><h3>fill_state_names </h3><div class="refsect3" title="attributes"><a name="d0e5340"></a><h4>attributes </h4><p> fill_state_names has for attribute:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p><code class="code">char const** m_names</code>: an already allocated + array of const char* where the typeid-generated names of a + state machine states will be witten.</p></li></ul></div></div><div class="refsect3" title="constructor"><a name="d0e5351"></a><h4>constructor </h4><code class="constructorsynopsis"><span class="methodparam">char const** names_to_fill</span>(<span class="methodparam">char const** names_to_fill</span>);</code></div><div class="refsect3" title="usage"><a name="d0e5358"></a><h4>usage</h4><p> fill_state_names is made for use in a mpl::for_each iterating on a + state list and writing inside a pre-allocated array the state names. + Example:</p><pre class="programlisting">typedef some_fsm::stt Stt; +typedef msm::back::generate_state_set<Stt>::type all_states; //states +static char const* state_names[mpl::size<all_states>::value]; +// array to fill with names +// fill the names of the states defined in the state machine +mpl::for_each<all_states,boost::msm::wrap<mpl::placeholders::_1> > + (msm::back::fill_state_names<Stt>(state_names)); +// display all active states +for (unsigned int i=0;i<some_fsm::nr_regions::value;++i) +{ + std::cout << " -> " + << state_names[my_fsm_instance.current_state()[i]] + << std::endl; +}</pre></div></div><div class="refsect2" title="get_state_name"><a name="d0e5365"></a><h3>get_state_name </h3><div class="refsect3" title="attributes"><a name="d0e5368"></a><h4> attributes </h4><p>get_state_name has for attributes:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>std::string& m_name: the return value of the + iteration</p></li><li class="listitem"><p>int m_state_id: the searched state's id</p></li></ul></div></div><div class="refsect3" title="constructor"><a name="d0e5380"></a><h4>constructor</h4><p>The constructor takes as argument a reference to the string to fill + with the state name and the id which must be searched.</p><code class="constructorsynopsis"><span class="methodparam">string& name_to_fill,int state_id</span>(<span class="methodparam">string& name_to_fill,int state_id</span>);</code></div><div class="refsect3" title="usage"><a name="d0e5389"></a><h4> usage</h4><p>This type is made for the same search as in the previous example, + using a mpl::for_each to iterate on states. After the iteration, the + state name reference has been set.</p></div></div><div class="refsect2" title="display_type"><a name="d0e5394"></a><h3>display_type </h3><div class="refsect3" title="attributes"><a name="d0e5397"></a><h4> attributes </h4><p>none</p></div><div class="refsect3" title="usage"><a name="d0e5402"></a><h4> usage</h4><p>Reusing the state list from the previous example, we can output all + state names:</p><p><code class="code">mpl::for_each<all_states,boost::msm::wrap<mpl::placeholders::_1> + >(msm::back::display_type ());</code></p></div></div></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="re01.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="pt02.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="re03.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">Common headers </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> Front-end</td></tr></table></div></body></html>
\ No newline at end of file diff --git a/libs/msm/doc/HTML/re03.html b/libs/msm/doc/HTML/re03.html new file mode 100644 index 0000000000..159747ab40 --- /dev/null +++ b/libs/msm/doc/HTML/re03.html @@ -0,0 +1,778 @@ +<html><head> + <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> + <title>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="pt02.html" title="Part II. Reference"><link rel="prev" href="re02.html" title="Back-end"></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">Front-end</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="re02.html">Prev</a> </td><th width="60%" align="center">Part II. Reference</th><td width="20%" align="right"> </td></tr></table><hr></div><div class="refentry" title="Front-end"><a name="d0e5410"></a><div class="titlepage"></div><div class="refnamediv"><h2>Name</h2><p>Front-end — The front-end headers</p></div><div class="refsect1" title="msm/front/common_states.hpp"><a name="d0e5416"></a><h2>msm/front/common_states.hpp</h2><p>This header contains the predefined types to serve as base for states or state machines:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>default_base_state: non-polymorphic empty type.</p></li><li class="listitem"><p>polymorphic_state: type with a virtual destructor, which makes all + states polymorphic.</p></li></ul></div></div><div class="refsect1" title="msm/front/completion_event.hpp"><a name="d0e5428"></a><h2>msm/front/completion_event.hpp</h2><p>This header contains one type, <code class="code">none</code>. This type has several + meanings inside a transition table:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>as action or guard: that there is no action or guard</p></li><li class="listitem"><p>as target state: that the transition is an internal + transition</p></li><li class="listitem"><p>as event: the transition is an anonymous (completion) + transition</p></li></ul></div></div><div class="refsect1" title="msm/front/functor_row.hpp"><a name="d0e5446"></a><h2>msm/front/functor_row.hpp</h2><p>This header implements the functor front-end's transitions and helpers.</p><div class="refsect2" title="Row"><a name="d0e5451"></a><h3>Row</h3><div class="refsect3" title="definition"><a name="d0e5454"></a><h4>definition</h4><pre class="classsynopsis"> <span class="ooclass"><span class="classname">template <class Source,class Event,class Target,class + Action,class Guard> Row</span></span> {<br>}</pre></div><div class="refsect3" title="tags"><a name="d0e5461"></a><h4>tags</h4><p>row_type_tag is defined differently for every specialization:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>all 5 template parameters means a normal transition with + action and guard: <code class="code">typedef row_tag + row_type_tag;</code></p></li><li class="listitem"><p>Row<Source,Event,Target,none,none> a normal transition + without action or guard: <code class="code">typedef _row_tag + row_type_tag;</code></p></li><li class="listitem"><p>Row<Source,Event,Target,Action,none> a normal + transition without guard: <code class="code">typedef a_row_tag + row_type_tag;</code></p></li><li class="listitem"><p>Row<Source,Event,Target,none,Guard> a normal transition + without action: <code class="code">typedef g_row_tag + row_type_tag;</code></p></li><li class="listitem"><p>Row<Source,Event,none,Action,none> an internal + transition without guard: <code class="code">typedef a_irow_tag + row_type_tag;</code></p></li><li class="listitem"><p>Row<Source,Event,none,none,Guard> an internal + transition without action: <code class="code">typedef g_irow_tag + row_type_tag;</code></p></li><li class="listitem"><p>Row<Source,Event,none,none,Guard> an internal + transition with action and guard: <code class="code">typedef irow_tag + row_type_tag;</code></p></li><li class="listitem"><p>Row<Source,Event,none,none,none> an internal transition + without action or guard: <code class="code">typedef _irow_tag + row_type_tag;</code></p></li></ul></div></div><div class="refsect3" title="methods"><a name="d0e5507"></a><h4>methods</h4><p>Like any other front-end, Row implements the two necessary static + functions for action and guard call. Each function receives as parameter + the (deepest-level) state machine processsing the event, the event + itself, the source and target states and all the states contained in a + state machine.</p><div class="funcsynopsis"><table border="0" summary="Function synopsis" cellspacing="0" cellpadding="0" class="funcprototype-table"><tr><td><code class="funcdef">template <class Fsm,class SourceState,class TargetState, + class AllStates> static void action_call(</code></td><td><code>)</code>;</td><td> </td></tr></table><div class="paramdef-list"><code> + <code>(</code>Fsm& fsm,Event const& + evt,SourceState&,TargetState,AllStates&<code>)</code> + </code>;</div><div class="funcprototype-spacer"> </div></div><div class="funcsynopsis"><table border="0" summary="Function synopsis" cellspacing="0" cellpadding="0" class="funcprototype-table"><tr><td><code class="funcdef">template <class Fsm,class SourceState,class TargetState, + class AllStates> static bool guard_call(</code></td><td><code>)</code>;</td><td> </td></tr></table><div class="paramdef-list"><code> + <code>(</code>Fsm& fsm,Event const& + evt,SourceState&,TargetState,AllStates&<code>)</code> + </code>;</div><div class="funcprototype-spacer"> </div></div></div></div><div class="refsect2" title="Internal"><a name="d0e5530"></a><h3>Internal</h3><div class="refsect3" title="definition"><a name="d0e5533"></a><h4>definition</h4><pre class="classsynopsis"> <span class="ooclass"><span class="classname">template <class Event,class Action,class Guard> + Internal</span></span> {<br>}</pre></div><div class="refsect3" title="tags"><a name="d0e5540"></a><h4>tags</h4><p>row_type_tag is defined differently for every specialization:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>all 3 template parameters means an internal transition + with action and guard: <code class="code">typedef sm_i_row_tag + row_type_tag;</code></p></li><li class="listitem"><p>Internal<Event,none,none> an internal transition + without action or guard: <code class="code">typedef sm__i_row_tag + row_type_tag;</code></p></li><li class="listitem"><p>Internal<Event,Action,none> an internal transition + without guard: <code class="code">typedef sm_a_i_row_tag + row_type_tag;</code></p></li><li class="listitem"><p>Internal<Event,none,Guard> an internal transition + without action: <code class="code">typedef sm_g_i_row_tag + row_type_tag;</code></p></li></ul></div></div><div class="refsect3" title="methods"><a name="d0e5566"></a><h4>methods</h4><p>Like any other front-end, Internal implements the two necessary static + functions for action and guard call. Each function receives as parameter + the (deepest-level) state machine processsing the event, the event + itself, the source and target states and all the states contained in a + state machine.</p><div class="funcsynopsis"><table border="0" summary="Function synopsis" cellspacing="0" cellpadding="0" class="funcprototype-table"><tr><td><code class="funcdef">template <class Fsm,class SourceState,class TargetState, + class AllStates> static void action_call(</code></td><td><code>)</code>;</td><td> </td></tr></table><div class="paramdef-list"><code> + <code>(</code>Fsm& fsm,Event const& + evt,SourceState&,TargetState,AllStates&<code>)</code> + </code>;</div><div class="funcprototype-spacer"> </div></div><div class="funcsynopsis"><table border="0" summary="Function synopsis" cellspacing="0" cellpadding="0" class="funcprototype-table"><tr><td><code class="funcdef">template <class Fsm,class SourceState,class TargetState, + class AllStates> static bool guard_call(</code></td><td><code>)</code>;</td><td> </td></tr></table><div class="paramdef-list"><code> + <code>(</code>Fsm& fsm,Event const& + evt,SourceState&,TargetState,AllStates&<code>)</code> + </code>;</div><div class="funcprototype-spacer"> </div></div></div></div><div class="refsect2" title="ActionSequence_"><a name="d0e5589"></a><h3>ActionSequence_</h3><p>This functor calls every element of the template Sequence (which are also + callable functors) in turn. It is also the underlying implementation of the + eUML sequence grammar (action1,action2,...).</p><div class="refsect3" title="definition"><a name="d0e5594"></a><h4>definition</h4><pre class="classsynopsis"> <span class="ooclass"><span class="classname">template <class Sequence> ActionSequence_</span></span> {<br>}</pre></div><div class="refsect3" title="methods"><a name="d0e5601"></a><h4>methods</h4><p>This helper functor is made for use in a transition table and in a + state behavior and therefore implements an operator() with 3 and with 4 + arguments:</p><p> + </p><div class="funcsynopsis"><table border="0" summary="Function synopsis" cellspacing="0" cellpadding="0" class="funcprototype-table"><tr><td><code class="funcdef">template <class Evt,class Fsm,class + SourceState,class TargetState> operator()(</code></td><td><code>)</code>;</td><td> </td></tr></table><div class="paramdef-list"><code>Evt const& ,Fsm& ,SourceState& + ,TargetState& </code>;</div><div class="funcprototype-spacer"> </div></div><p> + </p><p> + </p><div class="funcsynopsis"><table border="0" summary="Function synopsis" cellspacing="0" cellpadding="0" class="funcprototype-table"><tr><td><code class="funcdef">template <class Evt,class Fsm,class State> + operator()(</code></td><td><code>)</code>;</td><td> </td></tr></table><div class="paramdef-list"><code>Evt const&, Fsm&, State&</code>;</div><div class="funcprototype-spacer"> </div></div><p> + </p></div></div><div class="refsect2" title="Defer"><a name="d0e5624"></a><h3>Defer</h3><div class="refsect3" title="definition"><a name="d0e5627"></a><h4>definition</h4><pre class="classsynopsis"> <span class="ooclass"><span class="classname">Defer</span></span> {<br>}</pre></div><div class="refsect3" title="methods"><a name="d0e5634"></a><h4>methods</h4><p>This helper functor is made for use in a transition table and + therefore implements an operator() with 4 arguments:</p><div class="funcsynopsis"><table border="0" summary="Function synopsis" cellspacing="0" cellpadding="0" class="funcprototype-table"><tr><td><code class="funcdef">template <class Evt,class Fsm,class SourceState,class + TargetState> operator()(</code></td><td><code>)</code>;</td><td> </td></tr></table><div class="paramdef-list"><code>Evt const&, Fsm& , SourceState&, + TargetState&</code>;</div><div class="funcprototype-spacer"> </div></div></div></div></div><div class="refsect1" title="msm/front/internal_row.hpp"><a name="d0e5645"></a><h2>msm/front/internal_row.hpp</h2><p>This header implements the internal transition rows for use inside an + internal_transition_table. All these row types have no source or target state, + as the backend will recognize internal transitions from this + internal_transition_table.</p><div class="refsect2" title="methods"><a name="d0e5650"></a><h3>methods</h3><p>Like any other front-end, the following transition row types implements + the two necessary static functions for action and guard call. Each function + receives as parameter the (deepest-level) state machine processsing the + event, the event itself, the source and target states and all the states + contained in a state machine.</p><div class="funcsynopsis"><table border="0" summary="Function synopsis" cellspacing="0" cellpadding="0" class="funcprototype-table"><tr><td><code class="funcdef">template <class Fsm,class SourceState,class TargetState, + class AllStates> static void action_call(</code></td><td><code>)</code>;</td><td> </td></tr></table><div class="paramdef-list"><code> + <code>(</code>Fsm& fsm,Event const& + evt,SourceState&,TargetState,AllStates&<code>)</code> + </code>;</div><div class="funcprototype-spacer"> </div></div><div class="funcsynopsis"><table border="0" summary="Function synopsis" cellspacing="0" cellpadding="0" class="funcprototype-table"><tr><td><code class="funcdef">template <class Fsm,class SourceState,class TargetState, + class AllStates> static bool guard_call(</code></td><td><code>)</code>;</td><td> </td></tr></table><div class="paramdef-list"><code> + <code>(</code>Fsm& fsm,Event const& + evt,SourceState&,TargetState,AllStates&<code>)</code> + </code>;</div><div class="funcprototype-spacer"> </div></div></div><div class="refsect2" title="a_internal"><a name="d0e5673"></a><h3>a_internal</h3><div class="refsect3" title="definition"><a name="d0e5676"></a><h4>definition</h4><p>This is an internal transition with an action called during the + transition.</p><pre class="classsynopsis"> <span class="ooclass"><span class="classname">template< class Event, class CalledForAction, void + (CalledForAction::*action)(Event const&)> + a_internal</span></span> {<br>}</pre></div><div class="refsect3" title="template parameters"><a name="d0e5685"></a><h4>template parameters</h4><p> + </p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Event: the event triggering the internal + transition.</p></li><li class="listitem"><p>CalledForAction: the type on which the action method will + be called. It can be either a state of the containing state + machine or the state machine itself.</p></li><li class="listitem"><p>action: a pointer to the method which CalledForAction + provides.</p></li></ul></div><p> + </p></div></div><div class="refsect2" title="g_internal"><a name="d0e5701"></a><h3>g_internal</h3><p>This is an internal transition with a guard called before the transition + and allowing the transition if returning true.</p><div class="refsect3" title="definition"><a name="d0e5706"></a><h4>definition</h4><pre class="classsynopsis"> <span class="ooclass"><span class="classname">template< class Event, class CalledForGuard, bool + (CalledForGuard::*guard)(Event const&)> + g_internal</span></span> {<br>}</pre></div><div class="refsect3" title="template parameters"><a name="d0e5713"></a><h4>template parameters</h4><p> + </p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Event: the event triggering the internal + transition.</p></li><li class="listitem"><p>CalledForGuard: the type on which the guard method will be + called. It can be either a state of the containing state + machine or the state machine itself.</p></li><li class="listitem"><p>guard: a pointer to the method which CalledForGuard + provides.</p></li></ul></div><p> + </p></div></div><div class="refsect2" title="internal"><a name="d0e5729"></a><h3>internal</h3><p>This is an internal transition with a guard called before the transition + and allowing the transition if returning true. It also calls an action + called during the transition.</p><div class="refsect3" title="definition"><a name="d0e5734"></a><h4>definition</h4><pre class="classsynopsis"> <span class="ooclass"><span class="classname">template< class Event, class CalledForAction, void + (CalledForAction::*action)(Event const&), class + CalledForGuard, bool (CalledForGuard::*guard)(Event const&)> + internal</span></span> {<br>}</pre></div><div class="refsect3" title="template parameters"><a name="d0e5741"></a><h4>template parameters</h4><p> + </p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Event: the event triggering the internal transition</p></li><li class="listitem"><p>CalledForAction: the type on which the action method will + be called. It can be either a state of the containing state + machine or the state machine itself.</p></li><li class="listitem"><p>action: a pointer to the method which CalledForAction + provides.</p></li><li class="listitem"><p>CalledForGuard: the type on which the guard method will be + called. It can be either a state of the containing state + machine or the state machine itself.</p></li><li class="listitem"><p>guard: a pointer to the method which CalledForGuard + provides.</p></li></ul></div><p> + </p></div></div><div class="refsect2" title="_internal"><a name="d0e5763"></a><h3>_internal</h3><p>This is an internal transition without action or guard. This is equivalent + to an explicit "ignore event".</p><div class="refsect3" title="definition"><a name="d0e5768"></a><h4>definition</h4><pre class="classsynopsis"> <span class="ooclass"><span class="classname">template< class Event > _internal</span></span> {<br>}</pre></div><div class="refsect3" title="template parameters"><a name="d0e5775"></a><h4>template parameters</h4><p> + </p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Event: the event triggering the internal + transition.</p></li></ul></div><p> + </p></div></div></div><div class="refsect1" title="msm/front/row2.hpp"><a name="d0e5785"></a><h2>msm/front/row2.hpp</h2><p>This header contains the variants of row2, which are an extension of the + standard row transitions for use in the transition table. They offer the + possibility to define action and guard not only in the state machine, but in any + state of the state machine. They can also be used in internal transition tables + through their irow2 variants.</p><div class="refsect2" title="methods"><a name="d0e5790"></a><h3>methods</h3><p>Like any other front-end, the following transition row types implements + the two necessary static functions for action and guard call. Each function + receives as parameter the (deepest-level) state machine processsing the + event, the event itself, the source and target states and all the states + contained in a state machine.</p><div class="funcsynopsis"><table border="0" summary="Function synopsis" cellspacing="0" cellpadding="0" class="funcprototype-table"><tr><td><code class="funcdef">template <class Fsm,class SourceState,class TargetState, + class AllStates> static void action_call(</code></td><td><code>)</code>;</td><td> </td></tr></table><div class="paramdef-list"><code> + <code>(</code>Fsm& fsm,Event const& + evt,SourceState&,TargetState,AllStates&<code>)</code> + </code>;</div><div class="funcprototype-spacer"> </div></div><div class="funcsynopsis"><table border="0" summary="Function synopsis" cellspacing="0" cellpadding="0" class="funcprototype-table"><tr><td><code class="funcdef">template <class Fsm,class SourceState,class TargetState, + class AllStates> static bool guard_call(</code></td><td><code>)</code>;</td><td> </td></tr></table><div class="paramdef-list"><code> + <code>(</code>Fsm& fsm,Event const& + evt,SourceState&,TargetState,AllStates&<code>)</code> + </code>;</div><div class="funcprototype-spacer"> </div></div></div><div class="refsect2" title="_row2"><a name="d0e5813"></a><h3>_row2</h3><p>This is a transition without action or guard. The state machine only + changes active state.</p><div class="refsect3" title="definition"><a name="d0e5818"></a><h4>definition</h4><pre class="classsynopsis"> <span class="ooclass"><span class="classname">template< class Source, class Event, class Target > + _row2</span></span> {<br>}</pre></div><div class="refsect3" title="template parameters"><a name="d0e5825"></a><h4>template parameters</h4><p> + </p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Event: the event triggering the transition.</p></li><li class="listitem"><p>Source: the source state of the transition.</p></li><li class="listitem"><p>Target: the target state of the transition.</p></li></ul></div><p> + </p></div></div><div class="refsect2" title="a_row2"><a name="d0e5841"></a><h3>a_row2</h3><p>This is a transition with action and without guard.</p><div class="refsect3" title="definition"><a name="d0e5846"></a><h4>definition</h4><pre class="classsynopsis"> <span class="ooclass"><span class="classname">template< class Source, class Event, class Target, + </span></span> {<br>}</pre><pre class="classsynopsis"> <span class="ooclass"><span class="classname">class CalledForAction, void + (CalledForAction::*action)(Event const&) > _row2</span></span> {<br>}</pre></div><div class="refsect3" title="template parameters"><a name="d0e5857"></a><h4>template parameters</h4><p> + </p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Event: the event triggering the transition.</p></li><li class="listitem"><p>Source: the source state of the transition.</p></li><li class="listitem"><p>Target: the target state of the transition.</p></li><li class="listitem"><p>CalledForAction: the type on which the action method will + be called. It can be either a state of the containing state + machine or the state machine itself.</p></li><li class="listitem"><p>action: a pointer to the method which CalledForAction + provides.</p></li></ul></div><p> + </p></div></div><div class="refsect2" title="g_row2"><a name="d0e5879"></a><h3>g_row2</h3><p>This is a transition with guard and without action.</p><div class="refsect3" title="definition"><a name="d0e5884"></a><h4>definition</h4><pre class="classsynopsis"> <span class="ooclass"><span class="classname">template< class Source, class Event, class Target, + </span></span> {<br>}</pre><pre class="classsynopsis"> <span class="ooclass"><span class="classname">class CalledForGuard, bool (CalledForGuard::*guard)(Event + const&) > _row2</span></span> {<br>}</pre></div><div class="refsect3" title="template parameters"><a name="d0e5895"></a><h4>template parameters</h4><p> + </p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Event: the event triggering the transition.</p></li><li class="listitem"><p>Source: the source state of the transition.</p></li><li class="listitem"><p>Target: the target state of the transition.</p></li><li class="listitem"><p>CalledForGuard: the type on which the guard method will be + called. It can be either a state of the containing state + machine or the state machine itself.</p></li><li class="listitem"><p>guard: a pointer to the method which CalledForGuard + provides.</p></li></ul></div><p> + </p></div></div><div class="refsect2" title="row2"><a name="d0e5917"></a><h3>row2</h3><p>This is a transition with guard and action.</p><div class="refsect3" title="definition"><a name="d0e5922"></a><h4>definition</h4><pre class="classsynopsis"> <span class="ooclass"><span class="classname">template< class Source, class Event, class Target, + </span></span> {<br>}</pre><pre class="classsynopsis"> <span class="ooclass"><span class="classname">class CalledForAction, void + (CalledForAction::*action)(Event const&), </span></span> {<br>}</pre><pre class="classsynopsis"> <span class="ooclass"><span class="classname">class CalledForGuard, bool (CalledForGuard::*guard)(Event + const&) > _row2</span></span> {<br>}</pre></div><div class="refsect3" title="template parameters"><a name="d0e5937"></a><h4>template parameters</h4><p> + </p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Event: the event triggering the transition.</p></li><li class="listitem"><p>Source: the source state of the transition.</p></li><li class="listitem"><p>Target: the target state of the transition.</p></li><li class="listitem"><p>CalledForAction: the type on which the action method will + be called. It can be either a state of the containing state + machine or the state machine itself.</p></li><li class="listitem"><p>action: a pointer to the method which CalledForAction + provides.</p></li><li class="listitem"><p>CalledForGuard: the type on which the guard method will be + called. It can be either a state of the containing state + machine or the state machine itself.</p></li><li class="listitem"><p>guard: a pointer to the method which CalledForGuard + provides.</p></li></ul></div><p> + </p></div></div><div class="refsect2" title="a_irow2"><a name="d0e5965"></a><h3>a_irow2</h3><p>This is an internal transition for use inside a transition table, with + action and without guard.</p><div class="refsect3" title="definition"><a name="d0e5970"></a><h4>definition</h4><pre class="classsynopsis"> <span class="ooclass"><span class="classname">template< class Source, class Event, </span></span> {<br>}</pre><pre class="classsynopsis"> <span class="ooclass"><span class="classname">class CalledForAction, void + (CalledForAction::*action)(Event const&) > _row2</span></span> {<br>}</pre></div><div class="refsect3" title="template parameters"><a name="d0e5981"></a><h4>template parameters</h4><p> + </p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Event: the event triggering the transition.</p></li><li class="listitem"><p>Source: the source state of the transition.</p></li><li class="listitem"><p>CalledForAction: the type on which the action method will + be called. It can be either a state of the containing state + machine or the state machine itself.</p></li><li class="listitem"><p>action: a pointer to the method which CalledForAction + provides.</p></li></ul></div><p> + </p></div></div><div class="refsect2" title="g_irow2"><a name="d0e6000"></a><h3>g_irow2</h3><p>This is an internal transition for use inside a transition table, with + guard and without action.</p><div class="refsect3" title="definition"><a name="d0e6005"></a><h4>definition</h4><pre class="classsynopsis"> <span class="ooclass"><span class="classname">template< class Source, class Event, </span></span> {<br>}</pre><pre class="classsynopsis"> <span class="ooclass"><span class="classname">class CalledForGuard, bool (CalledForGuard::*guard)(Event + const&) > _row2</span></span> {<br>}</pre></div><div class="refsect3" title="template parameters"><a name="d0e6016"></a><h4>template parameters</h4><p> + </p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Event: the event triggering the transition.</p></li><li class="listitem"><p>Source: the source state of the transition.</p></li><li class="listitem"><p>CalledForGuard: the type on which the guard method will be + called. It can be either a state of the containing state + machine or the state machine itself.</p></li><li class="listitem"><p>guard: a pointer to the method which CalledForGuard + provides.</p></li></ul></div><p> + </p></div></div><div class="refsect2" title="irow2"><a name="d0e6035"></a><h3>irow2</h3><p>This is an internal transition for use inside a transition table, with + guard and action.</p><div class="refsect3" title="definition"><a name="d0e6040"></a><h4>definition</h4><pre class="classsynopsis"> <span class="ooclass"><span class="classname">template< class Source, class Event, </span></span> {<br>}</pre><pre class="classsynopsis"> <span class="ooclass"><span class="classname">class CalledForAction, void + (CalledForAction::*action)(Event const&), </span></span> {<br>}</pre><pre class="classsynopsis"> <span class="ooclass"><span class="classname">class CalledForGuard, bool (CalledForGuard::*guard)(Event + const&) > _row2</span></span> {<br>}</pre></div><div class="refsect3" title="template parameters"><a name="d0e6055"></a><h4>template parameters</h4><p> + </p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Event: the event triggering the transition.</p></li><li class="listitem"><p>Source: the source state of the transition.</p></li><li class="listitem"><p>CalledForAction: the type on which the action method will + be called. It can be either a state of the containing state + machine or the state machine itself.</p></li><li class="listitem"><p>action: a pointer to the method which CalledForAction + provides.</p></li><li class="listitem"><p>CalledForGuard: the type on which the guard method will be + called. It can be either a state of the containing state + machine or the state machine itself.</p></li><li class="listitem"><p>guard: a pointer to the method which CalledForGuard + provides.</p></li></ul></div><p> + </p></div></div></div><div class="refsect1" title="msm/front/state_machine_def.hpp"><a name="d0e6080"></a><h2>msm/front/state_machine_def.hpp</h2><p>This header provides the implementation of the <span class="command"><strong><a class="command" href="ch03s02.html#basic-front-end">basic front-end</a></strong></span>. It contains one + type, <code class="code">state_machine_def</code></p><div class="refsect2" title="state_machine_def definition"><a name="d0e6090"></a><h3>state_machine_def definition</h3><p>This type is the basic class for a basic (or possibly any other) + front-end. It provides the standard row types (which includes internal + transitions) and a default implementation of the required methods and + typedefs.</p><pre class="classsynopsis"> <span class="ooclass"><span class="classname">template <class Derived,class BaseState = + default_base_state> state_machine_def</span></span> {<br>}</pre><div class="refsect3" title="typedefs"><a name="d0e6099"></a><h4>typedefs</h4><p> + </p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>flag_list: by default, no flag is set in the state + machine</p></li><li class="listitem"><p>deferred_events: by default, no event is deferred.</p></li><li class="listitem"><p>configuration: by default, no configuration customization + is done.</p></li></ul></div><p> + </p></div><div class="refsect3" title="row methods"><a name="d0e6115"></a><h4>row methods</h4><p>Like any other front-end, the following transition row types + implements the two necessary static functions for action and guard call. + Each function receives as parameter the (deepest-level) state machine + processsing the event, the event itself, the source and target states + and all the states contained in a state machine (ignored).</p><div class="funcsynopsis"><table border="0" summary="Function synopsis" cellspacing="0" cellpadding="0" class="funcprototype-table"><tr><td><code class="funcdef">template <class Fsm,class SourceState,class TargetState, + class AllStates> static void action_call(</code></td><td><code>)</code>;</td><td> </td></tr></table><div class="paramdef-list"><code> + <code>(</code>Fsm& fsm,Event const& + evt,SourceState&,TargetState,AllStates&<code>)</code> + </code>;</div><div class="funcprototype-spacer"> </div></div><div class="funcsynopsis"><table border="0" summary="Function synopsis" cellspacing="0" cellpadding="0" class="funcprototype-table"><tr><td><code class="funcdef">template <class Fsm,class SourceState,class TargetState, + class AllStates> static bool guard_call(</code></td><td><code>)</code>;</td><td> </td></tr></table><div class="paramdef-list"><code> + <code>(</code>Fsm& fsm,Event const& + evt,SourceState&,TargetState,AllStates&<code>)</code> + </code>;</div><div class="funcprototype-spacer"> </div></div></div><div class="refsect3" title="a_row"><a name="d0e6138"></a><h4>a_row</h4><p>This is a transition with action and without guard.</p><p><code class="classname">template< class Source, class Event, class Target, + void (Derived::*action)(Event const&) > a_row</code></p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Event: the event triggering the transition.</p></li><li class="listitem"><p>Source: the source state of the transition.</p></li><li class="listitem"><p>Target: the target state of the transition.</p></li><li class="listitem"><p>action: a pointer to the method provided by the concrete + front-end (represented by <code class="code">Derived</code>).</p></li></ul></div></div><div class="refsect3" title="g_row"><a name="d0e6163"></a><h4>g_row</h4><p>This is a transition with guard and without action.</p><p><code class="classname">template< class Source, class Event, class Target, + bool (Derived::*guard)(Event const&) > g_row</code></p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Event: the event triggering the transition.</p></li><li class="listitem"><p>Source: the source state of the transition.</p></li><li class="listitem"><p>Target: the target state of the transition.</p></li><li class="listitem"><p>guard: a pointer to the method provided by the concrete + front-end (represented by <code class="code">Derived</code>).</p></li></ul></div></div><div class="refsect3" title="row"><a name="d0e6188"></a><h4>row</h4><p>This is a transition with guard and action.</p><p><code class="classname">template< class Source, class Event, class Target, + void (Derived::*action)(Event const&), bool + (Derived::*guard)(Event const&) > row</code></p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Event: the event triggering the transition.</p></li><li class="listitem"><p>Source: the source state of the transition.</p></li><li class="listitem"><p>Target: the target state of the transition.</p></li><li class="listitem"><p>action: a pointer to the method provided by the concrete + front-end (represented by <code class="code">Derived</code>).</p></li><li class="listitem"><p>guard: a pointer to the method provided by the concrete + front-end (represented by <code class="code">Derived</code>).</p></li></ul></div></div><div class="refsect3" title="_row"><a name="d0e6219"></a><h4>_row</h4><p>This is a transition without action or guard. The state machine only + changes active state.</p><p><code class="classname">template< class Source, class Event, class Target > + _row</code></p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Event: the event triggering the transition.</p></li><li class="listitem"><p>Source: the source state of the transition.</p></li><li class="listitem"><p>Target: the target state of the transition.</p></li></ul></div></div><div class="refsect3" title="a_irow"><a name="d0e6238"></a><h4>a_irow</h4><p>This is an internal transition for use inside a transition table, with + action and without guard.</p><p><code class="classname">template< class Source, class Event, void + (Derived::*action)(Event const&) > a_irow</code></p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Event: the event triggering the transition.</p></li><li class="listitem"><p>Source: the source state of the transition.</p></li><li class="listitem"><p>action: a pointer to the method provided by the concrete + front-end (represented by <code class="code">Derived</code>).</p></li></ul></div></div><div class="refsect3" title="g_irow"><a name="d0e6260"></a><h4>g_irow</h4><p>This is an internal transition for use inside a transition table, with + guard and without action.</p><p><code class="classname">template< class Source, class Event, bool + (Derived::*guard)(Event const&) > g_irow</code></p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Event: the event triggering the transition.</p></li><li class="listitem"><p>Source: the source state of the transition.</p></li><li class="listitem"><p>guard: a pointer to the method provided by the concrete + front-end (represented by <code class="code">Derived</code>).</p></li></ul></div></div><div class="refsect3" title="irow"><a name="d0e6282"></a><h4>irow</h4><p>This is an internal transition for use inside a transition table, with + guard and action.</p><p><code class="classname">template< class Source, class Event, void + (Derived::*action)(Event const&), bool + (Derived::*guard)(Event const&) > irow</code></p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Event: the event triggering the transition.</p></li><li class="listitem"><p>Source: the source state of the transition.</p></li><li class="listitem"><p>action: a pointer to the method provided by the concrete + front-end (represented by <code class="code">Derived</code>).</p></li><li class="listitem"><p>guard: a pointer to the method provided by the concrete + front-end (represented by <code class="code">Derived</code>).</p></li></ul></div></div><div class="refsect3" title="_irow"><a name="d0e6310"></a><h4>_irow</h4><p>This is an internal transition without action or guard. As it does + nothing, it means "ignore event".</p><p><code class="classname">template< class Source, class Event > + _irow</code></p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Event: the event triggering the transition.</p></li><li class="listitem"><p>Source: the source state of the transition.</p></li></ul></div></div><div class="refsect3" title="methods"><a name="d0e6326"></a><h4>methods</h4><p><code class="code">state_machine_def</code> provides a default implementation in + case of an event which cannot be processed by a state machine (no + transition found). The implementation is using a + <code class="code">BOOST_ASSERT</code> so that the error will only be noticed in + debug mode. Overwrite this method in your implementation to change the + behavior.</p><p> + </p><div class="funcsynopsis"><table border="0" summary="Function synopsis" cellspacing="0" cellpadding="0" class="funcprototype-table"><tr><td><code class="funcdef">template <class Fsm,class Event> static void + no_transition(</code></td><td><code>)</code>;</td><td> </td></tr></table><div class="paramdef-list"><code> + <code>(</code>Event const& ,Fsm&, int + state<code>)</code> + </code>;</div><div class="funcprototype-spacer"> </div></div><p> + </p><p><code class="code">state_machine_def</code> provides a default implementation in + case an exception is thrown by a state (entry/exit) or transition + (action/guard) behavior. The implementation is using a + <code class="code">BOOST_ASSERT</code> so that the error will only be noticed in + debug mode. Overwrite this method in your implementation to change the + behavior. This method will be called only if exception handling is not + deactivated (default) by defining + <code class="code">has_no_message_queue</code>.</p><p> + </p><div class="funcsynopsis"><table border="0" summary="Function synopsis" cellspacing="0" cellpadding="0" class="funcprototype-table"><tr><td><code class="funcdef">template <class Fsm,class Event> static void + exception_caught(</code></td><td><code>)</code>;</td><td> </td></tr></table><div class="paramdef-list"><code> + <code>(</code>Event const& ,Fsm&, + std::exception&<code>)</code> + </code>;</div><div class="funcprototype-spacer"> </div></div><p> + </p></div></div></div><div class="refsect1" title="msm/front/states.hpp"><a name="d0e6370"></a><h2>msm/front/states.hpp </h2><p>This header provides the different states (except state machines) for the + basic front-end (or mixed with other front-ends).</p><div class="refsect2" title="types"><a name="d0e6375"></a><h3>types</h3><p>This header provides the following types:</p><div class="refsect3" title="no_sm_ptr"><a name="d0e6380"></a><h4>no_sm_ptr</h4><p>deprecated: default policy for states. It means that states do not + need to save a pointer to their containing state machine.</p></div><div class="refsect3" title="sm_ptr"><a name="d0e6385"></a><h4>sm_ptr</h4><p>deprecated: state policy. It means that states need to save a pointer + to their containing state machine. When seeing this flag, the back-end + will call set_sm_ptr(fsm*) and give itself as argument.</p></div><div class="refsect3" title="state"><a name="d0e6390"></a><h4>state</h4><p>Basic type for simple states. Inherit from this type to define a + simple state. The first argument is needed if you want your state (and + all others used in a concrete state machine) to inherit a basic type for + logging or providing a common behavior.</p><pre class="classsynopsis"> <span class="ooclass"><span class="classname">template<class Base = default_base_state,class + SMPtrPolicy = no_sm_ptr> state</span></span> {<br>}</pre></div><div class="refsect3" title="terminate_state"><a name="d0e6399"></a><h4>terminate_state</h4><p>Basic type for terminate states. Inherit from this type to define a + terminate state. The first argument is needed if you want your state + (and all others used in a concrete state machine) to inherit a basic + type for logging or providing a common behavior.</p><pre class="classsynopsis"> <span class="ooclass"><span class="classname">template<class Base = default_base_state,class + SMPtrPolicy = no_sm_ptr> terminate_state</span></span> {<br>}</pre></div><div class="refsect3" title="interrupt_state"><a name="d0e6408"></a><h4>interrupt_state</h4><p>Basic type for interrupt states. Interrupt states prevent any further + event handling until EndInterruptEvent is sent. Inherit from this type + to define a terminate state. The first argument is the name of the event + ending the interrupt. The second argument is needed if you want your + state (and all others used in a concrete state machine) to inherit a + basic type for logging or providing a common behavior.</p><pre class="classsynopsis"> <span class="ooclass"><span class="classname">template<class EndInterruptEvent,class Base = + default_base_state,</span></span> {<br>}</pre><pre class="classsynopsis"> <span class="ooclass"><span class="classname">class SMPtrPolicy = no_sm_ptr> + interrupt_state</span></span> {<br>}</pre></div><div class="refsect3" title="explicit_entry"><a name="d0e6421"></a><h4>explicit_entry</h4><p>Inherit from this type <span class="underline">in + addition</span> to the desired state type to enable this state + for direct entering. The template parameter gives the region id of the + state (regions are numbered in the order of the + <code class="code">initial_state</code> typedef).</p><pre class="classsynopsis"> <span class="ooclass"><span class="classname">template <int ZoneIndex=-1> explicit_entry</span></span> {<br>}</pre></div><div class="refsect3" title="entry_pseudo_state"><a name="d0e6436"></a><h4>entry_pseudo_state</h4><p>Basic type for entry pseudo states. Entry pseudo states are an + predefined entry into a submachine and connect two transitions. The + first argument is the id of the region entered by this state (regions + are numbered in the order of the <code class="code">initial_state</code> typedef). + The second argument is needed if you want your state (and all others + used in a concrete state machine) to inherit a basic type for logging or + providing a common behavior.</p><pre class="classsynopsis"> <span class="ooclass"><span class="classname">template<int RegionIndex=-1,class Base = + default_base_state,</span></span> {<br>}</pre><pre class="classsynopsis"> <span class="ooclass"><span class="classname">class SMPtrPolicy = no_sm_ptr> + entry_pseudo_state</span></span> {<br>}</pre></div><div class="refsect3" title="exit_pseudo_state"><a name="d0e6452"></a><h4>exit_pseudo_state</h4><p>Basic type for exit pseudo states. Exit pseudo states are an + predefined exit from a submachine and connect two transitions. The first + argument is the name of the event which will be "thrown" out of the exit + point. This event does not need to be the same as the one sent by the + inner region but must be convertible from it. The second argument is + needed if you want your state (and all others used in a concrete state + machine) to inherit a basic type for logging or providing a common + behavior.</p><pre class="classsynopsis"> <span class="ooclass"><span class="classname">template<class Event,class Base = + default_base_state,</span></span> {<br>}</pre><pre class="classsynopsis"> <span class="ooclass"><span class="classname">class SMPtrPolicy = no_sm_ptr> + exit_pseudo_state</span></span> {<br>}</pre></div></div></div><div class="refsect1" title="msm/front/euml/euml.hpp"><a name="d0e6465"></a><h2>msm/front/euml/euml.hpp</h2><p>This header includes all of eUML except the STL functors.</p></div><div class="refsect1" title="msm/front/euml/stl.hpp"><a name="d0e6470"></a><h2>msm/front/euml/stl.hpp</h2><p>This header includes all the functors for STL support in eUML. These <span class="command"><strong><a class="command" href="ch10.html#eUML-STL-all">tables</a></strong></span> show a full description.</p></div><div class="refsect1" title="msm/front/euml/algorithm.hpp"><a name="d0e6478"></a><h2>msm/front/euml/algorithm.hpp</h2><p>This header includes all the functors for STL algorithms support in eUML. + These <span class="command"><strong><a class="command" href="ch10.html#eUML-STL-all">tables</a></strong></span> show a full + description.</p></div><div class="refsect1" title="msm/front/euml/iteration.hpp"><a name="d0e6486"></a><h2>msm/front/euml/iteration.hpp</h2><p>This header includes iteration functors for STL support in eUML. This <span class="command"><strong><a class="command" href="ch10.html#eUML-STL-iteration">tables</a></strong></span> shows a full + description.</p></div><div class="refsect1" title="msm/front/euml/querying.hpp"><a name="d0e6494"></a><h2>msm/front/euml/querying.hpp</h2><p>This header includes querying functors for STL support in eUML. This <span class="command"><strong><a class="command" href="ch10.html#eUML-STL-querying">tables</a></strong></span> shows a full + description.</p></div><div class="refsect1" title="msm/front/euml/transformation.hpp"><a name="d0e6502"></a><h2>msm/front/euml/transformation.hpp</h2><p>This header includes transformation functors for STL support in eUML. This + <span class="command"><strong><a class="command" href="ch10.html#eUML-STL-transformation">tables</a></strong></span> shows a full + description.</p></div><div class="refsect1" title="msm/front/euml/container.hpp"><a name="d0e6510"></a><h2>msm/front/euml/container.hpp</h2><p>This header includes container functors for STL support in eUML (functors + calling container methods). This <span class="command"><strong><a class="command" href="ch10.html#eUML-STL-container">tables</a></strong></span> shows a full description. It also provides npos for + strings.</p><div class="refsect2" title="Npos_<container type>"><a name="d0e6518"></a><h3>Npos_<container type></h3><p>Functor returning npos for transition or state behaviors. Like all + constants, only the functor form exists, so parenthesis are necessary. + Example:</p><p><code class="code">string_find_(event_(m_song),Char_<'S'>(),Size_t_<0>()) != + Npos_<string>() // compare result of string::find with + npos</code></p></div></div><div class="refsect1" title="msm/front/euml/stt_grammar.hpp"><a name="d0e6526"></a><h2>msm/front/euml/stt_grammar.hpp</h2><p>This header provides the transition table grammars. This includes internal + transition tables.</p><div class="refsect2" title="functions"><a name="d0e6531"></a><h3>functions</h3><div class="refsect3" title="build_stt"><a name="d0e6534"></a><h4>build_stt</h4><p>The function build_stt evaluates the grammar-conform expression as + parameter. It returns a transition table, which is a mpl::vector of + transitions (rows) or, if the expression is ill-formed (does not match + the grammar), the type <code class="code">invalid_type</code>, which will lead to a + compile-time static assertion when this transition table is passed to a + state machine. </p><div class="funcsynopsis"><table border="0" summary="Function synopsis" cellspacing="0" cellpadding="0" class="funcprototype-table"><tr><td><code class="funcdef">template<class Expr> [mpl::vector<...> / + msm::front::euml::invalid_type] build_stt(</code></td><td><code>)</code>;</td><td> </td></tr></table><div class="paramdef-list"><code>Expr const& expr</code>;</div><div class="funcprototype-spacer"> </div></div></div><div class="refsect3" title="build_internal_stt"><a name="d0e6548"></a><h4>build_internal_stt</h4><p>The function build_internal_stt evaluates the grammar-conform + expression as parameter. It returns a transition table, which is a + mpl::vector of transitions (rows) or, if the expression is ill-formed + (does not match the grammar), the type <code class="code">invalid_type</code>, which + will lead to a compile-time static assertion when this transition table + is passed to a state machine. </p><div class="funcsynopsis"><table border="0" summary="Function synopsis" cellspacing="0" cellpadding="0" class="funcprototype-table"><tr><td><code class="funcdef">template<class Expr> [mpl::vector<...> / + msm::front::euml::invalid_type] build_internal_stt(</code></td><td><code>)</code>;</td><td> </td></tr></table><div class="paramdef-list"><code>Expr const& expr</code>;</div><div class="funcprototype-spacer"> </div></div></div></div><div class="refsect2" title="grammars"><a name="d0e6562"></a><h3>grammars</h3><div class="refsect3" title="transition table"><a name="d0e6565"></a><h4><span class="command"><strong><a name="reference-stt-grammar"></a>transition + table</strong></span></h4><p>The transition table accepts the following grammar:</p><pre class="programlisting">Stt := Row | (Stt ',' Stt) +Row := (Target '==' (SourcePlusEvent)) /* first syntax*/ + | ( (SourcePlusEvent) '==' Target ) /* second syntax*/ + | (SourcePlusEvent) /* internal transitions */ +SourcePlusEvent := (BuildSource '+' BuildEvent)/* standard transition*/ + | (BuildSource) /* anonymous transition */ +BuildSource := state_tag | (state_tag '/' Action) | (state_tag '[' Guard ']') + | (state_tag '[' Guard ']' '/' Action) +BuildEvent := event_tag | (event_tag '/' Action) | (event_tag '[' Guard ']') + | (event_tag '[' Guard ']' '/' Action)</pre><p>The grammars Action and Guard are defined in state_grammar.hpp and + guard_grammar.hpp respectively. state_tag and event_tag are inherited + from euml_state (or other state variants) and euml_event respectively. + For example, following declarations are possible:</p><pre class="programlisting">target == source + event [guard] / action, +source + event [guard] / action == target, +source + event [guard] / (action1,action2) == target, +target == source + event [guard] / (action1,action2), +target == source + event, +source + event == target, +target == source + event [guard], +source + event [guard] == target, +target == source + event / action, +source + event /action == target, +source / action == target, /*anonymous transition*/ +target == source / action, /*anonymous transition*/ +source + event /action, /* internal transition*/</pre></div><div class="refsect3" title="internal transition table"><a name="d0e6577"></a><h4>internal transition table</h4><p>The internal transition table accepts the following grammar:</p><pre class="programlisting">IStt := BuildEvent | (IStt ',' IStt)</pre><p>BuildEvent being defined for both internal and standard transition + tables.</p></div></div></div><div class="refsect1" title="msm/front/euml/guard_grammar.hpp"><a name="d0e6586"></a><h2>msm/front/euml/guard_grammar.hpp</h2><p>This header contains the <code class="code">Guard</code> grammar used in the previous + section. This grammar is long but pretty simple:</p><pre class="programlisting">Guard := action_tag | (Guard '&&' Guard) + | (Guard '||' Guard) | ... /* operators*/ + | (if_then_else_(Guard,Guard,Guard)) | (function (Action,...Action))</pre><p>Most C++ operators are supported (address-of is not). With + <code class="code">function</code> is meant any eUML predefined function or any self-made + (using <code class="code">MSM_EUML_METHOD</code> or <code class="code">MSM_EUML_FUNCTION</code>). Action + is a grammar defined in state_grammar.hpp.</p></div><div class="refsect1" title="msm/front/euml/state_grammar.hpp"><a name="d0e6607"></a><h2>msm/front/euml/state_grammar.hpp</h2><p>This header provides the grammar for actions and the different grammars and + functions to build states using eUML.</p><div class="refsect2" title="action grammar"><a name="d0e6612"></a><h3>action grammar</h3><p>Like the guard grammar, this grammar supports relevant C++ operators and + eUML functions:</p><pre class="programlisting">Action := action_tag | (Action '+' Action) + | ('--' Action) | ... /* operators*/ + | if_then_else_(Guard,Action,Action) | if_then_(Action) + | while_(Guard,Action) + | do_while_(Guard,Action) | for_(Action,Guard,Action,Action) + | (function(Action,...Action)) +ActionSequence := Action | (Action ',' Action)</pre><p>Relevant operators are: ++ (post/pre), -- (post/pre), dereferencing, + + (unary/binary), - (unary/binary), *, /, %, &(bitwise), | (bitwise), + ^(bitwise), +=, -=, *=, /=, %=, <<=, >>=, <<, >>, =, [].</p></div><div class="refsect2" title="attributes"><a name="d0e6621"></a><h3>attributes</h3><p>This grammar is used to add attributes to states (or state machines) or + events: It evaluates to a fusion::map. You can use two forms:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p><code class="code">attributes_ << no_attributes_</code></p></li><li class="listitem"><p><code class="code">attributes_ << attribute_1 << ... << + attribute_n</code></p></li></ul></div><p>Attributes can be of any default-constructible type (fusion + requirement).</p></div><div class="refsect2" title="configure"><a name="d0e6637"></a><h3>configure</h3><p>This grammar also has two forms:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p><code class="code">configure_ << no_configure_</code></p></li><li class="listitem"><p><code class="code">configure_ << type_1 << ... << + type_n</code></p></li></ul></div><p>This grammar is used to create inside one syntax:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>flags: <code class="code">configure_ << some_flag</code> where + some_flag inherits from <code class="code">euml_flag<some_flag></code> or + is defined using BOOST_MSM_EUML_FLAG.</p></li><li class="listitem"><p>deferred events: <code class="code">configure_ << some_event</code> + where some_event inherits from + <code class="code">euml_event<some_event></code> or is defined using + BOOST_MSM_EUML_EVENT or + BOOST_MSM_EUML_EVENT_WITH_ATTRIBUTES.</p></li><li class="listitem"><p>configuration (message queue, manual deferring, exception + handling): <code class="code">configure_ << some_config</code> where + some_config inherits from + <code class="code">euml_config<some_config></code>. At the moment, + three predefined objects exist (in msm//front/euml/common.hpp):</p><div class="itemizedlist"><ul class="itemizedlist" type="circle"><li class="listitem"><p>no_exception: disable catching exceptions</p></li><li class="listitem"><p>no_msg_queue: disable message queue</p></li><li class="listitem"><p>deferred_events: manually enable handling of + deferred events</p></li></ul></div></li></ul></div></div><div class="refsect2" title="initial states"><a name="d0e6691"></a><h3>initial states</h3><p>The grammar to define initial states for a state machine is: <code class="code">init_ + << state_1 << ... << state_n</code> where + state_1...state_n inherit from euml_state or is defined using + BOOST_MSM_EUML_STATE, BOOST_MSM_EUML_INTERRUPT_STATE, + BOOST_MSM_EUML_TERMINATE_STATE, BOOST_MSM_EUML_EXPLICIT_ENTRY_STATE, + BOOST_MSM_EUML_ENTRY_STATE or BOOST_MSM_EUML_EXIT_STATE.</p></div><div class="refsect2" title="functions"><a name="d0e6699"></a><h3>functions</h3><div class="refsect3" title="build_sm"><a name="d0e6702"></a><h4>build_sm</h4><p>This function has several overloads. The return type is not relevant + to you as only decltype (return type) is what one needs.</p><p>Defines a state machine without entry or exit:</p><div class="funcsynopsis"><table border="0" summary="Function synopsis" cellspacing="0" cellpadding="0" class="funcprototype-table"><tr><td><code class="funcdef">template <class StateNameTag,class Stt,class Init> + func_state_machine<...> build_sm(</code></td><td><code>)</code>;</td><td> </td></tr></table><div class="paramdef-list"><code>Stt ,Init</code>;</div><div class="funcprototype-spacer"> </div></div><p>Defines a state machine with entry behavior:</p><div class="funcsynopsis"><table border="0" summary="Function synopsis" cellspacing="0" cellpadding="0" class="funcprototype-table"><tr><td><code class="funcdef">template <class StateNameTag,class Stt,class Init,class + Expr1> func_state_machine<...> build_sm(</code></td><td><code>)</code>;</td><td> </td></tr></table><div class="paramdef-list"><code>Stt ,Init,Expr1 const&</code>;</div><div class="funcprototype-spacer"> </div></div><p>Defines a state machine with entry and exit behaviors:</p><div class="funcsynopsis"><table border="0" summary="Function synopsis" cellspacing="0" cellpadding="0" class="funcprototype-table"><tr><td><code class="funcdef">template <class StateNameTag,class Stt,class Init,class + Expr1, class Expr2> func_state_machine<...> + build_sm(</code></td><td><code>)</code>;</td><td> </td></tr></table><div class="paramdef-list"><code>Stt ,Init,Expr1 const&,Expr2 const&</code>;</div><div class="funcprototype-spacer"> </div></div><p>Defines a state machine with entry, exit behaviors and + attributes:</p><div class="funcsynopsis"><table border="0" summary="Function synopsis" cellspacing="0" cellpadding="0" class="funcprototype-table"><tr><td><code class="funcdef">template <class StateNameTag,class Stt,class Init,class + Expr1, class Expr2, class Attributes> func_state_machine<...> + build_sm(</code></td><td><code>)</code>;</td><td> </td></tr></table><div class="paramdef-list"><code>Stt ,Init,Expr1 const&, Expr2 const&, Attributes + const&</code>;</div><div class="funcprototype-spacer"> </div></div><p>Defines a state machine with entry, exit behaviors, attributes and + configuration (deferred events, flags):</p><div class="funcsynopsis"><table border="0" summary="Function synopsis" cellspacing="0" cellpadding="0" class="funcprototype-table"><tr><td><code class="funcdef">template <class StateNameTag,class Stt,class Init,class + Expr1, class Expr2, class Attributes, class Configure> + func_state_machine<...> build_sm(</code></td><td><code>)</code>;</td><td> </td></tr></table><div class="paramdef-list"><code>Stt ,Init,Expr1 const&, Expr2 const&, Attributes + const&, Configure const&</code>;</div><div class="funcprototype-spacer"> </div></div><p>Defines a state machine with entry, exit behaviors, attributes, + configuration (deferred events, flags) and a base state:</p><div class="funcsynopsis"><table border="0" summary="Function synopsis" cellspacing="0" cellpadding="0" class="funcprototype-table"><tr><td><code class="funcdef">template <class StateNameTag,class Stt,class Init,class + Expr1, class Expr2, class Attributes, class Configure, class + Base> func_state_machine<...> build_sm(</code></td><td><code>)</code>;</td><td> </td></tr></table><div class="paramdef-list"><code>Stt ,Init,Expr1 const&, Expr2 const&, Attributes + const&, Configure const&, Base</code>;</div><div class="funcprototype-spacer"> </div></div><p>Notice that this function requires the extra parameter class + StateNameTag to disambiguate state machines having the same parameters + but still being different.</p></div><div class="refsect3" title="build_state"><a name="d0e6757"></a><h4>build_state</h4><p>This function has several overloads. The return type is not relevant + to you as only decltype (return type) is what one needs.</p><p>Defines a simple state without entry or exit:</p><div class="funcsynopsis"><table border="0" summary="Function synopsis" cellspacing="0" cellpadding="0" class="funcprototype-table"><tr><td><code class="funcdef">func_state<class StateNameTag,...> build_state(</code></td><td><code>)</code>;</td><td> </td></tr></table><div class="paramdef-list"><code></code>;</div><div class="funcprototype-spacer"> </div></div><p>Defines a simple state with entry behavior:</p><div class="funcsynopsis"><table border="0" summary="Function synopsis" cellspacing="0" cellpadding="0" class="funcprototype-table"><tr><td><code class="funcdef">template <class StateNameTag,class Expr1> + func_state<...> build_state(</code></td><td><code>)</code>;</td><td> </td></tr></table><div class="paramdef-list"><code>Expr1 const&</code>;</div><div class="funcprototype-spacer"> </div></div><p>Defines a simple state with entry and exit behaviors:</p><div class="funcsynopsis"><table border="0" summary="Function synopsis" cellspacing="0" cellpadding="0" class="funcprototype-table"><tr><td><code class="funcdef">template <class StateNameTag,class Expr1, class Expr2> + func_state<...> build_state(</code></td><td><code>)</code>;</td><td> </td></tr></table><div class="paramdef-list"><code>Expr1 const&,Expr2 const&</code>;</div><div class="funcprototype-spacer"> </div></div><p>Defines a simple state with entry, exit behaviors and + attributes:</p><div class="funcsynopsis"><table border="0" summary="Function synopsis" cellspacing="0" cellpadding="0" class="funcprototype-table"><tr><td><code class="funcdef">template <class StateNameTag,class Expr1, class Expr2, + class Attributes> func_state<...> build_state(</code></td><td><code>)</code>;</td><td> </td></tr></table><div class="paramdef-list"><code>Expr1 const&, Expr2 const&, Attributes + const&</code>;</div><div class="funcprototype-spacer"> </div></div><p>Defines a simple state with entry, exit behaviors, attributes and + configuration (deferred events, flags):</p><div class="funcsynopsis"><table border="0" summary="Function synopsis" cellspacing="0" cellpadding="0" class="funcprototype-table"><tr><td><code class="funcdef">template <class StateNameTag,class Expr1, class Expr2, + class Attributes, class Configure> func_state<...> + build_state(</code></td><td><code>)</code>;</td><td> </td></tr></table><div class="paramdef-list"><code>Expr1 const&, Expr2 const&, Attributes const&, + Configure const&</code>;</div><div class="funcprototype-spacer"> </div></div><p>Defines a simple state with entry, exit behaviors, attributes, + configuration (deferred events, flags) and a base state:</p><div class="funcsynopsis"><table border="0" summary="Function synopsis" cellspacing="0" cellpadding="0" class="funcprototype-table"><tr><td><code class="funcdef">template <class StateNameTag,class Expr1, class Expr2, + class Attributes, class Configure, class Base> + func_state<...> build_state(</code></td><td><code>)</code>;</td><td> </td></tr></table><div class="paramdef-list"><code>Expr1 const&, Expr2 const&, Attributes const&, + Configure const&, Base</code>;</div><div class="funcprototype-spacer"> </div></div><p>Notice that this function requires the extra parameter class + StateNameTag to disambiguate states having the same parameters but still + being different.</p></div><div class="refsect3" title="build_terminate_state"><a name="d0e6811"></a><h4>build_terminate_state</h4><p>This function has the same overloads as build_state.</p></div><div class="refsect3" title="build_interrupt_state"><a name="d0e6816"></a><h4>build_interrupt_state</h4><p>This function has several overloads. The return type is not relevant + to you as only decltype (return type) is what one needs.</p><p>Defines an interrupt state without entry or exit:</p><div class="funcsynopsis"><table border="0" summary="Function synopsis" cellspacing="0" cellpadding="0" class="funcprototype-table"><tr><td><code class="funcdef">template <class StateNameTag,class EndInterruptEvent> + func_state<...> build_interrupt_state(</code></td><td><code>)</code>;</td><td> </td></tr></table><div class="paramdef-list"><code>EndInterruptEvent const&</code>;</div><div class="funcprototype-spacer"> </div></div><p>Defines an interrupt state with entry behavior:</p><div class="funcsynopsis"><table border="0" summary="Function synopsis" cellspacing="0" cellpadding="0" class="funcprototype-table"><tr><td><code class="funcdef">template <class StateNameTag,class + EndInterruptEvent,class Expr1> func_state<...> + build_interrupt_state(</code></td><td><code>)</code>;</td><td> </td></tr></table><div class="paramdef-list"><code>EndInterruptEvent const&,Expr1 const&</code>;</div><div class="funcprototype-spacer"> </div></div><p>Defines an interrupt state with entry and exit behaviors:</p><div class="funcsynopsis"><table border="0" summary="Function synopsis" cellspacing="0" cellpadding="0" class="funcprototype-table"><tr><td><code class="funcdef">template <class StateNameTag,class + EndInterruptEvent,class Expr1, class Expr2> func_state<...> + build_interrupt_state(</code></td><td><code>)</code>;</td><td> </td></tr></table><div class="paramdef-list"><code>EndInterruptEvent const&,Expr1 const&,Expr2 + const&</code>;</div><div class="funcprototype-spacer"> </div></div><p>Defines an interrupt state with entry, exit behaviors and + attributes:</p><div class="funcsynopsis"><table border="0" summary="Function synopsis" cellspacing="0" cellpadding="0" class="funcprototype-table"><tr><td><code class="funcdef">template <class StateNameTag,class + EndInterruptEvent,class Expr1, class Expr2, class Attributes> + func_state<...> build_interrupt_state(</code></td><td><code>)</code>;</td><td> </td></tr></table><div class="paramdef-list"><code>EndInterruptEvent const&,Expr1 const&, Expr2 + const&, Attributes const&</code>;</div><div class="funcprototype-spacer"> </div></div><p>Defines an interrupt state with entry, exit behaviors, attributes and + configuration (deferred events, flags):</p><div class="funcsynopsis"><table border="0" summary="Function synopsis" cellspacing="0" cellpadding="0" class="funcprototype-table"><tr><td><code class="funcdef">template <class StateNameTag,class + EndInterruptEvent,class Expr1, class Expr2, class Attributes, + class Configure> func_state<...> + build_interrupt_state(</code></td><td><code>)</code>;</td><td> </td></tr></table><div class="paramdef-list"><code>EndInterruptEvent const&,Expr1 const&, Expr2 + const&, Attributes const&, Configure + const&</code>;</div><div class="funcprototype-spacer"> </div></div><p>Defines an interrupt state with entry, exit behaviors, attributes, + configuration (deferred events, flags) and a base state:</p><div class="funcsynopsis"><table border="0" summary="Function synopsis" cellspacing="0" cellpadding="0" class="funcprototype-table"><tr><td><code class="funcdef">template <class StateNameTag,class + EndInterruptEvent,class Expr1, class Expr2, class Attributes, + class Configure, class Base> func_state<...> + build_interrupt_state(</code></td><td><code>)</code>;</td><td> </td></tr></table><div class="paramdef-list"><code>EndInterruptEvent const&,Expr1 const&, Expr2 + const&, Attributes const&, Configure const&, + Base</code>;</div><div class="funcprototype-spacer"> </div></div><p>Notice that this function requires the extra parameter class + StateNameTag to disambiguate states having the same parameters but still + being different.</p></div><div class="refsect3" title="build_entry_state"><a name="d0e6871"></a><h4>build_entry_state</h4><p>This function has several overloads. The return type is not relevant + to you as only decltype (return type) is what one needs.</p><p>Defines an entry pseudo state without entry or exit:</p><div class="funcsynopsis"><table border="0" summary="Function synopsis" cellspacing="0" cellpadding="0" class="funcprototype-table"><tr><td><code class="funcdef">template <class StateNameTag,int RegionIndex> + entry_func_state<...> build_entry_state(</code></td><td><code>)</code>;</td><td> </td></tr></table><div class="paramdef-list"><code></code>;</div><div class="funcprototype-spacer"> </div></div><p>Defines an entry pseudo state with entry behavior:</p><div class="funcsynopsis"><table border="0" summary="Function synopsis" cellspacing="0" cellpadding="0" class="funcprototype-table"><tr><td><code class="funcdef">template <class StateNameTag,int RegionIndex,class + Expr1> entry_func_state<...> build_entry_state(</code></td><td><code>)</code>;</td><td> </td></tr></table><div class="paramdef-list"><code>Expr1 const&</code>;</div><div class="funcprototype-spacer"> </div></div><p>Defines an entry pseudo state with entry and exit behaviors:</p><div class="funcsynopsis"><table border="0" summary="Function synopsis" cellspacing="0" cellpadding="0" class="funcprototype-table"><tr><td><code class="funcdef">template <class StateNameTag,int RegionIndex,class + Expr1, class Expr2> entry_func_state<...> + build_entry_state(</code></td><td><code>)</code>;</td><td> </td></tr></table><div class="paramdef-list"><code>Expr1 const&,Expr2 const&</code>;</div><div class="funcprototype-spacer"> </div></div><p>Defines an entry pseudo state with entry, exit behaviors and + attributes:</p><div class="funcsynopsis"><table border="0" summary="Function synopsis" cellspacing="0" cellpadding="0" class="funcprototype-table"><tr><td><code class="funcdef">template <class StateNameTag,int RegionIndex,class + Expr1, class Expr2, class Attributes> entry_func_state<...> + build_entry_state(</code></td><td><code>)</code>;</td><td> </td></tr></table><div class="paramdef-list"><code>Expr1 const&, Expr2 const&, Attributes + const&</code>;</div><div class="funcprototype-spacer"> </div></div><p>Defines an entry pseudo state with entry, exit behaviors, attributes + and configuration (deferred events, flags):</p><div class="funcsynopsis"><table border="0" summary="Function synopsis" cellspacing="0" cellpadding="0" class="funcprototype-table"><tr><td><code class="funcdef">template <class StateNameTag,int RegionIndex,class + Expr1, class Expr2, class Attributes, class Configure> + entry_func_state<...> build_entry_state(</code></td><td><code>)</code>;</td><td> </td></tr></table><div class="paramdef-list"><code>Expr1 const&, Expr2 const&, Attributes const&, + Configure const&</code>;</div><div class="funcprototype-spacer"> </div></div><p>Defines an entry pseudo state with entry, exit behaviors, attributes, + configuration (deferred events, flags) and a base state:</p><div class="funcsynopsis"><table border="0" summary="Function synopsis" cellspacing="0" cellpadding="0" class="funcprototype-table"><tr><td><code class="funcdef">template <class StateNameTag,int RegionIndex,class + Expr1, class Expr2, class Attributes, class Configure, class + Base> entry_func_state<...> build_entry_state(</code></td><td><code>)</code>;</td><td> </td></tr></table><div class="paramdef-list"><code>Expr1 const&, Expr2 const&, Attributes const&, + Configure const&, Base</code>;</div><div class="funcprototype-spacer"> </div></div><p>Notice that this function requires the extra parameter class + StateNameTag to disambiguate states having the same parameters but still + being different.</p></div><div class="refsect3" title="build_exit_state"><a name="d0e6925"></a><h4>build_exit_state</h4><p>This function has several overloads. The return type is not relevant + to you as only decltype (return type) is what one needs.</p><p>Defines an exit pseudo state without entry or exit:</p><div class="funcsynopsis"><table border="0" summary="Function synopsis" cellspacing="0" cellpadding="0" class="funcprototype-table"><tr><td><code class="funcdef">template <class StateNameTag,class Event> + exit_func_state<...> build_exit_state(</code></td><td><code>)</code>;</td><td> </td></tr></table><div class="paramdef-list"><code>Event const&</code>;</div><div class="funcprototype-spacer"> </div></div><p>Defines an exit pseudo state with entry behavior:</p><div class="funcsynopsis"><table border="0" summary="Function synopsis" cellspacing="0" cellpadding="0" class="funcprototype-table"><tr><td><code class="funcdef">template <class StateNameTag,class Event,class Expr1> + exit_func_state<...> build_exit_state(</code></td><td><code>)</code>;</td><td> </td></tr></table><div class="paramdef-list"><code>Event const&,Expr1 const&</code>;</div><div class="funcprototype-spacer"> </div></div><p>Defines an exit pseudo state with entry and exit behaviors:</p><div class="funcsynopsis"><table border="0" summary="Function synopsis" cellspacing="0" cellpadding="0" class="funcprototype-table"><tr><td><code class="funcdef">template <class StateNameTag,class Event,class Expr1, + class Expr2> exit_func_state<...> build_exit_state(</code></td><td><code>)</code>;</td><td> </td></tr></table><div class="paramdef-list"><code>Event const&,Expr1 const&,Expr2 + const&</code>;</div><div class="funcprototype-spacer"> </div></div><p>Defines an exit pseudo state with entry, exit behaviors and + attributes:</p><div class="funcsynopsis"><table border="0" summary="Function synopsis" cellspacing="0" cellpadding="0" class="funcprototype-table"><tr><td><code class="funcdef">template <class StateNameTag,class Event,class Expr1, + class Expr2, class Attributes> exit_func_state<...> + build_exit_state(</code></td><td><code>)</code>;</td><td> </td></tr></table><div class="paramdef-list"><code>Event const&,Expr1 const&, Expr2 const&, + Attributes const&</code>;</div><div class="funcprototype-spacer"> </div></div><p>Defines an exit pseudo state with entry, exit behaviors, attributes + and configuration (deferred events, flags):</p><div class="funcsynopsis"><table border="0" summary="Function synopsis" cellspacing="0" cellpadding="0" class="funcprototype-table"><tr><td><code class="funcdef">template <class StateNameTag,class Event,class Expr1, + class Expr2, class Attributes, class Configure> + exit_func_state<...> build_exit_state(</code></td><td><code>)</code>;</td><td> </td></tr></table><div class="paramdef-list"><code>Event const&,Expr1 const&, Expr2 const&, + Attributes const&, Configure const&</code>;</div><div class="funcprototype-spacer"> </div></div><p>Defines an exit pseudo state with entry, exit behaviors, attributes, + configuration (deferred events, flags) and a base state:</p><div class="funcsynopsis"><table border="0" summary="Function synopsis" cellspacing="0" cellpadding="0" class="funcprototype-table"><tr><td><code class="funcdef">template <class StateNameTag,class Event,class Expr1, + class Expr2, class Attributes, class Configure, class Base> + exit_func_state<...> build_exit_state(</code></td><td><code>)</code>;</td><td> </td></tr></table><div class="paramdef-list"><code>Event const&,Expr1 const&, Expr2 const&, + Attributes const&, Configure const&, Base</code>;</div><div class="funcprototype-spacer"> </div></div><p>Notice that this function requires the extra parameter class + StateNameTag to disambiguate states having the same parameters but still + being different.</p></div><div class="refsect3" title="build_explicit_entry_state"><a name="d0e6980"></a><h4>build_explicit_entry_state</h4><p>This function has the same overloads as build_entry_state and + explicit_entry_func_state as return type.</p></div></div></div><div class="refsect1" title="msm/front/euml/common.hpp"><a name="d0e6985"></a><h2>msm/front/euml/common.hpp</h2><div class="refsect2" title="types"><a name="d0e6988"></a><h3>types</h3><div class="refsect3" title="euml_event"><a name="d0e6991"></a><h4>euml_event</h4><p>The basic type for events with eUML.</p><pre class="classsynopsis"> <span class="ooclass"><span class="classname">template <class EventName> euml_event;</span></span> {<br>}</pre><pre class="programlisting">struct play : euml_event<play>{};</pre></div><div class="refsect3" title="euml_state"><a name="d0e7002"></a><h4>euml_state</h4><p>The basic type for states with eUML. You will usually not use this + type directly as it is easier to use BOOST_MSM_EUML_STATE, + BOOST_MSM_EUML_INTERRUPT_STATE, BOOST_MSM_EUML_TERMINATE_STATE, + BOOST_MSM_EUML_EXPLICIT_ENTRY_STATE, BOOST_MSM_EUML_ENTRY_STATE or + BOOST_MSM_EUML_EXIT_STATE.</p><pre class="classsynopsis"> <span class="ooclass"><span class="classname">template <class StateName> euml_state;</span></span> {<br>}</pre><p>You can however use this type directly if you want to provide your + state with extra functions or provide entry or exit behaviors without + functors, for example:</p><pre class="programlisting">struct Empty : public msm::front::state<> , public euml_state<Empty> +{ + void foo() {...} + template <class Event,class Fsm> + void on_entry(Event const& evt,Fsm& fsm){...} +};</pre></div><div class="refsect3" title="euml_flag"><a name="d0e7015"></a><h4>euml_flag</h4><p>The basic type for flags with eUML.</p><pre class="classsynopsis"> <span class="ooclass"><span class="classname">template <class FlagName> euml_flag;</span></span> {<br>}</pre><pre class="programlisting">struct PlayingPaused: euml_flag<PlayingPaused>{};</pre></div><div class="refsect3" title="euml_action"><a name="d0e7026"></a><h4>euml_action</h4><p>The basic type for state or transition behaviors and guards with + eUML.</p><pre class="classsynopsis"> <span class="ooclass"><span class="classname">template <class AcionName> euml_action;</span></span> {<br>}</pre><pre class="programlisting">struct close_drawer : euml_action<close_drawer> +{ + template <class Fsm,class Evt,class SourceState,class TargetState> + void operator()(Evt const& , Fsm&, SourceState& ,TargetState& ) {...} +};</pre><p>Or, as state entry or exit behavior:</p><pre class="programlisting">struct Playing_Entry : euml_action<Playing_Entry> +{ + template <class Event,class Fsm,class State> + void operator()(Event const&,Fsm& fsm,State& ){...} +};</pre></div><div class="refsect3" title="euml_config"><a name="d0e7041"></a><h4>euml_config</h4><p>The basic type for configuration possibilities with eUML.</p><pre class="classsynopsis"> <span class="ooclass"><span class="classname">template <class ConfigName> euml_config;</span></span> {<br>}</pre><p>You normally do not use this type directly but instead the instances + of predefined configuration:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>no_exception: disable catching exceptions</p></li><li class="listitem"><p>no_msg_queue: disable message queue. The message queue + allows you to send an event for procesing while in an event + processing.</p></li><li class="listitem"><p>deferred_events: manually enable handling of deferred + events</p></li></ul></div></div><div class="refsect3" title="invalid_type"><a name="d0e7062"></a><h4>invalid_type</h4><p>Type returned by grammar parsers if the grammar is invalid. Seeing + this type will result in a static assertion.</p></div><div class="refsect3" title="no_action"><a name="d0e7067"></a><h4>no_action</h4><p>Placeholder type for use in entry/exit or transition behaviors, which + does absolutely nothing.</p></div><div class="refsect3" title="source_"><a name="d0e7072"></a><h4>source_</h4><p>Generic object or function for the source state of a given transition:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>as object: returns by reference the source state of a + transition, usually to be used by another function (usually + one created by MSM_EUML_METHOD or MSM_EUML_FUNCTION).</p><p>Example: + </p><pre class="programlisting">some_user_function_(source_)</pre></li><li class="listitem"><p>as function: returns by reference the attribute passed as + parameter.</p><p>Example: + </p><pre class="programlisting">source_(m_counter)++</pre></li></ul></div></div><div class="refsect3" title="target_"><a name="d0e7092"></a><h4>target_</h4><p>Generic object or function for the target state of a given transition:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>as object: returns by reference the target state of a + transition, usually to be used by another function (usually + one created by MSM_EUML_METHOD or MSM_EUML_FUNCTION).</p><p>Example: + </p><pre class="programlisting">some_user_function_(target_)</pre></li><li class="listitem"><p>as function: returns by reference the attribute passed as + parameter.</p><p>Example: + </p><pre class="programlisting">target_(m_counter)++</pre></li></ul></div></div><div class="refsect3" title="state_"><a name="d0e7112"></a><h4>state_</h4><p>Generic object or function for the state of a given entry / exit + behavior. state_ means source_ while in the context of an exit behavior + and target_ in the context of an entry behavior:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>as object: returns by reference the current state, usually + to be used by another function (usually one created by + MSM_EUML_METHOD or MSM_EUML_FUNCTION).</p><p>Example: + </p><pre class="programlisting">some_user_function_(state_) // calls some_user_function on the current state</pre></li><li class="listitem"><p>as function: returns by reference the attribute passed as + parameter.</p><p>Example: + </p><pre class="programlisting">state_(m_counter)++</pre></li></ul></div></div><div class="refsect3" title="event_"><a name="d0e7132"></a><h4>event_</h4><p>Generic object or function for the event triggering a given transition + (valid in a transition behavior, as well as in state entry/exit behaviors):</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>as object: returns by reference the event of a transition, + usually to be used by another function (usually one created + by MSM_EUML_METHOD or MSM_EUML_FUNCTION).</p><p>Example: + </p><pre class="programlisting">some_user_function_(event_)</pre></li><li class="listitem"><p>as function: returns by reference the attribute passed as + parameter.</p><p>Example: + </p><pre class="programlisting">event_(m_counter)++</pre></li></ul></div></div><div class="refsect3" title="fsm_"><a name="d0e7152"></a><h4>fsm_</h4><p>Generic object or function for the state machine containing a given transition:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>as object: returns by reference the event of a transition, + usually to be used by another function (usually one created + by MSM_EUML_METHOD or MSM_EUML_FUNCTION).</p><p>Example: + </p><pre class="programlisting">some_user_function_(fsm_)</pre></li><li class="listitem"><p>as function: returns by reference the attribute passed as + parameter.</p><p>Example: + </p><pre class="programlisting">fsm_(m_counter)++</pre></li></ul></div></div><div class="refsect3" title="substate_"><a name="d0e7172"></a><h4>substate_</h4><p>Generic object or function returning a state of a given state machine:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>with 1 parameter: returns by reference the state passed as + parameter, usually to be used by another function (usually + one created by MSM_EUML_METHOD or MSM_EUML_FUNCTION).</p><p>Example: + </p><pre class="programlisting">some_user_function_(substate_(my_state))</pre></li><li class="listitem"><p>with 2 parameters: returns by reference the state passed + as first parameter from the state machine passed as second + parameter, usually to be used by another function (usually + one created by MSM_EUML_METHOD or MSM_EUML_FUNCTION). This + makes sense when used in combination with attribute_.</p><p>Example (equivalent to the previous example): + </p><pre class="programlisting">some_user_function_(substate_(my_state,fsm_))</pre></li></ul></div></div><div class="refsect3" title="attribute_"><a name="d0e7192"></a><h4>attribute_</h4><p>Generic object or function returning the attribute passed (by name) as + second parameter of the thing passed as first (a state, event or state + machine). Example: </p><p> + </p><pre class="programlisting">attribute_(substate_(my_state),cd_name_attribute)++</pre><p> + </p></div><div class="refsect3" title="True_"><a name="d0e7202"></a><h4>True_</h4><p>Functor returning true for transition or state behaviors. Like all + constants, only the functor form exists, so parenthesis are necessary. + Example:</p><p> + </p><pre class="programlisting">if_then_(True_(),/* some action always called*/)</pre><p> + </p></div><div class="refsect3" title="False_"><a name="d0e7212"></a><h4>False_</h4><p>Functor returning false for transition or state behaviors. Like all + constants, only the functor form exists, so parenthesis are necessary. + Example:</p><p> + </p><pre class="programlisting">if_then_(False_(),/* some action never called */)</pre><p> + </p></div><div class="refsect3" title="Int_<int value>"><a name="d0e7222"></a><h4>Int_<int value></h4><p>Functor returning an integer value for transition or state behaviors. + Like all constants, only the functor form exists, so parenthesis are + necessary. Example:</p><p> + </p><pre class="programlisting">target_(m_ringing_cpt) = Int_<RINGING_TIME>() // RINGING_TIME is a constant</pre><p> + </p></div><div class="refsect3" title="Char_<char value>"><a name="d0e7232"></a><h4>Char_<char value></h4><p>Functor returning a char value for transition or state behaviors. Like + all constants, only the functor form exists, so parenthesis are + necessary. Example:</p><p> + </p><pre class="programlisting">// look for 'S' in event.m_song +[string_find_(event_(m_song),Char_<'S'>(),Size_t_<0>()) != Npos_<string>()]</pre><p> + </p></div><div class="refsect3" title="Size_t_<size_t value>"><a name="d0e7242"></a><h4>Size_t_<size_t value></h4><p>Functor returning a size_t value for transition or state behaviors. + Like all constants, only the functor form exists, so parenthesis are + necessary. Example:</p><p> + </p><pre class="programlisting">substr_(event_(m_song),Size_t_<1>()) // returns a substring of event.m_song</pre><p> + </p></div><div class="refsect3" title="String_ < mpl::string >"><a name="d0e7252"></a><h4>String_ < mpl::string ></h4><p>Functor returning a string for transition or state behaviors. Like all + constants, only the functor form exists, so parenthesis are necessary. + Requires boost >= 1.40 for mpl::string.</p><p>Example:</p><p> + </p><pre class="programlisting">// adds "Let it be" to fsm.m_src_container +push_back_(fsm_(m_src_container), String_<mpl::string<'Let','it ','be'> >())</pre><p> + </p></div><div class="refsect3" title="Predicate_ < some_stl_compatible_functor >"><a name="d0e7264"></a><h4>Predicate_ < some_stl_compatible_functor ></h4><p>This functor eUML-enables a STL functor (for use in an algorithm). + This is necessary because all what is in the transition table must be a + eUML terminal.</p><p>Example:</p><pre class="programlisting">//equivalent to: +//std::accumulate(fsm.m_vec.begin(),fsm.m_vec.end(),1,std::plus<int>())== 1 +accumulate_(begin_(fsm_(m_vec)),end_(fsm_(m_vec)),Int_<1>(), + Predicate_<std::plus<int> >()) == Int_<1>())</pre></div><div class="refsect3" title="process_"><a name="d0e7273"></a><h4>process_</h4><p>This function sends an event to up to 4 state machines by calling + <code class="code">process_event</code> on them:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p><code class="code">process_(some_event)</code> : processes an event in + the current (containing) state machine.</p></li><li class="listitem"><p><code class="code">process_(some_event [,fsm1...fsm4] )</code> : + processes the same event in the 1-4 state machines passed as + argument.</p></li></ul></div></div><div class="refsect3" title="process2_"><a name="d0e7292"></a><h4>process2_</h4><p>This function sends an event to up to 3 state machines by calling + <code class="code">process_event</code> on them and copy-constructing the event + from the data passed as second parameter:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p><code class="code">process2_(some_event, some_data)</code> : processes + an event in the current (containing) state machine.</p></li><li class="listitem"><p><code class="code">process2_(some_event, some_data [,fsm1...fsm3] + )</code> : processes the same event in the 1-3 state + machines passed as argument.</p></li></ul></div><p>Example: </p><p> + </p><pre class="programlisting">// processes NotFound on current state machine, +// copy-constructed with event.m_song +process2_(NotFound,event_(m_song))</pre><p> + </p><p>With the following definitions:</p><pre class="programlisting">BOOST_MSM_EUML_DECLARE_ATTRIBUTE(std::string,m_song)//declaration of m_song +NotFound (const string& data) // copy-constructor of NotFound</pre></div><div class="refsect3" title="is_flag_"><a name="d0e7322"></a><h4>is_flag_</h4><p>This function tells if a flag is active by calling + <code class="code">is_flag_active</code> on the current state machine or one + passed as parameter:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p><code class="code">is_flag_(some_flag)</code> : calls + <code class="code">is_flag_active</code> on the current (containing) + state machine.</p></li><li class="listitem"><p><code class="code">is_flag_(some_flag, some_fsm)</code> :calls + <code class="code">is_flag_active</code> on the state machine.passed + as argument.</p></li></ul></div></div><div class="refsect3" title="defer_"><a name="d0e7347"></a><h4>defer_</h4><p>This object defers the current event by calling + <code class="code">defer_event</code> on the current state machine. + Example:</p><pre class="programlisting">Empty() + play() / defer_</pre></div><div class="refsect3" title="explicit_(submachine-name,state-name)"><a name="d0e7357"></a><h4>explicit_(submachine-name,state-name)</h4><p>Used as transition's target, causes an explicit entry into the given + state from the given submachine. Several explicit_ as targets, separated + by commas, means a fork. The state must have been declared as such using + BOOST_MSM_EUML_EXPLICIT_ENTRY_STATE.</p></div><div class="refsect3" title="entry_pt_(submachine-name,state-name)"><a name="d0e7362"></a><h4>entry_pt_(submachine-name,state-name)</h4><p>Used as transition's target from a containing state machine, causes + submachine-name to be entered using the given entry pseudo-state. This + state must have been declared as pseudo entry using + BOOST_MSM_EUML_ENTRY_STATE.</p></div><div class="refsect3" title="exit_pt_(submachine-name,state-name)"><a name="d0e7367"></a><h4>exit_pt_(submachine-name,state-name)</h4><p>Used as transition's source from a containing state machine, causes + submachine-name to be left using the given exit pseudo-state. This state + must have been declared as pseudo exit using + BOOST_MSM_EUML_EXIT_STATE.</p></div><div class="refsect3" title="MSM_EUML_FUNCTION"><a name="d0e7372"></a><h4>MSM_EUML_FUNCTION</h4><p>This macro creates a eUML function and a functor for use with the + functor front-end, based on a free function:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>first parameter: the name of the functor</p></li><li class="listitem"><p>second parameter: the underlying function</p></li><li class="listitem"><p>third parameter: the eUML function name</p></li><li class="listitem"><p>fourth parameter: the return type if used in a transition + behavior</p></li><li class="listitem"><p>fifth parameter: the return type if used in a state + behavior (entry/exit)</p></li></ul></div><p> Note that the function itself can take up to 5 + arguments.</p><p>Example:</p><p> + </p><pre class="programlisting">MSM_EUML_FUNCTION(BinarySearch_,std::binary_search,binary_search_,bool,bool)</pre><p> + </p><p>Can be used like:</p><p> + </p><pre class="programlisting">binary_search_(begin_(fsm_(m_var)),end_(fsm_(m_var)),Int_<9>())</pre><p> + </p></div><div class="refsect3" title="MSM_EUML_METHOD"><a name="d0e7408"></a><h4>MSM_EUML_METHOD</h4><p>This macro creates a eUML function and a functor for use with the + functor front-end, based on a method:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>first parameter: the name of the functor</p></li><li class="listitem"><p>second parameter: the underlying function</p></li><li class="listitem"><p>third parameter: the eUML function name</p></li><li class="listitem"><p>fourth parameter: the return type if used in a transition + behavior</p></li><li class="listitem"><p>fifth parameter: the return type if used in a state + behavior (entry/exit)</p></li></ul></div><p> Note that the method itself can take up to 4 arguments + (5 like for a free function - 1 for the object on which the method is + called).</p><p>Example:</p><pre class="programlisting">struct Empty : public msm::front::state<> , public euml_state<Empty> +{ + void activate_empty() {std::cout << "switching to Empty " << std::endl;} +... +}; +MSM_EUML_METHOD(ActivateEmpty_,activate_empty,activate_empty_,void,void)</pre><p>Can be used like:</p><p> + </p><pre class="programlisting">Empty == Open + open_close / (close_drawer , activate_empty_(target_))</pre><p> + </p></div><div class="refsect3" title="BOOST_MSM_EUML_ACTION(action-instance-name)"><a name="d0e7441"></a><h4>BOOST_MSM_EUML_ACTION(action-instance-name)</h4><p>This macro declares a behavior type and a const instance for use in + state or transition behaviors. The action implementation itself follows + the macro declaration, for example:</p><pre class="programlisting">BOOST_MSM_EUML_ACTION(good_disk_format) +{ + template <class Fsm,class Evt,class SourceState,class TargetState> + void/bool operator()(Evt const& evt,Fsm&,SourceState& ,TargetState& ){...} +};</pre></div><div class="refsect3" title="BOOST_MSM_EUML_FLAG(flag-instance-name)"><a name="d0e7448"></a><h4>BOOST_MSM_EUML_FLAG(flag-instance-name)</h4><p>This macro declares a flag type and a const instance for use in + behaviors.</p></div><div class="refsect3" title="BOOST_MSM_EUML_FLAG_NAME(flag-instance-name)"><a name="d0e7453"></a><h4>BOOST_MSM_EUML_FLAG_NAME(flag-instance-name)</h4><p>This macro returns the name of the flag type generated by + BOOST_MSM_EUML_FLAG. You need this where the type is required (usually + with the back-end method is_flag_active). For example:</p><pre class="programlisting">fsm.is_flag_active<BOOST_MSM_EUML_FLAG_NAME(CDLoaded)>()</pre></div><div class="refsect3" title="BOOST_MSM_EUML_DECLARE_ATTRIBUTE(event-type,event-name)"><a name="d0e7460"></a><h4>BOOST_MSM_EUML_DECLARE_ATTRIBUTE(event-type,event-name)</h4><p>This macro declares an attribute called event-name of type event-type. + This attribute can then be made part of an attribute list using + BOOST_MSM_EUML_ATTRIBUTES.</p></div><div class="refsect3" title="BOOST_MSM_EUML_ATTRIBUTES(attributes-expression,attributes-name)"><a name="d0e7465"></a><h4>BOOST_MSM_EUML_ATTRIBUTES(attributes-expression,attributes-name)</h4><p>This macro declares an attribute list called attributes-name based on + the expression as first argument. These attributes can then be made part + of an event using BOOST_MSM_EUML_EVENT_WITH_ATTRIBUTES, of a state as + 3rd parameter of BOOST_MSM_EUML_STATE or of a state machine as 5th + parameter of BOOST_MSM_EUML_DECLARE_STATE_MACHINE.</p><p>Attributes are added using left-shift, for example:</p><pre class="programlisting">// m_song is of type std::string +BOOST_MSM_EUML_DECLARE_ATTRIBUTE(std::string,m_song) +// contains one attribute, m_song +BOOST_MSM_EUML_ATTRIBUTES((attributes_ << m_song ), FoundDef)</pre></div><div class="refsect3" title="BOOST_MSM_EUML_EVENT(event-instance name)"><a name="d0e7474"></a><h4>BOOST_MSM_EUML_EVENT(event-instance name)</h4><p>This macro defines an event type (event-instance-name_helper) and + declares a const instance of this event type called event-instance-name + for use in a transition table or state behaviors.</p></div><div class="refsect3" title="BOOST_MSM_EUML_EVENT_WITH_ATTRIBUTES(event-instance-name,attributes)"><a name="d0e7479"></a><h4>BOOST_MSM_EUML_EVENT_WITH_ATTRIBUTES(event-instance-name,attributes)</h4><p>This macro defines an event type (event-instance-name_helper) and + declares a const instance of this event type called event-instance-name + for use in a transition table or state behaviors. The event will have as + attributes the ones passed by the second argument:</p><p><code class="code">BOOST_MSM_EUML_EVENT_WITH_ATTRIBUTES(Found,FoundDef)</code> + </p><p>The created event instance supports operator()(attributes) so that + </p><pre class="programlisting">my_back_end.process_event(Found(some_string))</pre><p> + is possible.</p></div><div class="refsect3" title="BOOST_MSM_EUML_EVENT_NAME(event-instance-name)"><a name="d0e7493"></a><h4>BOOST_MSM_EUML_EVENT_NAME(event-instance-name)</h4><p>This macro returns the name of the event type generated by + BOOST_MSM_EUML_EVENT or BOOST_MSM_EUML_EVENT_WITH_ATTRIBUTES. You need + this where the type is required (usually inside a back-end definition). + For example:</p><p> + </p><pre class="programlisting">typedef msm::back::state_machine<Playing_, +msm::back::ShallowHistory<mpl::vector<BOOST_MSM_EUML_EVENT_NAME(end_pause) +> > > Playing_type;</pre><p> + </p></div><div class="refsect3" title="BOOST_MSM_EUML_STATE(build-expression,state-instance-name)"><a name="d0e7503"></a><h4>BOOST_MSM_EUML_STATE(build-expression,state-instance-name)</h4><p>This macro defines a state type (state-instance-name_helper) and + declares a const instance of this state type called state-instance-name + for use in a transition table or state behaviors.</p><p>There are several possibilitites for the expression syntax:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>(): state without entry or exit action.</p></li><li class="listitem"><p>(Expr1): state with entry but no exit action.</p></li><li class="listitem"><p>(Expr1,Expr2): state with entry and exit action.</p></li><li class="listitem"><p>(Expr1,Expr2,Attributes): state with entry and exit + action, defining some attributes.</p></li><li class="listitem"><p>(Expr1,Expr2,Attributes,Configure): state with entry and + exit action, defining some attributes and flags (standard + MSM flags) or deferred events (standard MSM deferred + events).</p></li><li class="listitem"><p>(Expr1,Expr2,Attributes,Configure,Base): state with entry + and exit action, defining some attributes, flags and + deferred events (plain msm deferred events) and a + non-default base state (as defined in standard MSM).</p></li></ul></div></div><div class="refsect3" title="BOOST_MSM_EUML_INTERRUPT_STATE(build-expression,state-instance-name)"><a name="d0e7529"></a><h4>BOOST_MSM_EUML_INTERRUPT_STATE(build-expression,state-instance-name)</h4><p>This macro defines an interrupt state type + (state-instance-name_helper) and declares a const instance of this state + type called state-instance-name for use in a transition table or state + behaviors.</p><p>There are several possibilitites for the expression syntax. In all of + them, the first argument is the name of the event (generated by one of + the previous macros) ending the interrupt:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>(end_interrupt_event): interrupt state without entry or + exit action.</p></li><li class="listitem"><p>(end_interrupt_event,Expr1): interrupt state with entry + but no exit action.</p></li><li class="listitem"><p>(end_interrupt_event,Expr1,Expr2): interrupt state with + entry and exit action.</p></li><li class="listitem"><p>(end_interrupt_event,Expr1,Expr2,Attributes): interrupt + state with entry and exit action, defining some + attributes.</p></li><li class="listitem"><p>(end_interrupt_event,Expr1,Expr2,Attributes,Configure): + interrupt state with entry and exit action, defining some + attributes and flags (standard MSM flags) or deferred events + (standard MSM deferred events).</p></li><li class="listitem"><p>(end_interrupt_event,Expr1,Expr2,Attributes,Configure,Base): + interrupt state with entry and exit action, defining some + attributes, flags and deferred events (plain msm deferred + events) and a non-default base state (as defined in standard + MSM).</p></li></ul></div></div><div class="refsect3" title="BOOST_MSM_EUML_TERMINATE_STATE(build-expression,state-instance-name)"><a name="d0e7555"></a><h4>BOOST_MSM_EUML_TERMINATE_STATE(build-expression,state-instance-name)</h4><p>This macro defines a terminate pseudo-state type + (state-instance-name_helper) and declares a const instance of this state + type called state-instance-name for use in a transition table or state + behaviors.</p><p>There are several possibilitites for the expression syntax:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>(): terminate pseudo-state without entry or exit + action.</p></li><li class="listitem"><p>(Expr1): terminate pseudo-state with entry but no exit + action.</p></li><li class="listitem"><p>(Expr1,Expr2): terminate pseudo-state with entry and exit + action.</p></li><li class="listitem"><p>(Expr1,Expr2,Attributes): terminate pseudo-state with + entry and exit action, defining some attributes.</p></li><li class="listitem"><p>(Expr1,Expr2,Attributes,Configure): terminate pseudo-state + with entry and exit action, defining some attributes and + flags (standard MSM flags) or deferred events (standard MSM + deferred events).</p></li><li class="listitem"><p>(Expr1,Expr2,Attributes,Configure,Base): terminate + pseudo-state with entry and exit action, defining some + attributes, flags and deferred events (plain msm deferred + events) and a non-default base state (as defined in standard + MSM).</p></li></ul></div></div><div class="refsect3" title="BOOST_MSM_EUML_EXIT_STATE(build-expression,state-instance-name)"><a name="d0e7581"></a><h4>BOOST_MSM_EUML_EXIT_STATE(build-expression,state-instance-name)</h4><p>This macro defines an exit pseudo-state type + (state-instance-name_helper) and declares a const instance of this state + type called state-instance-name for use in a transition table or state + behaviors.</p><p>There are several possibilitites for the expression syntax:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>(forwarded_event):exit pseudo-state without entry or exit + action.</p></li><li class="listitem"><p>(forwarded_event,Expr1): exit pseudo-state with entry but + no exit action.</p></li><li class="listitem"><p>(forwarded_event,Expr1,Expr2): exit pseudo-state with + entry and exit action.</p></li><li class="listitem"><p>(forwarded_event,Expr1,Expr2,Attributes): exit + pseudo-state with entry and exit action, defining some + attributes.</p></li><li class="listitem"><p>(forwarded_event,Expr1,Expr2,Attributes,Configure): exit + pseudo-state with entry and exit action, defining some + attributes and flags (standard MSM flags) or deferred events + (standard MSM deferred events).</p></li><li class="listitem"><p>(forwarded_event,Expr1,Expr2,Attributes,Configure,Base): + exit pseudo-state with entry and exit action, defining some + attributes, flags and deferred events (plain msm deferred + events) and a non-default base state (as defined in standard + MSM).</p></li></ul></div><p>Note that the forwarded_event must be constructible from the event + sent by the submachine containing the exit point.</p></div><div class="refsect3" title="BOOST_MSM_EUML_ENTRY_STATE(int region-index,build-expression,state-instance-name)"><a name="d0e7609"></a><h4>BOOST_MSM_EUML_ENTRY_STATE(int + region-index,build-expression,state-instance-name)</h4><p>This macro defines an entry pseudo-state type + (state-instance-name_helper) and declares a const instance of this state + type called state-instance-name for use in a transition table or state + behaviors.</p><p>There are several possibilitites for the expression syntax:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>(): entry pseudo-state without entry or exit + action.</p></li><li class="listitem"><p>(Expr1): entry pseudo-state with entry but no exit + action.</p></li><li class="listitem"><p>(Expr1,Expr2): entry pseudo-state with entry and exit + action.</p></li><li class="listitem"><p>(Expr1,Expr2,Attributes): entry pseudo-state with entry + and exit action, defining some attributes.</p></li><li class="listitem"><p>(Expr1,Expr2,Attributes,Configure): entry pseudo-state + with entry and exit action, defining some attributes and + flags (standard MSM flags) or deferred events (standard MSM + deferred events).</p></li><li class="listitem"><p>(Expr1,Expr2,Attributes,Configure,Base): entry + pseudo-state with entry and exit action, defining some + attributes, flags and deferred events (plain msm deferred + events) and a non-default base state (as defined in standard + MSM).</p></li></ul></div></div><div class="refsect3" title="BOOST_MSM_EUML_EXPLICIT_ENTRY_STATE(int region-index,build-expression,state-instance-name)"><a name="d0e7635"></a><h4>BOOST_MSM_EUML_EXPLICIT_ENTRY_STATE(int + region-index,build-expression,state-instance-name)</h4><p>This macro defines a submachine's substate type + (state-instance-name_helper), which can be explicitly entered and also + declares a const instance of this state type called state-instance-name + for use in a transition table or state behaviors.</p><p>There are several possibilitites for the expression syntax:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>(): state without entry or exit action.</p></li><li class="listitem"><p>(Expr1): state with entry but no exit action.</p></li><li class="listitem"><p>(Expr1,Expr2): state with entry and exit action.</p></li><li class="listitem"><p>(Expr1,Expr2,Attributes): state with entry and exit + action, defining some attributes.</p></li><li class="listitem"><p>(Expr1,Expr2,Attributes,Configure): state with entry and + exit action, defining some attributes and flags (standard + MSM flags) or deferred events (standard MSM deferred + events).</p></li><li class="listitem"><p>(Expr1,Expr2,Attributes,Configure,Base): state with entry + and exit action, defining some attributes, flags and + deferred events (plain msm deferred events) and a + non-default base state (as defined in standard MSM).</p></li></ul></div></div><div class="refsect3" title="BOOST_MSM_EUML_STATE_NAME(state-instance-name)"><a name="d0e7661"></a><h4>BOOST_MSM_EUML_STATE_NAME(state-instance-name)</h4><p>This macro returns the name of the state type generated by + BOOST_MSM_EUML_STATE or other state macros. You need this where the type + is required (usually using a backend function). For example:</p><p> + </p><pre class="programlisting">fsm.get_state<BOOST_MSM_EUML_STATE_NAME(StringFind)&>().some_state_function();</pre><p> + </p></div><div class="refsect3" title="BOOST_MSM_EUML_DECLARE_STATE(build-expression,state-instance-name)"><a name="d0e7671"></a><h4>BOOST_MSM_EUML_DECLARE_STATE(build-expression,state-instance-name)</h4><p>Like BOOST_MSM_EUML_STATE but does not provide an instance, simply a + type declaration.</p></div><div class="refsect3" title="BOOST_MSM_EUML_DECLARE_INTERRUPT_STATE(build-expression,state-instance-name)"><a name="d0e7676"></a><h4>BOOST_MSM_EUML_DECLARE_INTERRUPT_STATE(build-expression,state-instance-name)</h4><p>Like BOOST_MSM_EUML_INTERRUPT_STATE but does not provide an instance, + simply a type declaration.</p></div><div class="refsect3" title="BOOST_MSM_EUML_DECLARE_TERMINATE_STATE(build-expression,state-instance-name)"><a name="d0e7681"></a><h4>BOOST_MSM_EUML_DECLARE_TERMINATE_STATE(build-expression,state-instance-name)</h4><p>Like BOOST_MSM_EUML_TERMINATE_STATE but does not provide an instance, + simply a type declaration.</p></div><div class="refsect3" title="BOOST_MSM_EUML_DECLARE_EXIT_STATE(build-expression,state-instance-name)"><a name="d0e7686"></a><h4>BOOST_MSM_EUML_DECLARE_EXIT_STATE(build-expression,state-instance-name)</h4><p>Like BOOST_MSM_EUML_EXIT_STATE but does not provide an instance, + simply a type declaration.</p></div><div class="refsect3" title="BOOST_MSM_EUML_DECLARE_ENTRY_STATE(int region-index,build-expression,state-instance-name)"><a name="d0e7691"></a><h4>BOOST_MSM_EUML_DECLARE_ENTRY_STATE(int + region-index,build-expression,state-instance-name)</h4><p>Like BOOST_MSM_EUML_ENTRY_STATE but does not provide an instance, + simply a type declaration.</p></div><div class="refsect3" title="BOOST_MSM_EUML_DECLARE_EXPLICIT_ENTRY_STATE(int region-index,build-expression,state-instance-name)"><a name="d0e7696"></a><h4>BOOST_MSM_EUML_DECLARE_EXPLICIT_ENTRY_STATE(int + region-index,build-expression,state-instance-name)</h4><p>Like BOOST_MSM_EUML_EXPLICIT_ENTRY_STATE but does not provide an + instance, simply a type declaration.</p></div><div class="refsect3" title="BOOST_MSM_EUML_TRANSITION_TABLE(expression, table-instance-name)"><a name="d0e7701"></a><h4>BOOST_MSM_EUML_TRANSITION_TABLE(expression, + table-instance-name)</h4><p>This macro declares a transition table type and also declares a const + instance of the table which can then be used in a state machine + declaration (see BOOST_MSM_EUML_DECLARE_STATE_MACHINE).The expression + must follow the <span class="command"><strong><a class="command" href="re03.html#reference-stt-grammar">transition + table grammar</a></strong></span>.</p></div><div class="refsect3" title="BOOST_MSM_EUML_DECLARE_TRANSITION_TABLE(iexpression,table-instance-name)"><a name="d0e7709"></a><h4>BOOST_MSM_EUML_DECLARE_TRANSITION_TABLE(iexpression,table-instance-name)</h4><p>Like BOOST_MSM_EUML_TRANSITION_TABLE but does not provide an instance, + simply a type declaration.</p></div><div class="refsect3" title="BOOST_MSM_EUML_INTERNAL_TRANSITION_TABLE(expression, table-instance-name)"><a name="d0e7714"></a><h4>BOOST_MSM_EUML_INTERNAL_TRANSITION_TABLE(expression, + table-instance-name)</h4><p>This macro declares a transition table type and also declares a const + instance of the table.The expression must follow the <span class="command"><strong><a class="command" href="re03.html#reference-stt-grammar">transition table + grammar</a></strong></span>. For the moment, this macro is not used.</p></div><div class="refsect3" title="BOOST_MSM_EUML_DECLARE_INTERNAL_TRANSITION_TABLE(iexpression,table-instance-name)"><a name="d0e7722"></a><h4>BOOST_MSM_EUML_DECLARE_INTERNAL_TRANSITION_TABLE(iexpression,table-instance-name)</h4><p>Like BOOST_MSM_EUML_TRANSITION_TABLE but does not provide an instance, + simply a type declaration. This is currently the only way to declare an + internal transition table with eUML. For example:</p><pre class="programlisting">BOOST_MSM_EUML_DECLARE_STATE((Open_Entry,Open_Exit),Open_def) +struct Open_impl : public Open_def +{ + BOOST_MSM_EUML_DECLARE_INTERNAL_TRANSITION_TABLE(( + open_close [internal_guard1] / internal_action1 , + open_close [internal_guard2] / internal_action2 + )) +}; </pre></div></div></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="re02.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="pt02.html">Up</a></td><td width="40%" align="right"> </td></tr><tr><td width="40%" align="left" valign="top">Back-end </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> </td></tr></table></div></body></html>
\ No newline at end of file diff --git a/libs/msm/doc/PDF/examples/ActivateStateBeforeTransitionEuml.cpp b/libs/msm/doc/PDF/examples/ActivateStateBeforeTransitionEuml.cpp new file mode 100644 index 0000000000..fc69b777bc --- /dev/null +++ b/libs/msm/doc/PDF/examples/ActivateStateBeforeTransitionEuml.cpp @@ -0,0 +1,144 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <iostream> + +#include <boost/msm/back/state_machine.hpp> +#include <boost/msm/front/state_machine_def.hpp> +#include <boost/msm/front/euml/euml.hpp> + +using namespace std; +namespace msm = boost::msm; +namespace mpl = boost::mpl; +using namespace boost::msm::front::euml; + + +namespace // Concrete FSM implementation +{ + // events + BOOST_MSM_EUML_EVENT(connect) + BOOST_MSM_EUML_EVENT(disconnect) + + // flag + BOOST_MSM_EUML_FLAG(is_connected) + + BOOST_MSM_EUML_ACTION(SignalConnect) + { + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& fsm,SourceState& ,TargetState& ) + { + // by default, this would be wrong (shows false) + cout << "SignalConnect. Connected? " + << std::boolalpha + << fsm.template is_flag_active<BOOST_MSM_EUML_FLAG_NAME(is_connected)>() << endl; + } + }; + BOOST_MSM_EUML_ACTION(SignalDisconnect) + { + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& fsm,SourceState& ,TargetState& ) + { + // by default, this would be wrong (shows true) + cout << "SignalDisconnect. Connected? " + << std::boolalpha + << fsm.template is_flag_active<BOOST_MSM_EUML_FLAG_NAME(is_connected)>() + << endl; + } + }; + + // The list of FSM states + BOOST_MSM_EUML_ACTION(Connected_Entry) + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: Connected" << std::endl; + } + }; + BOOST_MSM_EUML_ACTION(Connected_Exit) + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "leaving: Connected" << std::endl; + } + }; + BOOST_MSM_EUML_STATE(( Connected_Entry,Connected_Exit, + attributes_ << no_attributes_, + configure_<< is_connected ),Connected) + + BOOST_MSM_EUML_ACTION(Disconnected_Entry) + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: Disconnected" << std::endl; + } + }; + BOOST_MSM_EUML_ACTION(Disconnected_Exit) + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "leaving: Disconnected" << std::endl; + } + }; + BOOST_MSM_EUML_STATE(( Disconnected_Entry,Disconnected_Exit ),Disconnected) + + // replaces the old transition table + BOOST_MSM_EUML_TRANSITION_TABLE(( + Disconnected == Connected + disconnect / SignalDisconnect , + Connected == Disconnected + connect / SignalConnect + // +------------------------------------------------------------------------------+ + ),transition_table) + + BOOST_MSM_EUML_ACTION(Log_No_Transition) + { + template <class FSM,class Event> + void operator()(Event const& e,FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + + // create a state machine "on the fly" + BOOST_MSM_EUML_DECLARE_STATE_MACHINE(( transition_table, //STT + init_ << Disconnected, // Init State + no_action, // Entry + no_action, // Exit + attributes_ << no_attributes_, // Attributes + configure_ << switch_active_before_transition, // configuration + Log_No_Transition // no_transition handler + ), + Connection_) //fsm name + + typedef msm::back::state_machine<Connection_> Connection; + + void test() + { + Connection connection; + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + connection.start(); + // signal a connection + connection.process_event(connect); + // signal a disconnection + connection.process_event(disconnect); + connection.stop(); + } +} + +int main() +{ + test(); + return 0; +} + + diff --git a/libs/msm/doc/PDF/examples/ActiveStateSetBeforeTransition.cpp b/libs/msm/doc/PDF/examples/ActiveStateSetBeforeTransition.cpp new file mode 100644 index 0000000000..769d7702f1 --- /dev/null +++ b/libs/msm/doc/PDF/examples/ActiveStateSetBeforeTransition.cpp @@ -0,0 +1,119 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> + +//front-end +#include <boost/msm/front/state_machine_def.hpp> +// functors +#include <boost/msm/front/functor_row.hpp> +#include <boost/msm/front/euml/common.hpp> + + +using namespace std; +namespace msm = boost::msm; +namespace mpl = boost::mpl; +using namespace msm::front; + +namespace // Concrete FSM implementation +{ + // events + struct disconnect {}; + struct connect {}; + + // flag + struct is_connected{}; + // front-end: define the FSM structure + struct Connection_ : public msm::front::state_machine_def<Connection_> + { + // when a transition is about to be taken, we already update our currently active state(s) + typedef msm::active_state_switch_before_transition active_state_switch_policy; + + // The list of FSM states + struct Connected : public msm::front::state<> + { + // in this state, we are connected + typedef mpl::vector1<is_connected> flag_list; + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Connected" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Connected" << std::endl;} + }; + struct Disconnected : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) {std::cout << "entering: Disconnected" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Disconnected" << std::endl;} + }; + + // transition actions + struct SignalConnect + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const&, FSM& fsm,SourceState& ,TargetState& ) + { + // by default, this would be wrong (shows false) + cout << "SignalConnect. Connected? " << std::boolalpha << fsm.template is_flag_active<is_connected>() << endl; + } + }; + struct SignalDisconnect + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const&, FSM& fsm,SourceState& ,TargetState& ) + { + // by default, this would be wrong (shows true) + cout << "SignalDisconnect. Connected? " << std::boolalpha << fsm.template is_flag_active<is_connected>() << endl; + } + }; + + // the initial state of the player SM. Must be defined + typedef Disconnected initial_state; + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +--------------+-------------+--------------+---------------------------+----------------------+ + Row < Connected , disconnect , Disconnected , SignalDisconnect , none >, + Row < Disconnected , connect , Connected , SignalConnect , none > + // +--------------+-------------+--------------+---------------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + // Pick a back-end + typedef msm::back::state_machine<Connection_> Connection; + + + void test() + { + Connection connection; + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + connection.start(); + // signal a connection + connection.process_event(connect()); + // signal a disconnection + connection.process_event(disconnect()); + connection.stop(); + } +} + +int main() +{ + test(); + return 0; +} diff --git a/libs/msm/doc/PDF/examples/AnonymousTutorial.cpp b/libs/msm/doc/PDF/examples/AnonymousTutorial.cpp new file mode 100644 index 0000000000..557f90b1d3 --- /dev/null +++ b/libs/msm/doc/PDF/examples/AnonymousTutorial.cpp @@ -0,0 +1,133 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> + +namespace msm = boost::msm; +namespace mpl = boost::mpl; +using namespace boost::msm::front; + +namespace +{ + // events + struct event1 {}; + + + // front-end: define the FSM structure + struct my_machine_ : public msm::front::state_machine_def<my_machine_> + { + // The list of FSM states + struct State1 : public msm::front::state<> + { + // every (optional) entry/exit methods get the event passed. + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: State1" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: State1" << std::endl;} + }; + struct State2 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) {std::cout << "entering: State2" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: State2" << std::endl;} + }; + + struct State3 : public msm::front::state<> + { + // when stopped, the CD is loaded + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) {std::cout << "entering: State3" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: State3" << std::endl;} + }; + + struct State4 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: State4" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: State4" << std::endl;} + }; + + // the initial state of the player SM. Must be defined + typedef State1 initial_state; + + // transition actions + void State2ToState3(none const&) { std::cout << "my_machine::State2ToState3\n"; } + void State3ToState4(none const&) { std::cout << "my_machine::State3ToState4\n"; } + // guard conditions + bool always_true(none const& evt) + { + std::cout << "always_true" << std::endl; + return true; + } + bool always_false(none const& evt) + { + std::cout << "always_false" << std::endl; + return false; + } + + typedef my_machine_ p; // makes transition table cleaner + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + _row < State1 , none , State2 >, + a_row < State2 , none , State3 , &p::State2ToState3 >, + // +---------+-------------+---------+---------------------+----------------------+ + row < State3 , none , State4 , &p::State3ToState4 , &p::always_true >, + g_row < State3 , none , State4 , &p::always_false >, + _row < State4 , event1 , State1 > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + // Pick a back-end + typedef msm::back::state_machine<my_machine_> my_machine; + + // + // Testing utilities. + // + static char const* const state_names[] = { "State1", "State2", "State3", "State4" }; + void pstate(my_machine const& p) + { + std::cout << " -> " << state_names[p.current_state()[0]] << std::endl; + } + + void test() + { + my_machine p; + + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + // in this case it will also immediately trigger all anonymous transitions + p.start(); + // this event will bring us back to the initial state and thus, a new "loop" will be started + p.process_event(event1()); + + } +} + +int main() +{ + test(); + return 0; +} diff --git a/libs/msm/doc/PDF/examples/AnonymousTutorialEuml.cpp b/libs/msm/doc/PDF/examples/AnonymousTutorialEuml.cpp new file mode 100644 index 0000000000..2ad0c5b3c3 --- /dev/null +++ b/libs/msm/doc/PDF/examples/AnonymousTutorialEuml.cpp @@ -0,0 +1,175 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +#include <boost/msm/front/euml/euml.hpp> + +namespace msm = boost::msm; +using namespace boost::msm::front::euml; + +namespace +{ + // events + BOOST_MSM_EUML_EVENT(event1) + + BOOST_MSM_EUML_ACTION(State1_Entry) + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: State1" << std::endl; + } + }; + BOOST_MSM_EUML_ACTION(State1_Exit) + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "leaving: State1" << std::endl; + } + }; + BOOST_MSM_EUML_ACTION(State2_Entry) + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: State2" << std::endl; + } + }; + BOOST_MSM_EUML_ACTION(State2_Exit) + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "leaving: State2" << std::endl; + } + }; + BOOST_MSM_EUML_ACTION(State3_Entry) + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: State3" << std::endl; + } + }; + BOOST_MSM_EUML_ACTION(State3_Exit) + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "leaving: State3" << std::endl; + } + }; + BOOST_MSM_EUML_ACTION(State4_Entry) + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: State4" << std::endl; + } + }; + BOOST_MSM_EUML_ACTION(State4_Exit) + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "leaving: State4" << std::endl; + } + }; + + // The list of FSM states + BOOST_MSM_EUML_STATE(( State1_Entry,State1_Exit ),State1) + BOOST_MSM_EUML_STATE(( State2_Entry,State2_Exit ),State2) + BOOST_MSM_EUML_STATE(( State3_Entry,State3_Exit ),State3) + BOOST_MSM_EUML_STATE(( State4_Entry,State4_Exit ),State4) + + // transition actions + BOOST_MSM_EUML_ACTION(State2ToState3) + { + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM&,SourceState& ,TargetState& ) + { + std::cout << "my_machine::State2ToState3" << std::endl; + } + }; + BOOST_MSM_EUML_ACTION(State3ToState4) + { + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM&,SourceState& ,TargetState& ) + { + std::cout << "my_machine::State3ToState4" << std::endl; + } + }; + // guard conditions + BOOST_MSM_EUML_ACTION(always_true) + { + template <class FSM,class EVT,class SourceState,class TargetState> + bool operator()(EVT const& evt,FSM&,SourceState& ,TargetState& ) + { + std::cout << "always_true" << std::endl; + return true; + } + }; + BOOST_MSM_EUML_ACTION(always_false) + { + template <class FSM,class EVT,class SourceState,class TargetState> + bool operator()(EVT const& evt,FSM&,SourceState& ,TargetState& ) + { + std::cout << "always_false" << std::endl; + return false; + } + }; + // replaces the old transition table + BOOST_MSM_EUML_TRANSITION_TABLE(( + State2 == State1 , + State3 == State2 / State2ToState3, + State4 == State3 [always_true] / State3ToState4, + State4 == State3 [always_false], + State1 == State4 + event1 + // +------------------------------------------------------------------------------+ + ),transition_table) + + // create a state machine "on the fly" + BOOST_MSM_EUML_DECLARE_STATE_MACHINE(( transition_table, //STT + init_ << State1 // Init State + ), + my_machine_) //fsm name + + // Pick a back-end + typedef msm::back::state_machine<my_machine_> my_machine; + + // + // Testing utilities. + // + static char const* const state_names[] = { "State1", "State2", "State3", "State4" }; + void pstate(my_machine const& p) + { + std::cout << " -> " << state_names[p.current_state()[0]] << std::endl; + } + + void test() + { + my_machine p; + + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + // in this case it will also immediately trigger all anonymous transitions + p.start(); + // this event will bring us back to the initial state and thus, a new "loop" will be started + p.process_event(event1); + } +} + +int main() +{ + test(); + return 0; +} diff --git a/libs/msm/doc/PDF/examples/AnonymousTutorialWithFunctors.cpp b/libs/msm/doc/PDF/examples/AnonymousTutorialWithFunctors.cpp new file mode 100644 index 0000000000..4bcd7ad37c --- /dev/null +++ b/libs/msm/doc/PDF/examples/AnonymousTutorialWithFunctors.cpp @@ -0,0 +1,158 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> +// functors +#include <boost/msm/front/functor_row.hpp> +#include <boost/msm/front/euml/common.hpp> + +namespace msm = boost::msm; +namespace mpl = boost::mpl; +using namespace boost::msm::front; + +namespace +{ + // events + struct event1 {}; + + + // front-end: define the FSM structure + struct my_machine_ : public msm::front::state_machine_def<my_machine_> + { + // The list of FSM states + struct State1 : public msm::front::state<> + { + // every (optional) entry/exit methods get the event passed. + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: State1" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: State1" << std::endl;} + }; + struct State2 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) {std::cout << "entering: State2" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: State2" << std::endl;} + }; + + struct State3 : public msm::front::state<> + { + // when stopped, the CD is loaded + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) {std::cout << "entering: State3" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: State3" << std::endl;} + }; + + struct State4 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: State4" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: State4" << std::endl;} + }; + + // the initial state of the player SM. Must be defined + typedef State1 initial_state; + + // transition actions + struct State2ToState3 + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + std::cout << "my_machine::State2ToState3" << std::endl; + } + }; + struct State3ToState4 + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + std::cout << "my_machine::State3ToState4" << std::endl; + } + }; + // guard conditions + struct always_true + { + template <class EVT,class FSM,class SourceState,class TargetState> + bool operator()(EVT const& evt,FSM& fsm,SourceState& src,TargetState& tgt) + { + std::cout << "always_true" << std::endl; + return true; + } + }; + struct always_false + { + template <class EVT,class FSM,class SourceState,class TargetState> + bool operator()(EVT const& evt,FSM& fsm,SourceState& src,TargetState& tgt) + { + std::cout << "always_false" << std::endl; + return true; + } + }; + + typedef my_machine_ p; // makes transition table cleaner + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + Row < State1 , none , State2 >, + Row < State2 , none , State3 , State2ToState3 >, + Row < State3 , none , State4 , none , always_false >, + // +---------+-------------+---------+---------------------+----------------------+ + Row < State3 , none , State4 , State3ToState4 , always_true >, + Row < State4 , event1 , State1 > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + // Pick a back-end + typedef msm::back::state_machine<my_machine_> my_machine; + + // + // Testing utilities. + // + static char const* const state_names[] = { "State1", "State2", "State3", "State4" }; + void pstate(my_machine const& p) + { + std::cout << " -> " << state_names[p.current_state()[0]] << std::endl; + } + + void test() + { + my_machine p; + + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + // in this case it will also immediately trigger all anonymous transitions + p.start(); + // this event will bring us back to the initial state and thus, a new "loop" will be started + p.process_event(event1()); + + } +} + +int main() +{ + test(); + return 0; +} diff --git a/libs/msm/doc/PDF/examples/BoostCon09Full.cpp b/libs/msm/doc/PDF/examples/BoostCon09Full.cpp new file mode 100644 index 0000000000..f4a2d7b047 --- /dev/null +++ b/libs/msm/doc/PDF/examples/BoostCon09Full.cpp @@ -0,0 +1,370 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <vector> +#include <iostream> +#include <boost/mpl/vector/vector50.hpp> +#include <boost/msm/back/state_machine.hpp> +#include <boost/msm/front/state_machine_def.hpp> +#include <boost/msm/back/tools.hpp> + +namespace msm = boost::msm; +namespace mpl = boost::mpl; + +namespace // Concrete FSM implementation +{ + // events + struct play {}; + struct end_pause {}; + struct stop {}; + struct pause {}; + struct open_close {}; + struct NextSong {}; + // event which every other event can convert to + struct AllSongsPlayed + { + template <class Event> + AllSongsPlayed(Event const&){} + }; + struct PreviousSong {}; + struct error_found {}; + struct end_error {}; + + // Flags. Allow information about a property of the current state + struct CDLoaded {}; + struct FirstSongPlaying {}; + + // A "complicated" event type that carries some data. + enum DiskTypeEnum + { + DISK_CD=0, + DISK_DVD=1 + }; + struct cd_detected + { + cd_detected(DiskTypeEnum diskType): disc_type(diskType) {} + DiskTypeEnum disc_type; + }; + + // Concrete FSM implementation + struct player_ : public msm::front::state_machine_def<player_> + { + // The list of FSM states + struct Empty : public msm::front::state<> + { + typedef mpl::vector<play> deferred_events; + // every (optional) entry/exit methods get the event packed as boost::any. Not useful very often. + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Empty" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Empty" << std::endl;} + }; + struct Open : public msm::front::state<> + { + typedef mpl::vector1<CDLoaded> flag_list; + typedef mpl::vector<play> deferred_events; + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Open" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Open" << std::endl;} + }; + // a state needing a pointer to the containing state machine + // and using for this the non-default policy + // if policy used, set_sm_ptr is needed + struct Stopped : public msm::front::state<default_base_state,msm::front::sm_ptr> + { + // when stopped, the CD is loaded + typedef mpl::vector1<CDLoaded> flag_list; + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Stopped" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Stopped" << std::endl;} + void set_sm_ptr(player_* pl){m_player=pl;} + player_* m_player; + }; + // the player state machine contains a state which is himself a state machine + // it demonstrates Shallow History: if the state gets activated with end_pause + // then it will remember the last active state and reactivate it + // also possible: AlwaysHistory, the last active state will always be reactivated + // or NoHistory, always restart from the initial state + struct Playing_ : public msm::front::state_machine_def<Playing_> + { + // when playing, the CD is loaded and we are in either pause or playing (duh) + typedef mpl::vector<CDLoaded> flag_list; + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Playing" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Playing" << std::endl;} + + // The list of FSM states + struct Song1 : public msm::front::state<> + { + typedef mpl::vector1<FirstSongPlaying> flag_list; + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: First song" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: First Song" << std::endl;} + }; + struct Song2 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: Second song" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: Second Song" << std::endl;} + }; + struct Song3 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: Third song" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: Third Song" << std::endl;} + }; + struct CDFinished : public msm::front::exit_pseudo_state<AllSongsPlayed> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Playing::CDFinished" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Playing::CDFinished" << std::endl;} + }; + // the initial state. Must be defined + typedef Song1 initial_state; + // transition actions + void start_next_song(NextSong const&) { std::cout << "Playing::start_next_song\n"; } + void start_prev_song(PreviousSong const&) { std::cout << "Playing::start_prev_song\n"; } + void all_songs_played(NextSong const&) { std::cout << "Playing::all_songs_played\n"; } + // guard conditions + + typedef Playing_ pl; // makes transition table cleaner + // Transition table for Playing + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+---------------+------------+---------------------+----------------------+ + a_row < Song1 , NextSong , Song2 , &pl::start_next_song >, + a_row < Song2 , PreviousSong, Song1 , &pl::start_prev_song >, + a_row < Song2 , NextSong , Song3 , &pl::start_next_song >, + a_row < Song3 , PreviousSong, Song2 , &pl::start_prev_song >, + a_row < Song3 , NextSong , CDFinished , &pl::all_songs_played > + // +---------+---------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + typedef msm::back::state_machine<Playing_,msm::back::ShallowHistory<mpl::vector<end_pause> > > Playing; + + // the player state machine contains a state which is himself a state machine (2 of them, Playing and Paused) + struct Paused : public msm::front::state<> + { + typedef mpl::vector<CDLoaded> flag_list; + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Paused" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Paused" << std::endl;} + }; + + struct AllOk : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: AllOk" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: AllOk" << std::endl;} + }; + struct ErrorMode : //public msm::front::terminate_state<> + public msm::front::interrupt_state<end_error> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: ErrorMode" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: ErrorMode" << std::endl;} + }; + + // the initial state of the player SM. Must be defined + typedef mpl::vector<Empty,AllOk> initial_state; + + // transition actions + void start_playback(play const&) { std::cout << "player::start_playback\n"; } + void open_drawer(open_close const&) { std::cout << "player::open_drawer\n"; } + void close_drawer(open_close const&) { std::cout << "player::close_drawer\n"; } + void store_cd_info(cd_detected const&) + { + std::cout << "player::store_cd_info\n"; + // generate another event to test the queue + //cd.m_player.process_event(play()); + } + void stop_playback(stop const&) { std::cout << "player::stop_playback\n"; } + void end_playback (AllSongsPlayed const&) { std::cout << "player::end_playback\n"; } + void pause_playback(pause const&) { std::cout << "player::pause_playback\n"; } + void resume_playback(end_pause const&) { std::cout << "player::resume_playback\n"; } + void stop_and_open(open_close const&) { std::cout << "player::stop_and_open\n"; } + void stopped_again(stop const&){std::cout << "player::stopped_again\n";} + void report_error(error_found const&) {std::cout << "player::report_error\n";} + void report_end_error(end_error const&) {std::cout << "player::report_end_error\n";} + // guard conditions + bool good_disk_format(cd_detected const& evt) + { + // to test a guard condition, let's say we understand only CDs, not DVD + if (evt.disc_type != DISK_CD) + { + std::cout << "wrong disk, sorry" << std::endl; + return false; + } + return true; + } + + typedef player_ p; // makes transition table cleaner + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +-------------+---------------+---------+---------------------+----------------------+ + a_row < Stopped , play , Playing , &p::start_playback >, + a_row < Stopped , open_close , Open , &p::open_drawer >, + a_row < Stopped , stop , Stopped , &p::stopped_again >, + // +-------------+---------------+---------+---------------------+----------------------+ + a_row < Open , open_close , Empty , &p::close_drawer >, + // +-------------+---------------+---------+---------------------+----------------------+ + a_row < Empty , open_close , Open , &p::open_drawer >, + row < Empty , cd_detected , Stopped , &p::store_cd_info ,&p::good_disk_format >, + // +-------------+---------------+---------+---------------------+----------------------+ + a_row < Playing , stop , Stopped , &p::stop_playback >, + a_row < Playing , pause , Paused , &p::pause_playback >, + a_row < Playing , open_close , Open , &p::stop_and_open >, + a_row < Playing::exit_pt< + Playing_::CDFinished> , AllSongsPlayed, Stopped , &p::end_playback >, + // +-------------+---------------+---------+---------------------+----------------------+ + a_row < Paused , end_pause , Playing , &p::resume_playback >, + a_row < Paused , stop , Stopped , &p::stop_playback >, + a_row < Paused , open_close , Open , &p::stop_and_open >, + // +-------------+---------------+---------+---------------------+----------------------+ + a_row < AllOk , error_found ,ErrorMode, &p::report_error >, + a_row < ErrorMode ,end_error ,AllOk , &p::report_end_error > + // +-------------+---------------+---------+---------------------+----------------------+ + > {}; + + + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + typedef msm::back::state_machine<player_> player; + + // + // Testing utilities. + // + + void pstate(player const& p) + { + typedef player::stt Stt; + typedef msm::back::generate_state_set<Stt>::type all_states; + static char const* state_names[mpl::size<all_states>::value]; + // fill the names of the states defined in the state machine + mpl::for_each<all_states,boost::msm::wrap<mpl::placeholders::_1> > + (msm::back::fill_state_names<Stt>(state_names)); + + for (unsigned int i=0;i<player::nr_regions::value;++i) + { + std::cout << " -> " << state_names[p.current_state()[i]] << std::endl; + } + } + + void test() + { + player p; + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + p.start(); + std::cout << "CDLoaded active:" << std::boolalpha << p.is_flag_active<CDLoaded>() << std::endl; //=> false (no CD yet) + + // test deferred event + // deferred in Empty and Open, will be handled only after event cd_detected + std::cout << "play is not handled in the current state but is marked as delayed" << std::endl; + p.process_event(play()); pstate(p); + std::cout << "cd_detected will cause play to be handled also" << std::endl; + // will be rejected, wrong disk type + p.process_event(cd_detected(DISK_DVD)); pstate(p); + // will be accepted, wrong disk type + p.process_event(cd_detected(DISK_CD)); pstate(p); + + std::cout << "FirstSong active:" << std::boolalpha << p.is_flag_active<FirstSongPlaying>() << std::endl; //=> true + p.process_event(NextSong());pstate(p); + // We are now in second song, Flag inactive + std::cout << "FirstSong active:" << std::boolalpha << p.is_flag_active<FirstSongPlaying>() << std::endl;//=> false + p.process_event(NextSong());pstate(p); + // 2nd song active + p.process_event(PreviousSong());pstate(p); + // Pause + p.process_event(pause()); pstate(p); + // go back to Playing + // but end_pause is an event activating the History + // => keep the last active State (SecondSong) + p.process_event(end_pause()); pstate(p); + // force an exit by listening all the songs + p.process_event(NextSong()); + p.process_event(NextSong());pstate(p); + + std::cout << "CDLoaded active:" << std::boolalpha << p.is_flag_active<CDLoaded>() << std::endl;//=> true + std::cout << "FirstSong active:" << std::boolalpha << p.is_flag_active<FirstSongPlaying>() << std::endl;//=> false + + // go back to Playing + // but play is not leading to Shallow History => do not remember the last active State (SecondSong) + // and activate again FirstSong and LightOn + p.process_event(play()); pstate(p); + p.process_event(error_found()); pstate(p); + + // try generating more events + std::cout << "Trying to generate another event" << std::endl; // will not work, fsm is terminated or interrupted + p.process_event(NextSong());pstate(p); + + std::cout << "Trying to end the error" << std::endl; // will work only if ErrorMode is interrupt state + p.process_event(end_error());pstate(p); + std::cout << "Trying to generate another event" << std::endl; // will work only if ErrorMode is interrupt state + p.process_event(NextSong());pstate(p); + + // the states and events of the higher level FSM (player) + typedef player::stt Stt; + typedef msm::back::generate_state_set<Stt>::type simple_states; + + std::cout << "the state list:" << std::endl; + mpl::for_each<simple_states,boost::msm::wrap<mpl::placeholders::_1> >(msm::back::display_type ()); + + std::cout << "the event list:" << std::endl; + typedef msm::back::generate_event_set<Stt>::type event_list; + mpl::for_each<event_list,boost::msm::wrap<mpl::placeholders::_1> >(msm::back::display_type ()); + std::cout << std::endl; + + // the states and events recursively searched + typedef msm::back::recursive_get_transition_table<player>::type recursive_stt; + + std::cout << "the state list (including sub-SMs):" << std::endl; + + typedef msm::back::generate_state_set<recursive_stt>::type all_states; + mpl::for_each<all_states,boost::msm::wrap<mpl::placeholders::_1> >(msm::back::display_type ()); + + std::cout << "the event list (including sub-SMs):" << std::endl; + typedef msm::back::generate_event_set<recursive_stt>::type all_events; + mpl::for_each<all_events,boost::msm::wrap<mpl::placeholders::_1> >(msm::back::display_type ()); + + } +} + +int main() +{ + test(); + return 0; +} + + diff --git a/libs/msm/doc/PDF/examples/CompilerStressTestEuml.cpp b/libs/msm/doc/PDF/examples/CompilerStressTestEuml.cpp new file mode 100644 index 0000000000..7bde7f5487 --- /dev/null +++ b/libs/msm/doc/PDF/examples/CompilerStressTestEuml.cpp @@ -0,0 +1,446 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <vector> +#include <list> +#include <set> +#include <map> +#include <iostream> + +// we need more than the default 10 states +#define FUSION_MAX_VECTOR_SIZE 15 + +#include <boost/msm/back/state_machine.hpp> +#include <boost/msm/front/euml/euml.hpp> +#include <boost/msm/front/euml/stl.hpp> + +using namespace std; +using namespace boost::msm::front::euml; +namespace msm = boost::msm; + +// how long the timer will ring when countdown elapsed. +#define RINGING_TIME 5 + +namespace // Concrete FSM implementation +{ + // flag + BOOST_MSM_EUML_FLAG(SomeFlag) + + // declares attributes with type and name. Can be used anywhere after + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(std::string,m_song) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(int,m_song_id) + + // declare that a type inheriting from OneSongDef will get these 2 attributes + BOOST_MSM_EUML_ATTRIBUTES((attributes_ << m_song << m_song_id ), OneSongDef) + // events + // this event is done "manually", not using any predefined macro + struct OneSong_impl : euml_event<OneSong_impl>,OneSongDef + { + OneSong_impl(){} + OneSong_impl(const string& asong) + { + get_attribute(m_song)=asong; + get_attribute(m_song_id)=1; + } + OneSong_impl(const OneSong_impl& asong) + { + get_attribute(m_song)=asong.get_attribute(m_song); + get_attribute(m_song_id)=1; + } + const string& get_data() const {return get_attribute(m_song);} + }; + // declare an instance for use in the transition table + OneSong_impl const OneSong; + + struct SongComparator : euml_action<SongComparator> + { + bool operator()(const OneSong_impl& lhs,const OneSong_impl& rhs)const + { + return lhs.get_data() == rhs.get_data(); + } + }; + struct SongLessComparator : euml_action<SongLessComparator> + { + bool operator()(const OneSong_impl& lhs,const OneSong_impl& rhs)const + { + return lhs.get_data() < rhs.get_data(); + } + }; + struct Comparator + { + template <class T> + bool operator()(const T& lhs,const T& rhs)const + { + return lhs < rhs; + } + }; + struct RemoveDummy + { + bool operator()(const OneSong_impl& lhs)const + { + return (lhs.get_attribute(m_song).compare(std::string("She-Dummy. Remove this one"))==0 ); + } + }; + template <int val> + struct LookFor + { + template <class T> + bool operator()(const T& lhs)const + { + return lhs == val; + } + }; + template <int val> + struct LessThan + { + template <class T> + bool operator()(const T& lhs)const + { + return lhs < val; + } + }; + BOOST_MSM_EUML_ACTION(SongDeleter) + { + bool operator()(const OneSong_impl& lhs)const + { + return lhs.get_data() == "Twist and Shout"; + } + }; + struct Generator + { + int operator()()const + { + return 1; + } + }; + struct Print + { + template <class T> + void operator()(const T& lhs)const + { + std::cout << "Song:" << lhs.get_data() << endl; + } + }; + BOOST_MSM_EUML_ATTRIBUTES((attributes_ << m_song ), NotFoundDef) + // declare an event instance called NotFound with the defined attributes + // these attributes can then be referenced anywhere (stt, state behaviors) + BOOST_MSM_EUML_EVENT_WITH_ATTRIBUTES(NotFound,NotFoundDef) + + BOOST_MSM_EUML_ATTRIBUTES((attributes_ << m_song ), FoundDef) + struct Found_impl : euml_event<Found_impl>,FoundDef + { + Found_impl(){} + Found_impl (const string& data) + { + get_attribute(m_song)=data; + } + int foo()const {std::cout << "foo()" << std::endl; return 0;} + int foo(int i)const {std::cout << "foo(int):" << i << std::endl; return 1;} + int foo(int i,int j)const {std::cout << "foo(int,int):" << i <<"," << j << std::endl; return 2;} + + }; + Found_impl const Found; + // some functions to call + // this macro creates a functor and an eUML function wrapper. Now, foo_ can be used anywhere + BOOST_MSM_EUML_METHOD(FoundFoo_ , foo , foo_ , int , int ) + + template <class T> + int do_print(T& t ) {std::cout << "print(T):" << typeid(T).name() << std::endl;return 1;} + BOOST_MSM_EUML_FUNCTION(PrintState_ , do_print , print_ , int , int ) + + BOOST_MSM_EUML_EVENT(Done) + + // Concrete FSM implementation + struct some_base + { + int foobar()const {std::cout << "foobar()" << std::endl; return 0;} + int foobar(int i)const {std::cout << "foobar(int):" << i << std::endl; return 1;} + int foobar(int i,int j)const {std::cout << "foobar(int,int):" << i <<"," << j << std::endl; return 2;} + }; + // some functions to call + BOOST_MSM_EUML_METHOD(FooBar_ , foobar , foobar_ , int , int ) + + // fsm attributes + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(vector<OneSong_impl>,m_src_container) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(list<OneSong_impl>,m_tgt_container) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(list<int>,m_var2) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(list<int>,m_var3) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(set<int>,m_var4) + typedef std::map<int,int> int_int_map; + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(int_int_map,m_var5) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(std::string,m_var6) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(std::string,m_var7) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(vector<int>,m_var8) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(vector<int>,m_var9) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(int,m_var10) + + // The list of FSM states + BOOST_MSM_EUML_STATE(( ( insert_(fsm_(m_tgt_container),end_(fsm_(m_tgt_container)), + append_(event_(m_song),fsm_(m_var7)) ),//foo_(event_,Int_<0>()) , + //foo_(event_,Int_<0>(),Int_<1>()),print_(state_), + process_(Done/*,fsm_*/),if_then_(true_,true_) ), + no_action + ),Insert) + + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(std::string,m_letters) + BOOST_MSM_EUML_STATE(( if_then_else_( (string_find_(event_(m_song),state_(m_letters),Size_t_<0>()) != Npos_<string>()&& + string_find_(event_(m_song),Char_<'S'>(),Size_t_<0>()) != Npos_<string>()&& + string_find_first_of_(event_(m_song),Char_<'S'>()) == Size_t_<0>() && + string_compare_(event_(m_song),Int_<0>(),size_(event_(m_song)),event_(m_song)) == Int_<0>() + //&& is_flag_(SomeFlag(),fsm_()) + //&& ( event_(m_song_id) == Int_<1>()) + //&& string_find_(event_(m_song),String_<mpl::string<'Sh','e'> >()) + // != Npos_<string>() + + ), + process2_(Found, + //string_insert_(event_(m_song),Size_t_<0>(),fsm_(m_var6)) ), + string_replace_( + string_assign_( + string_erase_( + string_insert_( + substr_(event_(m_song),Size_t_<1>()), + Size_t_<0>(), + Size_t_<1>(), + Char_<'S'>()), + Size_t_<0>(), + Size_t_<1>() ), + event_(m_song) ), + Size_t_<0>(), + Size_t_<1>(), + c_str_(fsm_(m_var6)) + /*Size_t_<1>(), + Char_<'s'>()*/ ) ), + process2_(NotFound,event_(m_song),fsm_) ) , + no_action, + attributes_ << m_letters, + configure_<< SomeFlag ),StringFind) + + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(vector<OneSong_impl>::iterator,m_src_it) + BOOST_MSM_EUML_STATE(( if_then_( (state_(m_src_it) != end_(fsm_(m_src_container)) && + //associative_find_(fsm_(m_var4),Int_<9>()) != end_(fsm_(m_var4))&& + //associative_count_(fsm_(m_var4),Int_<9>()) == Size_t_<1>() && + //*associative_upper_bound_(fsm_(m_var4),Int_<8>()) == Int_<9>()&& + //*associative_lower_bound_(fsm_(m_var4),Int_<9>()) == Int_<9>() && + //second_(associative_equal_range_(fsm_(m_var4),Int_<8>())) == associative_upper_bound_(fsm_(m_var4),Int_<8>()) && + //first_(associative_equal_range_(fsm_(m_var4),Int_<8>())) == associative_lower_bound_(fsm_(m_var4),Int_<8>())&& + //second_(*associative_lower_bound_(fsm_(m_var5),Int_<0>())) == Int_<0>() && //map => pair as return + //find_if_(begin_(fsm_(m_var4)),end_(fsm_(m_var4)),Predicate_<LookFor<8> >()) != end_(fsm_(m_var4))&& + //*lower_bound_(begin_(fsm_(m_var4)),end_(fsm_(m_var4)),Int_<9>(),Predicate_<std::less<int> >()) == Int_<9>()&& + //*upper_bound_(begin_(fsm_(m_var4)),end_(fsm_(m_var4)),Int_<8>(),Predicate_<std::less<int> >()) == Int_<9>() && + //second_(equal_range_(begin_(fsm_(m_var4)),end_(fsm_(m_var4)),Int_<8>())) + // == upper_bound_(begin_(fsm_(m_var4)),end_(fsm_(m_var4)),Int_<8>()) && + //first_(equal_range_(begin_(fsm_(m_var4)),end_(fsm_(m_var4)),Int_<9>(),Predicate_<std::less<int> >())) + // == lower_bound_(begin_(fsm_(m_var4)),end_(fsm_(m_var4)),Int_<9>(),Predicate_<std::less<int> >())&& + //binary_search_(begin_(fsm_(m_var4)),end_(fsm_(m_var4)),Int_<9>(),Predicate_<std::less<int> >())&& + //binary_search_(begin_(fsm_(m_var4)),end_(fsm_(m_var4)),Int_<9>())&& + //count_(begin_(fsm_(m_var4)),end_(fsm_(m_var4)),Int_<9>()) == Int_<1>()&& + //count_if_(begin_(fsm_(m_var4)),end_(fsm_(m_var4)),Predicate_<LookFor<9> >()) == Int_<1>()&& + //distance_(begin_(fsm_(m_var4)),end_(fsm_(m_var4))) == Int_<2>()&& + //*min_element_(begin_(fsm_(m_var4)),end_(fsm_(m_var4)),Predicate_<std::less<int> >()) == Int_<8>()&& + //*max_element_(begin_(fsm_(m_var4)),end_(fsm_(m_var4)),Predicate_<std::less<int> >()) == Int_<9>()&& + //adjacent_find_(begin_(fsm_(m_var4)),end_(fsm_(m_var4))) == end_(fsm_(m_var4))&& + //*find_end_(begin_(fsm_(m_var8)),end_(fsm_(m_var8)),begin_(fsm_(m_var9)),end_(fsm_(m_var9))) + // == Int_<1>()&& + //*find_first_of_(begin_(fsm_(m_var8)),end_(fsm_(m_var8)),begin_(fsm_(m_var9)),end_(fsm_(m_var9))) + // == Int_<1>()&& + //equal_(begin_(fsm_(m_var9)),end_(fsm_(m_var9)),begin_(fsm_(m_var8)))&& + //*search_(begin_(fsm_(m_var8)),end_(fsm_(m_var8)),begin_(fsm_(m_var9)),end_(fsm_(m_var9))) + // == Int_<1>()&& + //includes_(begin_(fsm_(m_var8)),end_(fsm_(m_var8)),begin_(fsm_(m_var9)),end_(fsm_(m_var9)))&& + //!lexicographical_compare_(begin_(fsm_(m_var8)),end_(fsm_(m_var8)), + // begin_(fsm_(m_var9)),end_(fsm_(m_var9)))&& + //first_(mismatch_(begin_(fsm_(m_var9)),end_(fsm_(m_var9)),begin_(fsm_(m_var8)))) + // == end_(fsm_(m_var9)) && + accumulate_(begin_(fsm_(m_var9)),end_(fsm_(m_var9)),Int_<1>(), + Predicate_<std::plus<int> >()) == Int_<1>() + ), + (process2_(OneSong,*(state_(m_src_it)++))/*,foobar_(fsm_,Int_<0>())*/ ) ), + no_action, + attributes_ << m_src_it + , configure_<< SomeFlag ),Foreach) + + + // replaces the old transition table + BOOST_MSM_EUML_TRANSITION_TABLE(( + StringFind == Foreach + OneSong[if_then_else_(true_,true_,true_)], + Insert == StringFind + Found / (if_then_(true_,no_action)), + Foreach == StringFind + NotFound , + Foreach == Insert + Done + // +------------------------------------------------------------------------------+ + ),transition_table ) + + BOOST_MSM_EUML_ACTION(Log_No_Transition) + { + template <class FSM,class Event> + void operator()(Event const& e,FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + // create a state machine "on the fly" + BOOST_MSM_EUML_DECLARE_STATE_MACHINE(( transition_table, //STT + init_ << Foreach, // Init + //insert_(state_(m_var4),begin_(state_(m_var2)),end_(state_(m_var2))), + (insert_(state_(m_var4),Int_<5>()),insert_(state_(m_var4),Int_<6>()),insert_(state_(m_var4),Int_<7>()), + insert_(state_(m_var4),Int_<8>()),insert_(state_(m_var4),Int_<9>()), + associative_erase_(state_(m_var4),Int_<6>()),associative_erase_(state_(m_var4),begin_(state_(m_var4))), + associative_erase_(state_(m_var4),begin_(state_(m_var4)),++begin_(state_(m_var4))), + insert_(state_(m_var2),begin_(state_(m_var2)),begin_(state_(m_var3)),end_(state_(m_var3))), + state_(m_var5)[Int_<0>()]=Int_<0>(),state_(m_var5)[Int_<1>()]=Int_<1>() + ,attribute_(substate_(Foreach,fsm_),m_src_it) + = begin_(fsm_(m_src_container)) + //,fill_(begin_(state_(m_var9)),end_(state_(m_var9)),Int_<0>()) + //,fill_n_(begin_(state_(m_var9)),Size_t_<2>(),Int_<0>()) + //,transform_(begin_(state_(m_var4)),end_(state_(m_var4)),begin_(state_(m_var2)),begin_(state_(m_var4)), + // Predicate_<std::plus<int> >()) + //,process_(Done,fsm_(),fsm_) + //,process_(Done,fsm_) + //,fsm_ + //,foobar_(state_,Int_<0>(),Int_<1>()) + //,nth_element_(begin_(state_(m_var9)),++begin_(state_(m_var9)),end_(state_(m_var9)),Predicate_<std::less<int> >()) + //,partial_sort_(begin_(state_(m_var9)),end_(state_(m_var9)),end_(state_(m_var9)),Predicate_<std::less<int> >()) + //,partial_sort_copy_(begin_(state_(m_var9)),end_(state_(m_var9)),begin_(state_(m_var9)),end_(state_(m_var9)),Predicate_<std::less<int> >()) + //,list_sort_(state_(m_var2)) + //,sort_(begin_(state_(m_var9)),end_(state_(m_var9)),Predicate_<std::less<int> >()) + //,inner_product_(begin_(state_(m_var9)),end_(state_(m_var9)),begin_(state_(m_var9)),Int_<1>()) + //,replace_copy_(begin_(state_(m_var4)),end_(state_(m_var4)),begin_(state_(m_var4)),Int_<8>(),Int_<7>()) + //,replace_copy_if_(begin_(state_(m_var4)),end_(state_(m_var4)),begin_(state_(m_var4)),Predicate_<LookFor<9> >(),Int_<8>()) + //,replace_(begin_(state_(m_var4)),end_(state_(m_var4)),Int_<8>(),Int_<7>()) + //,replace_if_(begin_(state_(m_var4)),end_(state_(m_var4)),Predicate_<LookFor<9> >(),Int_<8>()) + //,adjacent_difference_(begin_(state_(m_var9)),end_(state_(m_var9)),begin_(state_(m_var9))) + //,partial_sum_(begin_(state_(m_var9)),end_(state_(m_var9)),begin_(state_(m_var9))) + //,inner_product_(begin_(state_(m_var9)),end_(state_(m_var9)),begin_(state_(m_var9)),Int_<1>()) + //,next_permutation_(begin_(state_(m_var9)),end_(state_(m_var9)),Predicate_<std::less<int> >()) + //,prev_permutation_(begin_(state_(m_var9)),end_(state_(m_var9)),Predicate_<std::less<int> >()) + //,set_union_(begin_(state_(m_var9)),end_(state_(m_var9)),begin_(state_(m_var9)),end_(state_(m_var9)),begin_(state_(m_var9))) + //,inplace_merge_(begin_(state_(m_var9)),end_(state_(m_var9)),end_(state_(m_var9)),Predicate_<std::less<int> >()) + //,merge_(begin_(state_(m_var9)),end_(state_(m_var9)),begin_(state_(m_var9)),end_(state_(m_var9)) + // ,begin_(state_(m_var9)),Predicate_<std::less<int> >()) + //,stable_sort_(begin_(state_(m_var9)),end_(state_(m_var9)),Predicate_<std::less<int> >()) + //,partition_(begin_(state_(m_var2)),end_(state_(m_var2)),Predicate_<LessThan<3> >()) + //,stable_partition_(begin_(state_(m_var2)),end_(state_(m_var2)),Predicate_<LessThan<3> >()) + //,rotate_copy_(begin_(state_(m_var2)),++begin_(state_(m_var2)),end_(state_(m_var2)),begin_(state_(m_var2))) + //,rotate_(begin_(state_(m_var2)),++begin_(state_(m_var2)),end_(state_(m_var2))) + //,unique_(begin_(state_(m_var2)),end_(state_(m_var2))) + //,unique_copy_(begin_(state_(m_var2)),end_(state_(m_var2)),begin_(state_(m_var2))) + //,random_shuffle_(begin_(state_(m_var9)),end_(state_(m_var9))) + //,generate_(begin_(state_(m_var9)),end_(state_(m_var9)),Predicate_<Generator>()) + //,generate_n_(begin_(state_(m_var9)),Int_<2>(),Predicate_<Generator>()) + //,reverse_copy_(begin_(state_(m_var2)),end_(state_(m_var2)),begin_(state_(m_var2))) + //erase_(state_(m_src_container), + // remove_if_(begin_(state_(m_src_container)),end_(state_(m_src_container)), + // Predicate_<RemoveDummy>()), + // end_(state_(m_src_container))), + //list_remove_(state_(m_var2),Int_<3>()), + //remove_copy_if_(begin_(state_(m_var9)),end_(state_(m_var9)),back_inserter_(state_(m_var2)), + // Predicate_<LookFor<2> >() ) + //for_each_(begin_(state_(m_src_container)),end_(state_m_src_container()), + // Predicate_<Print>() ), + //copy_(begin_(state_(m_var9)),end_(state_(m_var9)),inserter_(state_(m_var2),end_(state_(m_var2)))), + //reverse_(begin_(state_(m_var2)),end_(state_(m_var2))) + ), + //no_action, // Entry + //splice_(state_(m_var2),begin_(state_(m_var2)),state_(m_var3),begin_(state_(m_var3)),end_(state_(m_var3))), + //(list_remove_(state_(m_var2),Int_<3>()),list_merge_(state_(m_var2),state_(m_var3),Comparator())),//no_action, // Entry + no_action, // Exit + attributes_ << m_src_container // song list + << m_tgt_container // result + << m_var2 + << m_var3 + << m_var4 + << m_var5 + << m_var6 + << m_var7 + << m_var8 + << m_var9 + << m_var10, + configure_<< no_configure_, + Log_No_Transition + ),iPodSearch_helper) + + struct iPodSearch_ : public iPodSearch_helper, public some_base + { + }; + + + // choice of back-end + typedef msm::back::state_machine<iPodSearch_> iPodSearch; + + void test() + { + iPodSearch search; + // fill our song list + //search.get_attribute<m_src_container>().push_back(OneSong("She-Dummy. Remove this one")); + search.get_attribute(m_src_container).push_back(OneSong_impl("Let it be")); + search.get_attribute(m_src_container).push_back(OneSong_impl("Yellow submarine")); + search.get_attribute(m_src_container).push_back(OneSong_impl("Twist and Shout")); + search.get_attribute(m_src_container).push_back(OneSong_impl("She Loves You")); + + search.get_attribute(m_var2).push_back(1); + search.get_attribute(m_var2).push_back(3); + search.get_attribute(m_var2).push_back(4); + + search.get_attribute(m_var3).push_back(2); + search.get_attribute(m_var3).push_back(4); + + search.get_attribute(m_var6) = "S"; + search.get_attribute(m_var7) = "- Some text"; + + search.get_attribute(m_var8).push_back(1); + search.get_attribute(m_var8).push_back(2); + search.get_attribute(m_var8).push_back(3); + search.get_attribute(m_var8).push_back(4); + + search.get_attribute(m_var9).push_back(1); + search.get_attribute(m_var9).push_back(2); + + + // look for "She Loves You" using the first letters + // BOOST_MSM_EUML_STATE_NAME returns the name of the event type of which StringFind is an instance + search.get_state<BOOST_MSM_EUML_STATE_NAME(StringFind)&>().get_attribute(m_letters)="Sh"; + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + search.start(); + // display all the songs + for (list<OneSong_impl>::const_iterator it = search.get_attribute(m_tgt_container).begin(); + it != search.get_attribute(m_tgt_container).end();++it) + { + cout << "candidate song:" << (*it).get_data() << endl; + } + for (list<int>::const_iterator iti = search.get_attribute(m_var2).begin(); + iti != search.get_attribute(m_var2).end();++iti) + { + cout << "int in attribute m_var2:" << (*iti) << endl; + } + for (set<int>::const_iterator its = search.get_attribute(m_var4).begin(); + its != search.get_attribute(m_var4).end();++its) + { + cout << "int in attribute m_var4:" << (*its) << endl; + } + cout << "search using more letters" << endl; + // look for "She Loves You" using more letters + search.get_state<BOOST_MSM_EUML_STATE_NAME(StringFind)&>().get_attribute(m_letters)="She"; + search.get_attribute(m_tgt_container).clear(); + search.start(); + // display all the songs + for (list<OneSong_impl>::const_iterator it = search.get_attribute(m_tgt_container).begin(); + it != search.get_attribute(m_tgt_container).end();++it) + { + cout << "candidate song:" << (*it).get_data() << endl; + } + } +} + +int main() +{ + test(); + return 0; +} diff --git a/libs/msm/doc/PDF/examples/CompositeTutorial.cpp b/libs/msm/doc/PDF/examples/CompositeTutorial.cpp new file mode 100644 index 0000000000..b0ee192eec --- /dev/null +++ b/libs/msm/doc/PDF/examples/CompositeTutorial.cpp @@ -0,0 +1,244 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> + +namespace msm = boost::msm; +namespace mpl = boost::mpl; + +namespace // Concrete FSM implementation +{ + // events + struct play {}; + struct end_pause {}; + struct stop {}; + struct pause {}; + struct open_close {}; + struct NextSong {}; + struct PreviousSong {}; + + // A "complicated" event type that carries some data. + struct cd_detected + { + cd_detected(std::string name) + : name(name) + {} + + std::string name; + }; + + // front-end: define the FSM structure + struct player_ : public msm::front::state_machine_def<player_> + { + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) {std::cout << "entering: Player" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Player" << std::endl;} + + // The list of FSM states + struct Empty : public msm::front::state<> + { + // every (optional) entry/exit methods get the event passed. + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Empty" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Empty" << std::endl;} + }; + struct Open : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Open" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Open" << std::endl;} + }; + + struct Stopped : public msm::front::state<> + { + // when stopped, the CD is loaded + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Stopped" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Stopped" << std::endl;} + }; + + // the player state machine contains a state which is himself a state machine + // as you see, no need to declare it anywhere so Playing can be developed separately + // by another team in another module. For simplicity I just declare it inside player + struct Playing_ : public msm::front::state_machine_def<Playing_> + { + // when playing, the CD is loaded and we are in either pause or playing (duh) + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Playing" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Playing" << std::endl;} + + // The list of FSM states + struct Song1 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: First song" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: First Song" << std::endl;} + + }; + struct Song2 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: Second song" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: Second Song" << std::endl;} + }; + struct Song3 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: Third song" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: Third Song" << std::endl;} + }; + // the initial state. Must be defined + typedef Song1 initial_state; + // transition actions + void start_next_song(NextSong const&) { std::cout << "Playing::start_next_song\n"; } + void start_prev_song(PreviousSong const&) { std::cout << "Playing::start_prev_song\n"; } + // guard conditions + + typedef Playing_ pl; // makes transition table cleaner + // Transition table for Playing + struct transition_table : mpl::vector4< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Song1 , NextSong , Song2 , &pl::start_next_song >, + a_row < Song2 , PreviousSong, Song1 , &pl::start_prev_song >, + a_row < Song2 , NextSong , Song3 , &pl::start_next_song >, + a_row < Song3 , PreviousSong, Song2 , &pl::start_prev_song > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + // back-end + typedef msm::back::state_machine<Playing_> Playing; + + + // state not defining any entry or exit + struct Paused : public msm::front::state<> + { + }; + + // the initial state of the player SM. Must be defined + typedef Empty initial_state; + + // transition actions + void start_playback(play const&) { std::cout << "player::start_playback\n"; } + void open_drawer(open_close const&) { std::cout << "player::open_drawer\n"; } + void close_drawer(open_close const&) { std::cout << "player::close_drawer\n"; } + void store_cd_info(cd_detected const& cd) {std::cout << "player::store_cd_info\n";} + void stop_playback(stop const&) { std::cout << "player::stop_playback\n"; } + void pause_playback(pause const&) { std::cout << "player::pause_playback\n"; } + void resume_playback(end_pause const&) { std::cout << "player::resume_playback\n"; } + void stop_and_open(open_close const&) { std::cout << "player::stop_and_open\n"; } + void stopped_again(stop const&){std::cout << "player::stopped_again\n";} + // guard conditions + + typedef player_ p; // makes transition table cleaner + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Stopped , play , Playing , &p::start_playback >, + a_row < Stopped , open_close , Open , &p::open_drawer >, + a_row < Stopped , stop , Stopped , &p::stopped_again >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Open , open_close , Empty , &p::close_drawer >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Empty , open_close , Open , &p::open_drawer >, + a_row < Empty , cd_detected , Stopped , &p::store_cd_info >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Playing , stop , Stopped , &p::stop_playback >, + a_row < Playing , pause , Paused , &p::pause_playback >, + a_row < Playing , open_close , Open , &p::stop_and_open >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Paused , end_pause , Playing , &p::resume_playback >, + a_row < Paused , stop , Stopped , &p::stop_playback >, + a_row < Paused , open_close , Open , &p::stop_and_open > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + + }; + // Pick a back-end + typedef msm::back::state_machine<player_> player; + + // + // Testing utilities. + // + static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" }; + + void pstate(player const& p) + { + std::cout << " -> " << state_names[p.current_state()[0]] << std::endl; + } + + void test() + { + player p; + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + p.start(); + // go to Open, call on_exit on Empty, then action, then on_entry on Open + p.process_event(open_close()); pstate(p); + p.process_event(open_close()); pstate(p); + p.process_event(cd_detected("louie, louie")); + p.process_event(play()); + + // at this point, Play is active + // make transition happen inside it. Player has no idea about this event but it's ok. + p.process_event(NextSong());pstate(p); //2nd song active + p.process_event(NextSong());pstate(p);//3rd song active + p.process_event(PreviousSong());pstate(p);//2nd song active + + p.process_event(pause()); pstate(p); + // go back to Playing + // as you see, it starts back from the original state + p.process_event(end_pause()); pstate(p); + p.process_event(pause()); pstate(p); + p.process_event(stop()); pstate(p); + // event leading to the same state + p.process_event(stop()); pstate(p); + // stop the fsm (call on_exit's, including the submachines) + p.process_event(play()); + std::cout << "stop fsm" << std::endl; + p.stop(); + std::cout << "restart fsm" << std::endl; + p.start(); + } +} + +int main() +{ + test(); + return 0; +} diff --git a/libs/msm/doc/PDF/examples/CompositeTutorialEuml.cpp b/libs/msm/doc/PDF/examples/CompositeTutorialEuml.cpp new file mode 100644 index 0000000000..516d3dbd50 --- /dev/null +++ b/libs/msm/doc/PDF/examples/CompositeTutorialEuml.cpp @@ -0,0 +1,182 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <vector> +#include <iostream> + +#include <boost/msm/back/state_machine.hpp> +#include <boost/msm/front/euml/euml.hpp> + +using namespace std; +using namespace boost::msm::front::euml; +namespace msm = boost::msm; + +// entry/exit/action/guard logging functors +#include "logging_functors.h" + +namespace // Concrete FSM implementation +{ + // events + BOOST_MSM_EUML_EVENT(play) + BOOST_MSM_EUML_EVENT(end_pause) + BOOST_MSM_EUML_EVENT(stop) + BOOST_MSM_EUML_EVENT(pause) + BOOST_MSM_EUML_EVENT(open_close) + BOOST_MSM_EUML_EVENT(next_song) + BOOST_MSM_EUML_EVENT(previous_song) + BOOST_MSM_EUML_EVENT(region2_evt) + + // A "complicated" event type that carries some data. + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(std::string,cd_name) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(DiskTypeEnum,cd_type) + BOOST_MSM_EUML_ATTRIBUTES((attributes_ << cd_name << cd_type ), cd_detected_attributes) + BOOST_MSM_EUML_EVENT_WITH_ATTRIBUTES(cd_detected,cd_detected_attributes) + + // Concrete FSM implementation + + // The list of FSM states + // state not defining any entry or exit + BOOST_MSM_EUML_STATE((),Paused) + BOOST_MSM_EUML_STATE(( Empty_Entry,Empty_Exit ),Empty) + BOOST_MSM_EUML_STATE(( Open_Entry,Open_Exit ),Open) + BOOST_MSM_EUML_STATE(( Stopped_Entry,Stopped_Exit ),Stopped) + + + // Playing is now a state machine itself. + + // It has 5 substates + BOOST_MSM_EUML_STATE(( Song1_Entry,Song1_Exit ),Song1) + BOOST_MSM_EUML_STATE(( Song2_Entry,Song2_Exit ),Song2) + BOOST_MSM_EUML_STATE(( Song3_Entry,Song3_Exit ),Song3) + BOOST_MSM_EUML_STATE(( Region2State1_Entry,Region2State1_Exit ),Region2State1) + BOOST_MSM_EUML_STATE(( Region2State2_Entry,Region2State2_Exit ),Region2State2) + + // Playing has a transition table + BOOST_MSM_EUML_TRANSITION_TABLE(( + // +------------------------------------------------------------------------------+ + Song2 == Song1 + next_song / start_next_song, + Song1 == Song2 + previous_song / start_prev_song, + Song3 == Song2 + next_song / start_next_song, + Song2 == Song3 + previous_song / start_prev_song, + Region2State2 == Region2State1 + region2_evt + // +------------------------------------------------------------------------------+ + ),playing_transition_table ) + + BOOST_MSM_EUML_DECLARE_STATE_MACHINE( (playing_transition_table, //STT + init_ << Song1 << Region2State1 // Init State + ),Playing_) + // choice of back-end + typedef msm::back::state_machine<Playing_> Playing_type; + Playing_type const Playing; + + // guard conditions + BOOST_MSM_EUML_ACTION(good_disk_format) + { + template <class FSM,class EVT,class SourceState,class TargetState> + bool operator()(EVT const& evt,FSM&,SourceState& ,TargetState& ) + { + // to test a guard condition, let's say we understand only CDs, not DVD + if (evt.get_attribute(cd_type)!=DISK_CD) + { + std::cout << "wrong disk, sorry" << std::endl; + // just for logging, does not block any transition + return true; + } + std::cout << "good disk" << std::endl; + return true; + } + }; + // replaces the old transition table + BOOST_MSM_EUML_TRANSITION_TABLE(( + Playing == Stopped + play / start_playback , + Playing == Paused + end_pause / resume_playback, + // +------------------------------------------------------------------------------+ + Empty == Open + open_close / close_drawer, + // +------------------------------------------------------------------------------+ + Open == Empty + open_close / open_drawer, + Open == Paused + open_close / stop_and_open, + Open == Stopped + open_close / open_drawer, + Open == Playing + open_close / stop_and_open, + // +------------------------------------------------------------------------------+ + Paused == Playing + pause / pause_playback, + // +------------------------------------------------------------------------------+ + Stopped == Playing + stop / stop_playback, + Stopped == Paused + stop / stop_playback, + Stopped == Empty + cd_detected [good_disk_format &&(event_(cd_type)==Int_<DISK_CD>())] + / (store_cd_info,process_(play)), + Stopped == Stopped + stop + // +------------------------------------------------------------------------------+ + ),transition_table) + + // create a state machine "on the fly" + BOOST_MSM_EUML_DECLARE_STATE_MACHINE(( transition_table, //STT + init_ << Empty, // Init State + no_action, // Entry + no_action, // Exit + attributes_ << no_attributes_, // Attributes + configure_ << no_configure_, // configuration + Log_No_Transition // no_transition handler + ), + player_) //fsm name + + // choice of back-end + typedef msm::back::state_machine<player_> player; + + // + // Testing utilities. + // + static char const* const state_names[] = { "Stopped", "Paused", "Open", "Empty", "Playing" }; + void pstate(player const& p) + { + std::cout << " -> " << state_names[p.current_state()[0]] << std::endl; + } + + void test() + { + player p; + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + p.start(); + // note that we write open_close and not open_close(), like usual. Both are possible with eUML, but + // you now have less to type. + // go to Open, call on_exit on Empty, then action, then on_entry on Open + p.process_event(open_close); pstate(p); + p.process_event(open_close); pstate(p); + // will be rejected, wrong disk type + p.process_event( + cd_detected("louie, louie",DISK_DVD)); pstate(p); + p.process_event( + cd_detected("louie, louie",DISK_CD)); pstate(p); + // no need to call play() as the previous event does it in its action method + //p.process_event(play); + + // make transition happen inside it. Player has no idea about this event but it's ok. + p.process_event(next_song);pstate(p); //2nd song active + p.process_event(next_song);pstate(p);//3rd song active + p.process_event(previous_song);pstate(p);//2nd song active + + // at this point, Play is active + p.process_event(pause); pstate(p); + + // go back to Playing + p.process_event(end_pause); pstate(p); + p.process_event(pause); pstate(p); + p.process_event(stop); pstate(p); + + // event leading to the same state + // no action method called as none is defined in the transition table + p.process_event(stop); pstate(p); + } +} + +int main() +{ + test(); + return 0; +} diff --git a/libs/msm/doc/PDF/examples/CompositeTutorialWithEumlTable.cpp b/libs/msm/doc/PDF/examples/CompositeTutorialWithEumlTable.cpp new file mode 100644 index 0000000000..7783d2cdf6 --- /dev/null +++ b/libs/msm/doc/PDF/examples/CompositeTutorialWithEumlTable.cpp @@ -0,0 +1,213 @@ +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> +#include <boost/msm/front/euml/euml.hpp> + +namespace msm = boost::msm; +namespace mpl = boost::mpl; +using namespace std; + +// entry/exit/action/guard logging functors +#include "logging_functors.h" + +namespace +{ + // events + struct play_impl : msm::front::euml::euml_event<play_impl> {}; + struct end_pause_impl : msm::front::euml::euml_event<end_pause_impl>{}; + struct stop_impl : msm::front::euml::euml_event<stop_impl>{}; + struct pause_impl : msm::front::euml::euml_event<pause_impl>{}; + struct open_close_impl : msm::front::euml::euml_event<open_close_impl>{}; + struct cd_detected_impl : msm::front::euml::euml_event<cd_detected_impl>{}; + struct next_song_impl : msm::front::euml::euml_event<next_song_impl>{}; + struct previous_song_impl : msm::front::euml::euml_event<previous_song_impl>{}; + + // define some dummy instances for use in the transition table + // it is also possible to default-construct them instead: + // struct play {}; + // inside the table: play() + play_impl play; + end_pause_impl end_pause; + stop_impl stop; + pause_impl pause; + open_close_impl open_close; + cd_detected_impl cd_detected; + next_song_impl next_song; + previous_song_impl previous_song; + + // The list of FSM states + // they have to be declared outside of the front-end only to make VC happy :( + // note: gcc would have no problem + struct Empty_impl : public msm::front::state<> , public msm::front::euml::euml_state<Empty_impl> + { + // every (optional) entry/exit methods get the event passed. + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Empty" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Empty" << std::endl;} + }; + struct Open_impl : public msm::front::state<> , public msm::front::euml::euml_state<Open_impl> + { + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) {std::cout << "entering: Open" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Open" << std::endl;} + }; + + struct Stopped_impl : public msm::front::state<> , public msm::front::euml::euml_state<Stopped_impl> + { + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) {std::cout << "entering: Stopped" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Stopped" << std::endl;} + }; + + // Playing Submachine front-end + struct Playing_ : public msm::front::state_machine_def<Playing_> + { + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) {std::cout << "entering: Playing" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Playing" << std::endl;} + + struct Song1_ : public msm::front::state<> , public msm::front::euml::euml_state<Song1_> + { + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) {std::cout << "entering: Song1" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Song1" << std::endl;} + }; + struct Song2_ : public msm::front::state<> , public msm::front::euml::euml_state<Song2_> + { + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) {std::cout << "entering: Song2" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Song2" << std::endl;} + }; + struct Song3_ : public msm::front::state<> , public msm::front::euml::euml_state<Song3_> + { + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) {std::cout << "entering: Song3" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Song3" << std::endl;} + }; + // to make the transition table more readable + // VC seems to need them static + static Song1_ Song1; + static Song2_ Song2; + static Song3_ Song3; + typedef Song1_ initial_state; + + // Playing has a transition table + BOOST_MSM_EUML_DECLARE_TRANSITION_TABLE(( + // +------------------------------------------------------------------------------+ + Song2 == Song1 + next_song / start_next_song, + Song1 == Song2 + previous_song / start_prev_song, + Song3 == Song2 + next_song / start_next_song, + Song2 == Song3 + previous_song / start_prev_song + // +------------------------------------------------------------------------------+ + ),transition_table ) + + }; + // Playing Submachine back-end + typedef boost::msm::back::state_machine<Playing_> Playing_helper; + struct Playing_impl : public Playing_helper, + public msm::front::euml::euml_state<Playing_helper> + { + }; + + // state not defining any entry or exit + struct Paused_impl : public msm::front::state<> , public msm::front::euml::euml_state<Paused_impl> + { + }; + //to make the transition table more readable + Empty_impl const Empty; + Open_impl const Open; + Stopped_impl const Stopped; + Playing_impl const Playing; + Paused_impl const Paused; + + // front-end: define the FSM structure + struct player_ : public msm::front::state_machine_def<player_> + { + + // the initial state of the player SM. Must be defined + typedef Empty_impl initial_state; + + // Transition table for player + // replaces the old transition table + BOOST_MSM_EUML_DECLARE_TRANSITION_TABLE(( + Stopped + play / start_playback == Playing , + Stopped + open_close / open_drawer == Open , + Stopped + stop == Stopped , + // +------------------------------------------------------------------------------+ + Open + open_close / close_drawer == Empty , + // +------------------------------------------------------------------------------+ + Empty + open_close / open_drawer == Open , + Empty + cd_detected /(store_cd_info, + msm::front::euml::process_(play)) == Stopped , + // +------------------------------------------------------------------------------+ + Playing + stop / stop_playback == Stopped , + Playing + pause / pause_playback == Paused , + Playing + open_close / stop_and_open == Open , + // +------------------------------------------------------------------------------+ + Paused + end_pause / resume_playback == Playing , + Paused + stop / stop_playback == Stopped , + Paused + open_close / stop_and_open == Open + // +------------------------------------------------------------------------------+ + ),transition_table) + + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + // Pick a back-end + typedef msm::back::state_machine<player_> player; + + // + // Testing utilities. + // + static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" }; + void pstate(player const& p) + { + std::cout << " -> " << state_names[p.current_state()[0]] << std::endl; + } + + void test() + { + player p; + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + p.start(); + // go to Open, call on_exit on Empty, then action, then on_entry on Open + p.process_event(open_close); pstate(p); + p.process_event(open_close); pstate(p); + p.process_event(cd_detected); pstate(p); + + // at this point, Play is active + // make transition happen inside it. Player has no idea about this event but it's ok. + p.process_event(next_song);pstate(p); //2nd song active + p.process_event(next_song);pstate(p);//3rd song active + p.process_event(previous_song);pstate(p);//2nd song active + + p.process_event(pause); pstate(p); + // go back to Playing + p.process_event(end_pause); pstate(p); + p.process_event(pause); pstate(p); + p.process_event(stop); pstate(p); + // event leading to the same state + // no action method called as it is not present in the transition table + p.process_event(stop); pstate(p); + } +} + +int main() +{ + test(); + return 0; +} diff --git a/libs/msm/doc/PDF/examples/Constructor.cpp b/libs/msm/doc/PDF/examples/Constructor.cpp new file mode 100644 index 0000000000..8ae05412d7 --- /dev/null +++ b/libs/msm/doc/PDF/examples/Constructor.cpp @@ -0,0 +1,292 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> + +namespace msm = boost::msm; +namespace mpl = boost::mpl; + +struct SomeExternalContext +{ + SomeExternalContext(int b):bla(b){} + int bla; +}; + +namespace +{ + // events + struct play {}; + struct end_pause {}; + struct stop {}; + struct pause {}; + struct open_close {}; + struct NextSong {}; + struct PreviousSong {}; + + // A "complicated" event type that carries some data. + enum DiskTypeEnum + { + DISK_CD=0, + DISK_DVD=1 + }; + struct cd_detected + { + cd_detected(std::string name, DiskTypeEnum diskType) + : name(name), + disc_type(diskType) + {} + + std::string name; + DiskTypeEnum disc_type; + }; + + // front-end: define the FSM structure + struct player_ : public msm::front::state_machine_def<player_> + { + player_(SomeExternalContext& context,int someint) + :context_(context) + { + std::cout << "context value:" << context_.bla << " with value:" << someint << std::endl; + context.bla = 10; + } + + SomeExternalContext& context_; + + // The list of FSM states + struct Empty : public msm::front::state<> + { + int data_; + Empty():data_(0){} + Empty(int i):data_(i){} + // every (optional) entry/exit methods get the event passed. + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Empty" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Empty" << std::endl;} + }; + struct Open : public msm::front::state<> + { + int data_; + Open():data_(0){} + Open(int i):data_(i){} + + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) {std::cout << "entering: Open" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Open" << std::endl;} + }; + + // sm_ptr still supported but deprecated as functors are a much better way to do the same thing + struct Stopped : public msm::front::state<msm::front::default_base_state,msm::front::sm_ptr> + { + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) {std::cout << "entering: Stopped" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Stopped" << std::endl;} + void set_sm_ptr(player_* pl) + { + m_player=pl; + } + player_* m_player; + }; + + struct Playing_ : public msm::front::state_machine_def<Playing_> + { + // when playing, the CD is loaded and we are in either pause or playing (duh) + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Playing" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Playing" << std::endl;} + + // The list of FSM states + struct Song1 : public msm::front::state<> + { + int data_; + Song1():data_(0){} + Song1(int i):data_(i){} + + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: First song" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: First Song" << std::endl;} + + }; + struct Song2 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: Second song" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: Second Song" << std::endl;} + }; + struct Song3 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: Third song" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: Third Song" << std::endl;} + }; + // the initial state. Must be defined + typedef Song1 initial_state; + // transition actions + void start_next_song(NextSong const&) { std::cout << "Playing::start_next_song\n"; } + void start_prev_song(PreviousSong const&) { std::cout << "Playing::start_prev_song\n"; } + // guard conditions + + typedef Playing_ pl; // makes transition table cleaner + // Transition table for Playing + struct transition_table : mpl::vector4< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Song1 , NextSong , Song2 , &pl::start_next_song >, + a_row < Song2 , PreviousSong, Song1 , &pl::start_prev_song >, + a_row < Song2 , NextSong , Song3 , &pl::start_next_song >, + a_row < Song3 , PreviousSong, Song2 , &pl::start_prev_song > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + // back-end + typedef msm::back::state_machine<Playing_> Playing; + + // state not defining any entry or exit + struct Paused : public msm::front::state<> + { + }; + + // the initial state of the player SM. Must be defined + typedef Empty initial_state; + + // transition actions + void start_playback(play const&) { std::cout << "player::start_playback\n"; } + void open_drawer(open_close const&) { std::cout << "player::open_drawer\n"; } + void close_drawer(open_close const&) { std::cout << "player::close_drawer\n"; } + void store_cd_info(cd_detected const&) { std::cout << "player::store_cd_info\n"; } + void stop_playback(stop const&) { std::cout << "player::stop_playback\n"; } + void pause_playback(pause const&) { std::cout << "player::pause_playback\n"; } + void resume_playback(end_pause const&) { std::cout << "player::resume_playback\n"; } + void stop_and_open(open_close const&) { std::cout << "player::stop_and_open\n"; } + void stopped_again(stop const&) {std::cout << "player::stopped_again\n";} + // guard conditions + bool good_disk_format(cd_detected const& evt) + { + // to test a guard condition, let's say we understand only CDs, not DVD + if (evt.disc_type != DISK_CD) + { + std::cout << "wrong disk, sorry" << std::endl; + return false; + } + return true; + } + // used to show a transition conflict. This guard will simply deactivate one transition and thus + // solve the conflict + bool auto_start(cd_detected const&) + { + return false; + } + + typedef player_ p; // makes transition table cleaner + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Stopped , play , Playing , &p::start_playback >, + a_row < Stopped , open_close , Open , &p::open_drawer >, + _row < Stopped , stop , Stopped >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Open , open_close , Empty , &p::close_drawer >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Empty , open_close , Open , &p::open_drawer >, + row < Empty , cd_detected , Stopped , &p::store_cd_info ,&p::good_disk_format >, + row < Empty , cd_detected , Playing , &p::store_cd_info ,&p::auto_start >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Playing , stop , Stopped , &p::stop_playback >, + a_row < Playing , pause , Paused , &p::pause_playback >, + a_row < Playing , open_close , Open , &p::stop_and_open >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Paused , end_pause , Playing , &p::resume_playback >, + a_row < Paused , stop , Stopped , &p::stop_playback >, + a_row < Paused , open_close , Open , &p::stop_and_open > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + // Pick a back-end + typedef msm::back::state_machine<player_> player; + + // + // Testing utilities. + // + static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" }; + void pstate(player const& p) + { + std::cout << " -> " << state_names[p.current_state()[0]] << std::endl; + } + + void test() + { + SomeExternalContext ctx(3); + // example 1 + // create a player with an argument by reference and an int + // (go to the constructor front-end) + player p1(boost::ref(ctx),5); + + // example 2 + // create a player with a copy of an Empty state, an argument by reference and an int + // (last 2 go to the constructor front-end) + player p2(msm::back::states_ << player_::Empty(1),boost::ref(ctx),5); + std::cout << "Empty's data should be 1. Is: " << p2.get_state<player_::Empty&>().data_ << std::endl; + + std::cout << "Set a new Empty state" << std::endl; + p2.set_states(msm::back::states_ << player_::Empty(5)); + std::cout << "Empty's data should be 5. Is: " << p2.get_state<player_::Empty&>().data_ << std::endl; + + std::cout << "Set new Empty and Open states" << std::endl; + p2.set_states(msm::back::states_ << player_::Empty(7) << player_::Open(2)); + std::cout << "Empty's data should be 7. Is: " << p2.get_state<player_::Empty&>().data_ << std::endl; + std::cout << "Open's data should be 2. Is: " << p2.get_state<player_::Open&>().data_ << std::endl; + + // example 3 + // create a player with a copy of an Empty state, a copy of a Playing submachine + // (which is itself created by a copy of Song1) + // an argument by reference and an int + // (last 2 go to the constructor front-end) + player p(msm::back::states_ << player_::Empty(1) + << player_::Playing(msm::back::states_ << player_::Playing_::Song1(8)), + boost::ref(ctx),5); + + std::cout << "Song1's data should be 8. Is: " + << p.get_state<player_::Playing&>().get_state<player_::Playing_::Song1&>().data_ << std::endl; + + + } +} + +int main() +{ + test(); + return 0; +} diff --git a/libs/msm/doc/PDF/examples/DirectEntryEuml.cpp b/libs/msm/doc/PDF/examples/DirectEntryEuml.cpp new file mode 100644 index 0000000000..7cde090e8d --- /dev/null +++ b/libs/msm/doc/PDF/examples/DirectEntryEuml.cpp @@ -0,0 +1,386 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <vector> +#include <iostream> + +#include <boost/msm/back/state_machine.hpp> +#include <boost/msm/front/euml/euml.hpp> + +using namespace std; +using namespace boost::msm::front::euml; +namespace msm = boost::msm; +namespace mpl = boost::mpl; + + +namespace // Concrete FSM implementation +{ + // events + BOOST_MSM_EUML_EVENT(event1) + BOOST_MSM_EUML_EVENT(event2) + BOOST_MSM_EUML_EVENT(event3) + BOOST_MSM_EUML_EVENT(event4) + BOOST_MSM_EUML_EVENT(event5) + // if we need something special, like a template constructor, we cannot use the helper macros + struct event6_impl : euml_event<event6_impl> + { + event6_impl(){} + template <class Event> + event6_impl(Event const&){} + }; + event6_impl const event6; + + //Sub fsm state definition + BOOST_MSM_EUML_ACTION(SubState1_Entry) + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: SubFsm2::SubState1" << std::endl; + } + }; + BOOST_MSM_EUML_ACTION(SubState1_Exit) + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "leaving: SubFsm2::SubState1" << std::endl; + } + }; + BOOST_MSM_EUML_STATE(( SubState1_Entry,SubState1_Exit ),SubState1) + + BOOST_MSM_EUML_ACTION(SubState1b_Entry) + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: SubFsm2::SubState1b" << std::endl; + } + }; + BOOST_MSM_EUML_ACTION(SubState1b_Exit) + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "leaving: SubFsm2::SubState1b" << std::endl; + } + }; + BOOST_MSM_EUML_STATE(( SubState1b_Entry,SubState1b_Exit ),SubState1b) + + BOOST_MSM_EUML_ACTION(SubState1c_Entry) + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: SubFsm2::SubState1c" << std::endl; + } + }; + BOOST_MSM_EUML_ACTION(SubState1c_Exit) + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "leaving: SubFsm2::SubState1c" << std::endl; + } + }; + BOOST_MSM_EUML_STATE(( SubState1c_Entry,SubState1c_Exit ),SubState1c) + + BOOST_MSM_EUML_ACTION(SubState2_Entry) + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: SubFsm2::SubState2" << std::endl; + } + }; + BOOST_MSM_EUML_ACTION(SubState2_Exit) + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "leaving: SubFsm2::SubState2" << std::endl; + } + }; + BOOST_MSM_EUML_EXPLICIT_ENTRY_STATE(0,( SubState2_Entry,SubState2_Exit ),SubState2) + + BOOST_MSM_EUML_ACTION(SubState2b_Entry) + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: SubFsm2::SubState2b" << std::endl; + } + }; + BOOST_MSM_EUML_ACTION(SubState2b_Exit) + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "leaving: SubFsm2::SubState2b" << std::endl; + } + }; + BOOST_MSM_EUML_EXPLICIT_ENTRY_STATE(1,( SubState2b_Entry,SubState2b_Exit ),SubState2b) + + BOOST_MSM_EUML_ACTION(SubState2c_Entry) + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: SubFsm2::SubState2c" << std::endl; + } + }; + BOOST_MSM_EUML_ACTION(SubState2c_Exit) + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "leaving: SubFsm2::SubState2c" << std::endl; + } + }; + BOOST_MSM_EUML_EXPLICIT_ENTRY_STATE(2,( SubState2c_Entry,SubState2c_Exit ),SubState2c) + + BOOST_MSM_EUML_ACTION(PseudoEntry1_Entry) + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: SubFsm2::PseudoEntry1" << std::endl; + } + }; + BOOST_MSM_EUML_ACTION(PseudoEntry1_Exit) + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "leaving: SubFsm2::PseudoEntry1" << std::endl; + } + }; + BOOST_MSM_EUML_ENTRY_STATE(0,( PseudoEntry1_Entry,PseudoEntry1_Exit ),PseudoEntry1) + + BOOST_MSM_EUML_ACTION(SubState3_Entry) + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: SubFsm2::SubState3" << std::endl; + } + }; + BOOST_MSM_EUML_ACTION(SubState3_Exit) + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "leaving: SubFsm2::SubState3" << std::endl; + } + }; + BOOST_MSM_EUML_STATE(( SubState3_Entry,SubState3_Exit ),SubState3) + + BOOST_MSM_EUML_ACTION(SubState3b_Entry) + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: SubFsm2::SubState3b" << std::endl; + } + }; + BOOST_MSM_EUML_ACTION(SubState3b_Exit) + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "leaving: SubFsm2::SubState3b" << std::endl; + } + }; + BOOST_MSM_EUML_STATE(( SubState3b_Entry,SubState3b_Exit ),SubState3b) + + BOOST_MSM_EUML_ACTION(PseudoExit1_Entry) + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: SubFsm2::PseudoExit1" << std::endl; + } + }; + BOOST_MSM_EUML_ACTION(PseudoExit1_Exit) + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "leaving: SubFsm2::PseudoExit1" << std::endl; + } + }; + BOOST_MSM_EUML_EXIT_STATE(( event6,PseudoExit1_Entry,PseudoExit1_Exit ),PseudoExit1) + + // actions + BOOST_MSM_EUML_ACTION(entry_action) + { + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(FSM& ,EVT const& ,SourceState& ,TargetState& ) + { + cout << "calling entry_action" << endl; + } + }; + // SubFsm definition + BOOST_MSM_EUML_ACTION(SubFsm2_Entry) + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: SubFsm2" << std::endl; + } + }; + BOOST_MSM_EUML_ACTION(SubFsm2_Exit) + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "leaving: SubFsm2" << std::endl; + } + }; + BOOST_MSM_EUML_TRANSITION_TABLE(( + // +------------------------------------------------------------------------------+ + SubState3 == PseudoEntry1 + event4 / entry_action , + SubState1 == SubState2 + event6 , + PseudoExit1 == SubState3 + event5 + // +------------------------------------------------------------------------------+ + ), SubFsm2_transition_table) + + BOOST_MSM_EUML_DECLARE_STATE_MACHINE( (SubFsm2_transition_table, //STT + init_ << SubState1 << SubState1b << SubState1c, // Init State + SubFsm2_Entry, // Entry + SubFsm2_Exit + ),SubFsm2_def) + // inherit to add some typedef + struct SubFsm2_ : public SubFsm2_def + { + // these 2 states are not found in the transition table because they are accessed only through + // a fork, so we need to create them explicitly + typedef mpl::vector<BOOST_MSM_EUML_STATE_NAME(SubState2b), + BOOST_MSM_EUML_STATE_NAME(SubState2c)> explicit_creation; + }; + + // back-end + typedef msm::back::state_machine<SubFsm2_> SubFsm2_type; + SubFsm2_type const SubFsm2; + + // Fsm state definitions + BOOST_MSM_EUML_ACTION(State1_Entry) + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: State1" << std::endl; + } + }; + BOOST_MSM_EUML_ACTION(State1_Exit) + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "leaving: State1" << std::endl; + } + }; + BOOST_MSM_EUML_STATE(( State1_Entry,State1_Exit ),State1) + + BOOST_MSM_EUML_ACTION(State2_Entry) + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: State2" << std::endl; + } + }; + BOOST_MSM_EUML_ACTION(State2_Exit) + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "leaving: State2" << std::endl; + } + }; + BOOST_MSM_EUML_STATE(( State2_Entry,State2_Exit ),State2) + + // Fsm definition + BOOST_MSM_EUML_TRANSITION_TABLE(( + // +------------------------------------------------------------------------------+ + SubFsm2 == State1 + event1 , + explicit_(SubFsm2,SubState2) == State1 + event2, + (explicit_(SubFsm2,SubState2), + explicit_(SubFsm2,SubState2b), + explicit_(SubFsm2,SubState2c)) == State1 + event3 , + entry_pt_(SubFsm2,PseudoEntry1) == State1 + event4 , + State1 == SubFsm2 + event1 , + State2 == exit_pt_ + (SubFsm2,PseudoExit1) + event6 + // +------------------------------------------------------------------------------+ + ),transition_table ) + + + BOOST_MSM_EUML_ACTION(Log_No_Transition) + { + template <class Event,class FSM,class STATE> + void operator()(Event const& e,FSM&,STATE& ) + { + std::cout << "no transition in Fsm" + << " on event " << typeid(e).name() << std::endl; + } + }; + BOOST_MSM_EUML_DECLARE_STATE_MACHINE(( transition_table, //STT + init_ << State1, // Init State + no_action, // Entry + no_action, // Exit + attributes_ << no_attributes_, // Attributes + configure_ << no_configure_, // configuration + Log_No_Transition // no_transition handler + ), + Fsm_) //fsm name + + //back-end + typedef msm::back::state_machine<Fsm_> Fsm; + + // + // Testing utilities. + // + static char const* const state_names[] = { "State1", "SubFsm2","State2" }; + void pstate(Fsm const& p) + { + std::cout << " -> " << state_names[p.current_state()[0]] << std::endl; + } + + void test() + { + Fsm p; + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + p.start(); + std::cout << "Simply move in and out of the composite, activate init states" << std::endl; + p.process_event(event1); pstate(p); + p.process_event(event1); pstate(p); + std::cout << "Direct entry into SubFsm2::SubState2, then transition to SubState1 and back to State1" << std::endl; + p.process_event(event2); pstate(p); + p.process_event(event6); pstate(p); + p.process_event(event1); pstate(p); + std::cout << "processing fork to SubFsm2::SubState2, SubFsm2::SubState2b and SubFsm2::SubState2c" << std::endl; + p.process_event(event3); pstate(p); + p.process_event(event1); pstate(p); + std::cout << "processing entry pseudo state" << std::endl; + p.process_event(event4); pstate(p); + p.process_event(event1); pstate(p); + std::cout << "processing entry + exit pseudo state" << std::endl; + p.process_event(event4); pstate(p); + std::cout << "using exit pseudo state" << std::endl; + p.process_event(event5); pstate(p); + } +} + +int main() +{ + test(); + return 0; +} diff --git a/libs/msm/doc/PDF/examples/DirectEntryTutorial.cpp b/libs/msm/doc/PDF/examples/DirectEntryTutorial.cpp new file mode 100644 index 0000000000..35bf9fdd22 --- /dev/null +++ b/libs/msm/doc/PDF/examples/DirectEntryTutorial.cpp @@ -0,0 +1,221 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <vector> +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> + +namespace msm = boost::msm; +namespace mpl = boost::mpl; + +namespace +{ + // events + struct event1 {}; + struct event2 {}; + struct event3 {}; + struct event4 {}; + struct event5 {}; + struct event6 + { + event6(){} + template <class Event> + event6(Event const&){} + }; + // front-end: define the FSM structure + struct Fsm_ : public msm::front::state_machine_def<Fsm_> + { + // The list of FSM states + struct State1 : public msm::front::state<> + { + // every (optional) entry/exit methods get the event passed. + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: State1" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: State1" << std::endl;} + }; + struct State2 : public msm::front::state<> + { + // every (optional) entry/exit methods get the event passed. + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: State2" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: State2" << std::endl;} + }; + struct SubFsm2_ : public msm::front::state_machine_def<SubFsm2_> + { + typedef msm::back::state_machine<SubFsm2_> SubFsm2; + + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: SubFsm2" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: SubFsm2" << std::endl;} + + struct SubState1 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: SubFsm2::SubState1" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: SubFsm2::SubState1" << std::endl;} + }; + struct SubState1b : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: SubFsm2::SubState1b" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: SubFsm2::SubState1b" << std::endl;} + }; + struct SubState2 : public msm::front::state<> , public msm::front::explicit_entry<0> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: SubFsm2::SubState2" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: SubFsm2::SubState2" << std::endl;} + }; + struct SubState2b : public msm::front::state<> , public msm::front::explicit_entry<1> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: SubFsm2::SubState2b" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: SubFsm2::SubState2b" << std::endl;} + }; + // test with a pseudo entry + struct PseudoEntry1 : public msm::front::entry_pseudo_state<0> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: SubFsm2::PseudoEntry1" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: SubFsm2::PseudoEntry1" << std::endl;} + }; + struct SubState3 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: SubFsm2::SubState3" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: SubFsm2::SubState3" << std::endl;} + }; + struct SubState3b : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: SubFsm2::SubState3b" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: SubFsm2::SubState3b" << std::endl;} + }; + struct PseudoExit1 : public msm::front::exit_pseudo_state<event6> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: SubFsm2::PseudoExit1" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: SubFsm2::PseudoExit1" << std::endl;} + }; + // action methods + void entry_action(event4 const&) + { + std::cout << "calling entry_action" << std::endl; + } + // the initial state. Must be defined + typedef mpl::vector<SubState1,SubState1b> initial_state; + + typedef mpl::vector<SubState2b> explicit_creation; + + // Transition table for SubFsm2 + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +--------------+-------------+------------+------------------------+----------------------+ + a_row < PseudoEntry1 , event4 , SubState3 ,&SubFsm2_::entry_action >, + _row < SubState2 , event6 , SubState1 >, + _row < SubState3 , event5 , PseudoExit1 > + // +--------------+-------------+------------+------------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + typedef msm::back::state_machine<SubFsm2_> SubFsm2; + + // the initial state of the player SM. Must be defined + typedef State1 initial_state; + + // transition actions + // guard conditions + + // Transition table for Fsm + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------------------+--------+------------------------------------+-------+--------+ + _row < State1 , event1 , SubFsm2 >, + _row < State1 , event2 , SubFsm2::direct<SubFsm2_::SubState2> >, + _row < State1 , event3 , mpl::vector<SubFsm2::direct<SubFsm2_::SubState2>, + SubFsm2::direct<SubFsm2_::SubState2b> > >, + _row < State1 , event4 , SubFsm2::entry_pt + <SubFsm2_::PseudoEntry1> >, + // +---------------------+--------+------------------------------------+-------+--------+ + _row < SubFsm2 , event1 , State1 >, + _row < SubFsm2::exit_pt + <SubFsm2_::PseudoExit1>, event6 , State2 > + // +---------------------+--------+------------------------------------+-------+--------+ + > {}; + + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + typedef msm::back::state_machine<Fsm_> Fsm; + + // + // Testing utilities. + // + static char const* const state_names[] = { "State1", "SubFsm2","State2" }; + void pstate(Fsm const& p) + { + std::cout << " -> " << state_names[p.current_state()[0]] << std::endl; + } + + void test() + { + Fsm p; + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + p.start(); + std::cout << "Simply move in and out of the composite, activate init states" << std::endl; + p.process_event(event1()); pstate(p); + p.process_event(event1()); pstate(p); + std::cout << "Direct entry into SubFsm2::SubState2, then transition to SubState1 and back to State1" << std::endl; + p.process_event(event2()); pstate(p); + p.process_event(event6()); pstate(p); + p.process_event(event1()); pstate(p); + std::cout << "processing fork to SubFsm2::SubState2 and SubFsm2::SubState2b" << std::endl; + p.process_event(event3()); pstate(p); + p.process_event(event1()); pstate(p); + std::cout << "processing entry pseudo state" << std::endl; + p.process_event(event4()); pstate(p); + p.process_event(event1()); pstate(p); + std::cout << "processing entry + exit pseudo state" << std::endl; + p.process_event(event4()); pstate(p); + std::cout << "using exit pseudo state" << std::endl; + p.process_event(event5()); pstate(p); + } +} + +int main() +{ + test(); + return 0; +} diff --git a/libs/msm/doc/PDF/examples/EumlInternal.cpp b/libs/msm/doc/PDF/examples/EumlInternal.cpp new file mode 100644 index 0000000000..407740dbe0 --- /dev/null +++ b/libs/msm/doc/PDF/examples/EumlInternal.cpp @@ -0,0 +1,154 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <vector> +#include <iostream> + +#include <boost/msm/back/state_machine.hpp> +#include <boost/msm/front/euml/euml.hpp> + +using namespace std; +using namespace boost::msm::front::euml; +using namespace boost::msm::front; +namespace msm = boost::msm; + +// entry/exit/action/guard logging functors +#include "logging_functors.h" + +namespace // Concrete FSM implementation +{ + // events + BOOST_MSM_EUML_EVENT(play) + BOOST_MSM_EUML_EVENT(end_pause) + BOOST_MSM_EUML_EVENT(stop) + BOOST_MSM_EUML_EVENT(pause) + BOOST_MSM_EUML_EVENT(open_close) + BOOST_MSM_EUML_EVENT(internal_event) + + // A "complicated" event type that carries some data. + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(std::string,cd_name) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(DiskTypeEnum,cd_type) + BOOST_MSM_EUML_ATTRIBUTES((attributes_ << cd_name << cd_type ), cd_detected_attributes) + BOOST_MSM_EUML_EVENT_WITH_ATTRIBUTES(cd_detected,cd_detected_attributes) + + // Concrete FSM implementation + + // The list of FSM states + BOOST_MSM_EUML_STATE(( Empty_Entry,Empty_Exit ),Empty) + BOOST_MSM_EUML_STATE(( Open_Entry,Open_Exit ),Open) + BOOST_MSM_EUML_STATE(( Stopped_Entry,Stopped_Exit ),Stopped) + BOOST_MSM_EUML_STATE(( Playing_Entry,Playing_Exit ),Playing) + // state not needing any entry or exit + BOOST_MSM_EUML_STATE((),Paused) + + // guard conditions + BOOST_MSM_EUML_ACTION(good_disk_format) + { + template <class FSM,class EVT,class SourceState,class TargetState> + bool operator()(EVT const& evt,FSM&,SourceState& ,TargetState& ) + { + // to test a guard condition, let's say we understand only CDs, not DVD + if (evt.get_attribute(cd_type)!=DISK_CD) + { + std::cout << "wrong disk, sorry" << std::endl; + // just for logging, does not block any transition + return true; + } + std::cout << "good disk" << std::endl; + return true; + } + }; + // replaces the old transition table + BOOST_MSM_EUML_TRANSITION_TABLE(( + Playing == Stopped + play / start_playback , + Playing == Paused + end_pause / resume_playback, + // +------------------------------------------------------------------------------+ + Empty == Open + open_close / close_drawer, + // +------------------------------------------------------------------------------+ + Open == Empty + open_close / open_drawer, + Open == Paused + open_close / stop_and_open, + Open == Stopped + open_close / open_drawer, + Open == Playing + open_close / stop_and_open, + Open + open_close [internal_guard1] / internal_action1, + Open + open_close [internal_guard2] / internal_action2, + Open + internal_event / internal_action , + // +------------------------------------------------------------------------------+ + Paused == Playing + pause / pause_playback, + // +------------------------------------------------------------------------------+ + Stopped == Playing + stop / stop_playback, + Stopped == Paused + stop / stop_playback, + Stopped == Empty + cd_detected [good_disk_format&& + (event_(cd_type)==Int_<DISK_CD>())] + / (store_cd_info,process_(play)), + Stopped == Stopped + stop + // +------------------------------------------------------------------------------+ + ),transition_table) + + + // create a state machine "on the fly" + BOOST_MSM_EUML_DECLARE_STATE_MACHINE(( transition_table, //STT + init_ << Empty, // Init State + no_action, // Entry + no_action, // Exit + attributes_ << no_attributes_, // Attributes + configure_ << no_configure_, // configuration + Log_No_Transition // no_transition handler + ), + player_) //fsm name + // choice of back-end + typedef msm::back::state_machine<player_> player; + + // + // Testing utilities. + // + static char const* const state_names[] = { "Stopped", "Paused", "Open", "Empty", "Playing" }; + void pstate(player const& p) + { + std::cout << " -> " << state_names[p.current_state()[0]] << std::endl; + } + + void test() + { + player p; + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + p.start(); + // go to Open, call on_exit on Empty, then action, then on_entry on Open + p.process_event(open_close); pstate(p); + std::cout << "sending internal event (not rejected)" << std::endl; + p.process_event(internal_event); + std::cout << "sending open_close event. Conflict with internal transitions (rejecting event)" << std::endl; + p.process_event(open_close); pstate(p); + // will be rejected, wrong disk type + p.process_event( + cd_detected("louie, louie",DISK_DVD)); pstate(p); + p.process_event( + cd_detected("louie, louie",DISK_CD)); pstate(p); + // no need to call play() as the previous event does it in its action method + //p.process_event(play); + + // at this point, Play is active + p.process_event(pause); pstate(p); + // go back to Playing + p.process_event(end_pause); pstate(p); + p.process_event(pause); pstate(p); + p.process_event(stop); pstate(p); + // event leading to the same state + // no action method called as none is defined in the transition table + p.process_event(stop); pstate(p); + // test call to no_transition + p.process_event(pause); pstate(p); + } +} + +int main() +{ + test(); + return 0; +} diff --git a/libs/msm/doc/PDF/examples/EumlInternalDistributed.cpp b/libs/msm/doc/PDF/examples/EumlInternalDistributed.cpp new file mode 100644 index 0000000000..c7371dfc50 --- /dev/null +++ b/libs/msm/doc/PDF/examples/EumlInternalDistributed.cpp @@ -0,0 +1,218 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <vector> +#include <iostream> + +#include <boost/msm/back/state_machine.hpp> +#include <boost/msm/front/euml/euml.hpp> + +using namespace std; +using namespace boost::msm::front::euml; +using namespace boost::msm::front; +namespace msm = boost::msm; + +// entry/exit/action/guard logging functors +#include "logging_functors.h" + +namespace // Concrete FSM implementation +{ + // events + // note that unlike the SimpleTutorial, events must derive from euml_event. + BOOST_MSM_EUML_EVENT(play) + BOOST_MSM_EUML_EVENT(end_pause) + BOOST_MSM_EUML_EVENT(stop) + BOOST_MSM_EUML_EVENT(pause) + BOOST_MSM_EUML_EVENT(open_close) + BOOST_MSM_EUML_EVENT(internal_event) + BOOST_MSM_EUML_EVENT(next_song) + BOOST_MSM_EUML_EVENT(previous_song) + + // A "complicated" event type that carries some data. + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(std::string,cd_name) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(DiskTypeEnum,cd_type) + BOOST_MSM_EUML_ATTRIBUTES((attributes_ << cd_name << cd_type ), cd_detected_attributes) + BOOST_MSM_EUML_EVENT_WITH_ATTRIBUTES(cd_detected,cd_detected_attributes) + + // Concrete FSM implementation + + // The list of FSM states + BOOST_MSM_EUML_STATE(( Empty_Entry,Empty_Exit ),Empty) + + // we just declare a state type but do not create any instance as we want to inherit from this type + BOOST_MSM_EUML_DECLARE_STATE((Open_Entry,Open_Exit),Open_def) + // derive to be able to add an internal transition table + struct Open_impl : public Open_def + { + BOOST_MSM_EUML_DECLARE_INTERNAL_TRANSITION_TABLE(( + open_close [internal_guard1] / internal_action1 , + open_close [internal_guard2] / internal_action2 , + internal_event / internal_action + )) + }; + // declare an instance for the stt as we are manually declaring a state + Open_impl const Open; + BOOST_MSM_EUML_STATE(( Stopped_Entry,Stopped_Exit ),Stopped) + + // Playing is a state machine itself. + // It has 3 substates + BOOST_MSM_EUML_STATE(( Song1_Entry,Song1_Exit ),Song1) + BOOST_MSM_EUML_STATE(( Song2_Entry,Song2_Exit ),Song2) + BOOST_MSM_EUML_STATE(( Song3_Entry,Song3_Exit ),Song3) + + // Playing has a transition table + BOOST_MSM_EUML_TRANSITION_TABLE(( + // +------------------------------------------------------------------------------+ + Song2 == Song1 + next_song / start_next_song, + Song1 == Song2 + previous_song / start_prev_song, + Song3 == Song2 + next_song / start_next_song, + Song2 == Song3 + previous_song / start_prev_song + // +------------------------------------------------------------------------------+ + ),playing_transition_table ) + + BOOST_MSM_EUML_DECLARE_STATE_MACHINE( (playing_transition_table, //STT + init_ << Song1 // Init State + ),Playing_def) + + // some action for the internal transition + BOOST_MSM_EUML_ACTION(playing_internal_action) + { + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const&, FSM& ,SourceState& ,TargetState& ) + { + cout << "Playing::internal action" << endl; + } + }; + // derive to be able to add an internal transition table + struct Playing_ : public Playing_def + { + BOOST_MSM_EUML_DECLARE_INTERNAL_TRANSITION_TABLE(( + internal_event / playing_internal_action + )) + }; + // choice of back-end + typedef msm::back::state_machine<Playing_> Playing_type; + Playing_type const Playing; + + + // state not needing any entry or exit + BOOST_MSM_EUML_STATE((),Paused) + + // guard conditions + BOOST_MSM_EUML_ACTION(good_disk_format) + { + template <class FSM,class EVT,class SourceState,class TargetState> + bool operator()(EVT const& evt,FSM&,SourceState& ,TargetState& ) + { + // to test a guard condition, let's say we understand only CDs, not DVD + if (evt.get_attribute(cd_type)!=DISK_CD) + { + std::cout << "wrong disk, sorry" << std::endl; + // just for logging, does not block any transition + return true; + } + std::cout << "good disk" << std::endl; + return true; + } + }; + // replaces the old transition table + BOOST_MSM_EUML_TRANSITION_TABLE(( + Stopped + play / start_playback == Playing , + Stopped + open_close / open_drawer == Open , + Stopped + stop == Stopped , + // +------------------------------------------------------------------------------+ + Open + open_close / close_drawer == Empty , + // +------------------------------------------------------------------------------+ + Empty + open_close / open_drawer == Open , + Empty + cd_detected + [good_disk_format&&(event_(cd_type)==Int_<DISK_CD>())] + / (store_cd_info,process_(play)) + == Stopped , + // +------------------------------------------------------------------------------+ + Playing + stop / stop_playback == Stopped , + Playing + pause / pause_playback == Paused , + Playing + open_close / stop_and_open == Open , + // +------------------------------------------------------------------------------+ + Paused + end_pause / resume_playback == Playing , + Paused + stop / stop_playback == Stopped , + Paused + open_close / stop_and_open == Open + // +------------------------------------------------------------------------------+ + ),transition_table) + + + + // create a state machine "on the fly" + BOOST_MSM_EUML_DECLARE_STATE_MACHINE(( transition_table, //STT + init_ << Empty, // Init State + no_action, // Entry + no_action, // Exit + attributes_ << no_attributes_, // Attributes + configure_ << no_configure_, // configuration + Log_No_Transition // no_transition handler + ), + player_) //fsm name + + // choice of back-end + typedef msm::back::state_machine<player_> player; + + // + // Testing utilities. + // + static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" }; + void pstate(player const& p) + { + std::cout << " -> " << state_names[p.current_state()[0]] << std::endl; + } + + void test() + { + player p; + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + p.start(); + // go to Open, call on_exit on Empty, then action, then on_entry on Open + p.process_event(open_close); pstate(p); + std::cout << "sending internal event (not rejected)" << std::endl; + p.process_event(internal_event); + std::cout << "sending open_close event. Conflict with internal transitions (rejecting event)" << std::endl; + p.process_event(open_close); pstate(p); + // will be rejected, wrong disk type + p.process_event( + cd_detected("louie, louie",DISK_DVD)); pstate(p); + p.process_event( + cd_detected("louie, louie",DISK_CD)); pstate(p); + // no need to call play as the previous event does it in its action method + //p.process_event(play); + // at this point, Play is active + // make transition happen inside it. Player has no idea about this event but it's ok. + p.process_event(next_song);pstate(p); //2nd song active + p.process_event(next_song);pstate(p);//3rd song active + p.process_event(previous_song);pstate(p);//2nd song active + // event handled internally in Playing, without region checking + std::cout << "sending internal event (not rejected)" << std::endl; + p.process_event(internal_event); + + p.process_event(pause); pstate(p); + // go back to Playing + p.process_event(end_pause); pstate(p); + p.process_event(pause); pstate(p); + p.process_event(stop); pstate(p); + // event leading to the same state + // no action method called as none is defined in the transition table + p.process_event(stop); pstate(p); + // test call to no_transition + p.process_event(pause); pstate(p); + } +} + +int main() +{ + test(); + return 0; +} diff --git a/libs/msm/doc/PDF/examples/EumlSimple.cpp b/libs/msm/doc/PDF/examples/EumlSimple.cpp new file mode 100644 index 0000000000..38eebd04fb --- /dev/null +++ b/libs/msm/doc/PDF/examples/EumlSimple.cpp @@ -0,0 +1,222 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include "stdafx.h" + +#include <boost/msm/back/state_machine.hpp> +#include <boost/msm/front/state_machine_def.hpp> +#include <boost/msm/front/euml/euml.hpp> +#include <boost/msm/front/euml/stl.hpp> + +namespace msm = boost::msm; +namespace mpl = boost::mpl; +using namespace boost::msm::front::euml; + +#include <iostream> +#ifdef WIN32 +#include "windows.h" +#else +#include <sys/time.h> +#endif + +namespace // Concrete FSM implementation +{ + // events + BOOST_MSM_EUML_EVENT(play) + BOOST_MSM_EUML_EVENT(end_pause) + BOOST_MSM_EUML_EVENT(stop) + BOOST_MSM_EUML_EVENT(pause) + BOOST_MSM_EUML_EVENT(open_close) + BOOST_MSM_EUML_EVENT(cd_detected) + + BOOST_MSM_EUML_ACTION(start_playback) + { + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM&,SourceState& ,TargetState& ) + { + } + }; + BOOST_MSM_EUML_ACTION(open_drawer) + { + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM&,SourceState& ,TargetState& ) + { + } + }; + BOOST_MSM_EUML_ACTION(close_drawer) + { + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM&,SourceState& ,TargetState& ) + { + } + }; + BOOST_MSM_EUML_ACTION(store_cd_info) + { + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const&, FSM& fsm ,SourceState& ,TargetState& ) + { + } + }; + BOOST_MSM_EUML_ACTION(stop_playback) + { + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM&,SourceState& ,TargetState& ) + { + } + }; + BOOST_MSM_EUML_ACTION(pause_playback) + { + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM&,SourceState& ,TargetState& ) + { + } + }; + BOOST_MSM_EUML_ACTION(resume_playback) + { + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM&,SourceState& ,TargetState& ) + { + } + }; + BOOST_MSM_EUML_ACTION(stop_and_open) + { + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM&,SourceState& ,TargetState& ) + { + } + }; + BOOST_MSM_EUML_ACTION(stopped_again) + { + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM&,SourceState& ,TargetState& ) + { + } + }; + + // The list of FSM states + BOOST_MSM_EUML_STATE((),Empty) + BOOST_MSM_EUML_STATE((),Open) + BOOST_MSM_EUML_STATE((),Stopped) + BOOST_MSM_EUML_STATE((),Playing) + BOOST_MSM_EUML_STATE((),Paused) + + // replaces the old transition table + BOOST_MSM_EUML_TRANSITION_TABLE(( + Playing == Stopped + play / start_playback , + Playing == Paused + end_pause / resume_playback , + // +------------------------------------------------------------------------------+ + Empty == Open + open_close / close_drawer , + // +------------------------------------------------------------------------------+ + Open == Empty + open_close / open_drawer , + Open == Paused + open_close / stop_and_open , + Open == Stopped + open_close / open_drawer , + Open == Playing + open_close / stop_and_open , + // +------------------------------------------------------------------------------+ + Paused == Playing + pause / pause_playback , + // +------------------------------------------------------------------------------+ + Stopped == Playing + stop / stop_playback , + Stopped == Paused + stop / stop_playback , + Stopped == Empty + cd_detected / store_cd_info , + Stopped == Stopped + stop / stopped_again + // +------------------------------------------------------------------------------+ + ),transition_table) + + BOOST_MSM_EUML_ACTION(Log_No_Transition) + { + template <class FSM,class Event> + void operator()(Event const& e,FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + + // create a state machine "on the fly" + BOOST_MSM_EUML_DECLARE_STATE_MACHINE(( transition_table, //STT + init_ << Empty, // Init State + no_action, // Entry + no_action, // Exit + attributes_ << no_attributes_, // Attributes + configure_ << no_exception << no_msg_queue, // configuration + Log_No_Transition // no_transition handler + ), + player_) //fsm name + + typedef msm::back::state_machine<player_> player; + + // + // Testing utilities. + // + static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" }; + + void pstate(player const& p) + { + std::cout << " -> " << state_names[p.current_state()[0]] << std::endl; + } + +} + +#ifndef WIN32 +long mtime(struct timeval& tv1,struct timeval& tv2) +{ + return (tv2.tv_sec-tv1.tv_sec) *1000000 + ((tv2.tv_usec-tv1.tv_usec)); +} +#endif + + +int main() +{ + // for timing +#ifdef WIN32 + LARGE_INTEGER res; + ::QueryPerformanceFrequency(&res); + LARGE_INTEGER li,li2; +#else + struct timeval tv1,tv2; + gettimeofday(&tv1,NULL); +#endif + + player p2; + p2.start(); + // for timing +#ifdef WIN32 + ::QueryPerformanceCounter(&li); +#else + gettimeofday(&tv1,NULL); +#endif + for (int i=0;i<100;++i) + { + p2.process_event(open_close); + p2.process_event(open_close); + p2.process_event(cd_detected); + p2.process_event(play); + p2.process_event(pause); + // go back to Playing + p2.process_event(end_pause); + p2.process_event(pause); + p2.process_event(stop); + // event leading to the same state + p2.process_event(stop); + p2.process_event(open_close); + p2.process_event(open_close); + } +#ifdef WIN32 + ::QueryPerformanceCounter(&li2); +#else + gettimeofday(&tv2,NULL); +#endif +#ifdef WIN32 + std::cout << "msm took in s:" << (double)(li2.QuadPart-li.QuadPart)/res.QuadPart <<"\n" <<std::endl; +#else + std::cout << "msm took in us:" << mtime(tv1,tv2) <<"\n" <<std::endl; +#endif + return 0; +} + diff --git a/libs/msm/doc/PDF/examples/ExampleCircularBuffer.cpp b/libs/msm/doc/PDF/examples/ExampleCircularBuffer.cpp new file mode 100644 index 0000000000..68a5b0a38f --- /dev/null +++ b/libs/msm/doc/PDF/examples/ExampleCircularBuffer.cpp @@ -0,0 +1,299 @@ +#include <vector> +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +// header for support of circular_buffer +#include <boost/msm/back/queue_container_circular.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> +// functors +#include <boost/msm/front/functor_row.hpp> +#include <boost/msm/front/euml/common.hpp> +// for And_ operator +#include <boost/msm/front/euml/operator.hpp> + +using namespace std; +namespace msm = boost::msm; +namespace mpl = boost::mpl; +using namespace msm::front; +// for And_ operator +using namespace msm::front::euml; + +namespace // Concrete FSM implementation +{ + // events + struct play {}; + struct end_pause {}; + struct stop {}; + struct pause {}; + struct open_close {}; + + // A "complicated" event type that carries some data. + enum DiskTypeEnum + { + DISK_CD=0, + DISK_DVD=1 + }; + struct cd_detected + { + cd_detected(std::string name, DiskTypeEnum diskType) + : name(name), + disc_type(diskType) + {} + + std::string name; + DiskTypeEnum disc_type; + }; + + // front-end: define the FSM structure + struct player_ : public msm::front::state_machine_def<player_> + { + // The list of FSM states + struct Empty : public msm::front::state<> + { + // every (optional) entry/exit methods get the event passed. + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Empty" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Empty" << std::endl;} + }; + struct Open : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) {std::cout << "entering: Open" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Open" << std::endl;} + }; + + struct Stopped : public msm::front::state<> + { + // when stopped, the CD is loaded + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) {std::cout << "entering: Stopped" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Stopped" << std::endl;} + }; + + struct Playing : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Playing" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Playing" << std::endl;} + }; + + // state not defining any entry or exit + struct Paused : public msm::front::state<> + { + }; + + // the initial state of the player SM. Must be defined + typedef Empty initial_state; + + // transition actions + // as the functors are generic on events, fsm and source/target state, + // you can reuse them in another machine if you wish + struct TestFct + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const&, FSM&,SourceState& ,TargetState& ) + { + cout << "transition with event:" << typeid(EVT).name() << endl; + } + }; + struct start_playback + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + cout << "player::start_playback" << endl; + } + }; + struct open_drawer + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + cout << "player::open_drawer" << endl; + } + }; + struct close_drawer + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + cout << "player::close_drawer" << endl; + } + }; + struct store_cd_info + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const&,FSM& fsm ,SourceState& ,TargetState& ) + { + cout << "player::store_cd_info" << endl; + fsm.process_event(play()); + } + }; + struct stop_playback + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + cout << "player::stop_playback" << endl; + } + }; + struct pause_playback + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + cout << "player::pause_playback" << endl; + } + }; + struct resume_playback + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + cout << "player::resume_playback" << endl; + } + }; + struct stop_and_open + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + cout << "player::stop_and_open" << endl; + } + }; + struct stopped_again + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + cout << "player::stopped_again" << endl; + } + }; + // guard conditions + struct DummyGuard + { + template <class EVT,class FSM,class SourceState,class TargetState> + bool operator()(EVT const& evt,FSM& fsm,SourceState& src,TargetState& tgt) + { + return true; + } + }; + struct good_disk_format + { + template <class EVT,class FSM,class SourceState,class TargetState> + bool operator()(EVT const& evt ,FSM&,SourceState& ,TargetState& ) + { + // to test a guard condition, let's say we understand only CDs, not DVD + if (evt.disc_type != DISK_CD) + { + std::cout << "wrong disk, sorry" << std::endl; + return false; + } + return true; + } + }; + struct always_true + { + template <class EVT,class FSM,class SourceState,class TargetState> + bool operator()(EVT const& evt ,FSM&,SourceState& ,TargetState& ) + { + return true; + } + }; + // we want to define one row with the classic look. + bool auto_start(cd_detected const& evt) + { + return false; + } + + typedef player_ p; // makes transition table cleaner + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------------+----------------------+ + Row < Stopped , play , Playing , ActionSequence_ + <mpl::vector< + TestFct,start_playback> > + , DummyGuard >, + Row < Stopped , open_close , Open , open_drawer , none >, + Row < Stopped , stop , Stopped , none , none >, + // +---------+-------------+---------+---------------------------+----------------------+ + Row < Open , open_close , Empty , close_drawer , none >, + // +---------+-------------+---------+---------------------------+----------------------+ + Row < Empty , open_close , Open , open_drawer , none >, + Row < Empty , cd_detected , Stopped , store_cd_info , And_<good_disk_format, + always_true> >, + // we here also mix with some "classical row" + g_row < Empty , cd_detected , Playing , &p::auto_start >, + // +---------+-------------+---------+---------------------------+----------------------+ + Row < Playing , stop , Stopped , stop_playback , none >, + Row < Playing , pause , Paused , pause_playback , none >, + Row < Playing , open_close , Open , stop_and_open , none >, + // +---------+-------------+---------+---------------------------+----------------------+ + Row < Paused , end_pause , Playing , resume_playback , none >, + Row < Paused , stop , Stopped , stop_playback , none >, + Row < Paused , open_close , Open , stop_and_open , none > + // +---------+-------------+---------+---------------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + // Pick a back-end + typedef msm::back::state_machine<player_, msm::back::queue_container_circular> player; + + // + // Testing utilities. + // + static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" }; + void pstate(player const& p) + { + std::cout << " -> " << state_names[p.current_state()[0]] << std::endl; + } + + void test() + { + player p; + // we get the message queue and limit it to capacity 1 + // get_message_queue returns the queue container (in this case circular_buffer) + p.get_message_queue().set_capacity(1); + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + p.start(); + // go to Open, call on_exit on Empty, then action, then on_entry on Open + p.process_event(open_close()); pstate(p); + p.process_event(open_close()); pstate(p); + // will be rejected, wrong disk type + p.process_event( + cd_detected("louie, louie",DISK_DVD)); pstate(p); + p.process_event( + cd_detected("louie, louie",DISK_CD)); pstate(p); + // no need to call play() as the previous event does it in its action method + //p.process_event(play()); + + // at this point, Play is active + p.process_event(pause()); pstate(p); + // go back to Playing + p.process_event(end_pause()); pstate(p); + p.process_event(pause()); pstate(p); + p.process_event(stop()); pstate(p); + // event leading to the same state + // no action method called as it is not present in the transition table + p.process_event(stop()); pstate(p); + } +} + +int main() +{ + test(); + return 0; +} diff --git a/libs/msm/doc/PDF/examples/Flags.cpp b/libs/msm/doc/PDF/examples/Flags.cpp new file mode 100644 index 0000000000..5226bcc43a --- /dev/null +++ b/libs/msm/doc/PDF/examples/Flags.cpp @@ -0,0 +1,254 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> + +namespace msm = boost::msm; +namespace mpl = boost::mpl; + +namespace // Concrete FSM implementation +{ + // events + struct play {}; + struct end_pause {}; + struct stop {}; + struct pause {}; + struct open_close {}; + struct NextSong {}; + struct PreviousSong {}; + + // Flags. Allow information about a property of the current state + struct PlayingPaused{}; + struct CDLoaded {}; + struct FirstSongPlaying {}; + + // A "complicated" event type that carries some data. + struct cd_detected + { + cd_detected(std::string name) + : name(name) + {} + + std::string name; + }; + + // front-end: define the FSM structure + struct player_ : public msm::front::state_machine_def<player_> + { + // The list of FSM states + struct Empty : public msm::front::state<> + { + // every (optional) entry/exit methods get the event passed. + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Empty" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Empty" << std::endl;} + }; + struct Open : public msm::front::state<> + { + typedef mpl::vector1<CDLoaded> flag_list; + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Open" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Open" << std::endl;} + }; + + struct Stopped : public msm::front::state<> + { + // when stopped, the CD is loaded + typedef mpl::vector1<CDLoaded> flag_list; + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Stopped" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Stopped" << std::endl;} + }; + + // the player state machine contains a state which is himself a state machine + // as you see, no need to declare it anywhere so Playing can be developed separately + // by another team in another module. For simplicity I just declare it inside player + struct Playing_ : public msm::front::state_machine_def<Playing_> + { + // when playing, the CD is loaded and we are in either pause or playing (duh) + typedef mpl::vector2<PlayingPaused,CDLoaded> flag_list; + + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Playing" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Playing" << std::endl;} + + // The list of FSM states + struct Song1 : public msm::front::state<> + { + typedef mpl::vector1<FirstSongPlaying> flag_list; + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: First song" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: First Song" << std::endl;} + + }; + struct Song2 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: Second song" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: Second Song" << std::endl;} + }; + struct Song3 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: Third song" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: Third Song" << std::endl;} + }; + // the initial state. Must be defined + typedef Song1 initial_state; + // transition actions + void start_next_song(NextSong const&) { std::cout << "Playing::start_next_song\n"; } + void start_prev_song(PreviousSong const&) { std::cout << "Playing::start_prev_song\n"; } + // guard conditions + + typedef Playing_ pl; // makes transition table cleaner + // Transition table for Playing + struct transition_table : mpl::vector4< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Song1 , NextSong , Song2 , &pl::start_next_song >, + a_row < Song2 , PreviousSong, Song1 , &pl::start_prev_song >, + a_row < Song2 , NextSong , Song3 , &pl::start_next_song >, + a_row < Song3 , PreviousSong, Song2 , &pl::start_prev_song > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + // back-end + typedef msm::back::state_machine<Playing_> Playing; + + // state not defining any entry or exit + struct Paused : public msm::front::state<> + { + typedef mpl::vector2<PlayingPaused,CDLoaded> flag_list; + }; + + // the initial state of the player SM. Must be defined + typedef Empty initial_state; + + // transition actions + void start_playback(play const&) { std::cout << "player::start_playback\n"; } + void open_drawer(open_close const&) { std::cout << "player::open_drawer\n"; } + void close_drawer(open_close const&) { std::cout << "player::close_drawer\n"; } + void store_cd_info(cd_detected const& cd) {std::cout << "player::store_cd_info\n";} + void stop_playback(stop const&) { std::cout << "player::stop_playback\n"; } + void pause_playback(pause const&) { std::cout << "player::pause_playback\n"; } + void resume_playback(end_pause const&) { std::cout << "player::resume_playback\n"; } + void stop_and_open(open_close const&) { std::cout << "player::stop_and_open\n"; } + void stopped_again(stop const&){std::cout << "player::stopped_again\n";} + // guard conditions + + typedef player_ p; // makes transition table cleaner + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Stopped , play , Playing , &p::start_playback >, + a_row < Stopped , open_close , Open , &p::open_drawer >, + a_row < Stopped , stop , Stopped , &p::stopped_again >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Open , open_close , Empty , &p::close_drawer >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Empty , open_close , Open , &p::open_drawer >, + a_row < Empty , cd_detected , Stopped , &p::store_cd_info >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Playing , stop , Stopped , &p::stop_playback >, + a_row < Playing , pause , Paused , &p::pause_playback >, + a_row < Playing , open_close , Open , &p::stop_and_open >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Paused , end_pause , Playing , &p::resume_playback >, + a_row < Paused , stop , Stopped , &p::stop_playback >, + a_row < Paused , open_close , Open , &p::stop_and_open > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + // Pick a back-end + typedef msm::back::state_machine<player_> player; + + // + // Testing utilities. + // + static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" }; + + void pstate(player const& p) + { + std::cout << " -> " << state_names[p.current_state()[0]] << std::endl; + } + + void test() + { + player p; + + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + p.start(); + // tests some flags + std::cout << "CDLoaded active:" << std::boolalpha << p.is_flag_active<CDLoaded>() << std::endl; //=> false (no CD yet) + // go to Open, call on_exit on Empty, then action, then on_entry on Open + p.process_event(open_close()); pstate(p); + p.process_event(open_close()); pstate(p); + p.process_event(cd_detected("louie, louie")); + p.process_event(play()); + + // at this point, Play is active + std::cout << "PlayingPaused active:" << std::boolalpha << p.is_flag_active<PlayingPaused>() << std::endl;//=> true + std::cout << "FirstSong active:" << std::boolalpha << p.is_flag_active<FirstSongPlaying>() << std::endl;//=> true + + // make transition happen inside it. Player has no idea about this event but it's ok. + p.process_event(NextSong());pstate(p); //2nd song active + p.process_event(NextSong());pstate(p);//3rd song active + p.process_event(PreviousSong());pstate(p);//2nd song active + std::cout << "FirstSong active:" << std::boolalpha << p.is_flag_active<FirstSongPlaying>() << std::endl;//=> false + + std::cout << "PlayingPaused active:" << std::boolalpha << p.is_flag_active<PlayingPaused>() << std::endl;//=> true + p.process_event(pause()); pstate(p); + std::cout << "PlayingPaused active:" << std::boolalpha << p.is_flag_active<PlayingPaused>() << std::endl;//=> true + // go back to Playing + // as you see, it starts back from the original state + p.process_event(end_pause()); pstate(p); + p.process_event(pause()); pstate(p); + p.process_event(stop()); pstate(p); + std::cout << "PlayingPaused active:" << std::boolalpha << p.is_flag_active<PlayingPaused>() << std::endl;//=> false + std::cout << "CDLoaded active:" << std::boolalpha << p.is_flag_active<CDLoaded>() << std::endl;//=> true + + // event leading to the same state + p.process_event(stop()); pstate(p); + } +} + +int main() +{ + test(); + return 0; +} diff --git a/libs/msm/doc/PDF/examples/FsmAsPtr.cpp b/libs/msm/doc/PDF/examples/FsmAsPtr.cpp new file mode 100644 index 0000000000..594c331386 --- /dev/null +++ b/libs/msm/doc/PDF/examples/FsmAsPtr.cpp @@ -0,0 +1,169 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include "FsmAsPtr.h" + +#include <boost/msm/back/state_machine.hpp> +#include <boost/msm/front/euml/euml.hpp> + +// cpp: using directives are okay +using namespace std; +using namespace boost::msm::front::euml; +namespace msm = boost::msm; + +// entry/exit/action/guard logging functors +#include "logging_functors.h" + +namespace +{ + // events + BOOST_MSM_EUML_EVENT(play) + BOOST_MSM_EUML_EVENT(end_pause) + BOOST_MSM_EUML_EVENT(stop) + BOOST_MSM_EUML_EVENT(pause) + BOOST_MSM_EUML_EVENT(open_close) + BOOST_MSM_EUML_EVENT(cd_detected) + + // Concrete FSM implementation + // The list of FSM states + // state not needing any entry or exit + BOOST_MSM_EUML_STATE((),Paused) + + // it is also possible to define a state which you can implement normally + // just make it a state, as usual, and also a grammar terminal, euml_state + struct Empty_impl : public msm::front::state<> , public euml_state<Empty_impl> + { + // this allows us to add some functions + void activate_empty() {std::cout << "switching to Empty " << std::endl;} + // standard entry behavior + template <class Event,class FSM> + void on_entry(Event const& evt,FSM& fsm) + { + std::cout << "entering: Empty" << std::endl; + } + template <class Event,class FSM> + void on_exit(Event const& evt,FSM& fsm) + { + std::cout << "leaving: Empty" << std::endl; + } + }; + //instance for use in the transition table + Empty_impl const Empty; + + // create a functor and a eUML function for the activate_empty method from Entry + BOOST_MSM_EUML_METHOD(ActivateEmpty_ , activate_empty , activate_empty_ , void , void ) + + // define more states + BOOST_MSM_EUML_STATE(( Open_Entry,Open_Exit ),Open) + BOOST_MSM_EUML_STATE(( Stopped_Entry,Stopped_Exit ),Stopped) + BOOST_MSM_EUML_STATE(( Playing_Entry,Playing_Exit ),Playing) + + // it is also possible to use a plain functor, with default-constructor in the transition table + struct start_play + { + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM&,SourceState& ,TargetState& ) + { + cout << "player::start_play" << endl; + } + }; + // replaces the old transition table + BOOST_MSM_EUML_TRANSITION_TABLE(( + Playing == Stopped + play / start_play() , + Playing == Paused + end_pause / resume_playback, + // +------------------------------------------------------------------------------+ + Empty == Open + open_close / (close_drawer,activate_empty_(target_)), + // +------------------------------------------------------------------------------+ + Open == Empty + open_close / open_drawer, + Open == Paused + open_close / stop_and_open, + Open == Stopped + open_close / open_drawer, + Open == Playing + open_close / stop_and_open, + // +------------------------------------------------------------------------------+ + Paused == Playing + pause / pause_playback, + // +------------------------------------------------------------------------------+ + Stopped == Playing + stop / stop_playback, + Stopped == Paused + stop / stop_playback, + Stopped == Empty + cd_detected / (store_cd_info,process_(play)), + Stopped == Stopped + stop + // +------------------------------------------------------------------------------+ + ),transition_table) + + // create a state machine "on the fly" + BOOST_MSM_EUML_DECLARE_STATE_MACHINE(( transition_table, //STT + init_ << Empty, // Init State + no_action, // Entry + no_action, // Exit + attributes_ << no_attributes_, // Attributes + configure_ << no_configure_, // configuration + Log_No_Transition // no_transition handler + ), + my_machine_impl_) //fsm name + + + // choice of back-end + typedef msm::back::state_machine<my_machine_impl_> my_machine_impl; +} +player::player() +: fsm_(new my_machine_impl) +{ + boost::static_pointer_cast<my_machine_impl>(fsm_)->start(); +} + +void player::do_play() +{ + boost::static_pointer_cast<my_machine_impl>(fsm_)->process_event(play); +} +void player::do_pause() +{ + boost::static_pointer_cast<my_machine_impl>(fsm_)->process_event(pause); +} +void player::do_open_close() +{ + boost::static_pointer_cast<my_machine_impl>(fsm_)->process_event(open_close); +} +void player::do_end_pause() +{ + boost::static_pointer_cast<my_machine_impl>(fsm_)->process_event(end_pause); +} +void player::do_stop() +{ + boost::static_pointer_cast<my_machine_impl>(fsm_)->process_event(stop); +} +void player::do_cd_detected() +{ + boost::static_pointer_cast<my_machine_impl>(fsm_)->process_event(cd_detected); +} + + +int main() +{ + player p; + // note that we write open_close and not open_close(), like usual. Both are possible with eUML, but + // you now have less to type. + // go to Open, call on_exit on Empty, then action, then on_entry on Open + p.do_open_close(); + p.do_open_close(); + p.do_cd_detected(); + // no need to call play as the previous event does it in its action method + + // at this point, Play is active + p.do_pause(); + // go back to Playing + p.do_end_pause(); + p.do_pause(); + p.do_stop(); + // event leading to the same state + // no action method called as none is defined in the transition table + p.do_stop(); + // test call to no_transition + p.do_pause(); + return 0; +} + diff --git a/libs/msm/doc/PDF/examples/FsmAsPtr.hpp b/libs/msm/doc/PDF/examples/FsmAsPtr.hpp new file mode 100644 index 0000000000..ef0376a42e --- /dev/null +++ b/libs/msm/doc/PDF/examples/FsmAsPtr.hpp @@ -0,0 +1,36 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef FSM_AS_PTR_H +#define FSM_AS_PTR_H + +#include <boost/shared_ptr.hpp> + +class player +{ +public: + player(); + virtual ~player(){} + + // public interface + void do_play(); + void do_pause(); + void do_open_close(); + void do_end_pause(); + void do_stop(); + void do_cd_detected(); + +private: + // my state machine, hidden + boost::shared_ptr<void> fsm_; + +}; + +#endif //FSM_AS_PTR_H diff --git a/libs/msm/doc/PDF/examples/History.cpp b/libs/msm/doc/PDF/examples/History.cpp new file mode 100644 index 0000000000..483e122e7f --- /dev/null +++ b/libs/msm/doc/PDF/examples/History.cpp @@ -0,0 +1,240 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> + +namespace msm = boost::msm; +namespace mpl = boost::mpl; + +namespace +{ + // events + struct play {}; + struct end_pause {}; + struct stop {}; + struct pause {}; + struct open_close {}; + struct NextSong {}; + struct PreviousSong {}; + + // A "complicated" event type that carries some data. + struct cd_detected + { + cd_detected(std::string name) + : name(name) + {} + + std::string name; + }; + + // front-end: define the FSM structure + struct player_ : public msm::front::state_machine_def<player_> + { + // The list of FSM states + struct Empty : public msm::front::state<> + { + // every (optional) entry/exit methods get the event passed + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Empty" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Empty" << std::endl;} + }; + struct Open : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Open" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Open" << std::endl;} + }; + + struct Stopped : public msm::front::state<> + { + // when stopped, the CD is loaded + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Stopped" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Stopped" << std::endl;} + }; + + struct Playing_ : public msm::front::state_machine_def<Playing_> + { + // when playing, the CD is loaded and we are in either pause or playing (duh) + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Playing" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Playing" << std::endl;} + + // The list of FSM states + struct Song1 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: First song" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: First Song" << std::endl;} + }; + struct Song2 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: Second song" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: Second Song" << std::endl;} + }; + struct Song3 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: Third song" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: Third Song" << std::endl;} + }; + // the initial state. Must be defined + typedef Song1 initial_state; + // transition actions + void start_next_song(NextSong const&) { std::cout << "Playing::start_next_song\n"; } + void start_prev_song(PreviousSong const&) { std::cout << "Playing::start_prev_song\n"; } + // guard conditions + + typedef Playing_ pl; // makes transition table cleaner + // Transition table for Playing + struct transition_table : mpl::vector4< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Song1 , NextSong , Song2 , &pl::start_next_song >, + a_row < Song2 , PreviousSong, Song1 , &pl::start_prev_song >, + a_row < Song2 , NextSong , Song3 , &pl::start_next_song >, + a_row < Song3 , PreviousSong, Song2 , &pl::start_prev_song > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + + }; + // back-end + // demonstrates Shallow History: if the state gets activated with end_pause + // then it will remember the last active state and reactivate it + // also possible: AlwaysHistory, the last active state will always be reactivated + // or NoHistory, always restart from the initial state + typedef msm::back::state_machine<Playing_,msm::back::ShallowHistory<mpl::vector<end_pause> > > Playing; + + // state not defining any entry or exit + struct Paused : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Paused" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Paused" << std::endl;} + }; + + // the initial state of the player SM. Must be defined + typedef Empty initial_state; + + // transition actions + void start_playback(play const&) { std::cout << "player::start_playback\n"; } + void open_drawer(open_close const&) { std::cout << "player::open_drawer\n"; } + void close_drawer(open_close const&) { std::cout << "player::close_drawer\n"; } + void store_cd_info(cd_detected const& cd) {std::cout << "player::store_cd_info\n";} + void stop_playback(stop const&) { std::cout << "player::stop_playback\n"; } + void pause_playback(pause const&) { std::cout << "player::pause_playback\n"; } + void resume_playback(end_pause const&) { std::cout << "player::resume_playback\n"; } + void stop_and_open(open_close const&) { std::cout << "player::stop_and_open\n"; } + void stopped_again(stop const&){std::cout << "player::stopped_again\n";} + // guard conditions + + typedef player_ p; // makes transition table cleaner + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Stopped , play , Playing , &p::start_playback >, + a_row < Stopped , open_close , Open , &p::open_drawer >, + a_row < Stopped , stop , Stopped , &p::stopped_again >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Open , open_close , Empty , &p::close_drawer >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Empty , open_close , Open , &p::open_drawer >, + a_row < Empty , cd_detected , Stopped , &p::store_cd_info >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Playing , stop , Stopped , &p::stop_playback >, + a_row < Playing , pause , Paused , &p::pause_playback >, + a_row < Playing , open_close , Open , &p::stop_and_open >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Paused , end_pause , Playing , &p::resume_playback >, + a_row < Paused , stop , Stopped , &p::stop_playback >, + a_row < Paused , open_close , Open , &p::stop_and_open > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + + }; + // Pick a back-end + typedef msm::back::state_machine<player_> player; + + // + // Testing utilities. + // + static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" }; + + void pstate(player const& p) + { + std::cout << " -> " << state_names[p.current_state()[0]] << std::endl; + } + + void test() + { + player p; + + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + p.start(); + // go to Open, call on_exit on Empty, then action, then on_entry on Open + p.process_event(open_close()); pstate(p); + p.process_event(open_close()); pstate(p); + p.process_event(cd_detected("louie, louie")); + p.process_event(play()); + + // at this point, Play is active + // make transition happen inside it. Player has no idea about this event but it's ok. + p.process_event(NextSong());pstate(p); //2nd song active + p.process_event(NextSong());pstate(p);//3rd song active + p.process_event(PreviousSong());pstate(p);//2nd song active + + p.process_event(pause()); pstate(p); + // go back to Playing + // as you see, remembers the original state as end_pause is an history trigger + p.process_event(end_pause()); pstate(p); + p.process_event(pause()); pstate(p); + p.process_event(stop()); pstate(p); + // event leading to the same state + p.process_event(stop()); pstate(p); + // play does not trigger shallow history => start back from 1st song + p.process_event(play()); pstate(p); + } +} + +int main() +{ + test(); + return 0; +} diff --git a/libs/msm/doc/PDF/examples/HistoryEuml.cpp b/libs/msm/doc/PDF/examples/HistoryEuml.cpp new file mode 100644 index 0000000000..15a68e2aa3 --- /dev/null +++ b/libs/msm/doc/PDF/examples/HistoryEuml.cpp @@ -0,0 +1,180 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <iostream> + +#include <boost/msm/back/state_machine.hpp> +#include <boost/msm/front/euml/euml.hpp> + +using namespace std; +using namespace boost::msm::front::euml; +namespace msm = boost::msm; +namespace mpl = boost::mpl; + +// entry/exit/action/guard logging functors +#include "logging_functors.h" + +namespace // Concrete FSM implementation +{ + // events + BOOST_MSM_EUML_EVENT(play) + BOOST_MSM_EUML_EVENT(end_pause) + BOOST_MSM_EUML_EVENT(stop) + BOOST_MSM_EUML_EVENT(pause) + BOOST_MSM_EUML_EVENT(open_close) + BOOST_MSM_EUML_EVENT(next_song) + BOOST_MSM_EUML_EVENT(previous_song) + + // A "complicated" event type that carries some data. + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(std::string,cd_name) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(DiskTypeEnum,cd_type) + BOOST_MSM_EUML_ATTRIBUTES((attributes_ << cd_name << cd_type ), cd_detected_attributes) + BOOST_MSM_EUML_EVENT_WITH_ATTRIBUTES(cd_detected,cd_detected_attributes) + + // Concrete FSM implementation + // The list of FSM states + // state not defining any entry or exit + BOOST_MSM_EUML_STATE((),Paused) + BOOST_MSM_EUML_STATE(( Empty_Entry,Empty_Exit ),Empty) + BOOST_MSM_EUML_STATE(( Open_Entry,Open_Exit ),Open) + BOOST_MSM_EUML_STATE(( Stopped_Entry,Stopped_Exit ),Stopped) + + + // Playing is now a state machine itself. + + // It has 3 substates + BOOST_MSM_EUML_STATE(( Song1_Entry,Song1_Exit ),Song1) + BOOST_MSM_EUML_STATE(( Song2_Entry,Song2_Exit ),Song2) + BOOST_MSM_EUML_STATE(( Song3_Entry,Song3_Exit ),Song3) + + + // Playing has a transition table + BOOST_MSM_EUML_TRANSITION_TABLE(( + // +------------------------------------------------------------------------------+ + Song2 == Song1 + next_song / start_next_song, + Song1 == Song2 + previous_song / start_prev_song, + Song3 == Song2 + next_song / start_next_song, + Song2 == Song3 + previous_song / start_prev_song + // +------------------------------------------------------------------------------+ + ),playing_transition_table ) + + // VC9 cannot compile the typedef with build_sm if one is also used for player + BOOST_MSM_EUML_DECLARE_STATE_MACHINE( (playing_transition_table, //STT + init_ << Song1 // Init State + ),Playing_) + // choice of back-end + typedef msm::back::state_machine<Playing_, + msm::back::ShallowHistory<mpl::vector<BOOST_MSM_EUML_EVENT_NAME(end_pause)> > > Playing_type; + Playing_type const Playing; + + // guard conditions + BOOST_MSM_EUML_ACTION(good_disk_format) + { + template <class FSM,class EVT,class SourceState,class TargetState> + bool operator()(EVT const& evt,FSM&,SourceState& ,TargetState& ) + { + // to test a guard condition, let's say we understand only CDs, not DVD + if (evt.get_attribute(cd_type)!=DISK_CD) + { + std::cout << "wrong disk, sorry" << std::endl; + // just for logging, does not block any transition + return true; + } + std::cout << "good disk" << std::endl; + return true; + } + }; + // replaces the old transition table + BOOST_MSM_EUML_TRANSITION_TABLE(( + Playing == Stopped + play / start_playback, + Playing == Paused + end_pause / resume_playback, + // +------------------------------------------------------------------------------+ + Empty == Open + open_close / close_drawer, + // +------------------------------------------------------------------------------+ + Open == Empty + open_close / open_drawer, + Open == Paused + open_close / stop_and_open, + Open == Stopped + open_close / open_drawer, + Open == Playing + open_close / stop_and_open, + // +------------------------------------------------------------------------------+ + Paused == Playing + pause / pause_playback, + // +------------------------------------------------------------------------------+ + Stopped == Playing + stop / stop_playback, + Stopped == Paused + stop / stop_playback, + Stopped == Empty + cd_detected [good_disk_format&& + (event_(cd_type)==Int_<DISK_CD>())] + / (store_cd_info,process_(play)), + Stopped == Stopped + stop + // +------------------------------------------------------------------------------+ + ),transition_table) + + // create a state machine "on the fly" + BOOST_MSM_EUML_DECLARE_STATE_MACHINE(( transition_table, //STT + init_ << Empty, // Init State + no_action, // Entry + no_action, // Exit + attributes_ << no_attributes_, // Attributes + configure_ << no_configure_, // configuration + Log_No_Transition // no_transition handler + ), + player_) //fsm name + + // choice of back-end + typedef msm::back::state_machine<player_> player; + + // + // Testing utilities. + // + static char const* const state_names[] = { "Stopped", "Paused", "Open", "Empty", "Playing" }; + void pstate(player const& p) + { + std::cout << " -> " << state_names[p.current_state()[0]] << std::endl; + } + + void test() + { + player p; + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + p.start(); + // go to Open, call on_exit on Empty, then action, then on_entry on Open + p.process_event(open_close); pstate(p); + p.process_event(open_close); pstate(p); + // will be rejected, wrong disk type + p.process_event( + cd_detected("louie, louie",DISK_DVD)); pstate(p); + p.process_event( + cd_detected("louie, louie",DISK_CD)); pstate(p); + // no need to call play as the previous event does it in its action method + //p.process_event(play); + + // make transition happen inside it. Player has no idea about this event but it's ok. + p.process_event(next_song);pstate(p); //2nd song active + p.process_event(next_song);pstate(p);//3rd song active + p.process_event(previous_song);pstate(p);//2nd song active + + // at this point, Play is active + p.process_event(pause); pstate(p); + // go back to Playing + // as you see, remembers the original state as end_pause is an history trigger + p.process_event(end_pause); pstate(p); + p.process_event(pause); pstate(p); + p.process_event(stop); pstate(p); + // event leading to the same state + // no action method called as none is defined in the transition table + p.process_event(stop); pstate(p); + // test call to no_transition + p.process_event(play); pstate(p); + } +} + +int main() +{ + test(); + return 0; +} diff --git a/libs/msm/doc/PDF/examples/MsmComposite.cpp b/libs/msm/doc/PDF/examples/MsmComposite.cpp new file mode 100644 index 0000000000..556aad42b3 --- /dev/null +++ b/libs/msm/doc/PDF/examples/MsmComposite.cpp @@ -0,0 +1,243 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <boost/msm/back/state_machine.hpp> +#include <boost/msm/front/state_machine_def.hpp> + +namespace msm = boost::msm; +namespace mpl = boost::mpl; + +#include <iostream> +#ifdef WIN32 +#include "windows.h" +#else +#include <sys/time.h> +#endif + +namespace test_fsm // Concrete FSM implementation +{ + // events + struct play {}; + struct end_pause {}; + struct stop {}; + struct pause {}; + struct open_close {}; + struct cd_detected{}; + struct NextSong {}; + struct PreviousSong {}; + + + // Concrete FSM implementation + struct player_ : public msm::front::state_machine_def<player_> + { + // no need for exception handling or message queue + typedef int no_exception_thrown; + typedef int no_message_queue; + + // The list of FSM states + struct Empty : public msm::front::state<> + { + // optional entry/exit methods + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {/*std::cout << "entering: Empty" << std::endl;*/} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {/*std::cout << "leaving: Empty" << std::endl;*/} + }; + struct Open : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {/*std::cout << "entering: Open" << std::endl;*/} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {/*std::cout << "leaving: Open" << std::endl;*/} + }; + + struct Stopped : public msm::front::state<> + { + // when stopped, the CD is loaded + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {/*std::cout << "entering: Stopped" << std::endl;*/} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {/*std::cout << "leaving: Stopped" << std::endl;*/} + }; + + struct Playing_ : public msm::front::state_machine_def<Playing_> + { + // no need for exception handling or message queue + typedef int no_exception_thrown; + typedef int no_message_queue; + + // The list of FSM states + struct Song1 : public msm::front::state<> {}; + struct Song2 : public msm::front::state<> {}; + struct Song3 : public msm::front::state<> {}; + // the initial state. Must be defined + typedef Song1 initial_state; + // transition actions + void start_next_song(NextSong const&) { /*std::cout << "Playing::start_next_song\n";*/ } + void start_prev_song(PreviousSong const&) { /*std::cout << "Playing::start_prev_song\n"; */} + // guard conditions + + typedef Playing_ pl; // makes transition table cleaner + // Transition table for Playing + struct transition_table : mpl::vector4< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Song1 , NextSong , Song2 , &pl::start_next_song >, + a_row < Song2 , PreviousSong, Song1 , &pl::start_prev_song >, + a_row < Song2 , NextSong , Song3 , &pl::start_next_song >, + a_row < Song3 , PreviousSong, Song2 , &pl::start_prev_song > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + typedef msm::back::state_machine<Playing_> Playing; + + // state not defining any entry or exit + struct Paused : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {/*std::cout << "entering: Paused" << std::endl;*/} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {/*std::cout << "leaving: Paused" << std::endl;*/} + }; + + // the initial state of the player SM. Must be defined + typedef Empty initial_state; + + // transition actions + void start_playback(play const&) { } + void open_drawer(open_close const&) { } + void close_drawer(open_close const&) { } + void store_cd_info(cd_detected const& cd) { } + void stop_playback(stop const&) { } + void pause_playback(pause const&) { } + void resume_playback(end_pause const&) { } + void stop_and_open(open_close const&) { } + void stopped_again(stop const&){} + // guard conditions + + typedef player_ p; // makes transition table cleaner + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Stopped , play , Playing , &p::start_playback >, + a_row < Stopped , open_close , Open , &p::open_drawer >, + a_row < Stopped , stop , Stopped , &p::stopped_again >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Open , open_close , Empty , &p::close_drawer >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Empty , open_close , Open , &p::open_drawer >, + a_row < Empty , cd_detected , Stopped , &p::store_cd_info >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Playing , stop , Stopped , &p::stop_playback >, + a_row < Playing , pause , Paused , &p::pause_playback >, + a_row < Playing , open_close , Open , &p::stop_and_open >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Paused , end_pause , Playing , &p::resume_playback >, + a_row < Paused , stop , Stopped , &p::stop_playback >, + a_row < Paused , open_close , Open , &p::stop_and_open > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + typedef msm::back::state_machine<player_> player; + + // + // Testing utilities. + // + static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" }; + + void pstate(player const& p) + { + std::cout << " -> " << state_names[p.current_state()[0]] << std::endl; + } + +} + +#ifndef WIN32 +long mtime(struct timeval& tv1,struct timeval& tv2) +{ + return (tv2.tv_sec-tv1.tv_sec) *1000000 + ((tv2.tv_usec-tv1.tv_usec)); +} +#endif + + +int main() +{ + // for timing +#ifdef WIN32 + LARGE_INTEGER res; + ::QueryPerformanceFrequency(&res); + LARGE_INTEGER li,li2; +#else + struct timeval tv1,tv2; + gettimeofday(&tv1,NULL); +#endif + + test_fsm::player p2; + p2.start(); + // for timing +#ifdef WIN32 + ::QueryPerformanceCounter(&li); +#else + gettimeofday(&tv1,NULL); +#endif + for (int i=0;i<100;++i) + { + p2.process_event(test_fsm::open_close()); + p2.process_event(test_fsm::open_close()); + p2.process_event(test_fsm::cd_detected()); + p2.process_event(test_fsm::play()); + for (int j=0;j<100;++j) + { + p2.process_event(test_fsm::NextSong()); + p2.process_event(test_fsm::NextSong()); + p2.process_event(test_fsm::PreviousSong()); + p2.process_event(test_fsm::PreviousSong()); + } + + p2.process_event(test_fsm::pause()); + // go back to Playing + p2.process_event(test_fsm::end_pause()); + p2.process_event(test_fsm::pause()); + p2.process_event(test_fsm::stop()); + // event leading to the same state + p2.process_event(test_fsm::stop()); + p2.process_event(test_fsm::open_close()); + p2.process_event(test_fsm::open_close()); + } +#ifdef WIN32 + ::QueryPerformanceCounter(&li2); +#else + gettimeofday(&tv2,NULL); +#endif +#ifdef WIN32 + std::cout << "msm took in s:" << (double)(li2.QuadPart-li.QuadPart)/res.QuadPart <<"\n" <<std::endl; +#else + std::cout << "msm took in us:" << mtime(tv1,tv2) <<"\n" <<std::endl; +#endif + return 0; +} + diff --git a/libs/msm/doc/PDF/examples/MsmSimple.cpp b/libs/msm/doc/PDF/examples/MsmSimple.cpp new file mode 100644 index 0000000000..f074a2fc4e --- /dev/null +++ b/libs/msm/doc/PDF/examples/MsmSimple.cpp @@ -0,0 +1,199 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <boost/msm/back/state_machine.hpp> +#include <boost/msm/front/state_machine_def.hpp> + +namespace msm = boost::msm; +namespace mpl = boost::mpl; + +#include <iostream> +#ifdef WIN32 +#include "windows.h" +#else +#include <sys/time.h> +#endif + +namespace test_fsm // Concrete FSM implementation +{ + // events + struct play {}; + struct end_pause {}; + struct stop {}; + struct pause {}; + struct open_close {}; + struct cd_detected{}; + + // Concrete FSM implementation + struct player_ : public msm::front::state_machine_def<player_> + { + // no need for exception handling or message queue + typedef int no_exception_thrown; + typedef int no_message_queue; + + // The list of FSM states + struct Empty : public msm::front::state<> + { + // optional entry/exit methods + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {/*std::cout << "entering: Empty" << std::endl;*/} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {/*std::cout << "leaving: Empty" << std::endl;*/} + }; + struct Open : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {/*std::cout << "entering: Open" << std::endl;*/} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {/*std::cout << "leaving: Open" << std::endl;*/} + }; + + struct Stopped : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {/*std::cout << "entering: Stopped" << std::endl;*/} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {/*std::cout << "leaving: Stopped" << std::endl;*/} + }; + + struct Playing : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {/*std::cout << "entering: Playing" << std::endl;*/} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {/*std::cout << "leaving: Playing" << std::endl;*/} + }; + + struct Paused : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {/*std::cout << "entering: Paused" << std::endl;*/} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {/*std::cout << "leaving: Paused" << std::endl;*/} + }; + + // the initial state of the player SM. Must be defined + typedef Empty initial_state; + // transition actions + void start_playback(play const&) { } + void open_drawer(open_close const&) { } + void close_drawer(open_close const&) { } + void store_cd_info(cd_detected const& cd) { } + void stop_playback(stop const&) { } + void pause_playback(pause const&) { } + void resume_playback(end_pause const&) { } + void stop_and_open(open_close const&) { } + void stopped_again(stop const&){} + // guard conditions + + typedef player_ p; // makes transition table cleaner + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + _row < Stopped , play , Playing >, + _row < Stopped , open_close , Open >, + _row < Stopped , stop , Stopped >, + // +---------+-------------+---------+---------------------+----------------------+ + _row < Open , open_close , Empty >, + // +---------+-------------+---------+---------------------+----------------------+ + _row < Empty , open_close , Open >, + _row < Empty , cd_detected , Stopped >, + // +---------+-------------+---------+---------------------+----------------------+ + _row < Playing , stop , Stopped >, + _row < Playing , pause , Paused >, + _row < Playing , open_close , Open >, + // +---------+-------------+---------+---------------------+----------------------+ + _row < Paused , end_pause , Playing >, + _row < Paused , stop , Stopped >, + _row < Paused , open_close , Open > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + typedef msm::back::state_machine<player_> player; + + // + // Testing utilities. + // + static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" }; + + void pstate(player const& p) + { + std::cout << " -> " << state_names[p.current_state()[0]] << std::endl; + } + +} + +#ifndef WIN32 +long mtime(struct timeval& tv1,struct timeval& tv2) +{ + return (tv2.tv_sec-tv1.tv_sec) *1000000 + ((tv2.tv_usec-tv1.tv_usec)); +} +#endif + + +int main() +{ + // for timing +#ifdef WIN32 + LARGE_INTEGER res; + ::QueryPerformanceFrequency(&res); + LARGE_INTEGER li,li2; +#else + struct timeval tv1,tv2; + gettimeofday(&tv1,NULL); +#endif + + test_fsm::player p2; + p2.start(); + // for timing +#ifdef WIN32 + ::QueryPerformanceCounter(&li); +#else + gettimeofday(&tv1,NULL); +#endif + for (int i=0;i<100;++i) + { + p2.process_event(test_fsm::open_close()); + p2.process_event(test_fsm::open_close()); + p2.process_event(test_fsm::cd_detected()); + p2.process_event(test_fsm::play()); + p2.process_event(test_fsm::pause()); + // go back to Playing + p2.process_event(test_fsm::end_pause()); + p2.process_event(test_fsm::pause()); + p2.process_event(test_fsm::stop()); + // event leading to the same state + p2.process_event(test_fsm::stop()); + p2.process_event(test_fsm::open_close()); + p2.process_event(test_fsm::open_close()); + } +#ifdef WIN32 + ::QueryPerformanceCounter(&li2); +#else + gettimeofday(&tv2,NULL); +#endif +#ifdef WIN32 + std::cout << "msm took in s:" << (double)(li2.QuadPart-li.QuadPart)/res.QuadPart <<"\n" <<std::endl; +#else + std::cout << "msm took in us:" << mtime(tv1,tv2) <<"\n" <<std::endl; +#endif + return 0; +} + diff --git a/libs/msm/doc/PDF/examples/MsmSimpleFunctors.cpp b/libs/msm/doc/PDF/examples/MsmSimpleFunctors.cpp new file mode 100644 index 0000000000..95c0aa650e --- /dev/null +++ b/libs/msm/doc/PDF/examples/MsmSimpleFunctors.cpp @@ -0,0 +1,254 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <boost/msm/back/state_machine.hpp> +#include <boost/msm/front/state_machine_def.hpp> +#include <boost/msm/front/functor_row.hpp> + +namespace msm = boost::msm; +namespace mpl = boost::mpl; +using namespace msm::front; + +#include <iostream> +#ifdef WIN32 +#include "windows.h" +#else +#include <sys/time.h> +#endif + +namespace test_fsm // Concrete FSM implementation +{ + // events + struct play {}; + struct end_pause {}; + struct stop {}; + struct pause {}; + struct open_close {}; + struct cd_detected{}; + + // Concrete FSM implementation + struct player_ : public msm::front::state_machine_def<player_> + { + // no need for exception handling or message queue + typedef int no_exception_thrown; + typedef int no_message_queue; + + // The list of FSM states + struct Empty : public msm::front::state<> + { + // optional entry/exit methods + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {/*std::cout << "entering: Empty" << std::endl;*/} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {/*std::cout << "leaving: Empty" << std::endl;*/} + }; + struct Open : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {/*std::cout << "entering: Open" << std::endl;*/} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {/*std::cout << "leaving: Open" << std::endl;*/} + }; + + struct Stopped : public msm::front::state<> + { + // when stopped, the CD is loaded + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {/*std::cout << "entering: Stopped" << std::endl;*/} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {/*std::cout << "leaving: Stopped" << std::endl;*/} + }; + + struct Playing : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {/*std::cout << "entering: Playing" << std::endl;*/} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {/*std::cout << "leaving: Playing" << std::endl;*/} + }; + + // state not defining any entry or exit + struct Paused : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {/*std::cout << "entering: Paused" << std::endl;*/} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {/*std::cout << "leaving: Paused" << std::endl;*/} + }; + + // the initial state of the player SM. Must be defined + typedef Empty initial_state; + // transition actions + struct start_playback + { + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM&,SourceState& ,TargetState& ) + { + } + }; + struct open_drawer + { + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM&,SourceState& ,TargetState& ) + { + } + }; + struct close_drawer + { + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM&,SourceState& ,TargetState& ) + { + } + }; + struct store_cd_info + { + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const&, FSM& fsm ,SourceState& ,TargetState& ) + { + } + }; + struct stop_playback + { + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM&,SourceState& ,TargetState& ) + { + } + }; + struct pause_playback + { + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM&,SourceState& ,TargetState& ) + { + } + }; + struct resume_playback + { + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM&,SourceState& ,TargetState& ) + { + } + }; + struct stop_and_open + { + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM&,SourceState& ,TargetState& ) + { + } + }; + struct stopped_again + { + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM&,SourceState& ,TargetState& ) + { + } + }; + // guard conditions + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + Row < Stopped , play , Playing , start_playback >, + Row < Stopped , open_close , Open , open_drawer >, + Row < Stopped , stop , Stopped , stopped_again >, + // +---------+-------------+---------+---------------------+----------------------+ + Row < Open , open_close , Empty , close_drawer >, + // +---------+-------------+---------+---------------------+----------------------+ + Row < Empty , open_close , Open , open_drawer >, + Row < Empty , cd_detected , Stopped , store_cd_info >, + // +---------+-------------+---------+---------------------+----------------------+ + Row < Playing , stop , Stopped , stop_playback >, + Row < Playing , pause , Paused , pause_playback >, + Row < Playing , open_close , Open , stop_and_open >, + // +---------+-------------+---------+---------------------+----------------------+ + Row < Paused , end_pause , Playing , resume_playback >, + Row < Paused , stop , Stopped , stop_playback >, + Row < Paused , open_close , Open , stop_and_open > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + typedef msm::back::state_machine<player_> player; + + // + // Testing utilities. + // + static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" }; + + void pstate(player const& p) + { + std::cout << " -> " << state_names[p.current_state()[0]] << std::endl; + } + +} + +#ifndef WIN32 +long mtime(struct timeval& tv1,struct timeval& tv2) +{ + return (tv2.tv_sec-tv1.tv_sec) *1000000 + ((tv2.tv_usec-tv1.tv_usec)); +} +#endif + + +int main() +{ + // for timing +#ifdef WIN32 + LARGE_INTEGER res; + ::QueryPerformanceFrequency(&res); + LARGE_INTEGER li,li2; +#else + struct timeval tv1,tv2; + gettimeofday(&tv1,NULL); +#endif + test_fsm::player p2; + p2.start(); + // for timing +#ifdef WIN32 + ::QueryPerformanceCounter(&li); +#else + gettimeofday(&tv1,NULL); +#endif + for (int i=0;i<100;++i) + { + p2.process_event(test_fsm::open_close()); + p2.process_event(test_fsm::open_close()); + p2.process_event(test_fsm::cd_detected()); + p2.process_event(test_fsm::play()); + p2.process_event(test_fsm::pause()); + // go back to Playing + p2.process_event(test_fsm::end_pause()); + p2.process_event(test_fsm::pause()); + p2.process_event(test_fsm::stop()); + // event leading to the same state + p2.process_event(test_fsm::stop()); + p2.process_event(test_fsm::open_close()); + p2.process_event(test_fsm::open_close()); + } +#ifdef WIN32 + ::QueryPerformanceCounter(&li2); +#else + gettimeofday(&tv2,NULL); +#endif +#ifdef WIN32 + std::cout << "msm took in s:" << (double)(li2.QuadPart-li.QuadPart)/res.QuadPart <<"\n" <<std::endl; +#else + std::cout << "msm took in us:" << mtime(tv1,tv2) <<"\n" <<std::endl; +#endif + return 0; +} + diff --git a/libs/msm/doc/PDF/examples/Orthogonal-deferred.cpp b/libs/msm/doc/PDF/examples/Orthogonal-deferred.cpp new file mode 100644 index 0000000000..facfa67c12 --- /dev/null +++ b/libs/msm/doc/PDF/examples/Orthogonal-deferred.cpp @@ -0,0 +1,314 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> + +namespace msm = boost::msm; +namespace mpl = boost::mpl; + +namespace +{ + // events + struct play {}; + struct end_pause {}; + struct stop {}; + struct pause {}; + struct open_close {}; + struct NextSong {}; + struct PreviousSong {}; + struct error_found {}; + struct end_error {}; + + // Flags. Allow information about a property of the current state + struct PlayingPaused{}; + struct CDLoaded {}; + struct FirstSongPlaying {}; + + // A "complicated" event type that carries some data. + struct cd_detected + { + cd_detected(std::string name) + : name(name) + {} + + std::string name; + }; + + // front-end: define the FSM structure + struct player_ : public msm::front::state_machine_def<player_> + { + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) + { + std::cout << "entering: Player" << std::endl; + } + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) + { + std::cout << "leaving: Player" << std::endl; + } + // The list of FSM states + struct Empty : public msm::front::state<> + { + // if the play event arrives in this state, defer it until a state handles it or + // rejects it + typedef mpl::vector<play> deferred_events; + // every (optional) entry/exit methods get the event passed. + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Empty" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Empty" << std::endl;} + }; + struct Open : public msm::front::state<> + { + // if the play event arrives in this state, defer it until a state handles it or + // rejects it + typedef mpl::vector<play> deferred_events; + typedef mpl::vector1<CDLoaded> flag_list; + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Open" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Open" << std::endl;} + }; + + struct Stopped : public msm::front::state<> + { + // when stopped, the CD is loaded + typedef mpl::vector1<CDLoaded> flag_list; + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Stopped" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Stopped" << std::endl;} + }; + + // the player state machine contains a state which is himself a state machine + // as you see, no need to declare it anywhere so Playing can be developed separately + // by another team in another module. For simplicity I just declare it inside player + struct Playing_ : public msm::front::state_machine_def<Playing_> + { + // when playing, the CD is loaded and we are in either pause or playing (duh) + typedef mpl::vector2<PlayingPaused,CDLoaded> flag_list; + + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Playing" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Playing" << std::endl;} + // The list of FSM states + struct Song1 : public msm::front::state<> + { + typedef mpl::vector1<FirstSongPlaying> flag_list; + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: First song" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: First Song" << std::endl;} + }; + struct Song2 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: Second song" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: Second Song" << std::endl;} + }; + struct Song3 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: Third song" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: Third Song" << std::endl;} + }; + // the initial state. Must be defined + typedef Song1 initial_state; + // transition actions + void start_next_song(NextSong const&) { std::cout << "Playing::start_next_song\n"; } + void start_prev_song(PreviousSong const&) { std::cout << "Playing::start_prev_song\n"; } + // guard conditions + + typedef Playing_ pl; // makes transition table cleaner + // Transition table for Playing + struct transition_table : mpl::vector4< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Song1 , NextSong , Song2 , &pl::start_next_song >, + a_row < Song2 , PreviousSong, Song1 , &pl::start_prev_song >, + a_row < Song2 , NextSong , Song3 , &pl::start_next_song >, + a_row < Song3 , PreviousSong, Song2 , &pl::start_prev_song > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + // back-end + typedef msm::back::state_machine<Playing_> Playing; + + // state not defining any entry or exit + struct Paused : public msm::front::state<> + { + typedef mpl::vector2<PlayingPaused,CDLoaded> flag_list; + }; + struct AllOk : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: AllOk" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: AllOk" << std::endl;} + }; + // this state is also made terminal so that all the events are blocked + struct ErrorMode : //public msm::front::terminate_state<> // ErrorMode terminates the state machine + public msm::front::interrupt_state<end_error> // ErroMode just interrupts. Will resume if + // the event end_error is generated + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: ErrorMode" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: ErrorMode" << std::endl;} + }; + // the initial state of the player SM. Must be defined + typedef mpl::vector<Empty,AllOk> initial_state; + + // transition actions + void start_playback(play const&) { std::cout << "player::start_playback\n"; } + void open_drawer(open_close const&) { std::cout << "player::open_drawer\n"; } + void close_drawer(open_close const&) { std::cout << "player::close_drawer\n"; } + void store_cd_info(cd_detected const& cd) {std::cout << "player::store_cd_info\n";} + void stop_playback(stop const&) { std::cout << "player::stop_playback\n"; } + void pause_playback(pause const&) { std::cout << "player::pause_playback\n"; } + void resume_playback(end_pause const&) { std::cout << "player::resume_playback\n"; } + void stop_and_open(open_close const&) { std::cout << "player::stop_and_open\n"; } + void stopped_again(stop const&){std::cout << "player::stopped_again\n";} + void report_error(error_found const&) {std::cout << "player::report_error\n";} + void report_end_error(end_error const&) {std::cout << "player::report_end_error\n";} + + // guard conditions + + typedef player_ p; // makes transition table cleaner + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Stopped , play , Playing , &p::start_playback >, + a_row < Stopped , open_close , Open , &p::open_drawer >, + a_row < Stopped , stop , Stopped , &p::stopped_again >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Open , open_close , Empty , &p::close_drawer >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Empty , open_close , Open , &p::open_drawer >, + a_row < Empty , cd_detected , Stopped , &p::store_cd_info >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Playing , stop , Stopped , &p::stop_playback >, + a_row < Playing , pause , Paused , &p::pause_playback >, + a_row < Playing , open_close , Open , &p::stop_and_open >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Paused , end_pause , Playing , &p::resume_playback >, + a_row < Paused , stop , Stopped , &p::stop_playback >, + a_row < Paused , open_close , Open , &p::stop_and_open >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < AllOk , error_found ,ErrorMode, &p::report_error >, + a_row <ErrorMode,end_error ,AllOk , &p::report_end_error > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + // Pick a back-end + typedef msm::back::state_machine<player_> player; + + // + // Testing utilities. + // + static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused","AllOk","ErrorMode" }; + + void pstate(player const& p) + { + // we have now several active states, which we show + for (unsigned int i=0;i<player::nr_regions::value;++i) + { + std::cout << " -> " << state_names[p.current_state()[i]] << std::endl; + } + } + + void test() + { + player p; + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + p.start(); + // test deferred event + // deferred in Empty and Open, will be handled only after event cd_detected + p.process_event(play()); + + // tests some flags + std::cout << "CDLoaded active:" << std::boolalpha << p.is_flag_active<CDLoaded>() << std::endl; //=> false (no CD yet) + // go to Open, call on_exit on Empty, then action, then on_entry on Open + p.process_event(open_close()); pstate(p); + p.process_event(open_close()); pstate(p); + p.process_event(cd_detected("louie, louie")); + + // at this point, Play is active (was deferred) + std::cout << "PlayingPaused active:" << std::boolalpha << p.is_flag_active<PlayingPaused>() << std::endl;//=> true + std::cout << "FirstSong active:" << std::boolalpha << p.is_flag_active<FirstSongPlaying>() << std::endl;//=> true + + // make transition happen inside it. Player has no idea about this event but it's ok. + p.process_event(NextSong());pstate(p); //2nd song active + p.process_event(NextSong());pstate(p);//3rd song active + p.process_event(PreviousSong());pstate(p);//2nd song active + std::cout << "FirstSong active:" << std::boolalpha << p.is_flag_active<FirstSongPlaying>() << std::endl;//=> false + + std::cout << "PlayingPaused active:" << std::boolalpha << p.is_flag_active<PlayingPaused>() << std::endl;//=> true + p.process_event(pause()); pstate(p); + std::cout << "PlayingPaused active:" << std::boolalpha << p.is_flag_active<PlayingPaused>() << std::endl;//=> true + // go back to Playing + // as you see, it starts back from the original state + p.process_event(end_pause()); pstate(p); + p.process_event(pause()); pstate(p); + p.process_event(stop()); pstate(p); + std::cout << "PlayingPaused active:" << std::boolalpha << p.is_flag_active<PlayingPaused>() << std::endl;//=> false + std::cout << "CDLoaded active:" << std::boolalpha << p.is_flag_active<CDLoaded>() << std::endl;//=> true + // by default, the flags are OR'ed but you can also use AND. Then the flag must be present in + // all of the active states + std::cout << "CDLoaded active with AND:" << std::boolalpha << p.is_flag_active<CDLoaded,player::Flag_AND>() << std::endl;//=> false + + // event leading to the same state + p.process_event(stop()); pstate(p); + + // event leading to a terminal/interrupt state + p.process_event(error_found()); pstate(p); + // try generating more events + std::cout << "Trying to generate another event" << std::endl; // will not work, fsm is terminated or interrupted + p.process_event(play());pstate(p); + std::cout << "Trying to end the error" << std::endl; // will work only if ErrorMode is interrupt state + p.process_event(end_error());pstate(p); + std::cout << "Trying to generate another event" << std::endl; // will work only if ErrorMode is interrupt state + p.process_event(play());pstate(p); + std::cout << "stop fsm" << std::endl; + p.stop(); + + } +} + +int main() +{ + test(); + return 0; +} diff --git a/libs/msm/doc/PDF/examples/Orthogonal-deferred2.cpp b/libs/msm/doc/PDF/examples/Orthogonal-deferred2.cpp new file mode 100644 index 0000000000..e5fa6c7cb3 --- /dev/null +++ b/libs/msm/doc/PDF/examples/Orthogonal-deferred2.cpp @@ -0,0 +1,303 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> +#include <boost/msm/front/functor_row.hpp> + +namespace msm = boost::msm; +namespace mpl = boost::mpl; +using namespace boost::msm::front; + +namespace +{ + // events + struct play {}; + struct end_pause {}; + struct stop {}; + struct pause {}; + struct open_close {}; + struct NextSong {}; + struct PreviousSong {}; + struct error_found {}; + struct end_error {}; + + // Flags. Allow information about a property of the current state + struct PlayingPaused{}; + struct CDLoaded {}; + struct FirstSongPlaying {}; + + // A "complicated" event type that carries some data. + struct cd_detected + { + cd_detected(std::string name) + : name(name) + {} + + std::string name; + }; + + // front-end: define the FSM structure + struct player_ : public msm::front::state_machine_def<player_> + { + // we want deferred events and no state requires deferred events (only the fsm in the + // transition table), so the fsm does. + typedef int activate_deferred_events; + // The list of FSM states + struct Empty : public msm::front::state<> + { + // every (optional) entry/exit methods get the event passed. + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Empty" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Empty" << std::endl;} + }; + struct Open : public msm::front::state<> + { + typedef mpl::vector1<CDLoaded> flag_list; + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Open" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Open" << std::endl;} + }; + + struct Stopped : public msm::front::state<> + { + // when stopped, the CD is loaded + typedef mpl::vector1<CDLoaded> flag_list; + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Stopped" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Stopped" << std::endl;} + }; + + // the player state machine contains a state which is himself a state machine + // as you see, no need to declare it anywhere so Playing can be developed separately + // by another team in another module. For simplicity I just declare it inside player + struct Playing_ : public msm::front::state_machine_def<Playing_> + { + // when playing, the CD is loaded and we are in either pause or playing (duh) + typedef mpl::vector2<PlayingPaused,CDLoaded> flag_list; + + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Playing" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Playing" << std::endl;} + // The list of FSM states + struct Song1 : public msm::front::state<> + { + typedef mpl::vector1<FirstSongPlaying> flag_list; + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: First song" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: First Song" << std::endl;} + }; + struct Song2 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: Second song" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: Second Song" << std::endl;} + }; + struct Song3 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: Third song" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: Third Song" << std::endl;} + }; + // the initial state. Must be defined + typedef Song1 initial_state; + // transition actions + void start_next_song(NextSong const&) { std::cout << "Playing::start_next_song\n"; } + void start_prev_song(PreviousSong const&) { std::cout << "Playing::start_prev_song\n"; } + // guard conditions + + typedef Playing_ pl; // makes transition table cleaner + // Transition table for Playing + struct transition_table : mpl::vector4< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Song1 , NextSong , Song2 , &pl::start_next_song >, + a_row < Song2 , PreviousSong, Song1 , &pl::start_prev_song >, + a_row < Song2 , NextSong , Song3 , &pl::start_next_song >, + a_row < Song3 , PreviousSong, Song2 , &pl::start_prev_song > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + // back-end + typedef msm::back::state_machine<Playing_> Playing; + + // state not defining any entry or exit + struct Paused : public msm::front::state<> + { + typedef mpl::vector2<PlayingPaused,CDLoaded> flag_list; + }; + struct AllOk : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: AllOk" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: AllOk" << std::endl;} + }; + // this state is also made terminal so that all the events are blocked + struct ErrorMode : //public msm::front::terminate_state<> // ErrorMode terminates the state machine + public msm::front::interrupt_state<end_error> // ErroMode just interrupts. Will resume if + // the event end_error is generated + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: ErrorMode" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: ErrorMode" << std::endl;} + }; + // the initial state of the player SM. Must be defined + typedef mpl::vector<Empty,AllOk> initial_state; + + // transition actions + void start_playback(play const&) { std::cout << "player::start_playback\n"; } + void open_drawer(open_close const&) { std::cout << "player::open_drawer\n"; } + void close_drawer(open_close const&) { std::cout << "player::close_drawer\n"; } + void store_cd_info(cd_detected const& cd) {std::cout << "player::store_cd_info\n";} + void stop_playback(stop const&) { std::cout << "player::stop_playback\n"; } + void pause_playback(pause const&) { std::cout << "player::pause_playback\n"; } + void resume_playback(end_pause const&) { std::cout << "player::resume_playback\n"; } + void stop_and_open(open_close const&) { std::cout << "player::stop_and_open\n"; } + void stopped_again(stop const&){std::cout << "player::stopped_again\n";} + void report_error(error_found const&) {std::cout << "player::report_error\n";} + void report_end_error(end_error const&) {std::cout << "player::report_end_error\n";} + + // guard conditions + + typedef player_ p; // makes transition table cleaner + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Stopped , play , Playing , &p::start_playback >, + a_row < Stopped , open_close , Open , &p::open_drawer >, + a_row < Stopped , stop , Stopped , &p::stopped_again >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Open , open_close , Empty , &p::close_drawer >, + Row < Open , play , none , Defer , none >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Empty , open_close , Open , &p::open_drawer >, + a_row < Empty , cd_detected , Stopped , &p::store_cd_info >, + Row < Empty , play , none , Defer , none >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Playing , stop , Stopped , &p::stop_playback >, + a_row < Playing , pause , Paused , &p::pause_playback >, + a_row < Playing , open_close , Open , &p::stop_and_open >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Paused , end_pause , Playing , &p::resume_playback >, + a_row < Paused , stop , Stopped , &p::stop_playback >, + a_row < Paused , open_close , Open , &p::stop_and_open >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < AllOk , error_found ,ErrorMode, &p::report_error >, + a_row <ErrorMode,end_error ,AllOk , &p::report_end_error > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + // Pick a back-end + typedef msm::back::state_machine<player_> player; + + // + // Testing utilities. + // + static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused","AllOk","ErrorMode" }; + + void pstate(player const& p) + { + // we have now several active states, which we show + for (unsigned int i=0;i<player::nr_regions::value;++i) + { + std::cout << " -> " << state_names[p.current_state()[i]] << std::endl; + } + } + + void test() + { + player p; + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + p.start(); + // test deferred event + // deferred in Empty and Open, will be handled only after event cd_detected + p.process_event(play()); + + // tests some flags + std::cout << "CDLoaded active:" << std::boolalpha << p.is_flag_active<CDLoaded>() << std::endl; //=> false (no CD yet) + // go to Open, call on_exit on Empty, then action, then on_entry on Open + p.process_event(open_close()); pstate(p); + p.process_event(open_close()); pstate(p); + p.process_event(cd_detected("louie, louie")); + + // at this point, Play is active (was deferred) + std::cout << "PlayingPaused active:" << std::boolalpha << p.is_flag_active<PlayingPaused>() << std::endl;//=> true + std::cout << "FirstSong active:" << std::boolalpha << p.is_flag_active<FirstSongPlaying>() << std::endl;//=> true + + // make transition happen inside it. Player has no idea about this event but it's ok. + p.process_event(NextSong());pstate(p); //2nd song active + p.process_event(NextSong());pstate(p);//3rd song active + p.process_event(PreviousSong());pstate(p);//2nd song active + std::cout << "FirstSong active:" << std::boolalpha << p.is_flag_active<FirstSongPlaying>() << std::endl;//=> false + + std::cout << "PlayingPaused active:" << std::boolalpha << p.is_flag_active<PlayingPaused>() << std::endl;//=> true + p.process_event(pause()); pstate(p); + std::cout << "PlayingPaused active:" << std::boolalpha << p.is_flag_active<PlayingPaused>() << std::endl;//=> true + // go back to Playing + // as you see, it starts back from the original state + p.process_event(end_pause()); pstate(p); + p.process_event(pause()); pstate(p); + p.process_event(stop()); pstate(p); + std::cout << "PlayingPaused active:" << std::boolalpha << p.is_flag_active<PlayingPaused>() << std::endl;//=> false + std::cout << "CDLoaded active:" << std::boolalpha << p.is_flag_active<CDLoaded>() << std::endl;//=> true + // by default, the flags are OR'ed but you can also use AND. Then the flag must be present in + // all of the active states + std::cout << "CDLoaded active with AND:" << std::boolalpha << p.is_flag_active<CDLoaded,player::Flag_AND>() << std::endl;//=> false + + // event leading to the same state + p.process_event(stop()); pstate(p); + + // event leading to a terminal/interrupt state + p.process_event(error_found()); pstate(p); + // try generating more events + std::cout << "Trying to generate another event" << std::endl; // will not work, fsm is terminated or interrupted + p.process_event(play());pstate(p); + std::cout << "Trying to end the error" << std::endl; // will work only if ErrorMode is interrupt state + p.process_event(end_error());pstate(p); + std::cout << "Trying to generate another event" << std::endl; // will work only if ErrorMode is interrupt state + p.process_event(play());pstate(p); + + } +} + +int main() +{ + test(); + return 0; +} diff --git a/libs/msm/doc/PDF/examples/OrthogonalDeferredEuml.cpp b/libs/msm/doc/PDF/examples/OrthogonalDeferredEuml.cpp new file mode 100644 index 0000000000..9b3553776b --- /dev/null +++ b/libs/msm/doc/PDF/examples/OrthogonalDeferredEuml.cpp @@ -0,0 +1,263 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <vector> +#include <iostream> + +#include <boost/msm/back/state_machine.hpp> +#include <boost/msm/front/euml/euml.hpp> + +using namespace std; +using namespace boost::msm::front::euml; +namespace msm = boost::msm; + +// entry/exit/action/guard logging functors +#include "logging_functors.h" + +namespace // Concrete FSM implementation +{ + // events + BOOST_MSM_EUML_EVENT(play) + BOOST_MSM_EUML_EVENT(end_pause) + BOOST_MSM_EUML_EVENT(stop) + BOOST_MSM_EUML_EVENT(pause) + BOOST_MSM_EUML_EVENT(open_close) + BOOST_MSM_EUML_EVENT(next_song) + BOOST_MSM_EUML_EVENT(previous_song) + BOOST_MSM_EUML_EVENT(end_error) + BOOST_MSM_EUML_EVENT(error_found) + + // A "complicated" event type that carries some data. + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(std::string,cd_name) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(DiskTypeEnum,cd_type) + BOOST_MSM_EUML_ATTRIBUTES((attributes_ << cd_name << cd_type ), cd_detected_attributes) + BOOST_MSM_EUML_EVENT_WITH_ATTRIBUTES(cd_detected,cd_detected_attributes) + + + // Flags. Allow information about a property of the current state + BOOST_MSM_EUML_FLAG(PlayingPaused) + BOOST_MSM_EUML_FLAG(CDLoaded) + BOOST_MSM_EUML_FLAG(FirstSongPlaying) + + // Concrete FSM implementation + + // The list of FSM states + + BOOST_MSM_EUML_STATE(( Empty_Entry, + Empty_Exit, + attributes_ << no_attributes_, + configure_ << play // defer play + ), + Empty) + + BOOST_MSM_EUML_STATE(( Open_Entry, + Open_Exit, + attributes_ << no_attributes_, + configure_<< CDLoaded << play // defer play, flag state with CDLoaded + ), + Open) + + BOOST_MSM_EUML_STATE(( Stopped_Entry, + Stopped_Exit, + attributes_ << no_attributes_, + configure_<< CDLoaded // flag state with CDLoaded + ), + Stopped) + + // state not defining any entry or exit + BOOST_MSM_EUML_STATE(( no_action, + no_action, + attributes_ << no_attributes_, + configure_<< PlayingPaused << CDLoaded // flag state with CDLoaded and PlayingPaused + ), + Paused) + + BOOST_MSM_EUML_STATE(( AllOk_Entry,AllOk_Exit ),AllOk) + + // a terminate state + //BOOST_MSM_EUML_TERMINATE_STATE(( ErrorMode_Entry,ErrorMode_Exit ),ErrorMode) + // or as an interrupt state + BOOST_MSM_EUML_INTERRUPT_STATE(( end_error,ErrorMode_Entry,ErrorMode_Exit ),ErrorMode) + + // Playing is now a state machine itself. + // It has 3 substates + BOOST_MSM_EUML_STATE(( Song1_Entry, + Song1_Exit, + attributes_ << no_attributes_, + configure_<< FirstSongPlaying ),Song1) + + BOOST_MSM_EUML_STATE(( Song2_Entry,Song2_Exit ),Song2) + BOOST_MSM_EUML_STATE(( Song3_Entry,Song3_Exit ),Song3) + + // Playing has a transition table + BOOST_MSM_EUML_TRANSITION_TABLE(( + // +------------------------------------------------------------------------------+ + Song2 == Song1 + next_song / start_next_song, + Song1 == Song2 + previous_song / start_prev_song, + Song3 == Song2 + next_song / start_next_song, + Song2 == Song3 + previous_song / start_prev_song + // +------------------------------------------------------------------------------+ + ),playing_transition_table ) + + BOOST_MSM_EUML_DECLARE_STATE_MACHINE( (playing_transition_table, //STT + init_ << Song1, // Init State + no_action, // entry + no_action, // exit + attributes_ << no_attributes_, //attributes + configure_<< PlayingPaused << CDLoaded //flags + ),Playing_) + // choice of back-end + typedef msm::back::state_machine<Playing_> Playing_type; + Playing_type const Playing; + + // guard conditions + BOOST_MSM_EUML_ACTION(good_disk_format) + { + template <class FSM,class EVT,class SourceState,class TargetState> + bool operator()(EVT const& evt,FSM&,SourceState& ,TargetState& ) + { + // to test a guard condition, let's say we understand only CDs, not DVD + if (evt.get_attribute(cd_type)!=DISK_CD) + { + std::cout << "wrong disk, sorry" << std::endl; + // just for logging, does not block any transition + return true; + } + std::cout << "good disk" << std::endl; + return true; + } + }; + // replaces the old transition table + BOOST_MSM_EUML_TRANSITION_TABLE(( + Playing == Stopped + play / start_playback , + Playing == Paused + end_pause / resume_playback, + // +------------------------------------------------------------------------------+ + Empty == Open + open_close / close_drawer, + // +------------------------------------------------------------------------------+ + Open == Empty + open_close / open_drawer, + Open == Paused + open_close / stop_and_open, + Open == Stopped + open_close / open_drawer, + Open == Playing + open_close / stop_and_open, + // +------------------------------------------------------------------------------+ + Paused == Playing + pause / pause_playback, + // +------------------------------------------------------------------------------+ + Stopped == Playing + stop / stop_playback, + Stopped == Paused + stop / stop_playback, + Stopped == Empty + cd_detected [good_disk_format&& + (event_(cd_type)==Int_<DISK_CD>())] + / (store_cd_info,process_(play)), + Stopped == Stopped + stop, + ErrorMode == AllOk + error_found / report_error, + AllOk == ErrorMode+ end_error / report_end_error + // +------------------------------------------------------------------------------+ + ),transition_table) + + // create a state machine "on the fly" + BOOST_MSM_EUML_DECLARE_STATE_MACHINE(( transition_table, //STT + init_ << Empty<< AllOk, // Init State + no_action, // Entry + no_action, // Exit + attributes_ << no_attributes_, // Attributes + configure_ << no_configure_, // configuration + Log_No_Transition // no_transition handler + ), + player_) //fsm name + + // choice of back-end + typedef msm::back::state_machine<player_> player; + + // + // Testing utilities. + // + static char const* const state_names[] = { "Stopped", "Paused", "Open", "Empty", "Playing","AllOk","ErrorMode" }; + void pstate(player const& p) + { + // we have now several active states, which we show + for (unsigned int i=0;i<player::nr_regions::value;++i) + { + std::cout << " -> " << state_names[p.current_state()[i]] << std::endl; + } + } + + void test() + { + player p; + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + p.start(); + + // tests some flags + std::cout << "CDLoaded active:" << std::boolalpha + << p.is_flag_active<BOOST_MSM_EUML_FLAG_NAME(CDLoaded)>() << std::endl; //=> false (no CD yet) + // go to Open, call on_exit on Empty, then action, then on_entry on Open + p.process_event(open_close); pstate(p); + p.process_event(open_close); pstate(p); + // will be rejected, wrong disk type + p.process_event( + cd_detected("louie, louie",DISK_DVD)); pstate(p); + p.process_event( + cd_detected("louie, louie",DISK_CD)); pstate(p); + // no need to call play as the previous event does it in its action method + //p.process_event(play); + + // at this point, Play is active + std::cout << "PlayingPaused active:" << std::boolalpha + << p.is_flag_active<BOOST_MSM_EUML_FLAG_NAME(PlayingPaused)>() << std::endl;//=> true + std::cout << "FirstSong active:" << std::boolalpha + << p.is_flag_active<BOOST_MSM_EUML_FLAG_NAME(FirstSongPlaying)>() << std::endl;//=> true + + // make transition happen inside it. Player has no idea about this event but it's ok. + p.process_event(next_song);pstate(p); //2nd song active + p.process_event(next_song);pstate(p);//3rd song active + p.process_event(previous_song);pstate(p);//2nd song active + std::cout << "FirstSong active:" << std::boolalpha + << p.is_flag_active<BOOST_MSM_EUML_FLAG_NAME(FirstSongPlaying)>() << std::endl;//=> false + std::cout << "PlayingPaused active:" << std::boolalpha + << p.is_flag_active<BOOST_MSM_EUML_FLAG_NAME(PlayingPaused)>() << std::endl;//=> true + + // at this point, Play is active + p.process_event(pause); pstate(p); + std::cout << "PlayingPaused active:" << std::boolalpha + << p.is_flag_active<BOOST_MSM_EUML_FLAG_NAME(PlayingPaused)>() << std::endl;//=> true + + // go back to Playing + p.process_event(end_pause); pstate(p); + p.process_event(pause); pstate(p); + p.process_event(stop); pstate(p); + std::cout << "PlayingPaused active:" << std::boolalpha + << p.is_flag_active<BOOST_MSM_EUML_FLAG_NAME(PlayingPaused)>() << std::endl;//=> false + std::cout << "CDLoaded active:" << std::boolalpha + << p.is_flag_active<BOOST_MSM_EUML_FLAG_NAME(CDLoaded)>() << std::endl;//=> true + // by default, the flags are OR'ed but you can also use AND. Then the flag must be present in + // all of the active states + std::cout << "CDLoaded active with AND:" << std::boolalpha + << p.is_flag_active<BOOST_MSM_EUML_FLAG_NAME(CDLoaded),player::Flag_AND>() << std::endl;//=> false + + // event leading to the same state + // no action method called as none is defined in the transition table + p.process_event(stop); pstate(p); + + // event leading to a terminal/interrupt state + p.process_event(error_found); pstate(p); + // try generating more events + std::cout << "Trying to generate another event" << std::endl; // will not work, fsm is terminated or interrupted + p.process_event(play);pstate(p); + std::cout << "Trying to end the error" << std::endl; // will work only if ErrorMode is interrupt state + p.process_event(end_error);pstate(p); + std::cout << "Trying to generate another event" << std::endl; // will work only if ErrorMode is interrupt state + p.process_event(play);pstate(p); + + } +} + +int main() +{ + test(); + return 0; +} diff --git a/libs/msm/doc/PDF/examples/OrthogonalDeferredEuml2.cpp b/libs/msm/doc/PDF/examples/OrthogonalDeferredEuml2.cpp new file mode 100644 index 0000000000..d0a80c69ae --- /dev/null +++ b/libs/msm/doc/PDF/examples/OrthogonalDeferredEuml2.cpp @@ -0,0 +1,251 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <vector> +#include <iostream> + +#include <boost/msm/back/state_machine.hpp> +#include <boost/msm/front/euml/euml.hpp> + +using namespace std; +using namespace boost::msm::front::euml; +namespace msm = boost::msm; + +// entry/exit/action/guard logging functors +#include "logging_functors.h" + +namespace // Concrete FSM implementation +{ + // events + BOOST_MSM_EUML_EVENT(play) + BOOST_MSM_EUML_EVENT(end_pause) + BOOST_MSM_EUML_EVENT(stop) + BOOST_MSM_EUML_EVENT(pause) + BOOST_MSM_EUML_EVENT(open_close) + BOOST_MSM_EUML_EVENT(next_song) + BOOST_MSM_EUML_EVENT(previous_song) + BOOST_MSM_EUML_EVENT(end_error) + BOOST_MSM_EUML_EVENT(error_found) + + // A "complicated" event type that carries some data. + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(std::string,cd_name) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(DiskTypeEnum,cd_type) + BOOST_MSM_EUML_ATTRIBUTES((attributes_ << cd_name << cd_type ), cd_detected_attributes) + BOOST_MSM_EUML_EVENT_WITH_ATTRIBUTES(cd_detected,cd_detected_attributes) + + // Flags. Allow information about a property of the current state + BOOST_MSM_EUML_FLAG(PlayingPaused) + BOOST_MSM_EUML_FLAG(CDLoaded) + BOOST_MSM_EUML_FLAG(FirstSongPlaying) + + // Concrete FSM implementation + // The list of FSM states + BOOST_MSM_EUML_STATE(( Empty_Entry, + Empty_Exit, + attributes_ << no_attributes_, + configure_ << no_configure_ + ), + Empty) + + BOOST_MSM_EUML_STATE(( Open_Entry, + Open_Exit, + attributes_ << no_attributes_, + configure_<< CDLoaded // flag state with CDLoaded + ), + Open) + + BOOST_MSM_EUML_STATE(( Stopped_Entry, + Stopped_Exit, + attributes_ << no_attributes_, + configure_<< CDLoaded // flag state with CDLoaded + ), + Stopped) + + // state not defining any entry or exit + BOOST_MSM_EUML_STATE(( no_action, + no_action, + attributes_ << no_attributes_, + configure_<< PlayingPaused << CDLoaded // flag state with CDLoaded and PlayingPaused + ), + Paused) + + BOOST_MSM_EUML_STATE(( AllOk_Entry,AllOk_Exit ),AllOk) + + // a terminate state + //BOOST_MSM_EUML_TERMINATE_STATE(( ErrorMode_Entry,ErrorMode_Exit ),ErrorMode) + // or as an interrupt state + BOOST_MSM_EUML_INTERRUPT_STATE(( end_error,ErrorMode_Entry,ErrorMode_Exit ),ErrorMode) + + // Playing is now a state machine itself. + + // It has 3 substates + BOOST_MSM_EUML_STATE(( Song1_Entry, + Song1_Exit, + attributes_ << no_attributes_, + configure_<< FirstSongPlaying ),Song1) + + BOOST_MSM_EUML_STATE(( Song2_Entry,Song2_Exit ),Song2) + BOOST_MSM_EUML_STATE(( Song3_Entry,Song3_Exit ),Song3) + + + // Playing has a transition table + BOOST_MSM_EUML_TRANSITION_TABLE(( + // +------------------------------------------------------------------------------+ + Song2 == Song1 + next_song / start_next_song, + Song1 == Song2 + previous_song / start_prev_song, + Song3 == Song2 + next_song / start_next_song, + Song2 == Song3 + previous_song / start_prev_song + // +------------------------------------------------------------------------------+ + ),playing_transition_table ) + + BOOST_MSM_EUML_DECLARE_STATE_MACHINE( (playing_transition_table, //STT + init_ << Song1, // Init State + no_action, // entry + no_action, // exit + attributes_ << no_attributes_, //attributes + configure_<< PlayingPaused << CDLoaded //flags + ),Playing_) + + // choice of back-end + typedef msm::back::state_machine<Playing_> Playing_type; + Playing_type const Playing; + + // guard conditions + BOOST_MSM_EUML_ACTION(good_disk_format) + { + template <class FSM,class EVT,class SourceState,class TargetState> + bool operator()(EVT const& evt,FSM&,SourceState& ,TargetState& ) + { + // to test a guard condition, let's say we understand only CDs, not DVD + if (evt.get_attribute(cd_type)!=DISK_CD) + { + std::cout << "wrong disk, sorry" << std::endl; + // just for logging, does not block any transition + return true; + } + std::cout << "good disk" << std::endl; + return true; + } + }; + // replaces the old transition table + BOOST_MSM_EUML_TRANSITION_TABLE(( + Playing == Stopped + play / start_playback , + Playing == Paused + end_pause / resume_playback , + // +------------------------------------------------------------------------------+ + Empty == Open + open_close / close_drawer, + // we now defer using the defer_ function. This will need deferred_events as config (see below) + Empty + play / defer_ , + // +------------------------------------------------------------------------------+ + Open == Empty + open_close / open_drawer , + Open == Paused + open_close / stop_and_open , + Open == Stopped + open_close / open_drawer , + Open == Playing + open_close / stop_and_open , + // we now defer using the defer_ function. This will need deferred_events as config (see below) + Open + play / defer_ , + // +------------------------------------------------------------------------------+ + Paused == Playing + pause / pause_playback , + // +------------------------------------------------------------------------------+ + Stopped == Playing + stop / stop_playback , + Stopped == Paused + stop / stop_playback , + Stopped == Empty + cd_detected [good_disk_format&& + (event_(cd_type)==Int_<DISK_CD>())] + / (store_cd_info,process_(play)), + Stopped == Stopped + stop , + ErrorMode == AllOk + error_found / report_error , + AllOk == ErrorMode+ end_error / report_end_error + // +------------------------------------------------------------------------------+ + ),transition_table) + + // create a state machine "on the fly" + BOOST_MSM_EUML_DECLARE_STATE_MACHINE(( transition_table, //STT + init_ << Empty << AllOk, // Init State + no_action, // Entry + no_action, // Exit + attributes_ << no_attributes_, // Attributes + configure_ << deferred_events, // configuration + Log_No_Transition // no_transition handler + ), + player_) //fsm name + + // choice of back-end + typedef msm::back::state_machine<player_> player; + + // + // Testing utilities. + // + static char const* const state_names[] = { "Stopped", "Paused", "Open", "Empty", "Playing","AllOk","ErrorMode" }; + void pstate(player const& p) + { + // we have now several active states, which we show + for (unsigned int i=0;i<player::nr_regions::value;++i) + { + std::cout << " -> " << state_names[p.current_state()[i]] << std::endl; + } + } + + void test() + { + player p; + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + p.start(); + + // tests some flags + std::cout << "CDLoaded active:" << std::boolalpha + << p.is_flag_active<BOOST_MSM_EUML_FLAG_NAME(CDLoaded)>() << std::endl; //=> false (no CD yet) + // go to Open, call on_exit on Empty, then action, then on_entry on Open + p.process_event(open_close); pstate(p); + p.process_event(open_close); pstate(p); + // will be rejected, wrong disk type + p.process_event( + cd_detected("louie, louie",DISK_DVD)); pstate(p); + p.process_event( + cd_detected("louie, louie",DISK_CD)); pstate(p); + // no need to call play as the previous event does it in its action method + //p.process_event(play); + + // at this point, Play is active + p.process_event(pause); pstate(p); + std::cout << "PlayingPaused active:" << std::boolalpha + << p.is_flag_active<BOOST_MSM_EUML_FLAG_NAME(PlayingPaused)>() << std::endl;//=> true + + // go back to Playing + p.process_event(end_pause); pstate(p); + p.process_event(pause); pstate(p); + p.process_event(stop); pstate(p); + std::cout << "PlayingPaused active:" << std::boolalpha + << p.is_flag_active<BOOST_MSM_EUML_FLAG_NAME(PlayingPaused)>() << std::endl;//=> false + std::cout << "CDLoaded active:" << std::boolalpha + << p.is_flag_active<BOOST_MSM_EUML_FLAG_NAME(CDLoaded)>() << std::endl;//=> true + // by default, the flags are OR'ed but you can also use AND. Then the flag must be present in + // all of the active states + std::cout << "CDLoaded active with AND:" << std::boolalpha + << p.is_flag_active<BOOST_MSM_EUML_FLAG_NAME(CDLoaded),player::Flag_AND>() << std::endl;//=> false + + // event leading to the same state + // no action method called as none is defined in the transition table + p.process_event(stop); pstate(p); + + // event leading to a terminal/interrupt state + p.process_event(error_found); pstate(p); + // try generating more events + std::cout << "Trying to generate another event" << std::endl; // will not work, fsm is terminated or interrupted + p.process_event(play);pstate(p); + std::cout << "Trying to end the error" << std::endl; // will work only if ErrorMode is interrupt state + p.process_event(end_error);pstate(p); + std::cout << "Trying to generate another event" << std::endl; // will work only if ErrorMode is interrupt state + p.process_event(play);pstate(p); + } +} + +int main() +{ + test(); + return 0; +} diff --git a/libs/msm/doc/PDF/examples/ParsingDigits.cpp b/libs/msm/doc/PDF/examples/ParsingDigits.cpp new file mode 100644 index 0000000000..6dadb4d53e --- /dev/null +++ b/libs/msm/doc/PDF/examples/ParsingDigits.cpp @@ -0,0 +1,479 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#define FUSION_MAX_VECTOR_SIZE 20 + +#include <boost/msm/back/state_machine.hpp> +#include "char_event_dispatcher.hpp" +#include <boost/msm/front/state_machine_def.hpp> +#include <boost/msm/front/functor_row.hpp> +#include <boost/timer.hpp> + +namespace msm = boost::msm; +namespace mpl = boost::mpl; +using namespace msm::front; + +#include <iostream> +#ifdef WIN32 +#include "windows.h" +#else +#include <sys/time.h> +#endif + +// events +struct end_sub {template <class Event> end_sub(Event const&){}}; +struct other_char {}; +struct default_char {}; +struct eos {}; + + +namespace test_fsm // Concrete FSM implementation +{ + // Concrete FSM implementation + struct parsing_ : public msm::front::state_machine_def<parsing_> + { + // no need for exception handling or message queue + typedef int no_exception_thrown; + typedef int no_message_queue; + + struct Waiting : public msm::front::state<> + { + // optional entry/exit methods + //template <class Event,class FSM> + //void on_entry(Event const&,FSM& ) {std::cout << "entering: Waiting" << std::endl;} + //template <class Event,class FSM> + //void on_exit(Event const&,FSM& ) {std::cout << "leaving: Waiting" << std::endl;} + }; + struct Digit1 : public msm::front::state<> + { + // optional entry/exit methods + //template <class Event,class FSM> + //void on_entry(Event const&,FSM& ) {std::cout << "entering: Digit1" << std::endl;} + //template <class Event,class FSM> + //void on_exit(Event const&,FSM& ) {std::cout << "leaving: Digit1" << std::endl;} + }; + struct Digit2 : public msm::front::state<> + { + // optional entry/exit methods + //template <class Event,class FSM> + //void on_entry(Event const&,FSM& ) {std::cout << "entering: Digit2" << std::endl;} + //template <class Event,class FSM> + //void on_exit(Event const&,FSM& ) {std::cout << "leaving: Digit2" << std::endl;} + }; + struct Digit3 : public msm::front::state<> + { + // optional entry/exit methods + //template <class Event,class FSM> + //void on_entry(Event const&,FSM& ) {std::cout << "entering: Digit3" << std::endl;} + //template <class Event,class FSM> + //void on_exit(Event const&,FSM& ) {std::cout << "leaving: Digit3" << std::endl;} + }; + struct Digit4 : public msm::front::state<> + { + // optional entry/exit methods + //template <class Event,class FSM> + //void on_entry(Event const&,FSM& ) {std::cout << "entering: Digit4" << std::endl;} + //template <class Event,class FSM> + //void on_exit(Event const&,FSM& ) {std::cout << "leaving: Digit4" << std::endl;} + }; + struct MinusChar1 : public msm::front::state<> + { + // optional entry/exit methods + //template <class Event,class FSM> + //void on_entry(Event const&,FSM& ) {std::cout << "entering: MinusChar1" << std::endl;} + //template <class Event,class FSM> + //void on_exit(Event const&,FSM& ) {std::cout << "leaving: MinusChar1" << std::endl;} + }; + struct Digit5 : public msm::front::state<> + { + // optional entry/exit methods + //template <class Event,class FSM> + //void on_entry(Event const&,FSM& ) {std::cout << "entering: Digit5" << std::endl;} + //template <class Event,class FSM> + //void on_exit(Event const&,FSM& ) {std::cout << "leaving: Digit5" << std::endl;} + }; + struct Digit6 : public msm::front::state<> + { + // optional entry/exit methods + //template <class Event,class FSM> + //void on_entry(Event const&,FSM& ) {std::cout << "entering: Digit6" << std::endl;} + //template <class Event,class FSM> + //void on_exit(Event const&,FSM& ) {std::cout << "leaving: Digit6" << std::endl;} + }; + struct Digit7 : public msm::front::state<> + { + // optional entry/exit methods + //template <class Event,class FSM> + //void on_entry(Event const&,FSM& ) {std::cout << "entering: Digit7" << std::endl;} + //template <class Event,class FSM> + //void on_exit(Event const&,FSM& ) {std::cout << "leaving: Digit7" << std::endl;} + }; + struct Digit8 : public msm::front::state<> + { + // optional entry/exit methods + //template <class Event,class FSM> + //void on_entry(Event const&,FSM& ) {std::cout << "entering: Digit8" << std::endl;} + //template <class Event,class FSM> + //void on_exit(Event const&,FSM& ) {std::cout << "leaving: Digit8" << std::endl;} + }; + struct MinusChar2 : public msm::front::state<> + { + // optional entry/exit methods + //template <class Event,class FSM> + //void on_entry(Event const&,FSM& ) {std::cout << "entering: MinusChar2" << std::endl;} + //template <class Event,class FSM> + //void on_exit(Event const&,FSM& ) {std::cout << "leaving: MinusChar2" << std::endl;} + }; + struct Digit9 : public msm::front::state<> + { + // optional entry/exit methods + //template <class Event,class FSM> + //void on_entry(Event const&,FSM& ) {std::cout << "entering: Digit9" << std::endl;} + //template <class Event,class FSM> + //void on_exit(Event const&,FSM& ) {std::cout << "leaving: Digit9" << std::endl;} + }; + struct Digit10 : public msm::front::state<> + { + // optional entry/exit methods + //template <class Event,class FSM> + //void on_entry(Event const&,FSM& ) {std::cout << "entering: Digit10" << std::endl;} + //template <class Event,class FSM> + //void on_exit(Event const&,FSM& ) {std::cout << "leaving: Digit10" << std::endl;} + }; + struct Digit11 : public msm::front::state<> + { + // optional entry/exit methods + //template <class Event,class FSM> + //void on_entry(Event const&,FSM& ) {std::cout << "entering: Digit11" << std::endl;} + //template <class Event,class FSM> + //void on_exit(Event const&,FSM& ) {std::cout << "leaving: Digit11" << std::endl;} + }; + struct Digit12 : public msm::front::state<> + { + // optional entry/exit methods + //template <class Event,class FSM> + //void on_entry(Event const&,FSM& ) {std::cout << "entering: Digit12" << std::endl;} + //template <class Event,class FSM> + //void on_exit(Event const&,FSM& ) {std::cout << "leaving: Digit12" << std::endl;} + }; + struct MinusChar3 : public msm::front::state<> + { + // optional entry/exit methods + //template <class Event,class FSM> + //void on_entry(Event const&,FSM& ) {std::cout << "entering: MinusChar3" << std::endl;} + //template <class Event,class FSM> + //void on_exit(Event const&,FSM& ) {std::cout << "leaving: MinusChar3" << std::endl;} + }; + struct Digit13 : public msm::front::state<> + { + // optional entry/exit methods + //template <class Event,class FSM> + //void on_entry(Event const&,FSM& ) {std::cout << "entering: Digit13" << std::endl;} + //template <class Event,class FSM> + //void on_exit(Event const&,FSM& ) {std::cout << "leaving: Digit13" << std::endl;} + }; + struct Digit14 : public msm::front::state<> + { + // optional entry/exit methods + //template <class Event,class FSM> + //void on_entry(Event const&,FSM& ) {std::cout << "entering: Digit14" << std::endl;} + //template <class Event,class FSM> + //void on_exit(Event const&,FSM& ) {std::cout << "leaving: Digit14" << std::endl;} + }; + struct Digit15 : public msm::front::state<> + { + // optional entry/exit methods + //template <class Event,class FSM> + //void on_entry(Event const&,FSM& ) {std::cout << "entering: Digit15" << std::endl;} + //template <class Event,class FSM> + //void on_exit(Event const&,FSM& ) {std::cout << "leaving: Digit15" << std::endl;} + }; + //struct Start : public msm::front::state<> {}; + struct Parsed : public msm::front::state<> {}; + //struct Failed : public msm::front::state<> {}; + + // the initial state of the player SM. Must be defined + typedef Waiting initial_state; + // transition actions + struct test_fct + { + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM&,SourceState& ,TargetState& ) + { + std::cout << "Parsed!" << std::endl; + } + }; + + // guard conditions + + + // Transition table for parsing_ + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +-------------+-------------------+---------+---------------------+----------------------+ + Row < Waiting , digit , Digit1 >, + Row < Digit1 , digit , Digit2 >, + Row < Digit2 , digit , Digit3 >, + Row < Digit3 , digit , Digit4 >, + Row < Digit4 , event_char<'-'> , MinusChar1 >, + Row < MinusChar1 , digit , Digit5 >, + Row < Digit5 , digit , Digit6 >, + Row < Digit6 , digit , Digit7 >, + Row < Digit7 , digit , Digit8 >, + Row < Digit8 , event_char<'-'> , MinusChar2 >, + Row < MinusChar2 , digit , Digit9 >, + Row < Digit9 , digit , Digit10 >, + Row < Digit10 , digit , Digit11 >, + Row < Digit11 , digit , Digit12 >, + Row < Digit12 , event_char<'-'> , MinusChar3 >, + Row < MinusChar3 , digit , Digit13 >, + Row < Digit13 , digit , Digit14 >, + Row < Digit14 , digit , Digit15 >, + Row < Digit15 , eos , Parsed >, + Row < Parsed , eos , Waiting > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + + + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + typedef msm::back::state_machine<parsing_> parsing; +} + +#ifndef WIN32 +long mtime(struct timeval& tv1,struct timeval& tv2) +{ + return (tv2.tv_sec-tv1.tv_sec) *1000000 + ((tv2.tv_usec-tv1.tv_usec)); +} +#endif + +// This declares the statically-initialized char_event_dispatcher instance. +template <class Fsm> +const msm::back::char_event_dispatcher<Fsm> +msm::back::char_event_dispatcher<Fsm>::instance; + +struct Parser +{ + Parser():p(){p.start();} + void new_char(char c) + { + typedef msm::back::char_event_dispatcher<test_fsm::parsing> table; + table::instance.process_event(p,c); + } + void finish_string(){p.process_event(eos());} + void reinit(){p.process_event(eos());} + test_fsm::parsing p; +}; + +void msm_match(const char* input) +{ + test_fsm::parsing p; + p.start(); + + int j=0; + while(input[j]) + //for (size_t j=0;j<len;++j) + { + switch (input[j]) + { + case '0': + p.process_event(char_0()); + break; + case '1': + p.process_event(char_1()); + break; + case '2': + p.process_event(char_2()); + break; + case '3': + p.process_event(char_3()); + break; + case '4': + p.process_event(char_4()); + break; + case '5': + p.process_event(char_5()); + break; + case '6': + p.process_event(char_6()); + break; + case '7': + p.process_event(char_7()); + break; + case '8': + p.process_event(char_8()); + break; + case '9': + p.process_event(char_9()); + break; + case '-': + p.process_event(event_char<'-'>()); + break; + default: + p.process_event(default_char()); + break; + } + ++j; + } + p.process_event(eos()); + p.process_event(eos()); +} + +double time_match(const char* text) +{ + boost::timer tim; + int iter = 1; + int counter, repeats; + double result = 0; + double run; + do + { + tim.restart(); + for(counter = 0; counter < iter; ++counter) + { + msm_match( text); + } + result = tim.elapsed(); + iter *= 2; + } while(result < 0.5); + iter /= 2; + + // repeat test and report least value for consistency: + for(repeats = 0; repeats < 10; ++repeats) + { + tim.restart(); + for(counter = 0; counter < iter; ++counter) + { + msm_match( text); + } + run = tim.elapsed(); + result = (std::min)(run, result); + } + return result / iter; +} +int main() +{ + // for timing +#ifdef WIN32 + LARGE_INTEGER res; + ::QueryPerformanceFrequency(&res); + LARGE_INTEGER li,li2; +#else + struct timeval tv1,tv2; + gettimeofday(&tv1,NULL); +#endif + + test_fsm::parsing p; + p.start(); + const char* input = "1234-5678-1234-456"; + size_t len = strlen(input); + // for timing +#ifdef WIN32 + ::QueryPerformanceCounter(&li); +#else + gettimeofday(&tv1,NULL); +#endif + for (int i=0;i<1000;++i) + { + int j=0; + while(input[j]) + //for (size_t j=0;j<len;++j) + { + switch (input[j]) + { + case '0': + p.process_event(char_0()); + break; + case '1': + p.process_event(char_1()); + break; + case '2': + p.process_event(char_2()); + break; + case '3': + p.process_event(char_3()); + break; + case '4': + p.process_event(char_4()); + break; + case '5': + p.process_event(char_5()); + break; + case '6': + p.process_event(char_6()); + break; + case '7': + p.process_event(char_7()); + break; + case '8': + p.process_event(char_8()); + break; + case '9': + p.process_event(char_9()); + break; + case '-': + p.process_event(event_char<'-'>()); + break; + default: + p.process_event(default_char()); + break; + } + ++j; + } + p.process_event(eos()); + p.process_event(eos()); + } +#ifdef WIN32 + ::QueryPerformanceCounter(&li2); +#else + gettimeofday(&tv2,NULL); +#endif +#ifdef WIN32 + std::cout << "msm(1) took in s:" << (double)(li2.QuadPart-li.QuadPart)/res.QuadPart <<"\n" <<std::endl; +#else + std::cout << "msm(1) took in us:" << mtime(tv1,tv2) <<"\n" <<std::endl; +#endif + + Parser parse; + // for timing +#ifdef WIN32 + ::QueryPerformanceCounter(&li); +#else + gettimeofday(&tv1,NULL); +#endif + for (int i=0;i<1000;++i) + { + for (size_t j=0;j<len;++j) + { + parse.new_char(input[j]); + } + parse.finish_string(); + parse.reinit(); + } +#ifdef WIN32 + ::QueryPerformanceCounter(&li2); +#else + gettimeofday(&tv2,NULL); +#endif +#ifdef WIN32 + std::cout << "msm(2) took in s:" << (double)(li2.QuadPart-li.QuadPart)/res.QuadPart <<"\n" <<std::endl; +#else + std::cout << "msm(2) took in us:" << mtime(tv1,tv2) <<"\n" <<std::endl; +#endif + std::cout << "msm(3) took in s:" << time_match(input) <<"\n" <<std::endl; + return 0; +} + diff --git a/libs/msm/doc/PDF/examples/SCComposite.cpp b/libs/msm/doc/PDF/examples/SCComposite.cpp new file mode 100644 index 0000000000..7a98fdabb6 --- /dev/null +++ b/libs/msm/doc/PDF/examples/SCComposite.cpp @@ -0,0 +1,207 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <boost/statechart/event.hpp> +#include <boost/statechart/state_machine.hpp> +#include <boost/statechart/simple_state.hpp> +#include <boost/statechart/transition.hpp> +#include "boost/mpl/list.hpp" + +#include <vector> + +#include <iostream> +#ifdef WIN32 +#include "windows.h" +#else +#include <sys/time.h> +#endif + +namespace sc = boost::statechart; +namespace mpl = boost::mpl; + +namespace test_sc +{ + + //events + struct play : sc::event< play > {}; + struct end_pause : sc::event< end_pause > {}; + struct stop : sc::event< stop > {}; + struct pause : sc::event< pause > {}; + struct open_close : sc::event< open_close > {}; + struct cd_detected : sc::event< cd_detected > {}; + struct NextSong: sc::event< NextSong > {}; + struct PreviousSong : sc::event< PreviousSong >{}; + + struct Empty; + struct Open; + struct Stopped; + struct Playing; + struct Paused; + // SM + struct player : sc::state_machine< player, Empty > + { + void open_drawer(open_close const&) { /*std::cout << "player::open_drawer\n";*/ } + void store_cd_info(cd_detected const& cd) {/*std::cout << "player::store_cd_info\n";*/ } + void close_drawer(open_close const&) { /*std::cout << "player::close_drawer\n";*/ } + void start_playback(play const&) { /*std::cout << "player::start_playback\n";*/ } + void stopped_again(stop const&) {/*std::cout << "player::stopped_again\n";*/} + void stop_playback(stop const&) { /*std::cout << "player::stop_playback\n";*/ } + void pause_playback(pause const&) { /*std::cout << "player::pause_playback\n"; */} + void stop_and_open(open_close const&) { /*std::cout << "player::stop_and_open\n";*/ } + void resume_playback(end_pause const&) { /*std::cout << "player::resume_playback\n";*/ } + }; + + struct Empty : sc::simple_state< Empty, player > + { + Empty() { /*std::cout << "entering Empty" << std::endl;*/ } // entry + ~Empty() { /*std::cout << "leaving Empty" << std::endl;*/ } // exit + typedef mpl::list< + sc::transition< open_close, Open, + player, &player::open_drawer >, + sc::transition< cd_detected, Stopped, + player, &player::store_cd_info > > reactions; + + }; + struct Open : sc::simple_state< Open, player > + { + Open() { /*std::cout << "entering Open" << std::endl;*/ } // entry + ~Open() { /*std::cout << "leaving Open" << std::endl;*/ } // exit + typedef sc::transition< open_close, Empty, + player, &player::close_drawer > reactions; + + }; + struct Stopped : sc::simple_state< Stopped, player > + { + Stopped() { /*std::cout << "entering Stopped" << std::endl;*/ } // entry + ~Stopped() { /*std::cout << "leaving Stopped" << std::endl;*/ } // exit + typedef mpl::list< + sc::transition< play, Playing, + player, &player::start_playback >, + sc::transition< open_close, Open, + player, &player::open_drawer >, + sc::transition< stop, Stopped, + player, &player::stopped_again > > reactions; + + }; + struct Song1; + struct Playing : sc::simple_state< Playing, player,Song1 > + { + Playing() { /*std::cout << "entering Playing" << std::endl;*/ } // entry + ~Playing() { /*std::cout << "leaving Playing" << std::endl;*/ } // exit + typedef mpl::list< + sc::transition< stop, Stopped, + player, &player::stop_playback >, + sc::transition< pause, Paused, + player, &player::pause_playback >, + sc::transition< open_close, Open, + player, &player::stop_and_open > > reactions; + void start_next_song(NextSong const&) { /*std::cout << "Playing::start_next_song\n";*/ } + void start_prev_song(PreviousSong const&) { /*std::cout << "Playing::start_prev_song\n";*/ } + }; + struct Song2; + struct Song1 : sc::simple_state< Song1, Playing > + { + Song1() { /*std::cout << "entering Song1" << std::endl;*/ } // entry + ~Song1() { /*std::cout << "leaving Song1" << std::endl;*/ } // exit + typedef sc::transition< NextSong, Song2, + Playing, &Playing::start_next_song > reactions; + }; + struct Song3; + struct Song2 : sc::simple_state< Song2, Playing > + { + Song2() { /*std::cout << "entering Song2" << std::endl;*/ } // entry + ~Song2() { /*std::cout << "leaving Song2" << std::endl;*/ } // exit + typedef mpl::list< + sc::transition< NextSong, Song3, + Playing, &Playing::start_next_song >, + sc::transition< PreviousSong, Song1, + Playing, &Playing::start_prev_song > > reactions; + }; + struct Song3 : sc::simple_state< Song3, Playing > + { + Song3() { /*std::cout << "entering Song3" << std::endl;*/ } // entry + ~Song3() { /*std::cout << "leaving Song3" << std::endl;*/ } // exit + typedef sc::transition< PreviousSong, Song2, + Playing, &Playing::start_prev_song > reactions; + }; + struct Paused : sc::simple_state< Paused, player > + { + Paused() { /*std::cout << "entering Paused" << std::endl;*/ } // entry + ~Paused() { /*std::cout << "leaving Paused" << std::endl;*/ } // exit + typedef mpl::list< + sc::transition< end_pause, Playing, + player, &player::resume_playback >, + sc::transition< stop, Stopped, + player, &player::stop_playback >, + sc::transition< open_close, Open, + player, &player::stop_and_open > > reactions; + }; +} + + +#ifndef WIN32 +long mtime(struct timeval& tv1,struct timeval& tv2) +{ + return (tv2.tv_sec-tv1.tv_sec) *1000000 + ((tv2.tv_usec-tv1.tv_usec)); +} +#endif + +int main() +{ + test_sc::player p; + p.initiate(); + // for timing +#ifdef WIN32 + LARGE_INTEGER res; + ::QueryPerformanceFrequency(&res); + LARGE_INTEGER li,li2; + ::QueryPerformanceCounter(&li); +#else + struct timeval tv1,tv2; + gettimeofday(&tv1,NULL); +#endif + + for (int i=0;i<100;++i) + { + p.process_event(test_sc::open_close()); + p.process_event(test_sc::open_close()); + p.process_event(test_sc::cd_detected()); + p.process_event(test_sc::play()); + for (int j=0;j<100;++j) + { + p.process_event(test_sc::NextSong()); + p.process_event(test_sc::NextSong()); + p.process_event(test_sc::PreviousSong()); + p.process_event(test_sc::PreviousSong()); + } + + p.process_event(test_sc::pause()); + // go back to Playing + p.process_event(test_sc::end_pause()); + p.process_event(test_sc::pause()); + p.process_event(test_sc::stop()); + // event leading to the same state + p.process_event(test_sc::stop()); + p.process_event(test_sc::open_close()); + p.process_event(test_sc::open_close()); + } +#ifdef WIN32 + ::QueryPerformanceCounter(&li2); +#else + gettimeofday(&tv2,NULL); +#endif +#ifdef WIN32 + std::cout << "sc took in s:" << (double)(li2.QuadPart-li.QuadPart)/res.QuadPart <<"\n" <<std::endl; +#else + std::cout << "sc took in us:" << mtime(tv1,tv2) <<"\n" <<std::endl; +#endif + return 0; +} + diff --git a/libs/msm/doc/PDF/examples/SCSimple.cpp b/libs/msm/doc/PDF/examples/SCSimple.cpp new file mode 100644 index 0000000000..0d1dddb6e2 --- /dev/null +++ b/libs/msm/doc/PDF/examples/SCSimple.cpp @@ -0,0 +1,169 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <boost/statechart/event.hpp> +#include <boost/statechart/state_machine.hpp> +#include <boost/statechart/simple_state.hpp> +#include <boost/statechart/transition.hpp> +#include "boost/mpl/list.hpp" + +#include <vector> + +#include <iostream> +#ifdef WIN32 +#include "windows.h" +#else +#include <sys/time.h> +#endif + +namespace sc = boost::statechart; +namespace mpl = boost::mpl; + +namespace test_sc +{ + + //events + struct play : sc::event< play > {}; + struct end_pause : sc::event< end_pause > {}; + struct stop : sc::event< stop > {}; + struct pause : sc::event< pause > {}; + struct open_close : sc::event< open_close > {}; + struct cd_detected : sc::event< cd_detected > {}; + + + struct Empty; + struct Open; + struct Stopped; + struct Playing; + struct Paused; + // SM + struct player : sc::state_machine< player, Empty > + { + void open_drawer(open_close const&) { /*std::cout << "player::open_drawer\n";*/ } + void store_cd_info(cd_detected const& cd) {/*std::cout << "player::store_cd_info\n";*/ } + void close_drawer(open_close const&) { /*std::cout << "player::close_drawer\n";*/ } + void start_playback(play const&) { /*std::cout << "player::start_playback\n";*/ } + void stopped_again(stop const&) {/*std::cout << "player::stopped_again\n";*/} + void stop_playback(stop const&) { /*std::cout << "player::stop_playback\n";*/ } + void pause_playback(pause const&) { /*std::cout << "player::pause_playback\n"; */} + void stop_and_open(open_close const&) { /*std::cout << "player::stop_and_open\n";*/ } + void resume_playback(end_pause const&) { /*std::cout << "player::resume_playback\n";*/ } + }; + + struct Empty : sc::simple_state< Empty, player > + { + Empty() { /*std::cout << "entering Empty" << std::endl;*/ } // entry + ~Empty() { /*std::cout << "leaving Empty" << std::endl;*/ } // exit + typedef mpl::list< + sc::transition< open_close, Open, + player, &player::open_drawer >, + sc::transition< cd_detected, Stopped, + player, &player::store_cd_info > > reactions; + + }; + struct Open : sc::simple_state< Open, player > + { + Open() { /*std::cout << "entering Open" << std::endl;*/ } // entry + ~Open() { /*std::cout << "leaving Open" << std::endl;*/ } // exit + typedef sc::transition< open_close, Empty, + player, &player::close_drawer > reactions; + + }; + struct Stopped : sc::simple_state< Stopped, player > + { + Stopped() { /*std::cout << "entering Stopped" << std::endl;*/ } // entry + ~Stopped() { /*std::cout << "leaving Stopped" << std::endl;*/ } // exit + typedef mpl::list< + sc::transition< play, Playing, + player, &player::start_playback >, + sc::transition< open_close, Open, + player, &player::open_drawer >, + sc::transition< stop, Stopped, + player, &player::stopped_again > > reactions; + + }; + struct Playing : sc::simple_state< Playing, player > + { + Playing() { /*std::cout << "entering Playing" << std::endl;*/ } // entry + ~Playing() { /*std::cout << "leaving Playing" << std::endl;*/ } // exit + typedef mpl::list< + sc::transition< stop, Stopped, + player, &player::stop_playback >, + sc::transition< pause, Paused, + player, &player::pause_playback >, + sc::transition< open_close, Open, + player, &player::stop_and_open > > reactions; + }; + struct Paused : sc::simple_state< Paused, player > + { + Paused() { /*std::cout << "entering Paused" << std::endl;*/ } // entry + ~Paused() { /*std::cout << "leaving Paused" << std::endl;*/ } // exit + typedef mpl::list< + sc::transition< end_pause, Playing, + player, &player::resume_playback >, + sc::transition< stop, Stopped, + player, &player::stop_playback >, + sc::transition< open_close, Open, + player, &player::stop_and_open > > reactions; + }; +} + + +#ifndef WIN32 +long mtime(struct timeval& tv1,struct timeval& tv2) +{ + return (tv2.tv_sec-tv1.tv_sec) *1000000 + ((tv2.tv_usec-tv1.tv_usec)); +} +#endif + + +int main() +{ + test_sc::player p; + p.initiate(); + // for timing +#ifdef WIN32 + LARGE_INTEGER res; + ::QueryPerformanceFrequency(&res); + LARGE_INTEGER li,li2; + ::QueryPerformanceCounter(&li); +#else + struct timeval tv1,tv2; + gettimeofday(&tv1,NULL); +#endif + for (int i=0;i<100;++i) + { + p.process_event(test_sc::open_close()); + p.process_event(test_sc::open_close()); + p.process_event(test_sc::cd_detected()); + p.process_event(test_sc::play()); + p.process_event(test_sc::pause()); + // go back to Playing + p.process_event(test_sc::end_pause()); + p.process_event(test_sc::pause()); + p.process_event(test_sc::stop()); + // event leading to the same state + p.process_event(test_sc::stop()); + p.process_event(test_sc::open_close()); + p.process_event(test_sc::open_close()); + } +#ifdef WIN32 + ::QueryPerformanceCounter(&li2); +#else + gettimeofday(&tv2,NULL); +#endif +#ifdef WIN32 + std::cout << "sc took in s:" << (double)(li2.QuadPart-li.QuadPart)/res.QuadPart <<"\n" <<std::endl; +#else + std::cout << "sc took in us:" << mtime(tv1,tv2) <<"\n" <<std::endl; +#endif + return 0; +} + diff --git a/libs/msm/doc/PDF/examples/SM-2Arg.cpp b/libs/msm/doc/PDF/examples/SM-2Arg.cpp new file mode 100644 index 0000000000..58efb7917a --- /dev/null +++ b/libs/msm/doc/PDF/examples/SM-2Arg.cpp @@ -0,0 +1,469 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <iostream> +#include <string> +#include "boost/mpl/vector/vector30.hpp" + +#include <boost/msm/back/state_machine.hpp> +#include <boost/msm/front/state_machine_def.hpp> +#include <boost/msm/back/tools.hpp> + +using namespace std; +namespace msm = boost::msm; + +namespace // Concrete FSM implementation +{ + // events + struct play {}; + struct end_pause {}; + struct stop {}; + struct pause {}; + struct open_close {}; + struct NextSong {}; + struct PreviousSong {}; + struct ThreeSec {}; + struct TenSec {}; + struct go_sleep {}; + struct error_found {}; + struct end_error {}; + + // Flags. Allow information about a property of the current state + struct PlayingPaused{}; + struct CDLoaded {}; + struct FirstSongPlaying {}; + + // A "complicated" event type that carries some data. + struct cd_detected + { + cd_detected(std::string name) + : name(name) + {} + + std::string name; + }; + // an easy visitor + struct SomeVisitor + { + template <class T> + void visit_state(T* astate,int i) + { + std::cout << "visiting state:" << typeid(*astate).name() + << " with data:" << i << std::endl; + } + }; + // overwrite of the base state (not default) + struct my_visitable_state + { + // signature of the accept function + typedef msm::back::args<void,SomeVisitor&,int> 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&,int) const {} + }; + + // Concrete FSM implementation + struct player_ : public msm::front::state_machine_def<player_,my_visitable_state> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: player" << std::endl;} + // The list of FSM states + struct Empty : public msm::front::state<my_visitable_state> + { + typedef mpl::vector<play> deferred_events; + // every (optional) entry/exit methods get the event packed as boost::any. Not useful very often. + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Empty" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Empty" << std::endl;} + void accept(SomeVisitor& vis,int i) const + { + vis.visit_state(this,i); + } + }; + struct Open : public msm::front::state<my_visitable_state> + { + typedef mpl::vector1<CDLoaded> flag_list; + typedef mpl::vector<play> deferred_events; + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Open" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Open" << std::endl;} + void accept(SomeVisitor& vis,int i) const + { + vis.visit_state(this,i); + } + }; + // a state needing a pointer to the containing state machine + // and using for this the non-default policy + // if policy used, set_sm_ptr is needed + struct Stopped : public msm::front::state<my_visitable_state> + { + // when stopped, the CD is loaded + typedef mpl::vector1<CDLoaded> flag_list; + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Stopped" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Stopped" << std::endl;} + }; + // the player state machine contains a state which is himself a state machine + // it demonstrates Shallow History: if the state gets activated with end_pause + // then it will remember the last active state and reactivate it + // also possible: AlwaysHistory, the last active state will always be reactivated + // or NoHistory, always restart from the initial state + struct Playing_ : public msm::front::state_machine_def<Playing_,my_visitable_state > + { + // when playing, the CD is loaded and we are in either pause or playing (duh) + typedef mpl::vector2<PlayingPaused,CDLoaded> flag_list; + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Playing" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Playing" << std::endl;} + void accept(SomeVisitor& vis,int i) const + { + // note that visiting will recursively visit sub-states + vis.visit_state(this,i); + } + // The list of FSM states + // the Playing state machine contains a state which is himself a state machine + // so we have a SM containing a SM containing a SM + struct Song1_ : public msm::front::state_machine_def<Song1_,my_visitable_state> + { + typedef mpl::vector1<FirstSongPlaying> flag_list; + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: First song" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: First Song" << std::endl;} + void accept(SomeVisitor& vis,int i) const + { + vis.visit_state(this,i); + } + struct LightOn : public msm::front::state<my_visitable_state> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: LightOn" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: LightOn" << std::endl;} + }; + struct LightOff : public msm::front::state<my_visitable_state> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: LightOff" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: LightOff" << std::endl;} + }; + // the initial state. Must be defined + typedef LightOn initial_state; + // transition actions + void turn_light_off(ThreeSec const&) { std::cout << "3s off::turn light off\n"; } + // guard conditions + + typedef Song1_ s; // makes transition table cleaner + // Transition table for Song1 + struct transition_table : mpl::vector1< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row < LightOn , ThreeSec , LightOff, &s::turn_light_off > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + typedef msm::back::state_machine<Song1_> Song1; + + struct Song2 : public msm::front::state<my_visitable_state> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: Second song" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: Second Song" << std::endl;} + }; + struct Song3 : public msm::front::state<my_visitable_state> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: Third song" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: Third Song" << std::endl;} + }; + // the initial state. Must be defined + typedef Song1 initial_state; + // transition actions + void start_next_song(NextSong const&) { std::cout << "Playing::start_next_song\n"; } + void start_prev_song(PreviousSong const&) { std::cout << "Playing::start_prev_song\n"; } + // guard conditions + + typedef Playing_ pl; // makes transition table cleaner + // Transition table for Playing + struct transition_table : mpl::vector4< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Song1 , NextSong , Song2 , &pl::start_next_song >, + a_row < Song2 , PreviousSong, Song1 , &pl::start_prev_song >, + a_row < Song2 , NextSong , Song3 , &pl::start_next_song >, + a_row < Song3 , PreviousSong, Song2 , &pl::start_prev_song > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + typedef msm::back::state_machine<Playing_,msm::back::ShallowHistory<mpl::vector<end_pause> > > Playing; + + // the player state machine contains a state which is himself a state machine (2 of them, Playing and Paused) + struct Paused_ : public msm::front::state_machine_def<Paused_,my_visitable_state> + { + typedef mpl::vector2<PlayingPaused,CDLoaded> flag_list; + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Paused" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Paused" << std::endl;} + + // The list of FSM states + struct StartBlinking : public msm::front::state<my_visitable_state> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: StartBlinking" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: StartBlinking" << std::endl;} + }; + struct StopBlinking : public msm::front::state<my_visitable_state> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: StopBlinking" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: StopBlinking" << std::endl;} + }; + // the initial state. Must be defined + typedef StartBlinking initial_state; + // transition actions + void start_blinking(TenSec const&) { std::cout << "Paused::start_blinking\n"; } + void stop_blinking(TenSec const&) { std::cout << "Paused::stop_blinking\n"; } + // guard conditions + + typedef Paused_ pa; // makes transition table cleaner + // Transition table + struct transition_table : mpl::vector2< + // Start Event Next Action Guard + // +---------------+-------------+--------------+---------------------+----------------------+ + a_row < StartBlinking , TenSec , StopBlinking , &pa::stop_blinking >, + a_row < StopBlinking , TenSec , StartBlinking , &pa::start_blinking > + // +---------------+-------------+---------------+--------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + typedef msm::back::state_machine<Paused_> Paused; + + struct SleepMode : public msm::front::state<my_visitable_state> + { + }; // dummy state just to test the automatic id generation + + struct AllOk : public msm::front::state<my_visitable_state> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: AllOk" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: AllOk" << std::endl;} + }; + struct ErrorMode : //public terminate_state<> + public msm::front::interrupt_state<end_error,my_visitable_state> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: ErrorMode" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: ErrorMode" << std::endl;} + }; + + // the initial state of the player SM. Must be defined + typedef mpl::vector<Empty,AllOk> initial_state; + //typedef Empty initial_state; // this is to have only one active state + + // transition actions + void start_playback(play const&) { std::cout << "player::start_playback\n"; } + void open_drawer(open_close const&) { std::cout << "player::open_drawer\n"; } + void close_drawer(open_close const&) { std::cout << "player::close_drawer\n"; } + void store_cd_info(cd_detected const&) + { + std::cout << "player::store_cd_info\n"; + // generate another event to test the queue + //process_event(play()); + } + void stop_playback(stop const&) { std::cout << "player::stop_playback\n"; } + void pause_playback(pause const&) { std::cout << "player::pause_playback\n"; } + void resume_playback(end_pause const&) { std::cout << "player::resume_playback\n"; } + void stop_and_open(open_close const&) { std::cout << "player::stop_and_open\n"; } + void stopped_again(stop const&){std::cout << "player::stopped_again\n";} + void start_sleep(go_sleep const&) { } + void report_error(error_found const&) {std::cout << "player::report_error\n";} + void report_end_error(end_error const&) {std::cout << "player::report_end_error\n";} + // guard conditions + + + typedef player_ p; // makes transition table cleaner + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Stopped , play , Playing , &p::start_playback >, + a_row < Stopped , open_close , Open , &p::open_drawer >, + a_row < Stopped , stop , Stopped , &p::stopped_again >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Open , open_close , Empty , &p::close_drawer >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Empty , open_close , Open , &p::open_drawer >, + a_row < Empty , cd_detected , Stopped , &p::store_cd_info >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Playing , stop , Stopped , &p::stop_playback >, + a_row < Playing , pause , Paused , &p::pause_playback >, + a_row < Playing , open_close , Open , &p::stop_and_open >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Paused , end_pause , Playing , &p::resume_playback >, + a_row < Paused , stop , Stopped , &p::stop_playback >, + a_row < Paused , open_close , Open , &p::stop_and_open >, + a_row < Paused , go_sleep ,SleepMode, &p::start_sleep >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < AllOk , error_found ,ErrorMode, &p::report_error >, + a_row <ErrorMode,end_error ,AllOk , &p::report_end_error > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + + // back-end + typedef msm::back::state_machine<player_> player; + + // + // Testing utilities. + // + + void pstate(player const& p) + { + static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused","AllOk","ErrorMode","SleepMode" }; + for (unsigned int i=0;i<player::nr_regions::value;++i) + { + std::cout << " -> " << state_names[p.current_state()[i]] << std::endl; + } + } + void test() + { + player p; + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + p.start(); + std::cout << "CDLoaded active:" << std::boolalpha << p.is_flag_active<CDLoaded>() << std::endl; //=> false (no CD yet) + + // test deferred event + // deferred in Empty and Open, will be handled only after event cd_detected + p.process_event(play()); + + // go to Open, call on_exit on Empty, then action, then on_entry on Open + p.process_event(open_close()); pstate(p); + // visiting Paused and AllOk, but only Paused cares + SomeVisitor vis; + p.visit_current_states(boost::ref(vis),1); + p.process_event(open_close()); pstate(p); + // visiting Empty and AllOk, but only Empty cares + p.visit_current_states(boost::ref(vis),2); + + + p.process_event(cd_detected("louie, louie")); + // no need to call play() as the previous event does it in its action method + //p.process_event(play()); + // at this point, Play is active, along FirstSong and LightOn + pstate(p); + // visiting Playing+Song1 and AllOk, but only Playing+Song1 care + p.visit_current_states(boost::ref(vis),3); + + std::cout << "PlayingPaused active:" << std::boolalpha << p.is_flag_active<PlayingPaused>() << std::endl;//=> true + // call on_exit on LightOn,FirstSong,Play like stated in the UML spec. + // and of course on_entry on Paused and StartBlinking + p.process_event(pause()); pstate(p); + std::cout << "PlayingPaused active:" << std::boolalpha << p.is_flag_active<PlayingPaused>() << std::endl;//=> true + // forward events to Paused + p.process_event(TenSec()); + p.process_event(TenSec()); + // go back to Playing + p.process_event(end_pause()); pstate(p); + std::cout << "FirstSong active:" << std::boolalpha << p.is_flag_active<FirstSongPlaying>() << std::endl; //=> true + p.process_event(ThreeSec()); pstate(p); + p.process_event(NextSong());pstate(p); + // We are now in second song, Flag inactive + std::cout << "FirstSong active:" << std::boolalpha << p.is_flag_active<FirstSongPlaying>() << std::endl;//=> false + // visiting Playing+Song2 and AllOk, but only Playing cares + p.visit_current_states(boost::ref(vis),4); + + p.process_event(NextSong());pstate(p); + // 2nd song active + p.process_event(PreviousSong());pstate(p); + // Pause + p.process_event(pause()); pstate(p); + // go back to Playing + // but end_pause is an event activating the History + // => keep the last active State (SecondSong) + p.process_event(end_pause()); pstate(p); + // test of an event from a state to itself. According to UML spec, call again exit/entry from Stopped + p.process_event(stop()); pstate(p); + p.process_event(stop()); pstate(p); + std::cout << "CDLoaded active:" << std::boolalpha << p.is_flag_active<CDLoaded>() << std::endl;//=> true + std::cout << "PlayingPaused active:" << std::boolalpha << p.is_flag_active<PlayingPaused>() << std::endl;//=> false + std::cout << "FirstSong active:" << std::boolalpha << p.is_flag_active<FirstSongPlaying>() << std::endl;//=> false + std::cout << "CDLoaded active with AND:" << std::boolalpha << p.is_flag_active<CDLoaded,player::Flag_AND>() << std::endl;//=> false + std::cout << "CDLoaded active with OR:" << std::boolalpha << p.is_flag_active<CDLoaded,player::Flag_OR>() << std::endl;//=> true + + // go back to Playing + // but play is not leading to Shallow History => do not remember the last active State (SecondSong) + // and activate again FirstSong and LightOn + p.process_event(play()); pstate(p); + p.process_event(error_found()); pstate(p); + + // try generating more events + std::cout << "Trying to generate another event" << std::endl; // will not work, fsm is terminated or interrupted + p.process_event(NextSong());pstate(p); + + std::cout << "Trying to end the error" << std::endl; // will work only if ErrorMode is interrupt state + p.process_event(end_error());pstate(p); + std::cout << "Trying to generate another event" << std::endl; // will work only if ErrorMode is interrupt state + p.process_event(NextSong());pstate(p); + + std::cout << "Simulate error. Event play is not valid" << std::endl; + p.process_event(play()); pstate(p); + } +} + +int main() +{ + test(); + return 0; +} + + diff --git a/libs/msm/doc/PDF/examples/Serialize.cpp b/libs/msm/doc/PDF/examples/Serialize.cpp new file mode 100644 index 0000000000..dc72c59eb5 --- /dev/null +++ b/libs/msm/doc/PDF/examples/Serialize.cpp @@ -0,0 +1,248 @@ +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> + +// include headers that implement a archive in simple text format +#include <boost/archive/text_oarchive.hpp> +#include <boost/archive/text_iarchive.hpp> +#include <boost/serialization/tracking.hpp> + +#include <fstream> + + +namespace msm = boost::msm; +namespace mpl = boost::mpl; + + +namespace +{ + // events + struct play {}; + struct end_pause {}; + struct stop {}; + struct pause {}; + struct open_close {}; + + // A "complicated" event type that carries some data. + enum DiskTypeEnum + { + DISK_CD=0, + DISK_DVD=1 + }; + struct cd_detected + { + cd_detected(std::string name, DiskTypeEnum diskType) + : name(name), + disc_type(diskType) + {} + + std::string name; + DiskTypeEnum disc_type; + }; + + // front-end: define the FSM structure + 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, ask for it + typedef int do_serialize; + // and provide a serialize + template<class Archive> + void serialize(Archive & ar, const unsigned int ) + { + ar & front_end_data; + } + // The list of FSM states + struct Empty : public msm::front::state<> + { + // we want Empty to be serialized + typedef int do_serialize; + template<class Archive> + void serialize(Archive & ar, const unsigned int ) + { + ar & some_dummy_data; + } + Empty():some_dummy_data(0){} + // every (optional) entry/exit methods get the event passed. + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Empty" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Empty" << std::endl;} + int some_dummy_data; + }; + struct Open : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) {std::cout << "entering: Open" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Open" << std::endl;} + }; + + // sm_ptr still supported but deprecated as functors are a much better way to do the same thing + struct Stopped : public msm::front::state<msm::front::default_base_state,msm::front::sm_ptr> + { + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) {std::cout << "entering: Stopped" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Stopped" << std::endl;} + void set_sm_ptr(player_* pl) + { + m_player=pl; + } + player_* m_player; + }; + + struct Playing : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Playing" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Playing" << std::endl;} + }; + + // state not defining any entry or exit + struct Paused : public msm::front::state<> + { + }; + + // the initial state of the player SM. Must be defined + typedef Empty initial_state; + + // transition actions + void start_playback(play const&) { std::cout << "player::start_playback\n"; } + void open_drawer(open_close const&) { std::cout << "player::open_drawer\n"; } + void close_drawer(open_close const&) { std::cout << "player::close_drawer\n"; } + void store_cd_info(cd_detected const&) { std::cout << "player::store_cd_info\n"; } + void stop_playback(stop const&) { std::cout << "player::stop_playback\n"; } + void pause_playback(pause const&) { std::cout << "player::pause_playback\n"; } + void resume_playback(end_pause const&) { std::cout << "player::resume_playback\n"; } + void stop_and_open(open_close const&) { std::cout << "player::stop_and_open\n"; } + void stopped_again(stop const&) {std::cout << "player::stopped_again\n";} + // guard conditions + bool good_disk_format(cd_detected const& evt) + { + // to test a guard condition, let's say we understand only CDs, not DVD + if (evt.disc_type != DISK_CD) + { + std::cout << "wrong disk, sorry" << std::endl; + return false; + } + return true; + } + // used to show a transition conflict. This guard will simply deactivate one transition and thus + // solve the conflict + bool auto_start(cd_detected const&) + { + return false; + } + + typedef player_ p; // makes transition table cleaner + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Stopped , play , Playing , &p::start_playback >, + a_row < Stopped , open_close , Open , &p::open_drawer >, + _row < Stopped , stop , Stopped >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Open , open_close , Empty , &p::close_drawer >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Empty , open_close , Open , &p::open_drawer >, + row < Empty , cd_detected , Stopped , &p::store_cd_info ,&p::good_disk_format >, + row < Empty , cd_detected , Playing , &p::store_cd_info ,&p::auto_start >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Playing , stop , Stopped , &p::stop_playback >, + a_row < Playing , pause , Paused , &p::pause_playback >, + a_row < Playing , open_close , Open , &p::stop_and_open >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Paused , end_pause , Playing , &p::resume_playback >, + a_row < Paused , stop , Stopped , &p::stop_playback >, + a_row < Paused , open_close , Open , &p::stop_and_open > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + // Pick a back-end + typedef msm::back::state_machine<player_> player; + + // + // Testing utilities. + // + static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" }; + void pstate(player const& p) + { + std::cout << " -> " << state_names[p.current_state()[0]] << std::endl; + } + + void test() + { + player p; + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + p.start(); + p.get_state<player_::Empty&>().some_dummy_data=3; + p.front_end_data=4; + + // go to Open, call on_exit on Empty, then action, then on_entry on Open + p.process_event(open_close()); pstate(p); + + std::ofstream ofs("fsm.txt"); + // save fsm to archive (current state is Open) + { + boost::archive::text_oarchive oa(ofs); + // write class instance to archive + oa << p; + } + // reload fsm in state Open + player p2; + { + // create and open an archive for input + std::ifstream ifs("fsm.txt"); + boost::archive::text_iarchive ia(ifs); + // read class state from archive + ia >> p2; + } + // we now use p2 as it was loaded + // check that we kept Empty's data value + std::cout << "Empty's data should be 3:" << p2.get_state<player_::Empty&>().some_dummy_data << std::endl; + std::cout << "front-end data should be 4:" << p2.front_end_data << std::endl; + + p2.process_event(open_close()); pstate(p2); + // will be rejected, wrong disk type + p2.process_event( + cd_detected("louie, louie",DISK_DVD)); pstate(p2); + p2.process_event( + cd_detected("louie, louie",DISK_CD)); pstate(p2); + p2.process_event(play()); + + // at this point, Play is active + p2.process_event(pause()); pstate(p2); + // go back to Playing + p2.process_event(end_pause()); pstate(p2); + p2.process_event(pause()); pstate(p2); + p2.process_event(stop()); pstate(p2); + // event leading to the same state + // no action method called as it is not present in the transition table + p2.process_event(stop()); pstate(p2); + } +} +// eliminate object tracking (even if serialized through a pointer) +// at the risk of a programming error creating duplicate objects. +// this is to get rid of warning because p is not const +BOOST_CLASS_TRACKING(player, boost::serialization::track_never) + +int main() +{ + test(); + return 0; +} diff --git a/libs/msm/doc/PDF/examples/SerializeCompositeAndHistory.cpp b/libs/msm/doc/PDF/examples/SerializeCompositeAndHistory.cpp new file mode 100644 index 0000000000..9699ab1e06 --- /dev/null +++ b/libs/msm/doc/PDF/examples/SerializeCompositeAndHistory.cpp @@ -0,0 +1,266 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> + +// include headers that implement a archive in simple text format +#include <boost/archive/text_oarchive.hpp> +#include <boost/archive/text_iarchive.hpp> +#include <boost/serialization/tracking.hpp> + +#include <fstream> + +namespace msm = boost::msm; +namespace mpl = boost::mpl; + +namespace +{ + // events + struct play {}; + struct end_pause {}; + struct stop {}; + struct pause {}; + struct open_close {}; + struct NextSong {}; + struct PreviousSong {}; + + // A "complicated" event type that carries some data. + struct cd_detected + { + cd_detected(std::string name) + : name(name) + {} + + std::string name; + }; + + // front-end: define the FSM structure + struct player_ : public msm::front::state_machine_def<player_> + { + // The list of FSM states + struct Empty : public msm::front::state<> + { + // every (optional) entry/exit methods get the event passed + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Empty" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Empty" << std::endl;} + }; + struct Open : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Open" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Open" << std::endl;} + }; + + struct Stopped : public msm::front::state<> + { + // when stopped, the CD is loaded + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Stopped" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Stopped" << std::endl;} + }; + + struct Playing_ : public msm::front::state_machine_def<Playing_> + { + // when playing, the CD is loaded and we are in either pause or playing (duh) + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Playing" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Playing" << std::endl;} + + // The list of FSM states + struct Song1 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: First song" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: First Song" << std::endl;} + }; + struct Song2 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: Second song" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: Second Song" << std::endl;} + }; + struct Song3 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: Third song" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: Third Song" << std::endl;} + }; + // the initial state. Must be defined + typedef Song1 initial_state; + // transition actions + void start_next_song(NextSong const&) { std::cout << "Playing::start_next_song\n"; } + void start_prev_song(PreviousSong const&) { std::cout << "Playing::start_prev_song\n"; } + // guard conditions + + typedef Playing_ pl; // makes transition table cleaner + // Transition table for Playing + struct transition_table : mpl::vector4< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Song1 , NextSong , Song2 , &pl::start_next_song >, + a_row < Song2 , PreviousSong, Song1 , &pl::start_prev_song >, + a_row < Song2 , NextSong , Song3 , &pl::start_next_song >, + a_row < Song3 , PreviousSong, Song2 , &pl::start_prev_song > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + + }; + // back-end + // demonstrates Shallow History: if the state gets activated with end_pause + // then it will remember the last active state and reactivate it + // also possible: AlwaysHistory, the last active state will always be reactivated + // or NoHistory, always restart from the initial state + typedef msm::back::state_machine<Playing_,msm::back::ShallowHistory<mpl::vector<end_pause> > > Playing; + + // state not defining any entry or exit + struct Paused : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Paused" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Paused" << std::endl;} + }; + + // the initial state of the player SM. Must be defined + typedef Empty initial_state; + + // transition actions + void start_playback(play const&) { std::cout << "player::start_playback\n"; } + void open_drawer(open_close const&) { std::cout << "player::open_drawer\n"; } + void close_drawer(open_close const&) { std::cout << "player::close_drawer\n"; } + void store_cd_info(cd_detected const& cd) {std::cout << "player::store_cd_info\n";} + void stop_playback(stop const&) { std::cout << "player::stop_playback\n"; } + void pause_playback(pause const&) { std::cout << "player::pause_playback\n"; } + void resume_playback(end_pause const&) { std::cout << "player::resume_playback\n"; } + void stop_and_open(open_close const&) { std::cout << "player::stop_and_open\n"; } + void stopped_again(stop const&){std::cout << "player::stopped_again\n";} + // guard conditions + + typedef player_ p; // makes transition table cleaner + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Stopped , play , Playing , &p::start_playback >, + a_row < Stopped , open_close , Open , &p::open_drawer >, + a_row < Stopped , stop , Stopped , &p::stopped_again >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Open , open_close , Empty , &p::close_drawer >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Empty , open_close , Open , &p::open_drawer >, + a_row < Empty , cd_detected , Stopped , &p::store_cd_info >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Playing , stop , Stopped , &p::stop_playback >, + a_row < Playing , pause , Paused , &p::pause_playback >, + a_row < Playing , open_close , Open , &p::stop_and_open >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Paused , end_pause , Playing , &p::resume_playback >, + a_row < Paused , stop , Stopped , &p::stop_playback >, + a_row < Paused , open_close , Open , &p::stop_and_open > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + + }; + // Pick a back-end + typedef msm::back::state_machine<player_> player; + + // + // Testing utilities. + // + static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" }; + + void pstate(player const& p) + { + std::cout << " -> " << state_names[p.current_state()[0]] << std::endl; + } + + void test() + { + player p; + + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + p.start(); + // go to Open, call on_exit on Empty, then action, then on_entry on Open + p.process_event(open_close()); pstate(p); + p.process_event(open_close()); pstate(p); + p.process_event(cd_detected("louie, louie")); + p.process_event(play()); + + // at this point, Play is active + // make transition happen inside it. Player has no idea about this event but it's ok. + p.process_event(NextSong());pstate(p); //2nd song active + p.process_event(NextSong());pstate(p);//3rd song active + p.process_event(PreviousSong());pstate(p);//2nd song active + p.process_event(pause()); pstate(p); + std::ofstream ofs("fsm.txt"); + // save fsm to archive (current state is Pause, Playing is in Song2) + { + boost::archive::text_oarchive oa(ofs); + // write class instance to archive + oa << p; + } + // reload fsm in state Open + player p2; + { + // create and open an archive for input + std::ifstream ifs("fsm.txt"); + boost::archive::text_iarchive ia(ifs); + // read class state from archive + ia >> p2; + } + // go back to Playing + // as you see, remembers the original state as end_pause is an history trigger + p2.process_event(end_pause()); pstate(p2); + p2.process_event(pause()); pstate(p2); + p2.process_event(stop()); pstate(p2); + // event leading to the same state + p2.process_event(stop()); pstate(p2); + // play does not trigger shallow history => start back from 1st song + p2.process_event(play()); pstate(p2); + } +} +// eliminate object tracking (even if serialized through a pointer) +// at the risk of a programming error creating duplicate objects. +// this is to get rid of warning because p is not const +BOOST_CLASS_TRACKING(player, boost::serialization::track_never) + +int main() +{ + test(); + return 0; +} diff --git a/libs/msm/doc/PDF/examples/SimplePhoenix.cpp b/libs/msm/doc/PDF/examples/SimplePhoenix.cpp new file mode 100644 index 0000000000..7df50096cb --- /dev/null +++ b/libs/msm/doc/PDF/examples/SimplePhoenix.cpp @@ -0,0 +1,258 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <vector> +#include <iostream> + +#include <boost/phoenix/phoenix.hpp> + +// add phoenix support in eUML +#define BOOST_MSM_EUML_PHOENIX_SUPPORT +#include <boost/msm/back/state_machine.hpp> +#include <boost/msm/front/euml/euml.hpp> + + +using namespace std; +using namespace boost::msm::front::euml; +namespace msm = boost::msm; +using namespace boost::phoenix; + +// entry/exit/action/guard logging functors +#include "logging_functors.h" + +namespace // Concrete FSM implementation +{ + // events + BOOST_MSM_EUML_EVENT(end_pause) + BOOST_MSM_EUML_EVENT(stop) + BOOST_MSM_EUML_EVENT(pause) + BOOST_MSM_EUML_EVENT(open_close) + struct play_event : boost::msm::front::euml::euml_event<play_event> + { + }; + play_event play; + + enum DiskTypeEnum + { + DISK_CD=0, + DISK_DVD=1 + }; + // A "complicated" event type that carries some data. + struct cd_detected_event : boost::msm::front::euml::euml_event<cd_detected_event> + { + cd_detected_event(){} + cd_detected_event(std::string const& name,DiskTypeEnum disk):cd_name(name),cd_type(disk){} + std::string cd_name; + DiskTypeEnum cd_type; + }; + // define an instance for a nicer transition table + cd_detected_event cd_detected; + + // Concrete FSM implementation + // The list of FSM states + // state not needing any entry or exit + BOOST_MSM_EUML_STATE((),Paused) + + // states with standard eUML actions + BOOST_MSM_EUML_STATE(( Stopped_Entry,Stopped_Exit ),Stopped) + BOOST_MSM_EUML_STATE(( Playing_Entry,Playing_Exit ),Playing) + + // a "standard" msm state + struct Empty_impl : public msm::front::state<> , public euml_state<Empty_impl> + { + // this allows us to add some functions + void foo() {std::cout << "Empty::foo " << std::endl;} + // standard entry behavior + template <class Event,class FSM> + void on_entry(Event const& evt,FSM& fsm) + { + std::cout << "entering: Empty" << std::endl; + } + template <class Event,class FSM> + void on_exit(Event const& evt,FSM& fsm) + { + std::cout << "leaving: Empty" << std::endl; + } + }; + //instance for use in the transition table + Empty_impl const Empty; + + // entry and exit actions as phoenix functions + struct open_entry_impl + { + typedef void result_type; + void operator()() + { + cout << "entering: Open" << endl; + } + }; + boost::phoenix::function<open_entry_impl> open_entry; + struct open_exit_impl + { + typedef void result_type; + void operator()() + { + cout << "leaving: Open" << endl; + } + }; + boost::phoenix::function<open_exit_impl> open_exit; + + // a state using phoenix for entry/exit actions + BOOST_MSM_EUML_STATE(( open_entry(),open_exit() ),Open) + + // actions and guards using boost::phoenix + struct start_playback_impl + { + typedef void result_type; + void operator()() + { + cout << "calling: start_playback" << endl; + } + }; + boost::phoenix::function<start_playback_impl> start_playback; + + // a guard taking the event as argument + struct good_disk_format_impl + { + typedef bool result_type; + + template <class Event> + bool operator()(Event const& evt) + { + // to test a guard condition, let's say we understand only CDs, not DVD + if (evt.cd_type!=DISK_CD) + { + std::cout << "wrong disk, sorry" << std::endl; + return false; + } + std::cout << "good disk" << std::endl; + return true; + } + }; + boost::phoenix::function<good_disk_format_impl> good_disk_format; + + // a simple action + struct store_cd_info_impl + { + typedef void result_type; + void operator()() + { + cout << "calling: store_cd_info" << endl; + } + }; + boost::phoenix::function<store_cd_info_impl> store_cd_info; + + // an action taking the fsm as argument and sending it a new event + struct process_play_impl + { + typedef void result_type; + + template <class Fsm> + void operator()(Fsm& fsm) + { + cout << "queuing a play event" << endl; + fsm.process_event(play); + } + }; + // it is also possible to use BOOST_PHOENIX_ADAPT_CALLABLE to avoid defining a global variable + BOOST_PHOENIX_ADAPT_CALLABLE(process_play, process_play_impl, 1) + + + // transition table. Actions and guards are written as phoenix functions + BOOST_MSM_EUML_TRANSITION_TABLE(( + //an action without arguments + Playing == Stopped + play / start_playback() , + Playing == Paused + end_pause , + // +------------------------------------------------------------------------------+ + Empty == Open + open_close , + // +------------------------------------------------------------------------------+ + Open == Empty + open_close , + Open == Paused + open_close , + Open == Stopped + open_close , + Open == Playing + open_close , + // +------------------------------------------------------------------------------+ + Paused == Playing + pause , + // +------------------------------------------------------------------------------+ + Stopped == Playing + stop , + Stopped == Paused + stop , + // a guard taking the event as argument + // and an action made of a phoenix expression of 2 actions + // _event is a placeholder for the current event + // _fsm is a placeholder for the current state machine + Stopped == Empty + cd_detected [good_disk_format(_event)] + / (store_cd_info(),process_play(_fsm)), + Stopped == Stopped + stop + // +------------------------------------------------------------------------------+ + ),transition_table) + + // create a state machine "on the fly" + BOOST_MSM_EUML_DECLARE_STATE_MACHINE(( transition_table, //STT + init_ << Empty, // Init State + no_action, // Entry + no_action, // Exit + attributes_ << no_attributes_, // Attributes + configure_ << no_configure_, // configuration + Log_No_Transition // no_transition handler + ), + player_) //fsm name + + // or simply, if no no_transition handler needed: + //BOOST_MSM_EUML_DECLARE_STATE_MACHINE(( transition_table, //STT + // Empty // Init State + // ),player_) + + // choice of back-end + typedef msm::back::state_machine<player_> player; + + // + // Testing utilities. + // + static char const* const state_names[] = { "Stopped", "Paused", "Open", "Empty", "Playing" }; + void pstate(player const& p) + { + std::cout << " -> " << state_names[p.current_state()[0]] << std::endl; + } + + void test() + { + player p; + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + p.start(); + // go to Open, call on_exit on Empty, then action, then on_entry on Open + p.process_event(open_close); pstate(p); + p.process_event(open_close); pstate(p); + // will be rejected, wrong disk type + p.process_event( + cd_detected_event("louie, louie",DISK_DVD)); pstate(p); + p.process_event( + cd_detected_event("louie, louie",DISK_CD)); pstate(p); + // no need to call play as the previous event does it in its action method + //p.process_event(play); + + // at this point, Play is active + p.process_event(pause); pstate(p); + // go back to Playing + p.process_event(end_pause); pstate(p); + p.process_event(pause); pstate(p); + p.process_event(stop); pstate(p); + // event leading to the same state + // no action method called as none is defined in the transition table + p.process_event(stop); pstate(p); + // test call to no_transition + p.process_event(pause); pstate(p); + } +} + +int main() +{ + test(); + return 0; +} + diff --git a/libs/msm/doc/PDF/examples/SimpleTimer.cpp b/libs/msm/doc/PDF/examples/SimpleTimer.cpp new file mode 100644 index 0000000000..25a5aaf9f0 --- /dev/null +++ b/libs/msm/doc/PDF/examples/SimpleTimer.cpp @@ -0,0 +1,159 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <vector> +#include <iostream> + +#include <boost/msm/back/state_machine.hpp> +#include <boost/msm/front/euml/euml.hpp> + +using namespace std; +using namespace boost::msm::front::euml; +namespace msm = boost::msm; + +// how long the timer will ring when countdown elapsed. +#define RINGING_TIME 5 + +namespace // Concrete FSM implementation +{ + // events + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(int,m_timer) + BOOST_MSM_EUML_ATTRIBUTES((attributes_ << m_timer ), start_timer_attr) + BOOST_MSM_EUML_EVENT_WITH_ATTRIBUTES(start_timer,start_timer_attr) + + BOOST_MSM_EUML_EVENT(stop_timer) + + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(int,m_tick) + BOOST_MSM_EUML_ATTRIBUTES((attributes_ << m_tick ), tick_attr) + BOOST_MSM_EUML_EVENT_WITH_ATTRIBUTES(tick,tick_attr) + + BOOST_MSM_EUML_EVENT(start_ringing) + + // Concrete FSM implementation + + // The list of FSM states + BOOST_MSM_EUML_ACTION(Stopped_Entry) + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: Stopped" << std::endl; + } + }; + BOOST_MSM_EUML_STATE(( Stopped_Entry ),Stopped) + + BOOST_MSM_EUML_ACTION(Started_Entry) + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: Started" << std::endl; + } + }; + + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(int,m_counter) + BOOST_MSM_EUML_STATE(( Started_Entry, + no_action, + attributes_ << m_counter + ), + Started) + + BOOST_MSM_EUML_ACTION(Ringing_Entry) + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: Ringing" << std::endl; + } + }; + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(int,m_ringing_cpt) + BOOST_MSM_EUML_STATE(( Ringing_Entry, + no_action, + attributes_ << m_ringing_cpt + ), + Ringing) + + // external function + void do_ring(int ringing_time) {std::cout << "ringing " << ringing_time << " s" << std::endl;} + // create functor and eUML function + BOOST_MSM_EUML_FUNCTION(Ring_ , do_ring , ring_ , void , void ) + + // replaces the old transition table + BOOST_MSM_EUML_TRANSITION_TABLE(( + // +------------------------------------------------------------------------------+ + // When we start the countdown, the countdown value is not hardcoded but contained in the start_timer event. + // We copy this value into Started + Started == Stopped + start_timer /(target_(m_counter)= event_(m_timer)) , + Stopped == Started + stop_timer , + // internal transition + Started + tick + // we here use the message queue to move to Started when the countdown is finished + // to do this we put start_ringing into the message queue + / if_then_( (source_(m_counter) -= event_(m_tick) ) <= Int_<0>(), + process_(start_ringing) ) , + // when we start ringing, we give to the state its hard-coded ringing time. + Ringing == Started + start_ringing + / (target_(m_ringing_cpt) = Int_<RINGING_TIME>(), + // call the external do_ring function + ring_(Int_<RINGING_TIME>())) , + // to change a bit, we now do not use the message queue but a transition conflict to solve the same problem. + // When tick is fired, we have an internal transition Ringing -> Ringing, as long as Counter > 0 + Ringing + tick [ source_(m_ringing_cpt) - event_(m_tick) > Int_<0>() ] + /(target_(m_ringing_cpt) -= event_(m_tick) ) , + // And we move to Stopped when the counter is 0 + Stopped == Ringing + tick[source_(m_ringing_cpt)-event_(m_tick) <= Int_<0>()] , + // we let the user manually stop the ringing by pressing any button + Stopped == Ringing + stop_timer , + Stopped == Ringing + start_timer + // +------------------------------------------------------------------------------+ + ),transition_table) + + // create a state machine "on the fly" + BOOST_MSM_EUML_DECLARE_STATE_MACHINE(( transition_table, //STT + init_ << Stopped // Init State + ), + SimpleTimer_) //fsm name + + // choice of back-end + typedef msm::back::state_machine<SimpleTimer_> SimpleTimer; + + // + // Testing utilities. + // + static char const* const state_names[] = { "Stopped", "Started","Ringing" }; + void pstate(SimpleTimer const& p) + { + std::cout << " -> " << state_names[p.current_state()[0]] << std::endl; + } + + void test() + { + SimpleTimer p; + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + p.start(); + + p.process_event(start_timer(5));pstate(p); //timer set to 5 ticks + p.process_event(tick(2));pstate(p); + p.process_event(tick(1));pstate(p); + p.process_event(tick(1));pstate(p); + p.process_event(tick(1));pstate(p); + // we are now ringing, let it ring a bit + p.process_event(tick(2));pstate(p); + p.process_event(tick(1));pstate(p); + p.process_event(tick(1));pstate(p); + p.process_event(tick(1));pstate(p); + } +} + +int main() +{ + test(); + return 0; +} diff --git a/libs/msm/doc/PDF/examples/SimpleTutorial.cpp b/libs/msm/doc/PDF/examples/SimpleTutorial.cpp new file mode 100644 index 0000000000..4fd879820e --- /dev/null +++ b/libs/msm/doc/PDF/examples/SimpleTutorial.cpp @@ -0,0 +1,213 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> + +namespace msm = boost::msm; +namespace mpl = boost::mpl; + +namespace +{ + // events + struct play {}; + struct end_pause {}; + struct stop {}; + struct pause {}; + struct open_close {}; + + // A "complicated" event type that carries some data. + enum DiskTypeEnum + { + DISK_CD=0, + DISK_DVD=1 + }; + struct cd_detected + { + cd_detected(std::string name, DiskTypeEnum diskType) + : name(name), + disc_type(diskType) + {} + + std::string name; + DiskTypeEnum disc_type; + }; + + // front-end: define the FSM structure + struct player_ : public msm::front::state_machine_def<player_> + { + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) + { + std::cout << "entering: Player" << std::endl; + } + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) + { + std::cout << "leaving: Player" << std::endl; + } + + // The list of FSM states + struct Empty : public msm::front::state<> + { + // every (optional) entry/exit methods get the event passed. + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Empty" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Empty" << std::endl;} + }; + struct Open : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) {std::cout << "entering: Open" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Open" << std::endl;} + }; + + // sm_ptr still supported but deprecated as functors are a much better way to do the same thing + struct Stopped : public msm::front::state<msm::front::default_base_state,msm::front::sm_ptr> + { + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) {std::cout << "entering: Stopped" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Stopped" << std::endl;} + void set_sm_ptr(player_* pl) + { + m_player=pl; + } + player_* m_player; + }; + + struct Playing : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Playing" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Playing" << std::endl;} + }; + + // state not defining any entry or exit + struct Paused : public msm::front::state<> + { + }; + + // the initial state of the player SM. Must be defined + typedef Empty initial_state; + + // transition actions + void start_playback(play const&) { std::cout << "player::start_playback\n"; } + void open_drawer(open_close const&) { std::cout << "player::open_drawer\n"; } + void close_drawer(open_close const&) { std::cout << "player::close_drawer\n"; } + void store_cd_info(cd_detected const&) { std::cout << "player::store_cd_info\n"; } + void stop_playback(stop const&) { std::cout << "player::stop_playback\n"; } + void pause_playback(pause const&) { std::cout << "player::pause_playback\n"; } + void resume_playback(end_pause const&) { std::cout << "player::resume_playback\n"; } + void stop_and_open(open_close const&) { std::cout << "player::stop_and_open\n"; } + void stopped_again(stop const&) {std::cout << "player::stopped_again\n";} + // guard conditions + bool good_disk_format(cd_detected const& evt) + { + // to test a guard condition, let's say we understand only CDs, not DVD + if (evt.disc_type != DISK_CD) + { + std::cout << "wrong disk, sorry" << std::endl; + return false; + } + return true; + } + // used to show a transition conflict. This guard will simply deactivate one transition and thus + // solve the conflict + bool auto_start(cd_detected const&) + { + return false; + } + + typedef player_ p; // makes transition table cleaner + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Stopped , play , Playing , &p::start_playback >, + a_row < Stopped , open_close , Open , &p::open_drawer >, + _row < Stopped , stop , Stopped >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Open , open_close , Empty , &p::close_drawer >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Empty , open_close , Open , &p::open_drawer >, + row < Empty , cd_detected , Stopped , &p::store_cd_info ,&p::good_disk_format >, + row < Empty , cd_detected , Playing , &p::store_cd_info ,&p::auto_start >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Playing , stop , Stopped , &p::stop_playback >, + a_row < Playing , pause , Paused , &p::pause_playback >, + a_row < Playing , open_close , Open , &p::stop_and_open >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Paused , end_pause , Playing , &p::resume_playback >, + a_row < Paused , stop , Stopped , &p::stop_playback >, + a_row < Paused , open_close , Open , &p::stop_and_open > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + // Pick a back-end + typedef msm::back::state_machine<player_> player; + + // + // Testing utilities. + // + static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" }; + void pstate(player const& p) + { + std::cout << " -> " << state_names[p.current_state()[0]] << std::endl; + } + + void test() + { + player p; + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + p.start(); + // go to Open, call on_exit on Empty, then action, then on_entry on Open + p.process_event(open_close()); pstate(p); + p.process_event(open_close()); pstate(p); + // will be rejected, wrong disk type + p.process_event( + cd_detected("louie, louie",DISK_DVD)); pstate(p); + p.process_event( + cd_detected("louie, louie",DISK_CD)); pstate(p); + p.process_event(play()); + + // at this point, Play is active + p.process_event(pause()); pstate(p); + // go back to Playing + p.process_event(end_pause()); pstate(p); + p.process_event(pause()); pstate(p); + p.process_event(stop()); pstate(p); + // event leading to the same state + // no action method called as it is not present in the transition table + p.process_event(stop()); pstate(p); + std::cout << "stop fsm" << std::endl; + p.stop(); + } +} + +int main() +{ + test(); + return 0; +} diff --git a/libs/msm/doc/PDF/examples/SimpleTutorial2.cpp b/libs/msm/doc/PDF/examples/SimpleTutorial2.cpp new file mode 100644 index 0000000000..65d2c62ae6 --- /dev/null +++ b/libs/msm/doc/PDF/examples/SimpleTutorial2.cpp @@ -0,0 +1,227 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> +#include <boost/msm/front/row2.hpp> + +namespace msm = boost::msm; +namespace mpl = boost::mpl; +using namespace msm::front; + +namespace +{ + // events + struct play {}; + struct end_pause {}; + struct stop {}; + struct pause {}; + struct open_close {}; + + // A "complicated" event type that carries some data. + enum DiskTypeEnum + { + DISK_CD=0, + DISK_DVD=1 + }; + struct cd_detected + { + cd_detected(std::string name, DiskTypeEnum diskType) + : name(name), + disc_type(diskType) + {} + + std::string name; + DiskTypeEnum disc_type; + }; + + // front-end: define the FSM structure + struct player_ : public msm::front::state_machine_def<player_> + { + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) + { + std::cout << "entering: Player" << std::endl; + } + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) + { + std::cout << "leaving: Player" << std::endl; + } + + // The list of FSM states + struct Empty : public msm::front::state<> + { + // every (optional) entry/exit methods get the event passed. + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Empty" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Empty" << std::endl;} + void open_drawer(open_close const&) { std::cout << "Empty::open_drawer\n"; } + // actions for Empty's internal transitions + void internal_action(cd_detected const&){ std::cout << "Empty::internal action\n"; } + bool internal_guard(cd_detected const&) + { + std::cout << "Empty::internal guard\n"; + return false; + } + }; + struct Open : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) {std::cout << "entering: Open" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Open" << std::endl;} + void close_drawer(open_close const&) { std::cout << "Open::close_drawer\n"; } + void stop_and_open(open_close const&) { std::cout << "Open::stop_and_open\n"; } + }; + + // sm_ptr still supported but deprecated as functors are a much better way to do the same thing + struct Stopped : public msm::front::state<msm::front::default_base_state,msm::front::sm_ptr> + { + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) {std::cout << "entering: Stopped" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Stopped" << std::endl;} + void set_sm_ptr(player_* pl) + { + m_player=pl; + } + player_* m_player; + void start_playback(play const&) { std::cout << "Stopped::start_playback\n"; } + void stop_playback(stop const&) { std::cout << "Stopped::stop_playback\n"; } + }; + + struct Playing : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Playing" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Playing" << std::endl;} + // guard conditions + // used to show a transition conflict. This guard will simply deactivate one transition and thus + // solve the conflict + bool auto_start(cd_detected const&) + { + return false; + } + }; + + // state not defining any entry or exit + struct Paused : public msm::front::state<> + { + void pause_playback(pause const&) { std::cout << "Paused::pause_playback\n"; } + void resume_playback(end_pause const&) { std::cout << "Paused::resume_playback\n"; } + }; + + // action + void store_cd_info(cd_detected const&) { std::cout << "Player::store_cd_info\n"; } + + // guard + bool good_disk_format(cd_detected const& evt) + { + // to test a guard condition, let's say we understand only CDs, not DVD + if (evt.disc_type != DISK_CD) + { + std::cout << "wrong disk, sorry" << std::endl; + return false; + } + return true; + } + // the initial state of the player SM. Must be defined + typedef Empty initial_state; + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action/Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row2 < Stopped , play , Playing , Stopped , &Stopped::start_playback >, + a_row2 < Stopped , open_close , Open , Empty , &Empty::open_drawer >, + _row < Stopped , stop , Stopped >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row2 < Open , open_close , Empty , Open , &Open::close_drawer >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row2 < Empty , open_close , Open , Empty ,&Empty::open_drawer >, + row2 < Empty , cd_detected , Stopped , player_ ,&player_::store_cd_info + , player_ ,&player_::good_disk_format >, + row2 < Empty , cd_detected , Playing , player_ ,&player_::store_cd_info + , Playing ,&Playing::auto_start >, + // conflict with some internal rows + irow2 < Empty , cd_detected , Empty ,&Empty::internal_action + , Empty ,&Empty::internal_guard >, + g_irow2 < Empty , cd_detected , Empty ,&Empty::internal_guard >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row2 < Playing , stop , Stopped , Stopped ,&Stopped::stop_playback >, + a_row2 < Playing , pause , Paused , Paused ,&Paused::pause_playback >, + a_row2 < Playing , open_close , Open , Open ,&Open::stop_and_open >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row2 < Paused , end_pause , Playing , Paused ,&Paused::resume_playback >, + a_row2 < Paused , stop , Stopped , Stopped ,&Stopped::stop_playback >, + a_row2 < Paused , open_close , Open , Open ,&Open::stop_and_open > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + // Pick a back-end + typedef msm::back::state_machine<player_> player; + + // + // Testing utilities. + // + static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" }; + void pstate(player const& p) + { + std::cout << " -> " << state_names[p.current_state()[0]] << std::endl; + } + + void test() + { + player p; + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + p.start(); + // go to Open, call on_exit on Empty, then action, then on_entry on Open + p.process_event(open_close()); pstate(p); + p.process_event(open_close()); pstate(p); + // will be rejected, wrong disk type + p.process_event( + cd_detected("louie, louie",DISK_DVD)); pstate(p); + p.process_event( + cd_detected("louie, louie",DISK_CD)); pstate(p); + p.process_event(play()); + + // at this point, Play is active + p.process_event(pause()); pstate(p); + // go back to Playing + p.process_event(end_pause()); pstate(p); + p.process_event(pause()); pstate(p); + p.process_event(stop()); pstate(p); + // event leading to the same state + // no action method called as it is not present in the transition table + p.process_event(stop()); pstate(p); + std::cout << "stop fsm" << std::endl; + p.stop(); + + } +} + +int main() +{ + test(); + return 0; +} diff --git a/libs/msm/doc/PDF/examples/SimpleTutorialEuml.cpp b/libs/msm/doc/PDF/examples/SimpleTutorialEuml.cpp new file mode 100644 index 0000000000..b227258f60 --- /dev/null +++ b/libs/msm/doc/PDF/examples/SimpleTutorialEuml.cpp @@ -0,0 +1,187 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <vector> +#include <iostream> + +#include <boost/msm/back/state_machine.hpp> +#include <boost/msm/front/euml/euml.hpp> + +using namespace std; +using namespace boost::msm::front::euml; +namespace msm = boost::msm; + +// entry/exit/action/guard logging functors +#include "logging_functors.h" + +namespace // Concrete FSM implementation +{ + // events + BOOST_MSM_EUML_EVENT(play) + BOOST_MSM_EUML_EVENT(end_pause) + BOOST_MSM_EUML_EVENT(stop) + BOOST_MSM_EUML_EVENT(pause) + BOOST_MSM_EUML_EVENT(open_close) + + // A "complicated" event type that carries some data. + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(std::string,cd_name) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(DiskTypeEnum,cd_type) + BOOST_MSM_EUML_ATTRIBUTES((attributes_ << cd_name << cd_type ), cd_detected_attributes) + BOOST_MSM_EUML_EVENT_WITH_ATTRIBUTES(cd_detected,cd_detected_attributes) + + // Concrete FSM implementation + // The list of FSM states + // state not needing any entry or exit + BOOST_MSM_EUML_STATE((),Paused) + + // it is also possible to define a state which you can implement normally + // just make it a state, as usual, and also a grammar terminal, euml_state + struct Empty_impl : public msm::front::state<> , public euml_state<Empty_impl> + { + // this allows us to add some functions + void activate_empty() {std::cout << "switching to Empty " << std::endl;} + // standard entry behavior + template <class Event,class FSM> + void on_entry(Event const& evt,FSM& fsm) + { + std::cout << "entering: Empty" << std::endl; + } + template <class Event,class FSM> + void on_exit(Event const& evt,FSM& fsm) + { + std::cout << "leaving: Empty" << std::endl; + } + }; + //instance for use in the transition table + Empty_impl const Empty; + + // create a functor and a eUML function for the activate_empty method from Entry + BOOST_MSM_EUML_METHOD(ActivateEmpty_ , activate_empty , activate_empty_ , void , void ) + + // define more states + BOOST_MSM_EUML_STATE(( Open_Entry,Open_Exit ),Open) + BOOST_MSM_EUML_STATE(( Stopped_Entry,Stopped_Exit ),Stopped) + BOOST_MSM_EUML_STATE(( Playing_Entry,Playing_Exit ),Playing) + + + // guard conditions + BOOST_MSM_EUML_ACTION(good_disk_format) + { + template <class FSM,class EVT,class SourceState,class TargetState> + bool operator()(EVT const& evt,FSM&,SourceState& ,TargetState& ) + { + // to test a guard condition, let's say we understand only CDs, not DVD + if (evt.get_attribute(cd_type)!=DISK_CD) + { + std::cout << "wrong disk, sorry" << std::endl; + // just for logging, does not block any transition + return true; + } + std::cout << "good disk" << std::endl; + return true; + } + }; + // it is also possible to use a plain functor, with default-constructor in the transition table + struct start_play + { + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM&,SourceState& ,TargetState& ) + { + cout << "player::start_play" << endl; + } + }; + // replaces the old transition table + BOOST_MSM_EUML_TRANSITION_TABLE(( + Playing == Stopped + play / start_play() , + Playing == Paused + end_pause / resume_playback, + // +------------------------------------------------------------------------------+ + Empty == Open + open_close / (close_drawer,activate_empty_(target_)), + // +------------------------------------------------------------------------------+ + Open == Empty + open_close / open_drawer, + Open == Paused + open_close / stop_and_open, + Open == Stopped + open_close / open_drawer, + Open == Playing + open_close / stop_and_open, + // +------------------------------------------------------------------------------+ + Paused == Playing + pause / pause_playback, + // +------------------------------------------------------------------------------+ + Stopped == Playing + stop / stop_playback, + Stopped == Paused + stop / stop_playback, + Stopped == Empty + cd_detected [good_disk_format && + (event_(cd_type)==Int_<DISK_CD>())] + / (store_cd_info,process_(play)), + Stopped == Stopped + stop + // +------------------------------------------------------------------------------+ + ),transition_table) + + // create a state machine "on the fly" + BOOST_MSM_EUML_DECLARE_STATE_MACHINE(( transition_table, //STT + init_ << Empty, // Init State + no_action, // Entry + no_action, // Exit + attributes_ << no_attributes_, // Attributes + configure_ << no_configure_, // configuration + Log_No_Transition // no_transition handler + ), + player_) //fsm name + + // or simply, if no no_transition handler needed: + //BOOST_MSM_EUML_DECLARE_STATE_MACHINE(( transition_table, //STT + // Empty // Init State + // ),player_) + + // choice of back-end + typedef msm::back::state_machine<player_> player; + + // + // Testing utilities. + // + static char const* const state_names[] = { "Stopped", "Paused", "Open", "Empty", "Playing" }; + void pstate(player const& p) + { + std::cout << " -> " << state_names[p.current_state()[0]] << std::endl; + } + + void test() + { + player p; + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + p.start(); + // note that we write open_close and not open_close(), like usual. Both are possible with eUML, but + // you now have less to type. + // go to Open, call on_exit on Empty, then action, then on_entry on Open + p.process_event(open_close); pstate(p); + p.process_event(open_close); pstate(p); + // will be rejected, wrong disk type + p.process_event( + cd_detected("louie, louie",DISK_DVD)); pstate(p); + p.process_event( + cd_detected("louie, louie",DISK_CD)); pstate(p); + // no need to call play as the previous event does it in its action method + //p.process_event(play); + + // at this point, Play is active + p.process_event(pause); pstate(p); + // go back to Playing + p.process_event(end_pause); pstate(p); + p.process_event(pause); pstate(p); + p.process_event(stop); pstate(p); + // event leading to the same state + // no action method called as none is defined in the transition table + p.process_event(stop); pstate(p); + // test call to no_transition + p.process_event(pause); pstate(p); + } +} + +int main() +{ + test(); + return 0; +} diff --git a/libs/msm/doc/PDF/examples/SimpleTutorialEuml2.cpp b/libs/msm/doc/PDF/examples/SimpleTutorialEuml2.cpp new file mode 100644 index 0000000000..2768caea66 --- /dev/null +++ b/libs/msm/doc/PDF/examples/SimpleTutorialEuml2.cpp @@ -0,0 +1,149 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <vector> +#include <iostream> + +#include <boost/msm/back/state_machine.hpp> +#include <boost/msm/front/euml/euml.hpp> + +using namespace std; +using namespace boost::msm::front::euml; +namespace msm = boost::msm; + +// entry/exit/action/guard logging functors +#include "logging_functors.h" + +namespace // Concrete FSM implementation +{ + // events + // note that unlike the SimpleTutorial, events must derive from euml_event. + BOOST_MSM_EUML_EVENT(play) + BOOST_MSM_EUML_EVENT(end_pause) + BOOST_MSM_EUML_EVENT(stop) + BOOST_MSM_EUML_EVENT(pause) + BOOST_MSM_EUML_EVENT(open_close) + + // A "complicated" event type that carries some data. + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(std::string,cd_name) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(DiskTypeEnum,cd_type) + BOOST_MSM_EUML_ATTRIBUTES((attributes_ << cd_name << cd_type ), cd_detected_attributes) + BOOST_MSM_EUML_EVENT_WITH_ATTRIBUTES(cd_detected,cd_detected_attributes) + + // Concrete FSM implementation + + // The list of FSM states + + // state not needing any entry or exit + BOOST_MSM_EUML_STATE((),Paused) + BOOST_MSM_EUML_STATE(( Empty_Entry,Empty_Exit ),Empty) + BOOST_MSM_EUML_STATE(( Open_Entry,Open_Exit ),Open) + BOOST_MSM_EUML_STATE(( Stopped_Entry,Stopped_Exit ),Stopped) + BOOST_MSM_EUML_STATE(( Playing_Entry,Playing_Exit ),Playing) + + // guard conditions + BOOST_MSM_EUML_ACTION(good_disk_format) + { + template <class FSM,class EVT,class SourceState,class TargetState> + bool operator()(EVT const& evt,FSM&,SourceState& ,TargetState& ) + { + // to test a guard condition, let's say we understand only CDs, not DVD + if (evt.get_attribute(cd_type)!=DISK_CD) + { + std::cout << "wrong disk, sorry" << std::endl; + // just for logging, does not block any transition + return true; + } + std::cout << "good disk" << std::endl; + return true; + } + }; + // replaces the old transition table + BOOST_MSM_EUML_TRANSITION_TABLE(( + Stopped + play / start_playback == Playing , + Stopped + open_close / open_drawer == Open , + Stopped + stop == Stopped, + // +------------------------------------------------------------------------------+ + Open + open_close / close_drawer == Empty , + // +------------------------------------------------------------------------------+ + Empty + open_close / open_drawer == Open , + Empty + cd_detected + [good_disk_format &&(event_(cd_type)==Int_<DISK_CD>())] + / (store_cd_info,process_(play)) == Stopped , + // +------------------------------------------------------------------------------+ + Playing + stop / stop_playback == Stopped , + Playing + pause / pause_playback == Paused , + Playing + open_close / stop_and_open == Open , + // +------------------------------------------------------------------------------+ + Paused + end_pause / resume_playback == Playing , + Paused + stop / stop_playback == Stopped , + Paused + open_close / stop_and_open == Open + // +------------------------------------------------------------------------------+ + ),transition_table) + + // create a state machine "on the fly" + BOOST_MSM_EUML_DECLARE_STATE_MACHINE(( transition_table, //STT + init_ << Empty, // Init State + no_action, // Entry + no_action, // Exit + attributes_ << no_attributes_, // Attributes + configure_ << no_configure_, // configuration + Log_No_Transition // no_transition handler + ), + player_) //fsm name + + // choice of back-end + typedef msm::back::state_machine<player_> player; + + // + // Testing utilities. + // + static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" }; + void pstate(player const& p) + { + std::cout << " -> " << state_names[p.current_state()[0]] << std::endl; + } + + void test() + { + player p; + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + p.start(); + // go to Open, call on_exit on Empty, then action, then on_entry on Open + p.process_event(open_close); pstate(p); + p.process_event(open_close); pstate(p); + // will be rejected, wrong disk type + p.process_event( + cd_detected("louie, louie",DISK_DVD)); pstate(p); + p.process_event( + cd_detected("louie, louie",DISK_CD)); pstate(p); + // no need to call play as the previous event does it in its action method + //p.process_event(play); + + // at this point, Play is active + p.process_event(pause); pstate(p); + // go back to Playing + p.process_event(end_pause); pstate(p); + p.process_event(pause); pstate(p); + p.process_event(stop); pstate(p); + // event leading to the same state + // no action method called as none is defined in the transition table + p.process_event(stop); pstate(p); + // test call to no_transition + p.process_event(pause); pstate(p); + } +} + +int main() +{ + test(); + return 0; +} + diff --git a/libs/msm/doc/PDF/examples/SimpleTutorialInternal.cpp b/libs/msm/doc/PDF/examples/SimpleTutorialInternal.cpp new file mode 100644 index 0000000000..49b71df3d4 --- /dev/null +++ b/libs/msm/doc/PDF/examples/SimpleTutorialInternal.cpp @@ -0,0 +1,226 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> +#include <boost/msm/front/functor_row.hpp> + +namespace msm = boost::msm; +namespace mpl = boost::mpl; +using namespace msm::front; + +namespace +{ + // events + struct play {}; + struct end_pause {}; + struct stop {}; + struct pause {}; + struct open_close {}; + struct to_ignore {}; + + // A "complicated" event type that carries some data. + enum DiskTypeEnum + { + DISK_CD=0, + DISK_DVD=1 + }; + struct cd_detected + { + cd_detected(std::string name, DiskTypeEnum diskType) + : name(name), + disc_type(diskType) + {} + + std::string name; + DiskTypeEnum disc_type; + }; + + // front-end: define the FSM structure + struct player_ : public msm::front::state_machine_def<player_> + { + // The list of FSM states + struct Empty : public msm::front::state<> + { + // every (optional) entry/exit methods get the event passed. + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Empty" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Empty" << std::endl;} + }; + struct Open : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) {std::cout << "entering: Open" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Open" << std::endl;} + }; + + // sm_ptr still supported but deprecated as functors are a much better way to do the same thing + struct Stopped : public msm::front::state<msm::front::default_base_state,msm::front::sm_ptr> + { + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) {std::cout << "entering: Stopped" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Stopped" << std::endl;} + void set_sm_ptr(player_* pl) + { + m_player=pl; + } + player_* m_player; + }; + + struct Playing : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Playing" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Playing" << std::endl;} + }; + + // state not defining any entry or exit + struct Paused : public msm::front::state<> + { + }; + + // the initial state of the player SM. Must be defined + typedef Empty initial_state; + + // transition actions + void start_playback(play const&) { std::cout << "player::start_playback\n"; } + void open_drawer(open_close const&) { std::cout << "player::open_drawer\n"; } + void close_drawer(open_close const&) { std::cout << "player::close_drawer\n"; } + void store_cd_info(cd_detected const&) { std::cout << "player::store_cd_info\n"; } + void stop_playback(stop const&) { std::cout << "player::stop_playback\n"; } + void pause_playback(pause const&) { std::cout << "player::pause_playback\n"; } + void resume_playback(end_pause const&) { std::cout << "player::resume_playback\n"; } + void stop_and_open(open_close const&) { std::cout << "player::stop_and_open\n"; } + void stopped_again(stop const&) {std::cout << "player::stopped_again\n";} + // guard conditions + bool good_disk_format(cd_detected const& evt) + { + // to test a guard condition, let's say we understand only CDs, not DVD + if (evt.disc_type != DISK_CD) + { + std::cout << "wrong disk, sorry" << std::endl; + return false; + } + return true; + } + // transitions internal to Empty + void internal_action(cd_detected const&){ std::cout << "Empty::internal action\n"; } + bool internal_guard(cd_detected const&) + { + std::cout << "Empty::internal guard\n"; + return false; + } + struct internal_guard_fct + { + template <class EVT,class FSM,class SourceState,class TargetState> + bool operator()(EVT const& evt ,FSM&,SourceState& ,TargetState& ) + { + std::cout << "Empty::internal guard functor\n"; + return false; + } + }; + struct internal_action_fct + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + std::cout << "Empty::internal action functor" << std::endl; + } + }; + + typedef player_ p; // makes transition table cleaner + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Stopped , play , Playing , &p::start_playback >, + a_row < Stopped , open_close , Open , &p::open_drawer >, + _row < Stopped , stop , Stopped >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Open , open_close , Empty , &p::close_drawer >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Empty , open_close , Open , &p::open_drawer >, + row < Empty , cd_detected , Stopped , &p::store_cd_info ,&p::good_disk_format >, + irow < Empty , cd_detected , &p::internal_action ,&p::internal_guard >, + _irow < Empty , to_ignore >, + g_irow < Empty , cd_detected ,&p::internal_guard >, + Row < Empty , cd_detected , none , internal_action_fct ,internal_guard_fct >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Playing , stop , Stopped , &p::stop_playback >, + a_row < Playing , pause , Paused , &p::pause_playback >, + a_row < Playing , open_close , Open , &p::stop_and_open >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Paused , end_pause , Playing , &p::resume_playback >, + a_row < Paused , stop , Stopped , &p::stop_playback >, + a_row < Paused , open_close , Open , &p::stop_and_open > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + // Pick a back-end + typedef msm::back::state_machine<player_> player; + + // + // Testing utilities. + // + static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" }; + void pstate(player const& p) + { + std::cout << " -> " << state_names[p.current_state()[0]] << std::endl; + } + + void test() + { + player p; + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + p.start(); + // this event will be ignored and not call no_transition + p.process_event(to_ignore()); + // go to Open, call on_exit on Empty, then action, then on_entry on Open + p.process_event(open_close()); pstate(p); + p.process_event(open_close()); pstate(p); + // will be rejected, wrong disk type + p.process_event( + cd_detected("louie, louie",DISK_DVD)); pstate(p); + p.process_event( + cd_detected("louie, louie",DISK_CD)); pstate(p); + p.process_event(play()); + + // at this point, Play is active + p.process_event(pause()); pstate(p); + // go back to Playing + p.process_event(end_pause()); pstate(p); + p.process_event(pause()); pstate(p); + p.process_event(stop()); pstate(p); + // event leading to the same state + // no action method called as it is not present in the transition table + p.process_event(stop()); pstate(p); + } +} + +int main() +{ + test(); + return 0; +} diff --git a/libs/msm/doc/PDF/examples/SimpleTutorialInternal2.cpp b/libs/msm/doc/PDF/examples/SimpleTutorialInternal2.cpp new file mode 100644 index 0000000000..f123fdba92 --- /dev/null +++ b/libs/msm/doc/PDF/examples/SimpleTutorialInternal2.cpp @@ -0,0 +1,231 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> +#include <boost/msm/front/functor_row.hpp> + +namespace msm = boost::msm; +using namespace msm::front; + +namespace +{ + // events + struct play {}; + struct end_pause {}; + struct stop {}; + struct pause {}; + struct open_close {}; + + // A "complicated" event type that carries some data. + enum DiskTypeEnum + { + DISK_CD=0, + DISK_DVD=1 + }; + struct cd_detected + { + cd_detected(std::string name, DiskTypeEnum diskType) + : name(name), + disc_type(diskType) + {} + + std::string name; + DiskTypeEnum disc_type; + }; + + // front-end: define the FSM structure + struct player_ : public msm::front::state_machine_def<player_> + { + // actions for Empty's internal transitions + void internal_action(cd_detected const&){ std::cout << "Empty::internal action\n"; } + bool internal_guard(cd_detected const&) + { + std::cout << "Empty::internal guard\n"; + return false; + } + struct internal_guard_fct + { + template <class EVT,class FSM,class SourceState,class TargetState> + bool operator()(EVT const& evt ,FSM&,SourceState& ,TargetState& ) + { + std::cout << "Empty::internal guard functor\n"; + return false; + } + }; + struct internal_action_fct + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + std::cout << "Empty::internal action functor" << std::endl; + } + }; + + // The list of FSM states + struct Empty : public msm::front::state<> + { + // every (optional) entry/exit methods get the event passed. + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Empty" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Empty" << std::endl;} + + // Transition table for Empty + struct internal_transition_table : mpl::vector< + // Start Event Next Action Guard + Internal < cd_detected , internal_action_fct ,internal_guard_fct > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + }; + struct Open : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) {std::cout << "entering: Open" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Open" << std::endl;} + }; + + // sm_ptr still supported but deprecated as functors are a much better way to do the same thing + struct Stopped : public msm::front::state<msm::front::default_base_state,msm::front::sm_ptr> + { + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) {std::cout << "entering: Stopped" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Stopped" << std::endl;} + void set_sm_ptr(player_* pl) + { + m_player=pl; + } + player_* m_player; + }; + + struct Playing : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Playing" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Playing" << std::endl;} + }; + + // state not defining any entry or exit + struct Paused : public msm::front::state<> + { + }; + + // the initial state of the player SM. Must be defined + typedef Empty initial_state; + + // transition actions + void start_playback(play const&) { std::cout << "player::start_playback\n"; } + void open_drawer(open_close const&) { std::cout << "player::open_drawer\n"; } + void close_drawer(open_close const&) { std::cout << "player::close_drawer\n"; } + void store_cd_info(cd_detected const&) { std::cout << "player::store_cd_info\n"; } + void stop_playback(stop const&) { std::cout << "player::stop_playback\n"; } + void pause_playback(pause const&) { std::cout << "player::pause_playback\n"; } + void resume_playback(end_pause const&) { std::cout << "player::resume_playback\n"; } + void stop_and_open(open_close const&) { std::cout << "player::stop_and_open\n"; } + void stopped_again(stop const&) {std::cout << "player::stopped_again\n";} + // guard conditions + bool good_disk_format(cd_detected const& evt) + { + // to test a guard condition, let's say we understand only CDs, not DVD + if (evt.disc_type != DISK_CD) + { + std::cout << "wrong disk, sorry" << std::endl; + return false; + } + return true; + } + + typedef player_ p; // makes transition table cleaner + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Stopped , play , Playing , &p::start_playback >, + a_row < Stopped , open_close , Open , &p::open_drawer >, + _row < Stopped , stop , Stopped >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Open , open_close , Empty , &p::close_drawer >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Empty , open_close , Open , &p::open_drawer >, + // conflict between a normal and 2 internal transitions (irow/g_irow) + row < Empty , cd_detected , Stopped , &p::store_cd_info ,&p::good_disk_format >, + irow < Empty , cd_detected , &p::internal_action ,&p::internal_guard >, + g_irow < Empty , cd_detected ,&p::internal_guard >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Playing , stop , Stopped , &p::stop_playback >, + a_row < Playing , pause , Paused , &p::pause_playback >, + a_row < Playing , open_close , Open , &p::stop_and_open >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Paused , end_pause , Playing , &p::resume_playback >, + a_row < Paused , stop , Stopped , &p::stop_playback >, + a_row < Paused , open_close , Open , &p::stop_and_open > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + typedef int no_message_queue; + + }; + // Pick a back-end + typedef msm::back::state_machine<player_> player; + + // + // Testing utilities. + // + static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" }; + void pstate(player const& p) + { + std::cout << " -> " << state_names[p.current_state()[0]] << std::endl; + } + + void test() + { + player p; + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + p.start(); + // go to Open, call on_exit on Empty, then action, then on_entry on Open + p.process_event(open_close()); pstate(p); + p.process_event(open_close()); pstate(p); + // will be rejected, wrong disk type + p.process_event( + cd_detected("louie, louie",DISK_DVD)); pstate(p); + p.process_event( + cd_detected("louie, louie",DISK_CD)); pstate(p); + p.process_event(play()); + + // at this point, Play is active + p.process_event(pause()); pstate(p); + // go back to Playing + p.process_event(end_pause()); pstate(p); + p.process_event(pause()); pstate(p); + p.process_event(stop()); pstate(p); + // event leading to the same state + // no action method called as it is not present in the transition table + p.process_event(stop()); pstate(p); + } +} + +int main() +{ + test(); + return 0; +} diff --git a/libs/msm/doc/PDF/examples/SimpleTutorialInternalFunctors.cpp b/libs/msm/doc/PDF/examples/SimpleTutorialInternalFunctors.cpp new file mode 100644 index 0000000000..77f08da69c --- /dev/null +++ b/libs/msm/doc/PDF/examples/SimpleTutorialInternalFunctors.cpp @@ -0,0 +1,325 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> +#include <boost/msm/front/functor_row.hpp> +#include <boost/msm/front/euml/common.hpp> +// for And_ operator +#include <boost/msm/front/euml/operator.hpp> + +using namespace std; +namespace msm = boost::msm; +using namespace msm::front; +namespace mpl = boost::mpl; +// for And_ operator +using namespace msm::front::euml; + +namespace +{ + // events + struct play {}; + struct end_pause {}; + struct stop {}; + struct pause {}; + struct open_close {}; + + // A "complicated" event type that carries some data. + enum DiskTypeEnum + { + DISK_CD=0, + DISK_DVD=1 + }; + struct cd_detected + { + cd_detected(std::string name, DiskTypeEnum diskType) + : name(name), + disc_type(diskType) + {} + + std::string name; + DiskTypeEnum disc_type; + }; + + // front-end: define the FSM structure + struct player_ : public msm::front::state_machine_def<player_> + { + // The list of FSM states + struct Empty : public msm::front::state<> + { + // every (optional) entry/exit methods get the event passed. + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Empty" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Empty" << std::endl;} + struct internal_guard_fct + { + template <class EVT,class FSM,class SourceState,class TargetState> + bool operator()(EVT const& evt ,FSM&,SourceState& ,TargetState& ) + { + std::cout << "Empty::internal_transition_table guard\n"; + return false; + } + }; + struct internal_action_fct + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + std::cout << "Empty::internal_transition_table action" << std::endl; + } + }; + // Transition table for Empty + struct internal_transition_table : mpl::vector< + // Start Event Next Action Guard + Internal < cd_detected , internal_action_fct ,internal_guard_fct > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + }; + struct Open : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) {std::cout << "entering: Open" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Open" << std::endl;} + }; + + struct Stopped : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) {std::cout << "entering: Stopped" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Stopped" << std::endl;} + }; + + struct Playing : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Playing" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Playing" << std::endl;} + }; + + // state not defining any entry or exit + struct Paused : public msm::front::state<> + { + }; + + // the initial state of the player SM. Must be defined + typedef Empty initial_state; + + // transition actions + // as the functors are generic on events, fsm and source/target state, + // you can reuse them in another machine if you wish + struct TestFct + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const&, FSM&,SourceState& ,TargetState& ) + { + cout << "transition with event:" << typeid(EVT).name() << endl; + } + }; + struct start_playback + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + cout << "player::start_playback" << endl; + } + }; + struct open_drawer + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + cout << "player::open_drawer" << endl; + } + }; + struct close_drawer + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + cout << "player::close_drawer" << endl; + } + }; + struct store_cd_info + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const&,FSM& fsm ,SourceState& ,TargetState& ) + { + cout << "player::store_cd_info" << endl; + fsm.process_event(play()); + } + }; + struct stop_playback + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + cout << "player::stop_playback" << endl; + } + }; + struct pause_playback + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + cout << "player::pause_playback" << endl; + } + }; + struct resume_playback + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + cout << "player::resume_playback" << endl; + } + }; + struct stop_and_open + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + cout << "player::stop_and_open" << endl; + } + }; + struct stopped_again + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + cout << "player::stopped_again" << endl; + } + }; + // guard conditions + struct DummyGuard + { + template <class EVT,class FSM,class SourceState,class TargetState> + bool operator()(EVT const& evt,FSM& fsm,SourceState& src,TargetState& tgt) + { + return true; + } + }; + struct good_disk_format + { + template <class EVT,class FSM,class SourceState,class TargetState> + bool operator()(EVT const& evt ,FSM&,SourceState& ,TargetState& ) + { + // to test a guard condition, let's say we understand only CDs, not DVD + if (evt.disc_type != DISK_CD) + { + std::cout << "wrong disk, sorry" << std::endl; + return false; + } + return true; + } + }; + struct always_true + { + template <class EVT,class FSM,class SourceState,class TargetState> + bool operator()(EVT const& evt ,FSM&,SourceState& ,TargetState& ) + { + return true; + } + }; + struct internal_guard + { + template <class EVT,class FSM,class SourceState,class TargetState> + bool operator()(EVT const& evt ,FSM&,SourceState& ,TargetState& ) + { + std::cout << "Empty::internal guard\n"; + return false; + } + }; + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------------+----------------------+ + Row < Stopped , play , Playing , ActionSequence_ + <mpl::vector< + TestFct,start_playback> > + , DummyGuard >, + Row < Stopped , open_close , Open , open_drawer , none >, + Row < Stopped , stop , Stopped , none , none >, + // +---------+-------------+---------+---------------------------+----------------------+ + Row < Open , open_close , Empty , close_drawer , none >, + // +---------+-------------+---------+---------------------------+----------------------+ + Row < Empty , open_close , Open , open_drawer , none >, + Row < Empty , cd_detected , Stopped , store_cd_info , And_<good_disk_format, + always_true> >, + // internal transition inside the stt: none as Target + Row < Empty , cd_detected , none , none , internal_guard >, + // +---------+-------------+---------+---------------------------+----------------------+ + Row < Playing , stop , Stopped , stop_playback , none >, + Row < Playing , pause , Paused , pause_playback , none >, + Row < Playing , open_close , Open , stop_and_open , none >, + // +---------+-------------+---------+---------------------------+----------------------+ + Row < Paused , end_pause , Playing , resume_playback , none >, + Row < Paused , stop , Stopped , stop_playback , none >, + Row < Paused , open_close , Open , stop_and_open , none > + // +---------+-------------+---------+---------------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + // Pick a back-end + typedef msm::back::state_machine<player_> player; + + // + // Testing utilities. + // + static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" }; + void pstate(player const& p) + { + std::cout << " -> " << state_names[p.current_state()[0]] << std::endl; + } + + void test() + { + player p; + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + p.start(); + // go to Open, call on_exit on Empty, then action, then on_entry on Open + p.process_event(open_close()); pstate(p); + p.process_event(open_close()); pstate(p); + // will be rejected, wrong disk type + p.process_event( + cd_detected("louie, louie",DISK_DVD)); pstate(p); + p.process_event( + cd_detected("louie, louie",DISK_CD)); pstate(p); + // no need to call play() as the previous transition does it in its action method + //p.process_event(play()); + + // at this point, Play is active + p.process_event(pause()); pstate(p); + // go back to Playing + p.process_event(end_pause()); pstate(p); + p.process_event(pause()); pstate(p); + p.process_event(stop()); pstate(p); + // event leading to the same state + // no action method called as it is not present in the transition table + p.process_event(stop()); pstate(p); + } +} + +int main() +{ + test(); + return 0; +} diff --git a/libs/msm/doc/PDF/examples/SimpleTutorialWithEumlTable.cpp b/libs/msm/doc/PDF/examples/SimpleTutorialWithEumlTable.cpp new file mode 100644 index 0000000000..e67b1aba3f --- /dev/null +++ b/libs/msm/doc/PDF/examples/SimpleTutorialWithEumlTable.cpp @@ -0,0 +1,158 @@ +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> +#include <boost/msm/front/euml/euml.hpp> + +namespace msm = boost::msm; +namespace mpl = boost::mpl; +using namespace std; + +// entry/exit/action/guard logging functors +#include "logging_functors.h" + +namespace +{ + // events + struct play_impl : msm::front::euml::euml_event<play_impl> {}; + struct end_pause_impl : msm::front::euml::euml_event<end_pause_impl>{}; + struct stop_impl : msm::front::euml::euml_event<stop_impl>{}; + struct pause_impl : msm::front::euml::euml_event<pause_impl>{}; + struct open_close_impl : msm::front::euml::euml_event<open_close_impl>{}; + struct cd_detected_impl : msm::front::euml::euml_event<cd_detected_impl>{}; + + // define some dummy instances for use in the transition table + // it is also possible to default-construct them instead: + // struct play {}; + // inside the table: play() + play_impl play; + end_pause_impl end_pause; + stop_impl stop; + pause_impl pause; + open_close_impl open_close; + cd_detected_impl cd_detected; + + // The list of FSM states + // they have to be declared outside of the front-end only to make VC happy :( + // note: gcc would have no problem + struct Empty_impl : public msm::front::state<> , public msm::front::euml::euml_state<Empty_impl> + { + // every (optional) entry/exit methods get the event passed. + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Empty" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Empty" << std::endl;} + }; + struct Open_impl : public msm::front::state<> , public msm::front::euml::euml_state<Open_impl> + { + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) {std::cout << "entering: Open" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Open" << std::endl;} + }; + + struct Stopped_impl : public msm::front::state<> , public msm::front::euml::euml_state<Stopped_impl> + { + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) {std::cout << "entering: Stopped" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Stopped" << std::endl;} + }; + + struct Playing_impl : public msm::front::state<> , public msm::front::euml::euml_state<Playing_impl> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Playing" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Playing" << std::endl;} + }; + + // state not defining any entry or exit + struct Paused_impl : public msm::front::state<> , public msm::front::euml::euml_state<Paused_impl> + { + }; + //to make the transition table more readable + Empty_impl const Empty; + Open_impl const Open; + Stopped_impl const Stopped; + Playing_impl const Playing; + Paused_impl const Paused; + + // front-end: define the FSM structure + struct player_ : public msm::front::state_machine_def<player_> + { + + // the initial state of the player SM. Must be defined + typedef Empty_impl initial_state; + + // Transition table for player + // replaces the old transition table + BOOST_MSM_EUML_DECLARE_TRANSITION_TABLE(( + Stopped + play / start_playback == Playing , + Stopped + open_close / open_drawer == Open , + Stopped + stop == Stopped , + // +------------------------------------------------------------------------------+ + Open + open_close / close_drawer == Empty , + // +------------------------------------------------------------------------------+ + Empty + open_close / open_drawer == Open , + Empty + cd_detected /(store_cd_info, + msm::front::euml::process_(play)) == Stopped , + // +------------------------------------------------------------------------------+ + Playing + stop / stop_playback == Stopped , + Playing + pause / pause_playback == Paused , + Playing + open_close / stop_and_open == Open , + // +------------------------------------------------------------------------------+ + Paused + end_pause / resume_playback == Playing , + Paused + stop / stop_playback == Stopped , + Paused + open_close / stop_and_open == Open + // +------------------------------------------------------------------------------+ + ),transition_table) + + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + // Pick a back-end + typedef msm::back::state_machine<player_> player; + + // + // Testing utilities. + // + static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" }; + void pstate(player const& p) + { + std::cout << " -> " << state_names[p.current_state()[0]] << std::endl; + } + + void test() + { + player p; + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + p.start(); + // go to Open, call on_exit on Empty, then action, then on_entry on Open + p.process_event(open_close); pstate(p); + p.process_event(open_close); pstate(p); + p.process_event(cd_detected); pstate(p); + + // at this point, Play is active + p.process_event(pause); pstate(p); + // go back to Playing + p.process_event(end_pause); pstate(p); + p.process_event(pause); pstate(p); + p.process_event(stop); pstate(p); + // event leading to the same state + // no action method called as it is not present in the transition table + p.process_event(stop); pstate(p); + } +} + +int main() +{ + test(); + return 0; +} diff --git a/libs/msm/doc/PDF/examples/SimpleWithFunctors.cpp b/libs/msm/doc/PDF/examples/SimpleWithFunctors.cpp new file mode 100644 index 0000000000..fa86f19030 --- /dev/null +++ b/libs/msm/doc/PDF/examples/SimpleWithFunctors.cpp @@ -0,0 +1,318 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <vector> +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> +// functors +#include <boost/msm/front/functor_row.hpp> +#include <boost/msm/front/euml/common.hpp> +// for And_ operator +#include <boost/msm/front/euml/operator.hpp> + +using namespace std; +namespace msm = boost::msm; +namespace mpl = boost::mpl; +using namespace msm::front; +// for And_ operator +using namespace msm::front::euml; + +namespace // Concrete FSM implementation +{ + // events + struct play {}; + struct end_pause {}; + struct stop {}; + struct pause {}; + struct open_close {}; + + // A "complicated" event type that carries some data. + enum DiskTypeEnum + { + DISK_CD=0, + DISK_DVD=1 + }; + struct cd_detected + { + cd_detected(std::string name, DiskTypeEnum diskType) + : name(name), + disc_type(diskType) + {} + + std::string name; + DiskTypeEnum disc_type; + }; + + // front-end: define the FSM structure + struct player_ : public msm::front::state_machine_def<player_> + { + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) + { + std::cout << "entering: Player" << std::endl; + } + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) + { + std::cout << "leaving: Player" << std::endl; + } + + // The list of FSM states + struct Empty : public msm::front::state<> + { + // every (optional) entry/exit methods get the event passed. + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Empty" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Empty" << std::endl;} + }; + struct Open : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) {std::cout << "entering: Open" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Open" << std::endl;} + }; + + struct Stopped : public msm::front::state<> + { + // when stopped, the CD is loaded + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) {std::cout << "entering: Stopped" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Stopped" << std::endl;} + }; + + struct Playing : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Playing" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Playing" << std::endl;} + }; + + // state not defining any entry or exit + struct Paused : public msm::front::state<> + { + }; + + // the initial state of the player SM. Must be defined + typedef Empty initial_state; + + // transition actions + // as the functors are generic on events, fsm and source/target state, + // you can reuse them in another machine if you wish + struct TestFct + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const&, FSM&,SourceState& ,TargetState& ) + { + cout << "transition with event:" << typeid(EVT).name() << endl; + } + }; + struct start_playback + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + cout << "player::start_playback" << endl; + } + }; + struct open_drawer + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + cout << "player::open_drawer" << endl; + } + }; + struct close_drawer + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + cout << "player::close_drawer" << endl; + } + }; + struct store_cd_info + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const&,FSM& fsm ,SourceState& ,TargetState& ) + { + cout << "player::store_cd_info" << endl; + fsm.process_event(play()); + } + }; + struct stop_playback + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + cout << "player::stop_playback" << endl; + } + }; + struct pause_playback + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + cout << "player::pause_playback" << endl; + } + }; + struct resume_playback + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + cout << "player::resume_playback" << endl; + } + }; + struct stop_and_open + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + cout << "player::stop_and_open" << endl; + } + }; + struct stopped_again + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + cout << "player::stopped_again" << endl; + } + }; + // guard conditions + struct DummyGuard + { + template <class EVT,class FSM,class SourceState,class TargetState> + bool operator()(EVT const& evt,FSM& fsm,SourceState& src,TargetState& tgt) + { + return true; + } + }; + struct good_disk_format + { + template <class EVT,class FSM,class SourceState,class TargetState> + bool operator()(EVT const& evt ,FSM&,SourceState& ,TargetState& ) + { + // to test a guard condition, let's say we understand only CDs, not DVD + if (evt.disc_type != DISK_CD) + { + std::cout << "wrong disk, sorry" << std::endl; + return false; + } + return true; + } + }; + struct always_true + { + template <class EVT,class FSM,class SourceState,class TargetState> + bool operator()(EVT const& evt ,FSM&,SourceState& ,TargetState& ) + { + return true; + } + }; + // we want to define one row with the classic look. + bool auto_start(cd_detected const& evt) + { + return false; + } + + typedef player_ p; // makes transition table cleaner + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------------+----------------------+ + Row < Stopped , play , Playing , ActionSequence_ + <mpl::vector< + TestFct,start_playback> > + , DummyGuard >, + Row < Stopped , open_close , Open , open_drawer , none >, + Row < Stopped , stop , Stopped , none , none >, + // +---------+-------------+---------+---------------------------+----------------------+ + Row < Open , open_close , Empty , close_drawer , none >, + // +---------+-------------+---------+---------------------------+----------------------+ + Row < Empty , open_close , Open , open_drawer , none >, + Row < Empty , cd_detected , Stopped , store_cd_info , And_<good_disk_format, + always_true> >, + // we here also mix with some "classical row" + g_row < Empty , cd_detected , Playing , &p::auto_start >, + // +---------+-------------+---------+---------------------------+----------------------+ + Row < Playing , stop , Stopped , stop_playback , none >, + Row < Playing , pause , Paused , pause_playback , none >, + Row < Playing , open_close , Open , stop_and_open , none >, + // +---------+-------------+---------+---------------------------+----------------------+ + Row < Paused , end_pause , Playing , resume_playback , none >, + Row < Paused , stop , Stopped , stop_playback , none >, + Row < Paused , open_close , Open , stop_and_open , none > + // +---------+-------------+---------+---------------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + // Pick a back-end + typedef msm::back::state_machine<player_> player; + + // + // Testing utilities. + // + static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" }; + void pstate(player const& p) + { + std::cout << " -> " << state_names[p.current_state()[0]] << std::endl; + } + + void test() + { + player p; + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + p.start(); + // go to Open, call on_exit on Empty, then action, then on_entry on Open + p.process_event(open_close()); pstate(p); + p.process_event(open_close()); pstate(p); + // will be rejected, wrong disk type + p.process_event( + cd_detected("louie, louie",DISK_DVD)); pstate(p); + p.process_event( + cd_detected("louie, louie",DISK_CD)); pstate(p); + // no need to call play() as the previous event does it in its action method + //p.process_event(play()); + + // at this point, Play is active + p.process_event(pause()); pstate(p); + // go back to Playing + p.process_event(end_pause()); pstate(p); + p.process_event(pause()); pstate(p); + p.process_event(stop()); pstate(p); + // event leading to the same state + // no action method called as it is not present in the transition table + p.process_event(stop()); pstate(p); + std::cout << "stop fsm" << std::endl; + p.stop(); + + } +} + +int main() +{ + test(); + return 0; +} diff --git a/libs/msm/doc/PDF/examples/SimpleWithFunctors2.cpp b/libs/msm/doc/PDF/examples/SimpleWithFunctors2.cpp new file mode 100644 index 0000000000..4ca3dc9fad --- /dev/null +++ b/libs/msm/doc/PDF/examples/SimpleWithFunctors2.cpp @@ -0,0 +1,320 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <vector> +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> +// functors +#include <boost/msm/front/functor_row.hpp> +#include <boost/msm/front/euml/common.hpp> +// for And_ operator +#include <boost/msm/front/euml/operator.hpp> +// for func_state and func_state_machine +#include <boost/msm/front/euml/state_grammar.hpp> + +using namespace std; +namespace msm = boost::msm; +namespace mpl = boost::mpl; +using namespace msm::front; +// for And_ operator +using namespace msm::front::euml; + +namespace // Concrete FSM implementation +{ + // events + struct play {}; + struct end_pause {}; + struct stop {}; + struct pause {}; + struct open_close {}; + + // A "complicated" event type that carries some data. + enum DiskTypeEnum + { + DISK_CD=0, + DISK_DVD=1 + }; + struct cd_detected + { + cd_detected(std::string name, DiskTypeEnum diskType) + : name(name), + disc_type(diskType) + {} + + std::string name; + DiskTypeEnum disc_type; + }; + + // front-end: define the FSM structure + struct player_ : public msm::front::state_machine_def<player_> + { + // The list of FSM states + // entry and exit functors for Empty + struct Empty_Entry + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: Empty" << std::endl; + } + }; + struct Empty_Exit + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "leaving: Empty" << std::endl; + } + }; + // definition of Empty + struct Empty_tag {}; + typedef msm::front::euml::func_state<Empty_tag,Empty_Entry,Empty_Exit> Empty; + + struct Open_Entry + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: Open" << std::endl; + } + }; + struct Open_Exit + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "leaving: Open" << std::endl; + } + }; + struct Open_tag {}; + typedef msm::front::euml::func_state<Open_tag,Open_Entry,Open_Exit> Open; + + // states without entry/exit actions (can be declared as functor state, just without functors ;-) ) + struct Stopped_tag {}; + typedef msm::front::euml::func_state<Stopped_tag> Stopped; + + struct Playing_tag {}; + typedef msm::front::euml::func_state<Playing_tag> Playing; + + // state not defining any entry or exit (declared as simple state. Equivalent) + struct Paused : public msm::front::state<> + { + }; + + // the initial state of the player SM. Must be defined + typedef Empty initial_state; + + // transition actions + // as the functors are generic on events, fsm and source/target state, + // you can reuse them in another machine if you wish + struct TestFct + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const&, FSM&,SourceState& ,TargetState& ) + { + cout << "transition with event:" << typeid(EVT).name() << endl; + } + }; + struct start_playback + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + cout << "player::start_playback" << endl; + } + }; + struct open_drawer + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + cout << "player::open_drawer" << endl; + } + }; + struct close_drawer + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + cout << "player::close_drawer" << endl; + } + }; + struct store_cd_info + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const&,FSM& fsm ,SourceState& ,TargetState& ) + { + cout << "player::store_cd_info" << endl; + fsm.process_event(play()); + } + }; + struct stop_playback + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + cout << "player::stop_playback" << endl; + } + }; + struct pause_playback + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + cout << "player::pause_playback" << endl; + } + }; + struct resume_playback + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + cout << "player::resume_playback" << endl; + } + }; + struct stop_and_open + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + cout << "player::stop_and_open" << endl; + } + }; + struct stopped_again + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + cout << "player::stopped_again" << endl; + } + }; + // guard conditions + struct DummyGuard + { + template <class EVT,class FSM,class SourceState,class TargetState> + bool operator()(EVT const& evt,FSM& fsm,SourceState& src,TargetState& tgt) + { + return true; + } + }; + struct good_disk_format + { + template <class EVT,class FSM,class SourceState,class TargetState> + bool operator()(EVT const& evt ,FSM&,SourceState& ,TargetState& ) + { + // to test a guard condition, let's say we understand only CDs, not DVD + if (evt.disc_type != DISK_CD) + { + std::cout << "wrong disk, sorry" << std::endl; + return false; + } + return true; + } + }; + struct always_true + { + template <class EVT,class FSM,class SourceState,class TargetState> + bool operator()(EVT const& evt ,FSM&,SourceState& ,TargetState& ) + { + return true; + } + }; + // we want to define one row with the classic look. + bool auto_start(cd_detected const& evt) + { + return false; + } + + typedef player_ p; // makes transition table cleaner + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------------+----------------------+ + Row < Stopped , play , Playing , ActionSequence_ + <mpl::vector< + TestFct,start_playback> > + , DummyGuard >, + Row < Stopped , open_close , Open , open_drawer , none >, + Row < Stopped , stop , Stopped , none , none >, + // +---------+-------------+---------+---------------------------+----------------------+ + Row < Open , open_close , Empty , close_drawer , none >, + // +---------+-------------+---------+---------------------------+----------------------+ + Row < Empty , open_close , Open , open_drawer , none >, + Row < Empty , cd_detected , Stopped , store_cd_info , And_<good_disk_format, + always_true> >, + // we here also mix with some "classical row" + g_row < Empty , cd_detected , Playing , &p::auto_start >, + // +---------+-------------+---------+---------------------------+----------------------+ + Row < Playing , stop , Stopped , stop_playback , none >, + Row < Playing , pause , Paused , pause_playback , none >, + Row < Playing , open_close , Open , stop_and_open , none >, + // +---------+-------------+---------+---------------------------+----------------------+ + Row < Paused , end_pause , Playing , resume_playback , none >, + Row < Paused , stop , Stopped , stop_playback , none >, + Row < Paused , open_close , Open , stop_and_open , none > + // +---------+-------------+---------+---------------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + // Pick a back-end + typedef msm::back::state_machine<player_> player; + + // + // Testing utilities. + // + static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" }; + void pstate(player const& p) + { + std::cout << " -> " << state_names[p.current_state()[0]] << std::endl; + } + + void test() + { + player p; + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + p.start(); + // go to Open, call on_exit on Empty, then action, then on_entry on Open + p.process_event(open_close()); pstate(p); + p.process_event(open_close()); pstate(p); + // will be rejected, wrong disk type + p.process_event( + cd_detected("louie, louie",DISK_DVD)); pstate(p); + p.process_event( + cd_detected("louie, louie",DISK_CD)); pstate(p); + // no need to call play() as the previous event does it in its action method + //p.process_event(play()); + + // at this point, Play is active + p.process_event(pause()); pstate(p); + // go back to Playing + p.process_event(end_pause()); pstate(p); + p.process_event(pause()); pstate(p); + p.process_event(stop()); pstate(p); + // event leading to the same state + // no action method called as it is not present in the transition table + p.process_event(stop()); pstate(p); + } +} + +int main() +{ + test(); + return 0; +} diff --git a/libs/msm/doc/PDF/examples/SimpleWithFunctors3.cpp b/libs/msm/doc/PDF/examples/SimpleWithFunctors3.cpp new file mode 100644 index 0000000000..6d654e79d6 --- /dev/null +++ b/libs/msm/doc/PDF/examples/SimpleWithFunctors3.cpp @@ -0,0 +1,307 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <vector> +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> +// functors +#include <boost/msm/front/functor_row.hpp> +#include <boost/msm/front/euml/common.hpp> +// for And_ operator +#include <boost/msm/front/euml/operator.hpp> +// for func_state and func_state_machine +#include <boost/msm/front/euml/state_grammar.hpp> + +using namespace std; +namespace msm = boost::msm; +namespace mpl = boost::mpl; +using namespace msm::front; +// for And_ operator +using namespace msm::front::euml; + +namespace // Concrete FSM implementation +{ + // events + struct play {}; + struct end_pause {}; + struct stop {}; + struct pause {}; + struct open_close {}; + + // A "complicated" event type that carries some data. + enum DiskTypeEnum + { + DISK_CD=0, + DISK_DVD=1 + }; + struct cd_detected + { + cd_detected(std::string name, DiskTypeEnum diskType) + : name(name), + disc_type(diskType) + {} + + std::string name; + DiskTypeEnum disc_type; + }; + + + // The list of FSM states + // entry and exit functors for Empty + struct Empty_Entry + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: Empty" << std::endl; + } + }; + struct Empty_Exit + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "leaving: Empty" << std::endl; + } + }; + // definition of Empty + struct Empty_tag {}; + typedef msm::front::euml::func_state<Empty_tag,Empty_Entry,Empty_Exit> Empty; + + struct Open_Entry + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: Open" << std::endl; + } + }; + struct Open_Exit + { + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "leaving: Open" << std::endl; + } + }; + struct Open_tag {}; + typedef msm::front::euml::func_state<Open_tag,Open_Entry,Open_Exit> Open; + + // states without entry/exit actions (can be declared as functor state, just without functors ;-) ) + struct Stopped_tag {}; + typedef msm::front::euml::func_state<Stopped_tag> Stopped; + + struct Playing_tag {}; + typedef msm::front::euml::func_state<Playing_tag> Playing; + + // state not defining any entry or exit (declared as simple state. Equivalent) + struct Paused_tag {}; + typedef msm::front::euml::func_state<Paused_tag> Paused; + + // the initial state of the player SM. Must be defined + typedef Empty initial_state; + + // transition actions + // as the functors are generic on events, fsm and source/target state, + // you can reuse them in another machine if you wish + struct TestFct + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const&, FSM&,SourceState& ,TargetState& ) + { + cout << "transition with event:" << typeid(EVT).name() << endl; + } + }; + struct start_playback + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + cout << "player::start_playback" << endl; + } + }; + struct open_drawer + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + cout << "player::open_drawer" << endl; + } + }; + struct close_drawer + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + cout << "player::close_drawer" << endl; + } + }; + struct store_cd_info + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const&,FSM& fsm ,SourceState& ,TargetState& ) + { + cout << "player::store_cd_info" << endl; + fsm.process_event(play()); + } + }; + struct stop_playback + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + cout << "player::stop_playback" << endl; + } + }; + struct pause_playback + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + cout << "player::pause_playback" << endl; + } + }; + struct resume_playback + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + cout << "player::resume_playback" << endl; + } + }; + struct stop_and_open + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + cout << "player::stop_and_open" << endl; + } + }; + struct stopped_again + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + cout << "player::stopped_again" << endl; + } + }; + // guard conditions + struct DummyGuard + { + template <class EVT,class FSM,class SourceState,class TargetState> + bool operator()(EVT const& evt,FSM& fsm,SourceState& src,TargetState& tgt) + { + return true; + } + }; + struct good_disk_format + { + template <class EVT,class FSM,class SourceState,class TargetState> + bool operator()(EVT const& evt ,FSM&,SourceState& ,TargetState& ) + { + // to test a guard condition, let's say we understand only CDs, not DVD + if (evt.disc_type != DISK_CD) + { + std::cout << "wrong disk, sorry" << std::endl; + return false; + } + return true; + } + }; + struct always_true + { + template <class EVT,class FSM,class SourceState,class TargetState> + bool operator()(EVT const& evt ,FSM&,SourceState& ,TargetState& ) + { + return true; + } + }; + + // Transition table for player + struct player_transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------------+----------------------+ + Row < Stopped , play , Playing , ActionSequence_ + <mpl::vector< + TestFct,start_playback> > + , DummyGuard >, + Row < Stopped , open_close , Open , open_drawer , none >, + Row < Stopped , stop , Stopped , none , none >, + // +---------+-------------+---------+---------------------------+----------------------+ + Row < Open , open_close , Empty , close_drawer , none >, + // +---------+-------------+---------+---------------------------+----------------------+ + Row < Empty , open_close , Open , open_drawer , none >, + Row < Empty , cd_detected , Stopped , store_cd_info , And_<good_disk_format, + always_true> >, + // +---------+-------------+---------+---------------------------+----------------------+ + Row < Playing , stop , Stopped , stop_playback , none >, + Row < Playing , pause , Paused , pause_playback , none >, + Row < Playing , open_close , Open , stop_and_open , none >, + // +---------+-------------+---------+---------------------------+----------------------+ + Row < Paused , end_pause , Playing , resume_playback , none >, + Row < Paused , stop , Stopped , stop_playback , none >, + Row < Paused , open_close , Open , stop_and_open , none > + // +---------+-------------+---------+---------------------------+----------------------+ + > {}; + // fsm definition + struct player_tag {}; + typedef msm::front::euml::func_state_machine<Playing_tag, + // transition table + player_transition_table, + //Initial state + Empty> player_; + // Pick a back-end + typedef msm::back::state_machine<player_> player; + + // + // Testing utilities. + // + static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" }; + void pstate(player const& p) + { + std::cout << " -> " << state_names[p.current_state()[0]] << std::endl; + } + + void test() + { + player p; + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + p.start(); + // go to Open, call on_exit on Empty, then action, then on_entry on Open + p.process_event(open_close()); pstate(p); + p.process_event(open_close()); pstate(p); + // will be rejected, wrong disk type + p.process_event( + cd_detected("louie, louie",DISK_DVD)); pstate(p); + p.process_event( + cd_detected("louie, louie",DISK_CD)); pstate(p); + // no need to call play() as the previous event does it in its action method + //p.process_event(play()); + + // at this point, Play is active + p.process_event(pause()); pstate(p); + // go back to Playing + p.process_event(end_pause()); pstate(p); + p.process_event(pause()); pstate(p); + p.process_event(stop()); pstate(p); + // event leading to the same state + // no action method called as it is not present in the transition table + p.process_event(stop()); pstate(p); + } +} + +int main() +{ + test(); + return 0; +} diff --git a/libs/msm/doc/PDF/examples/TestErrorOrthogonality.cpp b/libs/msm/doc/PDF/examples/TestErrorOrthogonality.cpp new file mode 100644 index 0000000000..fd67e75055 --- /dev/null +++ b/libs/msm/doc/PDF/examples/TestErrorOrthogonality.cpp @@ -0,0 +1,119 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <iostream> + +// back-end +#include <boost/msm/back/state_machine.hpp> +#include <boost/msm/back/mpl_graph_fsm_check.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> + +namespace msm = boost::msm; +namespace mpl = boost::mpl; +using namespace msm::back; +namespace +{ + // events + struct play {}; + struct end_pause {}; + struct stop {}; + struct pause {}; + struct open_close {}; + struct cd_detected{}; + struct error_found {}; + + // front-end: define the FSM structure + struct player_ : public msm::front::state_machine_def<player_> + { + // The list of FSM states + struct Empty : public msm::front::state<> + { + }; + struct Open : public msm::front::state<> + { + }; + + // sm_ptr still supported but deprecated as functors are a much better way to do the same thing + struct Stopped : public msm::front::state<> + { + }; + + struct Playing : public msm::front::state<> + { + }; + + // state not defining any entry or exit + struct Paused : public msm::front::state<> + { + }; + struct AllOk : public msm::front::state<> + { + }; + struct ErrorMode : public msm::front::state<> + { + }; + struct State1 : public msm::front::state<> + { + }; + struct State2 : public msm::front::state<> + { + }; + // the initial state of the player SM. Must be defined + typedef mpl::vector<Empty,AllOk> initial_state; + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + // adding this line makes non-reachable states and should cause a static assert + //_row < State1 , open_close , State2 >, + // adding this line makes non-orthogonal regions and should cause a static assert + //_row < Paused , error_found , ErrorMode >, + _row < Stopped , play , Playing >, + _row < Stopped , open_close , Open >, + _row < Stopped , stop , Stopped >, + // +---------+-------------+---------+---------------------+----------------------+ + _row < Open , open_close , Empty >, + // +---------+-------------+---------+---------------------+----------------------+ + _row < Empty , open_close , Open >, + _row < Empty , cd_detected , Stopped >, + _row < Empty , cd_detected , Playing >, + // +---------+-------------+---------+---------------------+----------------------+ + _row < Playing , stop , Stopped >, + _row < Playing , pause , Paused >, + _row < Playing , open_close , Open >, + // +---------+-------------+---------+---------------------+----------------------+ + _row < Paused , end_pause , Playing >, + _row < Paused , stop , Stopped >, + _row < Paused , open_close , Open >, + _row < AllOk , error_found , ErrorMode > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + } + }; + // Pick a back-end + typedef msm::back::state_machine<player_,msm::back::mpl_graph_fsm_check> player; + + void test() + { + player p; + } +} + +int main() +{ + test(); + return 0; +} diff --git a/libs/msm/doc/PDF/examples/TestInternal.cpp b/libs/msm/doc/PDF/examples/TestInternal.cpp new file mode 100644 index 0000000000..ea8f35c5a9 --- /dev/null +++ b/libs/msm/doc/PDF/examples/TestInternal.cpp @@ -0,0 +1,337 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <iostream> +#include "boost/mpl/vector/vector30.hpp" + +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> +#include <boost/msm/front/functor_row.hpp> +#include <boost/msm/front/internal_row.hpp> + +namespace msm = boost::msm; +namespace mpl = boost::mpl; +using namespace msm::front; + +namespace +{ + // events + struct play {}; + struct end_pause {}; + struct stop {}; + struct pause {}; + struct internal_event {}; + struct open_close {}; + struct NextSong {}; + struct PreviousSong {}; + struct to_ignore {}; + + // A "complicated" event type that carries some data. + enum DiskTypeEnum + { + DISK_CD=0, + DISK_DVD=1 + }; + struct cd_detected + { + cd_detected(std::string name, DiskTypeEnum diskType) + : name(name), + disc_type(diskType) + {} + + std::string name; + DiskTypeEnum disc_type; + }; + + // front-end: define the FSM structure + struct player_ : public msm::front::state_machine_def<player_> + { + // transition actions + void start_playback(play const&) { std::cout << "player::start_playback\n"; } + void open_drawer(open_close const&) { std::cout << "player::open_drawer\n"; } + void close_drawer(open_close const&) { std::cout << "player::close_drawer\n"; } + void store_cd_info(cd_detected const&) { std::cout << "player::store_cd_info\n"; } + void stop_playback(stop const&) { std::cout << "player::stop_playback\n"; } + void pause_playback(pause const&) { std::cout << "player::pause_playback\n"; } + void resume_playback(end_pause const&) { std::cout << "player::resume_playback\n"; } + void stop_and_open(open_close const&) { std::cout << "player::stop_and_open\n"; } + void stopped_again(stop const&) {std::cout << "player::stopped_again\n";} + // guard conditions + bool good_disk_format(cd_detected const& evt) + { + // to test a guard condition, let's say we understand only CDs, not DVD + if (evt.disc_type != DISK_CD) + { + std::cout << "wrong disk, sorry" << std::endl; + return false; + } + return true; + } + // transitions internal to Empty + void internal_action(cd_detected const&){ std::cout << "Empty::internal action\n"; } + bool internal_guard(cd_detected const&) + { + std::cout << "Empty::internal guard\n"; + return false; + } + void internal_action(internal_event const&){ std::cout << "Playing::internal action\n"; } + bool internal_guard(internal_event const&) + { + std::cout << "Playing::internal guard\n"; + return false; + } + + // The list of FSM states + struct Empty : public msm::front::state<> + { + // every (optional) entry/exit methods get the event passed. + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Empty" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Empty" << std::endl;} + + struct internal_guard_fct + { + template <class EVT,class FSM,class SourceState,class TargetState> + bool operator()(EVT const& evt ,FSM&,SourceState& ,TargetState& ) + { + std::cout << "Empty::internal guard functor\n"; + return false; + } + }; + struct internal_action_fct + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + std::cout << "Empty::internal action functor" << std::endl; + } + }; + void internal_action(to_ignore const&) { std::cout << "Empty::(almost)ignoring event\n"; } + // Transition table for Empty + struct internal_transition_table : mpl::vector< + // Start Event Next Action Guard + Row < Empty , cd_detected , none , internal_action_fct ,internal_guard_fct >, + Internal < cd_detected , internal_action_fct ,internal_guard_fct >, + a_internal< to_ignore , Empty,&Empty::internal_action > + // +---------+-------------+----------+------------------------+----------------------+ + > {}; + }; + struct Open : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) {std::cout << "entering: Open" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Open" << std::endl;} + }; + + struct Stopped : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) {std::cout << "entering: Stopped" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Stopped" << std::endl;} + }; + + struct Playing_ : public msm::front::state_machine_def<Playing_> + { + // when playing, the CD is loaded and we are in either pause or playing (duh) + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Playing" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Playing" << std::endl;} + + // The list of FSM states + struct Song1 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: First song" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: First Song" << std::endl;} + + }; + struct Song2 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: Second song" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: Second Song" << std::endl;} + }; + struct Song3 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: Third song" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: Third Song" << std::endl;} + }; + // the initial state. Must be defined + typedef Song1 initial_state; + // transition actions + void start_next_song(NextSong const&){std::cout << "Playing: start_next_song" << std::endl;} + void start_prev_song(PreviousSong const&){std::cout << "Playing: start_prev_song" << std::endl;} + // guard conditions + struct playing_internal_guard + { + template <class EVT,class FSM,class SourceState,class TargetState> + bool operator()(EVT const& evt ,FSM&,SourceState& ,TargetState& ) + { + std::cout << "Playing::internal guard fct\n"; + return true; + } + }; + struct playing_false_guard + { + template <class EVT,class FSM,class SourceState,class TargetState> + bool operator()(EVT const& evt ,FSM&,SourceState& ,TargetState& ) + { + std::cout << "Playing::false guard\n"; + return false; + } + }; + struct playing_internal_fct + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& evt ,FSM&,SourceState& ,TargetState& ) + { + std::cout << "Playing::internal fct\n"; + } + }; + typedef Playing_ pl; // makes transition table cleaner + // Transition table for Playing + struct transition_table : mpl::vector4< + // Start Event Next Action Guard + // +---------+---------------+---------+---------------------+----------------------+ + a_row < Song1 , NextSong , Song2 , &pl::start_next_song >, + a_row < Song2 , PreviousSong , Song1 , &pl::start_prev_song >, + a_row < Song2 , NextSong , Song3 , &pl::start_next_song >, + a_row < Song3 , PreviousSong , Song2 , &pl::start_prev_song > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Internal transition table for Playing + // +---------+----------------+---------+---------------------+-----------------------+ + struct internal_transition_table : mpl::vector< + // normal internal transition + // Start Event Next Action Guard + Internal < internal_event , playing_internal_fct,playing_internal_guard >, + // conflict between internal and the external defined above + Internal < PreviousSong , playing_internal_fct,playing_false_guard >, + internal < internal_event , player_,&player_::internal_action, + player_,&player_::internal_guard > + // +---------+----------------+---------+---------------------+-----------------------+ + > {}; + + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + // back-end + typedef msm::back::state_machine<Playing_> Playing; + + // state not defining any entry or exit + struct Paused : public msm::front::state<> + { + }; + + // the initial state of the player SM. Must be defined + typedef Empty initial_state; + + typedef player_ p; // makes transition table cleaner + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Stopped , play , Playing , &p::start_playback >, + a_row < Stopped , open_close , Open , &p::open_drawer >, + _row < Stopped , stop , Stopped >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Open , open_close , Empty , &p::close_drawer >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Empty , open_close , Open , &p::open_drawer >, + // conflict between a normal and 2 internal transitions (irow/g_irow) + // + a state-defined internals defined 2 ways (see Empty) + row < Empty , cd_detected , Stopped , &p::store_cd_info ,&p::good_disk_format >, + irow < Empty , cd_detected , &p::internal_action ,&p::internal_guard >, + g_irow < Empty , cd_detected ,&p::internal_guard >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Playing , stop , Stopped , &p::stop_playback >, + a_row < Playing , pause , Paused , &p::pause_playback >, + a_row < Playing , open_close , Open , &p::stop_and_open >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Paused , end_pause , Playing , &p::resume_playback >, + a_row < Paused , stop , Stopped , &p::stop_playback >, + a_row < Paused , open_close , Open , &p::stop_and_open > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + // Pick a back-end + typedef msm::back::state_machine<player_> player; + + // + // Testing utilities. + // + static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" }; + void pstate(player const& p) + { + std::cout << " -> " << state_names[p.current_state()[0]] << std::endl; + } + + void test() + { + player p; + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + p.start(); + // this event will be ignored and not call no_transition + p.process_event(to_ignore()); + // go to Open, call on_exit on Empty, then action, then on_entry on Open + p.process_event(open_close()); pstate(p); + p.process_event(open_close()); pstate(p); + // will be rejected, wrong disk type + p.process_event( + cd_detected("louie, louie",DISK_DVD)); pstate(p); + p.process_event( + cd_detected("louie, louie",DISK_CD)); pstate(p); + p.process_event(play()); + p.process_event(NextSong()); + std::cout << "sending an internal event" << std::endl; + p.process_event(internal_event()); + std::cout << "conflict between the internal and normal transition. Internal is tried last" << std::endl; + p.process_event(PreviousSong()); + + // at this point, Play is active + p.process_event(pause()); pstate(p); + // go back to Playing + p.process_event(end_pause()); pstate(p); + p.process_event(pause()); pstate(p); + p.process_event(stop()); pstate(p); + // event leading to the same state + // no action method called as it is not present in the transition table + p.process_event(stop()); pstate(p); + } +} + +int main() +{ + test(); + return 0; +} diff --git a/libs/msm/doc/PDF/examples/Visitor.cpp b/libs/msm/doc/PDF/examples/Visitor.cpp new file mode 100644 index 0000000000..03f2954060 --- /dev/null +++ b/libs/msm/doc/PDF/examples/Visitor.cpp @@ -0,0 +1,368 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <iostream> +#include <string> +#include "boost/mpl/vector/vector30.hpp" + +#include <boost/msm/back/state_machine.hpp> +#include <boost/msm/front/state_machine_def.hpp> +#include <boost/msm/back/tools.hpp> + +using namespace std; +namespace msm = boost::msm; +namespace mpl = boost::mpl; + +namespace // Concrete FSM implementation +{ + // events + struct play {}; + struct end_pause {}; + struct stop {}; + struct pause {}; + struct open_close {}; + struct NextSong {}; + struct PreviousSong {}; + struct ThreeSec {}; + struct TenSec {}; + struct error_found {}; + struct end_error {}; + struct cd_detected {}; + + // a simple visitor + struct SomeVisitor + { + template <class T> + void visit_state(T* astate,int i) + { + std::cout << "visiting state:" << typeid(*astate).name() + << " with data:" << i << std::endl; + } + }; + // base state for all states of ths fsm, to make them visitable + struct my_visitable_state + { + // signature of the accept function + typedef msm::back::args<void,SomeVisitor&,int> 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&,int) const {} + // or if you want all states to be visited, provide an implementation + /* + void accept(SomeVisitor& vis,int i) const + { + vis.visit_state(this,i); + } + */ + }; + + // Concrete FSM implementation + struct player_ : public msm::front::state_machine_def<player_,my_visitable_state> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: player" << std::endl;} + // The list of FSM states + struct Empty : public msm::front::state<my_visitable_state> + { + typedef mpl::vector<play> deferred_events; + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Empty" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Empty" << std::endl;} + // this state wants to be visited + void accept(SomeVisitor& vis,int i) const + { + vis.visit_state(this,i); + } + }; + struct Open : public msm::front::state<my_visitable_state> + { + typedef mpl::vector<play> deferred_events; + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Open" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Open" << std::endl;} + // this state wants to be visited + void accept(SomeVisitor& vis,int i) const + { + vis.visit_state(this,i); + } + }; + struct Stopped : public msm::front::state<my_visitable_state> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Stopped" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Stopped" << std::endl;} + // this state wants to be visited + void accept(SomeVisitor& vis,int i) const + { + // note that visiting will recursively visit sub-states + vis.visit_state(this,i); + } + }; + + struct Playing_ : public msm::front::state_machine_def<Playing_,my_visitable_state > + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Playing" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Playing" << std::endl;} + void accept(SomeVisitor& vis,int i) const + { + // note that visiting will recursively visit sub-states + vis.visit_state(this,i); + } + // The list of FSM states + // the Playing state machine contains a state which is himself a state machine + // so we have a SM containing a SM containing a SM + struct Song1_ : public msm::front::state_machine_def<Song1_,my_visitable_state> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: First song" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: First Song" << std::endl;} + void accept(SomeVisitor& vis,int i) const + { + vis.visit_state(this,i); + } + struct LightOn : public msm::front::state<my_visitable_state> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: LightOn" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: LightOn" << std::endl;} + void accept(SomeVisitor& vis,int i) const + { + // note that visiting will recursively visit sub-states + vis.visit_state(this,i); + } + }; + struct LightOff : public msm::front::state<my_visitable_state> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: LightOff" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: LightOff" << std::endl;} + }; + // the initial state. Must be defined + typedef LightOn initial_state; + // transition actions + void turn_light_off(ThreeSec const&) { std::cout << "3s off::turn light off\n"; } + // guard conditions + + typedef Song1_ s; // makes transition table cleaner + // Transition table for Song1 + struct transition_table : mpl::vector1< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row < LightOn , ThreeSec , LightOff, &s::turn_light_off > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + typedef msm::back::state_machine<Song1_> Song1; + + struct Song2 : public msm::front::state<my_visitable_state> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: Second song" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: Second Song" << std::endl;} + }; + struct Song3 : public msm::front::state<my_visitable_state> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: Third song" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: Third Song" << std::endl;} + }; + // the initial state. Must be defined + typedef Song1 initial_state; + // transition actions + void start_next_song(NextSong const&) { std::cout << "Playing::start_next_song\n"; } + void start_prev_song(PreviousSong const&) { std::cout << "Playing::start_prev_song\n"; } + // guard conditions + + typedef Playing_ pl; // makes transition table cleaner + // Transition table for Playing + struct transition_table : mpl::vector4< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Song1 , NextSong , Song2 , &pl::start_next_song >, + a_row < Song2 , PreviousSong, Song1 , &pl::start_prev_song >, + a_row < Song2 , NextSong , Song3 , &pl::start_next_song >, + a_row < Song3 , PreviousSong, Song2 , &pl::start_prev_song > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + typedef msm::back::state_machine<Playing_,msm::back::ShallowHistory<mpl::vector<end_pause> > > Playing; + + struct Paused : public msm::front::state<my_visitable_state> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Paused" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Paused" << std::endl;} + }; + + struct AllOk : public msm::front::state<my_visitable_state> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: AllOk" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: AllOk" << std::endl;} + }; + struct ErrorMode : + public msm::front::interrupt_state<end_error,my_visitable_state> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: ErrorMode" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: ErrorMode" << std::endl;} + void accept(SomeVisitor& vis,int i) const + { + vis.visit_state(this,i); + } + }; + + // the initial state of the player SM. Must be defined + typedef mpl::vector<Empty,AllOk> initial_state; + + // transition actions + void start_playback(play const&) { std::cout << "player::start_playback\n"; } + void open_drawer(open_close const&) { std::cout << "player::open_drawer\n"; } + void close_drawer(open_close const&) { std::cout << "player::close_drawer\n"; } + void store_cd_info(cd_detected const&) + { + std::cout << "player::store_cd_info\n"; + } + void stop_playback(stop const&) { std::cout << "player::stop_playback\n"; } + void pause_playback(pause const&) { std::cout << "player::pause_playback\n"; } + void resume_playback(end_pause const&) { std::cout << "player::resume_playback\n"; } + void stop_and_open(open_close const&) { std::cout << "player::stop_and_open\n"; } + void stopped_again(stop const&) {std::cout << "player::stopped_again\n";} + void report_error(error_found const&) {std::cout << "player::report_error\n";} + void report_end_error(end_error const&) {std::cout << "player::report_end_error\n";} + // guard conditions + + + typedef player_ p; // makes transition table cleaner + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Stopped , play , Playing , &p::start_playback >, + a_row < Stopped , open_close , Open , &p::open_drawer >, + a_row < Stopped , stop , Stopped , &p::stopped_again >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Open , open_close , Empty , &p::close_drawer >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Empty , open_close , Open , &p::open_drawer >, + a_row < Empty , cd_detected , Stopped , &p::store_cd_info >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Playing , stop , Stopped , &p::stop_playback >, + a_row < Playing , pause , Paused , &p::pause_playback >, + a_row < Playing , open_close , Open , &p::stop_and_open >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Paused , end_pause , Playing , &p::resume_playback >, + a_row < Paused , stop , Stopped , &p::stop_playback >, + a_row < Paused , open_close , Open , &p::stop_and_open >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < AllOk , error_found ,ErrorMode, &p::report_error >, + a_row <ErrorMode,end_error ,AllOk , &p::report_end_error > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + + // back-end + typedef msm::back::state_machine<player_> player; + + // + // Testing utilities. + // + + void pstate(player const& p) + { + static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused","AllOk","ErrorMode","SleepMode" }; + for (unsigned int i=0;i<player::nr_regions::value;++i) + { + std::cout << " -> " << state_names[p.current_state()[i]] << std::endl; + } + } + void test() + { + player p; + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + p.start(); + + // test deferred event + // deferred in Empty and Open, will be handled only after event cd_detected + p.process_event(play()); + + // go to Open, call on_exit on Empty, then action, then on_entry on Open + p.process_event(open_close()); pstate(p); + // visiting Paused and AllOk, but only Paused cares + SomeVisitor vis; + p.visit_current_states(boost::ref(vis),1); + p.process_event(open_close()); pstate(p); + // visiting Empty and AllOk, but only Empty cares + p.visit_current_states(boost::ref(vis),2); + + p.process_event(cd_detected()); + // no need to call play() as the previous event does it in its action method + //p.process_event(play()); + // at this point, Play is active, along FirstSong and LightOn + pstate(p); + // visiting Playing+Song1+LightOn and AllOk, but only Playing+Song1+LightOn care + p.visit_current_states(boost::ref(vis),3); + + // Stop will be active + p.process_event(stop()); pstate(p); + + // visiting when both regions have an active state who wants to be visited + p.process_event(error_found()); + p.visit_current_states(boost::ref(vis),5); + + + } +} + +int main() +{ + test(); + return 0; +} + + diff --git a/libs/msm/doc/PDF/examples/char_event_dispatcher.hpp b/libs/msm/doc/PDF/examples/char_event_dispatcher.hpp new file mode 100644 index 0000000000..023bb0d5ec --- /dev/null +++ b/libs/msm/doc/PDF/examples/char_event_dispatcher.hpp @@ -0,0 +1,92 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_MSM_CHAR_EVENT_DISPATCHER_HPP +#define BOOST_MSM_CHAR_EVENT_DISPATCHER_HPP + +#include <boost/msm/back/common_types.hpp> + +struct digit {}; +struct char_0 : public digit {}; +struct char_1 : public digit {}; +struct char_2 : public digit {}; +struct char_3 : public digit {}; +struct char_4 : public digit {}; +struct char_5 : public digit {}; +struct char_6 : public digit {}; +struct char_7 : public digit {}; +struct char_8 : public digit {}; +struct char_9 : public digit {}; +struct minus_char {}; +template <char c> +struct event_char{}; +template <> +struct event_char<'0'> : public digit{}; +template <> +struct event_char<'1'> : public digit{}; +template <> +struct event_char<'2'> : public digit{}; +template <> +struct event_char<'3'> : public digit{}; +template <> +struct event_char<'4'> : public digit{}; +template <> +struct event_char<'5'> : public digit{}; +template <> +struct event_char<'6'> : public digit{}; +template <> +struct event_char<'7'> : public digit{}; +template <> +struct event_char<'8'> : public digit{}; +template <> +struct event_char<'9'> : public digit{}; + +namespace boost { namespace msm { namespace back { + + +template <class Fsm> +struct char_event_dispatcher +{ + template <char c> + struct dispatch_event_helper + { + static execute_return apply(Fsm& fsm) + { + return fsm.process_event(event_char<c>()); + } + }; + char_event_dispatcher() + { + entries[0x30]=&dispatch_event_helper<'0'>::apply; + entries[0x31]=&dispatch_event_helper<'1'>::apply; + entries[0x32]=&dispatch_event_helper<'2'>::apply; + entries[0x33]=&dispatch_event_helper<'3'>::apply; + entries[0x34]=&dispatch_event_helper<'4'>::apply; + entries[0x35]=&dispatch_event_helper<'5'>::apply; + entries[0x36]=&dispatch_event_helper<'6'>::apply; + entries[0x37]=&dispatch_event_helper<'7'>::apply; + entries[0x38]=&dispatch_event_helper<'8'>::apply; + entries[0x39]=&dispatch_event_helper<'9'>::apply; + entries[0x2D]=&dispatch_event_helper<'-'>::apply; + entries[0x2B]=&dispatch_event_helper<'+'>::apply; + } + execute_return process_event(Fsm& fsm,char c) const + { + return entries[c](fsm); + } + + // The singleton instance. + static const char_event_dispatcher instance; + typedef execute_return (*cell)(Fsm&); + cell entries[255]; +}; + +}}} // boost::msm::back +#endif //BOOST_MSM_CHAR_EVENT_DISPATCHER_HPP diff --git a/libs/msm/doc/PDF/examples/distributed_table/DistributedTable.cpp b/libs/msm/doc/PDF/examples/distributed_table/DistributedTable.cpp new file mode 100644 index 0000000000..6a24e70fae --- /dev/null +++ b/libs/msm/doc/PDF/examples/distributed_table/DistributedTable.cpp @@ -0,0 +1,74 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <vector> +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> +// functors +#include <boost/msm/front/functor_row.hpp> +#include <boost/msm/front/euml/common.hpp> + +#include "Empty.hpp" +#include "Open.hpp" + +using namespace std; +namespace msm = boost::msm; +namespace mpl = boost::mpl; +using namespace msm::front; + + +// front-end: define the FSM structure +struct player_ : public msm::front::state_machine_def<player_> +{ + // the initial state of the player SM. Must be defined + typedef Empty initial_state; + typedef mpl::vector<Empty,Open> explicit_creation; + + typedef player_ p; // makes transition table cleaner + + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } +}; +// Pick a back-end +typedef msm::back::state_machine<player_> player; + +// +// Testing utilities. +// +static char const* const state_names[] = { "Empty", "Open" }; +void pstate(player const& p) +{ + std::cout << " -> " << state_names[p.current_state()[0]] << std::endl; +} + +void test() +{ + player p; + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + p.start(); + // go to Open, call on_exit on Empty, then action, then on_entry on Open + p.process_event(open_close()); pstate(p); + p.process_event(open_close()); pstate(p); +} + +int main() +{ + test(); + return 0; +} + diff --git a/libs/msm/doc/PDF/examples/distributed_table/Empty.cpp b/libs/msm/doc/PDF/examples/distributed_table/Empty.cpp new file mode 100644 index 0000000000..ecbb9a8208 --- /dev/null +++ b/libs/msm/doc/PDF/examples/distributed_table/Empty.cpp @@ -0,0 +1,17 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <iostream> +#include "Empty.hpp" + +void Empty::open_drawer(open_close const&) +{ + std::cout << "Empty::open_drawer\n"; +} diff --git a/libs/msm/doc/PDF/examples/distributed_table/Empty.hpp b/libs/msm/doc/PDF/examples/distributed_table/Empty.hpp new file mode 100644 index 0000000000..00dd0e5f65 --- /dev/null +++ b/libs/msm/doc/PDF/examples/distributed_table/Empty.hpp @@ -0,0 +1,43 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef EMPTY_HPP +#define EMPTY_HPP + +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> +#include <boost/msm/front/row2.hpp> + +#include "Events.hpp" + +struct Open; + +namespace msm = boost::msm; +namespace mpl = boost::mpl; + +struct Empty : public msm::front::state<> +{ + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Empty" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Empty" << std::endl;} + void open_drawer(open_close const&); + + struct internal_transition_table : mpl::vector< + // Start Event Next Action Guard + //+-------------+---------+-------------+---------+---------------------------+----------------------+ + msm::front::a_row2 < Empty , open_close , Open , Empty,&Empty::open_drawer > + //+-------------+---------+-------------+---------+---------------------------+----------------------+ + > {}; +}; + +#endif diff --git a/libs/msm/doc/PDF/examples/distributed_table/Events.hpp b/libs/msm/doc/PDF/examples/distributed_table/Events.hpp new file mode 100644 index 0000000000..023216bd7c --- /dev/null +++ b/libs/msm/doc/PDF/examples/distributed_table/Events.hpp @@ -0,0 +1,39 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef EVENTS_HPP +#define EVENTS_HPP + +// events +struct play {}; +struct end_pause {}; +struct stop {}; +struct pause {}; +struct open_close {}; + +// A "complicated" event type that carries some data. +enum DiskTypeEnum +{ + DISK_CD=0, + DISK_DVD=1 +}; +struct cd_detected +{ + cd_detected(std::string name, DiskTypeEnum diskType) + : name(name), + disc_type(diskType) + {} + + std::string name; + DiskTypeEnum disc_type; +}; + + +#endif diff --git a/libs/msm/doc/PDF/examples/distributed_table/Open.cpp b/libs/msm/doc/PDF/examples/distributed_table/Open.cpp new file mode 100644 index 0000000000..2d1b63aed6 --- /dev/null +++ b/libs/msm/doc/PDF/examples/distributed_table/Open.cpp @@ -0,0 +1,17 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <iostream> +#include "Open.hpp" + +void Open::close_drawer(open_close const&) +{ + std::cout << "Open::close_drawer\n"; +} diff --git a/libs/msm/doc/PDF/examples/distributed_table/Open.hpp b/libs/msm/doc/PDF/examples/distributed_table/Open.hpp new file mode 100644 index 0000000000..03a8e8f9d3 --- /dev/null +++ b/libs/msm/doc/PDF/examples/distributed_table/Open.hpp @@ -0,0 +1,44 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef OPEN_HPP +#define OPEN_HPP + +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> +#include <boost/msm/front/row2.hpp> + +#include "Events.hpp" + +struct Empty; + +namespace msm = boost::msm; +namespace mpl = boost::mpl; +using namespace msm::front; + +struct Open : public msm::front::state<> +{ + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) {std::cout << "entering: Open" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Open" << std::endl;} + void close_drawer(open_close const&); + + struct internal_transition_table : mpl::vector< + // Start Event Next Action Guard + //+-------------+---------+-------------+---------+---------------------------+----------------------+ + msm::front::a_row2 < Open , open_close , Empty , Open,&Open::close_drawer > + //+-------------+---------+-------------+---------+---------------------------+----------------------+ + > {}; +}; + +#endif diff --git a/libs/msm/doc/PDF/examples/iPodEuml.cpp b/libs/msm/doc/PDF/examples/iPodEuml.cpp new file mode 100644 index 0000000000..547da45202 --- /dev/null +++ b/libs/msm/doc/PDF/examples/iPodEuml.cpp @@ -0,0 +1,271 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <vector> +#include <set> +#include <string> +#include <iostream> +// we need more than the default 20 states +#define FUSION_MAX_VECTOR_SIZE 20 +// we need more than the default 20 transitions +#include "boost/mpl/vector/vector50.hpp" +#include <boost/msm/back/state_machine.hpp> +#include <boost/msm/front/euml/euml.hpp> + + +using namespace std; +using namespace boost::msm::front::euml; +namespace msm = boost::msm; + +// attribute names and types +BOOST_MSM_EUML_DECLARE_ATTRIBUTE(int,m_Selected) +BOOST_MSM_EUML_DECLARE_ATTRIBUTE(int,m_SongIndex) +BOOST_MSM_EUML_DECLARE_ATTRIBUTE(int,m_NumberOfSongs) +#include "ipod_functors.hpp" + + +namespace // Concrete FSM implementation +{ + //flags + BOOST_MSM_EUML_FLAG(MenuActive) + BOOST_MSM_EUML_FLAG(NoFastFwd) + // hardware-generated events + BOOST_MSM_EUML_EVENT(Hold) + BOOST_MSM_EUML_EVENT(NoHold) + BOOST_MSM_EUML_EVENT(SouthPressed) + BOOST_MSM_EUML_EVENT(SouthReleased) + BOOST_MSM_EUML_EVENT(MiddleButton) + BOOST_MSM_EUML_EVENT(EastPressed) + BOOST_MSM_EUML_EVENT(EastReleased) + BOOST_MSM_EUML_EVENT(Off) + BOOST_MSM_EUML_EVENT(MenuButton) + // internally defined events + BOOST_MSM_EUML_EVENT(PlayPause) + BOOST_MSM_EUML_EVENT(EndPlay) + struct CloseMenu_impl : euml_event<CloseMenu_impl> + { + CloseMenu_impl(){}//defined only for stt + template<class EVENT> + CloseMenu_impl(EVENT const &) {} + }; + CloseMenu_impl const CloseMenu; + BOOST_MSM_EUML_EVENT(OnOffTimer) + BOOST_MSM_EUML_EVENT(MenuMiddleButton) + BOOST_MSM_EUML_EVENT(SelectSong) + BOOST_MSM_EUML_EVENT(SongFinished) + + BOOST_MSM_EUML_ATTRIBUTES((attributes_ << m_Selected ), StartSongAttributes) + BOOST_MSM_EUML_EVENT_WITH_ATTRIBUTES(StartSong,StartSongAttributes) + BOOST_MSM_EUML_EVENT(PreviousSong) + BOOST_MSM_EUML_EVENT(NextSong) + BOOST_MSM_EUML_EVENT(ForwardTimer) + BOOST_MSM_EUML_EVENT(PlayingMiddleButton) + + // Concrete iPod implementation + // The list of iPod states + BOOST_MSM_EUML_STATE(( NotHolding_Entry ),NotHolding) + BOOST_MSM_EUML_INTERRUPT_STATE(( NoHold,Holding_Entry ),Holding) + BOOST_MSM_EUML_STATE(( NotPlaying_Entry ),NotPlaying) + BOOST_MSM_EUML_STATE(( NoMenuMode_Entry ),NoMenuMode) + BOOST_MSM_EUML_STATE(( NoOnOffButton_Entry ),NoOnOffButton) + BOOST_MSM_EUML_STATE(( OffDown_Entry ),OffDown) + BOOST_MSM_EUML_STATE(( PlayerOff_Entry ),PlayerOff) + BOOST_MSM_EUML_STATE(( CheckMiddleButton_Entry ),CheckMiddleButton) + + // Concrete PlayingMode_ implementation + // The list of PlayingMode_ states + BOOST_MSM_EUML_STATE(( Playing_Entry ),Playing) + BOOST_MSM_EUML_STATE(( WaitingForNextPrev_Entry ),WaitingForNextPrev) + BOOST_MSM_EUML_STATE(( Paused_Entry ),Paused) + BOOST_MSM_EUML_STATE(( WaitingForEnd_Entry ),WaitingForEnd) + BOOST_MSM_EUML_STATE(( NoForward_Entry ),NoForward) + BOOST_MSM_EUML_STATE(( ForwardPressed_Entry,ForwardPressed_Exit ),ForwardPressed) + BOOST_MSM_EUML_STATE(( FastForward_Entry,FastForward_Exit ),FastForward) + BOOST_MSM_EUML_STATE(( StdDisplay_Entry ),StdDisplay) + BOOST_MSM_EUML_STATE(( SetPosition_Entry ),SetPosition) + BOOST_MSM_EUML_STATE(( SetMark_Entry ),SetMark) + BOOST_MSM_EUML_EXIT_STATE(( EndPlay,PlayingExit_Entry ),PlayingExit) + + //stt + BOOST_MSM_EUML_TRANSITION_TABLE(( + // +------------------------------------------------------------------------------+ + Paused == Playing + PlayPause , + Paused == Playing + Off , + Playing == Playing + StartSong + / (if_then_(event_(m_Selected) > Int_<0>() && + event_(m_Selected) < fsm_(m_NumberOfSongs), + fsm_(m_SongIndex) = event_(m_Selected) ),show_selected_song) , + Playing == Playing + SongFinished + / (if_then_else_(++fsm_(m_SongIndex) <= fsm_(m_NumberOfSongs), /*if*/ + show_playing_song, /*then*/ + (fsm_(m_SongIndex)=Int_<1>(),process_(EndPlay))/*else*/ ) ) , + Playing == Paused + PlayPause , + Playing == Paused + StartSong + / (if_then_(event_(m_Selected) > Int_<0>() && + event_(m_Selected) < fsm_(m_NumberOfSongs), + fsm_(m_SongIndex) = event_(m_Selected) ),show_selected_song) , + WaitingForNextPrev == WaitingForNextPrev+ PreviousSong + /( if_then_else_(--fsm_(m_SongIndex) > Int_<0>(), /*if*/ + show_playing_song, /*then*/ + (fsm_(m_SongIndex)=Int_<1>(),process_(EndPlay)) /*else*/ ) ) , + WaitingForNextPrev == WaitingForNextPrev+ NextSong + / (if_then_else_(++fsm_(m_SongIndex) <= fsm_(m_NumberOfSongs), /*if*/ + show_playing_song, /*then*/ + (fsm_(m_SongIndex)=Int_<1>(),process_(EndPlay)) /*else*/ ) ), + + PlayingExit == WaitingForEnd + EndPlay , + ForwardPressed == NoForward + EastPressed [!is_flag_(NoFastFwd)] , + NoForward == ForwardPressed + EastReleased / process_(NextSong) , + FastForward == ForwardPressed + ForwardTimer / do_fast_forward , + FastForward == FastForward + ForwardTimer / do_fast_forward , + FastForward == NoForward + EastReleased , + SetPosition == StdDisplay + PlayingMiddleButton , + StdDisplay == SetPosition + StartSong , + SetMark == SetPosition + PlayingMiddleButton , + StdDisplay == SetMark + PlayingMiddleButton , + StdDisplay == SetMark + StartSong + // +------------------------------------------------------------------------------+ + ),playingmode_transition_table ) + + BOOST_MSM_EUML_DECLARE_STATE_MACHINE( (playingmode_transition_table, //STT + init_ << Playing << WaitingForNextPrev << WaitingForEnd + << NoForward << StdDisplay, // Init States + fsm_(m_NumberOfSongs)=Int_<5>(), // entry + no_action, // exit + attributes_ << m_SongIndex << m_NumberOfSongs, //attributes + configure_<< NoFastFwd // Flags, Deferred events, configuration + ),PlayingMode_) + + // choice of back-end + typedef msm::back::state_machine<PlayingMode_> PlayingMode_type; + PlayingMode_type const PlayingMode; + + // Concrete MenuMode_ implementation + // The list of MenuMode_ states + BOOST_MSM_EUML_STATE(( WaitingForSongChoice_Entry ),WaitingForSongChoice) + BOOST_MSM_EUML_STATE(( StartCurrentSong_Entry ),StartCurrentSong) + BOOST_MSM_EUML_EXIT_STATE(( CloseMenu,MenuExit_Entry ),MenuExit) + + //stt + BOOST_MSM_EUML_TRANSITION_TABLE(( + // +------------------------------------------------------------------------------+ + StartCurrentSong == WaitingForSongChoice + MenuMiddleButton , + MenuExit == StartCurrentSong + SelectSong + // +------------------------------------------------------------------------------+ + ),menumode_transition_table ) + + BOOST_MSM_EUML_DECLARE_STATE_MACHINE( (menumode_transition_table, //STT + init_ << WaitingForSongChoice, // Init States + no_action, // entry + no_action, // exit + attributes_ << no_attributes_, //attributes + configure_<< MenuActive // Flags, Deferred events, configuration + ),MenuMode_) + + typedef msm::back::state_machine<MenuMode_> MenuMode_type; + MenuMode_type const MenuMode; + + // iPod stt + BOOST_MSM_EUML_TRANSITION_TABLE(( + // +------------------------------------------------------------------------------+ + Holding == NotHolding + Hold , + NotHolding == Holding + NoHold , + PlayingMode == NotPlaying + PlayPause , + NotPlaying == exit_pt_(PlayingMode,PlayingExit) + EndPlay + / process_(MenuButton) , + MenuMode == NoMenuMode + MenuButton , + NoMenuMode == exit_pt_(MenuMode,MenuExit)+ CloseMenu + / process2_(StartSong,Int_<5>()) , + OffDown == NoOnOffButton + SouthPressed , + NoOnOffButton == OffDown + SouthReleased + / process_(PlayPause) , + PlayerOff == OffDown + OnOffTimer + / (show_player_off,process_(Off)) , + NoOnOffButton == PlayerOff + SouthPressed / show_player_on , + NoOnOffButton == PlayerOff + NoHold / show_player_on , + CheckMiddleButton == CheckMiddleButton + MiddleButton + [is_flag_(MenuActive)] / process_(PlayingMiddleButton) , + CheckMiddleButton == CheckMiddleButton + MiddleButton + [!is_flag_(MenuActive)] / process_(PlayingMiddleButton) + // +------------------------------------------------------------------------------+ + ),ipod_transition_table ) + + BOOST_MSM_EUML_DECLARE_STATE_MACHINE(( ipod_transition_table, //STT + init_ << NotHolding << NotPlaying << NoMenuMode + << NoOnOffButton << CheckMiddleButton + ), + iPod_) //fsm name + typedef msm::back::state_machine<iPod_> iPod; + + void test() + { + iPod sm; + sm.start(); + // we first press Hold + std::cout << "pressing hold" << std::endl; + sm.process_event(Hold); + // pressing a button is now ignored + std::cout << "pressing a button" << std::endl; + sm.process_event(SouthPressed); + // or even one contained in a submachine + sm.process_event(EastPressed); + // no more holding + std::cout << "no more holding, end interrupt event sent" << std::endl; + sm.process_event(NoHold); + std::cout << "pressing South button a short time" << std::endl; + sm.process_event(SouthPressed); + // we suppose a short pressing leading to playing a song + sm.process_event(SouthReleased); + // we move to the next song + std::cout << "we move to the next song" << std::endl; + sm.process_event(NextSong); + // then back to no song => exit from playing, menu active + std::cout << "we press twice the West button (simulated)=> end of playing" << std::endl; + sm.process_event(PreviousSong); + sm.process_event(PreviousSong); + // even in menu mode, pressing play will start playing the first song + std::cout << "pressing play/pause" << std::endl; + sm.process_event(SouthPressed); + sm.process_event(SouthReleased); + // of course pausing must be possible + std::cout << "pressing play/pause" << std::endl; + sm.process_event(SouthPressed); + sm.process_event(SouthReleased); + std::cout << "pressing play/pause" << std::endl; + sm.process_event(SouthPressed); + sm.process_event(SouthReleased); + // while playing, you can fast forward + std::cout << "pressing East button a long time" << std::endl; + sm.process_event(EastPressed); + // let's suppose the timer just fired + sm.process_event(ForwardTimer); + sm.process_event(ForwardTimer); + // end of fast forwarding + std::cout << "releasing East button" << std::endl; + sm.process_event(EastReleased); + // we now press the middle button to set playing at a given position + std::cout << "pressing Middle button, fast forwarding disabled" << std::endl; + sm.process_event(MiddleButton); + std::cout <<"pressing East button to fast forward" << std::endl; + sm.process_event(EastPressed); + // we switch off and on + std::cout <<"switch off player" << std::endl; + sm.process_event(SouthPressed); + sm.process_event(OnOffTimer); + std::cout <<"switch on player" << std::endl; + sm.process_event(SouthPressed); + } +} + +int main() +{ + test(); + return 0; +} diff --git a/libs/msm/doc/PDF/examples/iPodSearch.cpp b/libs/msm/doc/PDF/examples/iPodSearch.cpp new file mode 100644 index 0000000000..29aa41354f --- /dev/null +++ b/libs/msm/doc/PDF/examples/iPodSearch.cpp @@ -0,0 +1,208 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <set> +#include <string> +#include <iostream> +#include <boost/msm/back/state_machine.hpp> +#include <boost/msm/front/state_machine_def.hpp> + +using namespace std; +namespace msm = boost::msm; + +namespace // Concrete FSM implementation +{ + // events + struct OneSong + { + OneSong(string const& asong):m_Song(asong){} + const string& get_data() const {return m_Song;} + private: + string m_Song; + }; + template <class DATA> + struct NotFound + { + DATA get_data() const {return m_Data;} + DATA m_Data; + }; + template <class DATA> + struct Found + { + DATA get_data() const {return m_Data;} + DATA m_Data; + }; + struct Done {}; + + template <class Container,class BASE_TYPE,class FSMType> + struct Insert : public boost::msm::front::state<BASE_TYPE,boost::msm::front::sm_ptr> + { + template <class Event,class FSM> + void on_entry(Event const& evt,FSM& ) + { + //TODO other containers + if (m_Cont) + { + m_Cont->insert(evt.get_data()); + } + m_fsm->process_event(Done()); + } + void set_sm_ptr(FSMType* fsm){m_fsm=fsm;} + void set_container(Container* cont){m_Cont=cont;} + Container* m_Cont; + + private: + FSMType* m_fsm; + }; + template <class BASE_TYPE,class FSMType> + struct StringFind : public boost::msm::front::state<BASE_TYPE,boost::msm::front::sm_ptr> + { + template <class Event,class FSM> + void on_entry(Event const& evt,FSM& ) + { + //TODO other containers + // if the element in the event is found + if (evt.get_data().find(m_Cont) != std::string::npos ) + { + Found<std::string> res; + res.m_Data = evt.get_data(); + m_fsm->process_event(res); + } + // data not found + else + { + NotFound<std::string> res; + res.m_Data = evt.get_data(); + m_fsm->process_event(res); + } + } + void set_sm_ptr(FSMType* fsm){m_fsm=fsm;} + void set_container(const char* cont){m_Cont=cont;} + private: + std::string m_Cont; + FSMType* m_fsm; + }; + template <class EventType,class Container,class BASE_TYPE,class FSMType> + struct Foreach : public boost::msm::front::state<BASE_TYPE,boost::msm::front::sm_ptr> + { + template <class Event,class FSM> + void on_entry(Event const& evt,FSM& ) + { + //TODO other containers + if (!m_Cont.empty()) + { + typename Container::value_type next_event = *m_Cont.begin(); + m_Cont.erase(m_Cont.begin()); + m_fsm->process_event(EventType(next_event)); + } + } + void set_sm_ptr(FSMType* fsm){m_fsm=fsm;} + void set_container(Container* cont){m_Cont=*cont;} + + private: + Container m_Cont; + FSMType* m_fsm; + }; + + + // Concrete FSM implementation + struct iPodSearch_ : public msm::front::state_machine_def<iPodSearch_> + { + typedef msm::back::state_machine<iPodSearch_> iPodSearch; + // The list of FSM states + typedef std::set<std::string> Songset; + typedef Insert<Songset,boost::msm::front::default_base_state,iPodSearch> MyInsert; + typedef StringFind<boost::msm::front::default_base_state,iPodSearch> MyFind; + typedef Foreach<OneSong,Songset,boost::msm::front::default_base_state,iPodSearch> MyForeach; + + // the initial state of the player SM. Must be defined + typedef MyForeach initial_state; + + // transition actions + + // guard conditions + + typedef iPodSearch_ fsm; // makes transition table cleaner + + // Transition table for player + struct transition_table : mpl::vector4< + // Start Event Next Action Guard + // +-----------+------------------+------------+---------------------+----------------------+ + _row < MyForeach , OneSong , MyFind >, + _row < MyFind , NotFound<string> , MyForeach >, + _row < MyFind , Found<string> , MyInsert >, + _row < MyInsert , Done , MyForeach > + // +-----------+------------------+------------+---------------------+----------------------+ + > {}; + iPodSearch_():m_AllSongs(),m_ResultSearch() + { + // add a few songs for testing + m_AllSongs.insert("Let it be"); + m_AllSongs.insert("Yellow submarine"); + m_AllSongs.insert("Twist and Shout"); + m_AllSongs.insert("She Loves You"); + } + template <class Event,class FSM> + void on_entry(Event const&,FSM& fsm) + { + fsm.template get_state<MyForeach&>().set_container(&m_AllSongs); + fsm.template get_state<MyInsert&>().set_container(&m_ResultSearch); + } + const Songset& get_result(){return m_ResultSearch;} + void reset_search(){m_ResultSearch.clear();} + + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + + private: + Songset m_AllSongs; + Songset m_ResultSearch; + }; + typedef msm::back::state_machine<iPodSearch_> iPodSearch; + + + void test() + { + iPodSearch search; + // look for "She Loves You" using the first letters + search.get_state<iPodSearch::MyFind*>()->set_container("Sh");// will find 2 songs + + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + search.start(); + // display all the songs + const iPodSearch::Songset& res = search.get_result(); + for (iPodSearch::Songset::const_iterator it = res.begin();it != res.end();++it) + { + cout << "candidate song:" << *it << endl; + } + cout << "search using more letters" << endl; + // look for "She Loves You" using more letters + search.reset_search(); + search.get_state<iPodSearch::MyFind*>()->set_container("She");// will find 1 song + search.start(); + const iPodSearch::Songset& res2 = search.get_result(); + for (iPodSearch::Songset::const_iterator it = res2.begin();it != res2.end();++it) + { + cout << "candidate song:" << *it << endl; + } + + } +} + +int main() +{ + test(); + return 0; +} diff --git a/libs/msm/doc/PDF/examples/iPodSearchEuml.cpp b/libs/msm/doc/PDF/examples/iPodSearchEuml.cpp new file mode 100644 index 0000000000..e792ff134a --- /dev/null +++ b/libs/msm/doc/PDF/examples/iPodSearchEuml.cpp @@ -0,0 +1,166 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +// same as iPodSearch.cpp but using eUML +// requires boost >= v1.40 because using mpl::string + +#include <vector> +#include <iostream> + +#include <boost/msm/back/state_machine.hpp> +#include <boost/msm/front/euml/euml.hpp> +#include <boost/msm/front/euml/stl.hpp> + +using namespace std; +using namespace boost::msm::front::euml; +namespace msm = boost::msm; +namespace mpl = boost::mpl; + +// how long the timer will ring when countdown elapsed. +#define RINGING_TIME 5 + +namespace // Concrete FSM implementation +{ + // events + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(std::string,m_song) + BOOST_MSM_EUML_ATTRIBUTES((attributes_ << m_song ), OneSongDef) + struct OneSong_impl : euml_event<OneSong_impl>,OneSongDef + { + OneSong_impl(){} + OneSong_impl(const string& asong) + { + get_attribute(m_song)=asong; + } + OneSong_impl(const char* asong) + { + get_attribute(m_song)=asong; + } + OneSong_impl(const OneSong_impl& asong) + { + get_attribute(m_song)=asong.get_attribute(m_song); + } + const string& get_data() const {return get_attribute(m_song);} + }; + OneSong_impl const OneSong; + + // attribute definitions + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(vector<OneSong_impl>,m_src_container) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(vector<OneSong_impl>,m_tgt_container) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(std::string,m_letters) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(vector<OneSong_impl>::iterator,m_src_it) + + // the same attribute name can be reused + BOOST_MSM_EUML_ATTRIBUTES((attributes_ << m_song ), NotFoundDef) + BOOST_MSM_EUML_EVENT_WITH_ATTRIBUTES(NotFound,NotFoundDef) + + BOOST_MSM_EUML_ATTRIBUTES((attributes_ << m_song ), FoundDef) + BOOST_MSM_EUML_EVENT_WITH_ATTRIBUTES(Found,FoundDef) + + BOOST_MSM_EUML_EVENT(Done) + + // Concrete FSM implementation + + // The list of FSM states + BOOST_MSM_EUML_STATE(( (push_back_(fsm_(m_tgt_container),event_(m_song)) + ,process_(Done)), + no_action ),Insert) + + BOOST_MSM_EUML_STATE(( if_then_else_( string_find_(event_(m_song),state_(m_letters)) != Npos_<string>() , + process2_(Found,event_(m_song)), + process2_(NotFound,event_(m_song)) ) , + no_action, + attributes_ << m_letters ),StringFind) + + BOOST_MSM_EUML_STATE(( if_then_( state_(m_src_it) != end_(fsm_(m_src_container)), + process2_(OneSong,*(state_(m_src_it)++)) ), + no_action, + attributes_ << m_src_it ),Foreach) + + // replaces the old transition table + BOOST_MSM_EUML_TRANSITION_TABLE(( + StringFind == Foreach + OneSong , + Insert == StringFind + Found , + Foreach == StringFind + NotFound , + Foreach == Insert + Done + // +------------------------------------------------------------------------------+ + ),transition_table ) + + BOOST_MSM_EUML_ACTION(Log_No_Transition) + { + template <class FSM,class Event> + void operator()(Event const& e,FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + // create a state machine "on the fly" + BOOST_MSM_EUML_DECLARE_STATE_MACHINE(( transition_table, //STT + init_ << Foreach, // Init + ( + clear_(fsm_(m_src_container)), //clear source + clear_(fsm_(m_tgt_container)), //clear results + push_back_(fsm_(m_src_container), + String_<mpl::string<'Let ','it ','be'> >()),//add a song + push_back_(fsm_(m_src_container), + String_<mpl::string<'Yell','ow s','ubma','rine'> >()),//add a song + push_back_(fsm_(m_src_container), + String_<mpl::string<'Twis','t an','d Sh','out'> >()),//add a song + push_back_(fsm_(m_src_container), + String_<mpl::string<'She ','love','s yo','u'> >()),//add a song + attribute_(substate_(Foreach()),m_src_it) + = begin_(fsm_(m_src_container)) //set the search begin + ), // Entry + no_action, // Exit + attributes_ << m_src_container // song list + << m_tgt_container, // result + configure_<< no_configure_, + Log_No_Transition + ), + iPodSearch_) //fsm name + + + // choice of back-end + typedef msm::back::state_machine<iPodSearch_> iPodSearch; + + void test() + { + iPodSearch search; + + // look for "She Loves You" using the first letters + search.get_state<BOOST_MSM_EUML_STATE_NAME(StringFind)&>().get_attribute(m_letters)="Sh";// will find 2 songs + + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + search.start(); + // display all the songs + for (vector<OneSong_impl>::const_iterator it = search.get_attribute(m_tgt_container).begin(); + it != search.get_attribute(m_tgt_container).end();++it) + { + cout << "candidate song:" << (*it).get_attribute(m_song) << endl; + } + + cout << "search using more letters" << endl; + // look for "She Loves You" using more letters + search.get_state<BOOST_MSM_EUML_STATE_NAME(StringFind)&>().get_attribute(m_letters)="She";// will find 1 song + search.start(); + // display all the songs + for (vector<OneSong_impl>::const_iterator it = search.get_attribute(m_tgt_container).begin(); + it != search.get_attribute(m_tgt_container).end();++it) + { + cout << "candidate song:" << (*it).get_attribute(m_song) << endl; + } + } +} + +int main() +{ + test(); + return 0; +} diff --git a/libs/msm/doc/PDF/examples/iPod_distributed/Events.hpp b/libs/msm/doc/PDF/examples/iPod_distributed/Events.hpp new file mode 100644 index 0000000000..ce06e9f35a --- /dev/null +++ b/libs/msm/doc/PDF/examples/iPod_distributed/Events.hpp @@ -0,0 +1,50 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef EVENTS_HPP +#define EVENTS_HPP + +//flags +struct MenuActive{}; +// hardware-generated events +struct Hold {}; +struct NoHold {}; +struct SouthPressed {}; +struct SouthReleased {}; +struct MiddleButton {}; +struct EastPressed{}; +struct EastReleased{}; +struct Off {}; +struct MenuButton {}; + +// internally used events +struct PlayPause {}; +struct EndPlay {}; +struct CloseMenu +{ + template<class EVENT> + CloseMenu(EVENT const &) {} +}; +struct OnOffTimer {}; +struct MenuMiddleButton {}; +struct SelectSong {}; +struct SongFinished {}; +struct StartSong +{ + StartSong (int song_index):m_Selected(song_index){} + int m_Selected; +}; +struct PreviousSong{}; +struct NextSong{}; +struct NextSongDerived : public NextSong{}; +struct ForwardTimer{}; +struct PlayingMiddleButton{}; + +#endif // EVENTS_HPP diff --git a/libs/msm/doc/PDF/examples/iPod_distributed/MenuMode.cpp b/libs/msm/doc/PDF/examples/iPod_distributed/MenuMode.cpp new file mode 100644 index 0000000000..e23f167995 --- /dev/null +++ b/libs/msm/doc/PDF/examples/iPod_distributed/MenuMode.cpp @@ -0,0 +1,12 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include "MenuMode.hpp" +BOOST_MSM_BACK_GENERATE_PROCESS_EVENT(MenuMode) diff --git a/libs/msm/doc/PDF/examples/iPod_distributed/MenuMode.hpp b/libs/msm/doc/PDF/examples/iPod_distributed/MenuMode.hpp new file mode 100644 index 0000000000..cad441cf37 --- /dev/null +++ b/libs/msm/doc/PDF/examples/iPod_distributed/MenuMode.hpp @@ -0,0 +1,62 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef MENU_MODE_HPP +#define MENU_MODE_HPP + +#include <iostream> +#include <boost/any.hpp> + +#include "Events.hpp" +#include <boost/msm/back/favor_compile_time.hpp> +#include <boost/msm/back/state_machine.hpp> +#include <boost/msm/front/state_machine_def.hpp> + +using namespace std; +namespace msm = boost::msm; + +struct MenuMode_ : public msm::front::state_machine_def<MenuMode_> +{ + typedef mpl::vector1<MenuActive> flag_list; + struct WaitingForSongChoice : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: MenuMode::WaitingForSongChoice" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: MenuMode::WaitingForSongChoice" << std::endl;} + }; + struct StartCurrentSong : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: MenuMode::StartCurrentSong" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: MenuMode::StartCurrentSong" << std::endl;} + }; + struct MenuExit : public msm::front::exit_pseudo_state<CloseMenu> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: MenuMode::WaitingForSongChoice" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: MenuMode::WaitingForSongChoice" << std::endl;} + }; + typedef WaitingForSongChoice initial_state; + typedef MenuMode_ fsm; // makes transition table cleaner + // Transition table for player + struct transition_table : mpl::vector2< + // Start Event Next Action Guard + // +---------------------+------------------+-------------------+---------------------+----------------------+ + _row < WaitingForSongChoice , MenuMiddleButton , StartCurrentSong >, + _row < StartCurrentSong , SelectSong , MenuExit > + // +---------------------+------------------+-------------------+---------------------+----------------------+ + > {}; +}; +typedef msm::back::state_machine<MenuMode_> MenuMode; + +#endif diff --git a/libs/msm/doc/PDF/examples/iPod_distributed/PlayingMode.cpp b/libs/msm/doc/PDF/examples/iPod_distributed/PlayingMode.cpp new file mode 100644 index 0000000000..e35019c424 --- /dev/null +++ b/libs/msm/doc/PDF/examples/iPod_distributed/PlayingMode.cpp @@ -0,0 +1,12 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include "PlayingMode.hpp" +BOOST_MSM_BACK_GENERATE_PROCESS_EVENT(PlayingMode) diff --git a/libs/msm/doc/PDF/examples/iPod_distributed/PlayingMode.hpp b/libs/msm/doc/PDF/examples/iPod_distributed/PlayingMode.hpp new file mode 100644 index 0000000000..1328041ae4 --- /dev/null +++ b/libs/msm/doc/PDF/examples/iPod_distributed/PlayingMode.hpp @@ -0,0 +1,251 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef PLAYING_MODE_HPP +#define PLAYING_MODE_HPP + +#include <iostream> +#include <boost/any.hpp> +#define FUSION_MAX_VECTOR_SIZE 20 + +#include "Events.hpp" +#include <boost/msm/back/favor_compile_time.hpp> +#include <boost/msm/back/state_machine.hpp> +#include <boost/msm/front/state_machine_def.hpp> +#include <boost/msm/front/euml/euml.hpp> + +using namespace std; +namespace msm = boost::msm; +namespace euml = boost::msm::front::euml; + +struct PlayingMode_ : public msm::front::state_machine_def<PlayingMode_> +{ + //flags + struct NoFastFwd{}; + + struct Playing : public msm::front::state<default_base_state,msm::front::sm_ptr> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) + { + std::cout << "starting: PlayingMode::Playing" << std::endl; + std::cout << "playing song:" << m_fsm->get_current_song() << std::endl; + } + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: PlayingMode::Playing" << std::endl;} + void set_sm_ptr(PlayingMode_* pl) + { + m_fsm = pl; + } + private: + PlayingMode_* m_fsm; + }; + struct Paused : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: PlayingMode::Paused" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: PlayingMode::Paused" << std::endl;} + }; + struct WaitingForNextPrev : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: PlayingMode::WaitingForNextPrev" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: PlayingMode::WaitingForNextPrev" << std::endl;} + }; + struct WaitingForEnd : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: PlayingMode::WaitingForEnd" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: PlayingMode::WaitingForEnd" << std::endl;} + }; + struct NoForward : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: PlayingMode::NoForward" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: PlayingMode::NoForward" << std::endl;} + }; + struct ForwardPressed : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) + { + std::cout << "starting: PlayingMode::ForwardPressed," << "start timer" << std::endl; + } + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) + { + std::cout << "finishing: PlayingMode::ForwardPressed," << "stop timer" << std::endl; + } + }; + struct FastForward : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) + { + std::cout << "starting: PlayingMode::FastForward," << "start timer" << std::endl; + } + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) + { + std::cout << "finishing: PlayingMode::FastForward," << "stop timer" << std::endl; + } + }; + struct StdDisplay : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: PlayingMode::StdDisplay" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: PlayingMode::StdDisplay" << std::endl;} + }; + struct SetPosition : public msm::front::state<> + { + typedef mpl::vector1<NoFastFwd> flag_list; + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: PlayingMode::SetPosition" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: PlayingMode::SetPosition" << std::endl;} + }; + struct SetMark : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: PlayingMode::SetMark" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: PlayingMode::SetMark" << std::endl;} + }; + struct PlayingExit : public msm::front::exit_pseudo_state<EndPlay> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: PlayingMode::PlayingExit" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: PlayingMode::PlayingExit" << std::endl;} + }; + // transition action methods + struct inc_song_counter : euml::euml_action<inc_song_counter> + { + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& fsm,SourceState& ,TargetState& ) + { + if (++fsm.m_SongIndex <= fsm.m_NumberOfSongs ) + { + std::cout << "playing song:" << fsm.m_SongIndex << std::endl; + } + else + { + // last song => end playing, next play will start at the beginning + fsm.m_SongIndex = 1; + fsm.process_event(EndPlay()); + } + } + }; + + void select_song(StartSong const& evt) + { + if ((evt.m_Selected>0) && (evt.m_Selected<=m_NumberOfSongs)) + { + m_SongIndex = evt.m_Selected; + std::cout << "selecting song:" << m_SongIndex << std::endl; + } + else + { + // play current song + std::cout << "selecting song:" << m_SongIndex << std::endl; + } + } + struct dec_song_counter : euml::euml_action<dec_song_counter> + { + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& fsm,SourceState& ,TargetState& ) + { + if (--fsm.m_SongIndex >0 ) + { + std::cout << "playing song:" << fsm.m_SongIndex << std::endl; + } + else + { + // before first song => end playing + fsm.m_SongIndex = 1; + fsm.process_event(EndPlay()); + } + } + }; + struct send_NextSong : euml::euml_action<send_NextSong> + { + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& fsm,SourceState& ,TargetState& ) + { + fsm.process_event(NextSong()); + } + }; + + void do_fast_forward(ForwardTimer const&) + { + std::cout << "moving song forward..." << std::endl; + } + + // transition guard methods + struct fast_fwd_ok : euml::euml_action<fast_fwd_ok> + { + template <class FSM,class EVT,class SourceState,class TargetState> + bool operator()(EVT const& ,FSM& fsm,SourceState& ,TargetState& ) + { + // guard accepts only if fast forward is possible (No SetPosition mode) + return !fsm.is_flag_active<NoFastFwd>(); + } + }; + // initial states / orthogonal zones + typedef mpl::vector5<Playing,WaitingForNextPrev,WaitingForEnd,NoForward,StdDisplay> + initial_state; + typedef PlayingMode_ fsm; // makes transition table cleaner + // Transition table for player + struct transition_table : mpl::vector19< + // Start Event Next Action Guard + // +--------------------+---------------------+--------------------+--------------------------+----------------------+ + _row < Playing , PlayPause , Paused >, + _row < Playing , Off , Paused >, + a_row < Playing , StartSong , Playing , &fsm::select_song >, + _row < Paused , PlayPause , Playing >, + msm::front::Row < Playing , SongFinished , Playing , inc_song_counter , msm::front::none >, + a_row < Paused , StartSong , Playing , &fsm::select_song >, + // +--------------------+---------------------+--------------------+--------------------------+----------------------+ + msm::front::Row < WaitingForNextPrev , PreviousSong , WaitingForNextPrev , dec_song_counter , msm::front::none >, + msm::front::Row < WaitingForNextPrev , NextSong , WaitingForNextPrev , inc_song_counter , msm::front::none >, + // +--------------------+---------------------+--------------------+--------------------------+----------------------+ + _row < WaitingForEnd , EndPlay , PlayingExit >, + // +--------------------+---------------------+--------------------+--------------------------+----------------------+ + msm::front::Row < NoForward , EastPressed , ForwardPressed , msm::front::none , fast_fwd_ok >, + msm::front::Row < ForwardPressed , EastReleased , NoForward , send_NextSong , msm::front::none >, + a_row < ForwardPressed , ForwardTimer , FastForward , &fsm::do_fast_forward >, + a_row < FastForward , ForwardTimer , FastForward , &fsm::do_fast_forward >, + _row < FastForward , EastReleased , NoForward >, + // +--------------------+---------------------+---------------------+--------------------------+----------------------+ + _row < StdDisplay , PlayingMiddleButton , SetPosition >, + _row < SetPosition , StartSong , StdDisplay >, + _row < SetPosition , PlayingMiddleButton , SetMark >, + _row < SetMark , PlayingMiddleButton , StdDisplay >, + _row < SetMark , StartSong , StdDisplay > + // +--------------------+---------------------+---------------------+--------------------------+----------------------+ + > {}; + PlayingMode_(): + m_SongIndex(1), + // for simplicity we decide there are 5 songs + m_NumberOfSongs(5){} + + int get_current_song(){return m_SongIndex;} + int m_SongIndex; + int m_NumberOfSongs; + +}; +typedef msm::back::state_machine<PlayingMode_> PlayingMode; + +#endif // PLAYING_MODE_HPP diff --git a/libs/msm/doc/PDF/examples/iPod_distributed/iPod.cpp b/libs/msm/doc/PDF/examples/iPod_distributed/iPod.cpp new file mode 100644 index 0000000000..d5b3b79945 --- /dev/null +++ b/libs/msm/doc/PDF/examples/iPod_distributed/iPod.cpp @@ -0,0 +1,243 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <vector> +#include <set> +#include <string> +#include <iostream> +#define FUSION_MAX_VECTOR_SIZE 20 + +#include "boost/mpl/vector/vector50.hpp" +#include <boost/msm/back/state_machine.hpp> +#include <boost/msm/front/state_machine_def.hpp> + +#include "Events.hpp" +#include "PlayingMode.hpp" +#include "MenuMode.hpp" + +using namespace std; +namespace msm = boost::msm; + +namespace // Concrete FSM implementation +{ + struct iPod_; + typedef msm::back::state_machine<iPod_, + ::boost::msm::back::favor_compile_time> iPod; + + // Concrete FSM implementation + struct iPod_ : public msm::front::state_machine_def<iPod_> + { + // The list of FSM states + struct NotHolding : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: NotHolding" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: NotHolding" << std::endl;} + }; + struct Holding : public msm::front::interrupt_state<NoHold> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: Holding" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: Holding" << std::endl;} + }; + struct NotPlaying : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: NotPlaying" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: NotPlaying" << std::endl;} + }; + struct NoMenuMode : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: NoMenuMode" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: NoMenuMode" << std::endl;} + }; + struct NoOnOffButton : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: NoOnOffButton" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: NoOnOffButton" << std::endl;} + }; + struct OffDown : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: OffDown, start timer" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: OffDown, end timer" << std::endl;} + }; + struct PlayerOff : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: PlayerOff" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: PlayerOff" << std::endl;} + }; + struct CheckMiddleButton : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: CheckMiddleButton" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: CheckMiddleButton" << std::endl;} + }; + + // the initial state of the player SM. Must be defined + typedef mpl::vector5<NotHolding,NotPlaying,NoMenuMode,NoOnOffButton,CheckMiddleButton> + initial_state; + // transition actions + void send_ActivateMenu(EndPlay const&) + { + std::cout << "leaving Playing" << std::endl; + // we need to activate the menu and simulate its button + (static_cast<iPod*>(this))->process_event(MenuButton()); + } + void send_StartSong(CloseMenu const&) + { + // we suppose the 5th song was selected + (static_cast<iPod*>(this))->process_event(StartSong(5)); + } + void send_PlayPause(SouthReleased const&) + { + // action using the message queue to generate another event + (static_cast<iPod*>(this))->process_event(PlayPause()); + } + void send_Off(OnOffTimer const&) + { + std::cout << "turning player off" << std::endl; + (static_cast<iPod*>(this))->process_event(Off()); + } + void send_PlayingMiddleButton(MiddleButton const&) + { + (static_cast<iPod*>(this))->process_event(PlayingMiddleButton()); + } + void send_MenuMiddleButton(MiddleButton const&) + { + (static_cast<iPod*>(this))->process_event(MenuMiddleButton()); + } + // guard conditions + bool is_menu(MiddleButton const&) + { + return (static_cast<iPod*>(this))->is_flag_active<MenuActive>(); + } + bool is_no_menu(MiddleButton const& evt) + { + return !is_menu(evt); + } + template <class EVENT> + void switch_on(EVENT const&) + { + std::cout << "turning player on" << std::endl; + } + typedef iPod_ fsm; // makes transition table cleaner + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +-------------------+---------------+-------------------+--------------------------------+----------------------+ + _row < NotHolding , Hold , Holding >, + _row < Holding , NoHold , NotHolding >, + // +-------------------+---------------+-------------------+--------------------------------+----------------------+ + _row < NotPlaying , PlayPause , PlayingMode >, + a_row < PlayingMode::exit_pt<PlayingMode_:: + PlayingExit> , EndPlay , NotPlaying , &fsm::send_ActivateMenu >, + // +-------------------+---------------+-------------------+--------------------------------+----------------------+ + _row < NoMenuMode , MenuButton , MenuMode >, + a_row < MenuMode::exit_pt<MenuMode_:: + MenuExit> , CloseMenu , NoMenuMode , &fsm::send_StartSong >, + // +-------------------+---------------+-------------------+--------------------------------+----------------------+ + _row < NoOnOffButton , SouthPressed , OffDown >, + a_row < OffDown , SouthReleased , NoOnOffButton , &fsm::send_PlayPause >, + a_row < OffDown , OnOffTimer , PlayerOff , &fsm::send_Off >, + a_row < PlayerOff , SouthPressed , NoOnOffButton , &fsm::switch_on >, + a_row < PlayerOff , NoHold , NoOnOffButton , &fsm::switch_on >, + // +-------------------+---------------+--------------------+--------------------------------+----------------------+ + row < CheckMiddleButton , MiddleButton , CheckMiddleButton , &fsm::send_PlayingMiddleButton , &fsm::is_menu >, + row < CheckMiddleButton , MiddleButton , CheckMiddleButton , &fsm::send_MenuMiddleButton , &fsm::is_no_menu > + // +-------------------+---------------+--------------------+--------------------------------+----------------------+ + > {}; + + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + + void test() + { + iPod sm; + sm.start(); + // we first press Hold + std::cout << "pressing hold" << std::endl; + sm.process_event(Hold()); + // pressing a button is now ignored + std::cout << "pressing a button" << std::endl; + sm.process_event(SouthPressed()); + // or even one contained in a submachine + sm.process_event(EastPressed()); + // no more holding + std::cout << "no more holding, end interrupt event sent" << std::endl; + sm.process_event(NoHold()); + std::cout << "pressing South button a short time" << std::endl; + sm.process_event(SouthPressed()); + // we suppose a short pressing leading to playing a song + sm.process_event(SouthReleased()); + // we move to the next song + std::cout << "we move to the next song" << std::endl; + sm.process_event(NextSong()); + // then back to no song => exit from playing, menu active + std::cout << "we press twice the West button (simulated)=> end of playing" << std::endl; + sm.process_event(PreviousSong()); + sm.process_event(PreviousSong()); + // even in menu mode, pressing play will start playing the first song + std::cout << "pressing play/pause" << std::endl; + sm.process_event(SouthPressed()); + sm.process_event(SouthReleased()); + // of course pausing must be possible + std::cout << "pressing play/pause" << std::endl; + sm.process_event(SouthPressed()); + sm.process_event(SouthReleased()); + std::cout << "pressing play/pause" << std::endl; + sm.process_event(SouthPressed()); + sm.process_event(SouthReleased()); + // while playing, you can fast forward + std::cout << "pressing East button a long time" << std::endl; + sm.process_event(EastPressed()); + // let's suppose the timer just fired + sm.process_event(ForwardTimer()); + sm.process_event(ForwardTimer()); + // end of fast forwarding + std::cout << "releasing East button" << std::endl; + sm.process_event(EastReleased()); + // we now press the middle button to set playing at a given position + std::cout << "pressing Middle button, fast forwarding disabled" << std::endl; + sm.process_event(MiddleButton()); + std::cout <<"pressing East button to fast forward" << std::endl; + sm.process_event(EastPressed()); + // we switch off and on + std::cout <<"switch off player" << std::endl; + sm.process_event(SouthPressed()); + sm.process_event(OnOffTimer()); + std::cout <<"switch on player" << std::endl; + sm.process_event(SouthPressed()); + } +} + +int main() +{ + test(); + return 0; +} diff --git a/libs/msm/doc/PDF/examples/ipod_functors.hpp b/libs/msm/doc/PDF/examples/ipod_functors.hpp new file mode 100644 index 0000000000..c49c3473dd --- /dev/null +++ b/libs/msm/doc/PDF/examples/ipod_functors.hpp @@ -0,0 +1,248 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef IPOD_FUNCTORS_HPP +#define IPOD_FUNCTORS_HPP +#include <boost/msm/front/euml/euml.hpp> + +BOOST_MSM_EUML_ACTION(NotHolding_Entry) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: NotHolding" << std::endl; + } +}; +BOOST_MSM_EUML_ACTION(Holding_Entry) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: Holding" << std::endl; + } +}; +BOOST_MSM_EUML_ACTION(NotPlaying_Entry) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: NotPlaying" << std::endl; + } +}; +BOOST_MSM_EUML_ACTION(NoMenuMode_Entry) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: NoMenuMode" << std::endl; + } +}; +BOOST_MSM_EUML_ACTION(NoOnOffButton_Entry) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: NoOnOffButton" << std::endl; + } +}; +BOOST_MSM_EUML_ACTION(OffDown_Entry) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: OffDown" << std::endl; + } +}; +BOOST_MSM_EUML_ACTION(PlayerOff_Entry) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: PlayerOff" << std::endl; + } +}; +BOOST_MSM_EUML_ACTION(CheckMiddleButton_Entry) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: CheckMiddleButton" << std::endl; + } +}; +BOOST_MSM_EUML_ACTION(Playing_Entry) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM& fsm,STATE& ) + { + std::cout << "entering: Playing" << std::endl; + std::cout << "playing song:" << fsm.get_attribute(m_SongIndex) << std::endl; + } +}; +BOOST_MSM_EUML_ACTION(WaitingForNextPrev_Entry) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: WaitingForNextPrev" << std::endl; + } +}; +BOOST_MSM_EUML_ACTION(Paused_Entry) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: Paused" << std::endl; + } +}; +BOOST_MSM_EUML_ACTION(WaitingForEnd_Entry) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: OffDown" << std::endl; + } +}; +BOOST_MSM_EUML_ACTION(NoForward_Entry) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: NoForward" << std::endl; + } +}; +BOOST_MSM_EUML_ACTION(ForwardPressed_Entry) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: ForwardPressed" << std::endl; + } +}; +BOOST_MSM_EUML_ACTION(ForwardPressed_Exit) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "leaving: ForwardPressed" << std::endl; + } +}; +BOOST_MSM_EUML_ACTION(FastForward_Entry) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: FastForward" << std::endl; + } +}; +BOOST_MSM_EUML_ACTION(FastForward_Exit) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "leaving: FastForward" << std::endl; + } +}; +BOOST_MSM_EUML_ACTION(StdDisplay_Entry) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: StdDisplay" << std::endl; + } +}; +BOOST_MSM_EUML_ACTION(SetPosition_Entry) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: SetPosition" << std::endl; + } +}; +BOOST_MSM_EUML_ACTION(SetMark_Entry) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: SetMark" << std::endl; + } +}; +BOOST_MSM_EUML_ACTION(PlayingExit_Entry) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: PlayingExit" << std::endl; + } +}; +BOOST_MSM_EUML_ACTION(WaitingForSongChoice_Entry) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: WaitingForSongChoice" << std::endl; + } +}; +BOOST_MSM_EUML_ACTION(StartCurrentSong_Entry) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: StartCurrentSong" << std::endl; + } +}; +BOOST_MSM_EUML_ACTION(MenuExit_Entry) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: MenuExit" << std::endl; + } +}; +BOOST_MSM_EUML_ACTION(show_selected_song) +{ + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& fsm,SourceState& ,TargetState& ) + { + std::cout << "selecting song:" << fsm.get_attribute(m_SongIndex) << std::endl; + } +}; +BOOST_MSM_EUML_ACTION(do_fast_forward) +{ + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& fsm,SourceState& ,TargetState& ) + { + std::cout << "moving song forward..." << std::endl; + } +}; +BOOST_MSM_EUML_ACTION(show_playing_song) +{ + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& fsm,SourceState& ,TargetState& ) + { + std::cout << "playing song:" << fsm.get_attribute(m_SongIndex) << std::endl; + } +}; +BOOST_MSM_EUML_ACTION(show_player_off) +{ + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& fsm,SourceState& ,TargetState& ) + { + std::cout << "turning player off" << std::endl; + } +}; +BOOST_MSM_EUML_ACTION(show_player_on) +{ + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& fsm,SourceState& ,TargetState& ) + { + std::cout << "turning player on" << std::endl; + } +}; +#endif diff --git a/libs/msm/doc/PDF/examples/logging_functors.h b/libs/msm/doc/PDF/examples/logging_functors.h new file mode 100644 index 0000000000..7e68cedb07 --- /dev/null +++ b/libs/msm/doc/PDF/examples/logging_functors.h @@ -0,0 +1,370 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef LOGGING_FUNCTORS +#define LOGGING_FUNCTORS + +BOOST_MSM_EUML_ACTION(Empty_Entry) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: Empty" << std::endl; + } +}; +BOOST_MSM_EUML_ACTION(Empty_Exit) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "leaving: Empty" << std::endl; + } +}; + +BOOST_MSM_EUML_ACTION(Open_Entry) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: Open" << std::endl; + } +}; +BOOST_MSM_EUML_ACTION(Open_Exit) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "leaving: Open" << std::endl; + } +}; + +BOOST_MSM_EUML_ACTION(Stopped_Entry) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: Stopped" << std::endl; + } +}; +BOOST_MSM_EUML_ACTION(Stopped_Exit) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "leaving: Stopped" << std::endl; + } +}; + +BOOST_MSM_EUML_ACTION(AllOk_Entry) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "starting: AllOk" << std::endl; + } +}; +BOOST_MSM_EUML_ACTION(AllOk_Exit) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "finishing: AllOk" << std::endl; + } +}; + +BOOST_MSM_EUML_ACTION(ErrorMode_Entry) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "starting: ErrorMode" << std::endl; + } +}; +BOOST_MSM_EUML_ACTION(ErrorMode_Exit) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "finishing: ErrorMode" << std::endl; + } +}; + +BOOST_MSM_EUML_ACTION(Playing_Entry) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "entering: Playing" << std::endl; + } +}; +BOOST_MSM_EUML_ACTION(Playing_Exit) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "leaving: Playing" << std::endl; + } +}; + +BOOST_MSM_EUML_ACTION(Song1_Entry) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "starting: First song" << std::endl; + } +}; +BOOST_MSM_EUML_ACTION(Song1_Exit) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "finishing: First Song" << std::endl; + } +}; + +BOOST_MSM_EUML_ACTION(Song2_Entry) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "starting: Second song" << std::endl; + } +}; +BOOST_MSM_EUML_ACTION(Song2_Exit) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "finishing: Second Song" << std::endl; + } +}; + +BOOST_MSM_EUML_ACTION(Song3_Entry) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "starting: Third song" << std::endl; + } +}; +BOOST_MSM_EUML_ACTION(Song3_Exit) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "finishing: Third Song" << std::endl; + } +}; + +BOOST_MSM_EUML_ACTION(Region2State1_Entry) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "starting: Region2State1" << std::endl; + } +}; +BOOST_MSM_EUML_ACTION(Region2State1_Exit) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "finishing: Region2State1" << std::endl; + } +}; +BOOST_MSM_EUML_ACTION(Region2State2_Entry) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "starting: Region2State2" << std::endl; + } +}; +BOOST_MSM_EUML_ACTION(Region2State2_Exit) +{ + template <class Event,class FSM,class STATE> + void operator()(Event const&,FSM&,STATE& ) + { + std::cout << "finishing: Region2State2" << std::endl; + } +}; +// transition actions for Playing +BOOST_MSM_EUML_ACTION(start_next_song) +{ + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM&,SourceState& ,TargetState& ) + { + std::cout << "Playing::start_next_song" << endl; + } +}; +BOOST_MSM_EUML_ACTION(start_prev_song) +{ + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM&,SourceState& ,TargetState& ) + { + std::cout << "Playing::start_prev_song" << endl; + } +}; + +// transition actions +BOOST_MSM_EUML_ACTION(start_playback) +{ + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM&,SourceState& ,TargetState& ) + { + cout << "player::start_playback" << endl; + } +}; +BOOST_MSM_EUML_ACTION(open_drawer) +{ + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM&,SourceState& ,TargetState& ) + { + cout << "player::open_drawer" << endl; + } +}; +BOOST_MSM_EUML_ACTION(close_drawer) +{ + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM&,SourceState& ,TargetState& ) + { + cout << "player::close_drawer" << endl; + } +}; +BOOST_MSM_EUML_ACTION(store_cd_info) +{ + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const&, FSM& ,SourceState& ,TargetState& ) + { + cout << "player::store_cd_info" << endl; + // it is now easy to use the message queue. + // alternatively to the proces_ in the transition table, we could write: + // fsm.process_event(play()); + } +}; +BOOST_MSM_EUML_ACTION(stop_playback) +{ + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM&,SourceState& ,TargetState& ) + { + cout << "player::stop_playback" << endl; + } +}; +BOOST_MSM_EUML_ACTION(pause_playback) +{ + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM&,SourceState& ,TargetState& ) + { + cout << "player::pause_playback" << endl; + } +}; +BOOST_MSM_EUML_ACTION(resume_playback) +{ + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM&,SourceState& ,TargetState& ) + { + cout << "player::resume_playback" << endl; + } +}; +BOOST_MSM_EUML_ACTION(stop_and_open) +{ + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM&,SourceState& ,TargetState& ) + { + cout << "player::stop_and_open" << endl; + } +}; +BOOST_MSM_EUML_ACTION(stopped_again) +{ + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM&,SourceState& ,TargetState& ) + { + cout << "player::stopped_again" << endl; + } +}; + +BOOST_MSM_EUML_ACTION(report_error) +{ + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM&,SourceState& ,TargetState& ) + { + cout << "player::report_error" << endl; + } +}; + +BOOST_MSM_EUML_ACTION(report_end_error) +{ + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM&,SourceState& ,TargetState& ) + { + cout << "player::report_end_error" << endl; + } +}; +BOOST_MSM_EUML_ACTION(internal_action1) +{ + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const&, FSM& ,SourceState& ,TargetState& ) + { + cout << "Open::internal action1" << endl; + } +}; +BOOST_MSM_EUML_ACTION(internal_action2) +{ + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const&, FSM& ,SourceState& ,TargetState& ) + { + cout << "Open::internal action2" << endl; + } +}; +BOOST_MSM_EUML_ACTION(internal_action) +{ + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const&, FSM& ,SourceState& ,TargetState& ) + { + cout << "Open::internal action" << endl; + } +}; +enum DiskTypeEnum +{ + DISK_CD=0, + DISK_DVD=1 +}; + +// Handler called when no_transition detected +BOOST_MSM_EUML_ACTION(Log_No_Transition) +{ + template <class FSM,class Event> + void operator()(Event const& e,FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } +}; + +BOOST_MSM_EUML_ACTION(internal_guard1) +{ + template <class FSM,class EVT,class SourceState,class TargetState> + bool operator()(EVT const&, FSM& ,SourceState& ,TargetState& ) + { + cout << "Open::internal guard1" << endl; + return false; + } +}; +BOOST_MSM_EUML_ACTION(internal_guard2) +{ + template <class FSM,class EVT,class SourceState,class TargetState> + bool operator()(EVT const&, FSM& ,SourceState& ,TargetState& ) + { + cout << "Open::internal guard2" << endl; + return false; + } +}; +#endif // LOGGING_FUNCTORS diff --git a/libs/msm/doc/PDF/msm.pdf b/libs/msm/doc/PDF/msm.pdf Binary files differnew file mode 100644 index 0000000000..491b9bfd87 --- /dev/null +++ b/libs/msm/doc/PDF/msm.pdf diff --git a/libs/msm/doc/images/AnnexA.jpg b/libs/msm/doc/images/AnnexA.jpg Binary files differnew file mode 100644 index 0000000000..be6b75d35e --- /dev/null +++ b/libs/msm/doc/images/AnnexA.jpg diff --git a/libs/msm/doc/images/Anonymous.jpg b/libs/msm/doc/images/Anonymous.jpg Binary files differnew file mode 100644 index 0000000000..23adbeafad --- /dev/null +++ b/libs/msm/doc/images/Anonymous.jpg diff --git a/libs/msm/doc/images/CompositeTutorial.jpg b/libs/msm/doc/images/CompositeTutorial.jpg Binary files differnew file mode 100644 index 0000000000..ed7d143c19 --- /dev/null +++ b/libs/msm/doc/images/CompositeTutorial.jpg diff --git a/libs/msm/doc/images/FlagsTutorial.jpg b/libs/msm/doc/images/FlagsTutorial.jpg Binary files differnew file mode 100644 index 0000000000..a5cd6abf07 --- /dev/null +++ b/libs/msm/doc/images/FlagsTutorial.jpg diff --git a/libs/msm/doc/images/HistoryTutorial.jpg b/libs/msm/doc/images/HistoryTutorial.jpg Binary files differnew file mode 100644 index 0000000000..76b2a4abaa --- /dev/null +++ b/libs/msm/doc/images/HistoryTutorial.jpg diff --git a/libs/msm/doc/images/Orthogonal-deferred.jpg b/libs/msm/doc/images/Orthogonal-deferred.jpg Binary files differnew file mode 100644 index 0000000000..790470575f --- /dev/null +++ b/libs/msm/doc/images/Orthogonal-deferred.jpg diff --git a/libs/msm/doc/images/ParsingDigits.jpg b/libs/msm/doc/images/ParsingDigits.jpg Binary files differnew file mode 100644 index 0000000000..171f91997c --- /dev/null +++ b/libs/msm/doc/images/ParsingDigits.jpg diff --git a/libs/msm/doc/images/SimpleTutorial.jpg b/libs/msm/doc/images/SimpleTutorial.jpg Binary files differnew file mode 100644 index 0000000000..ba38eeb442 --- /dev/null +++ b/libs/msm/doc/images/SimpleTutorial.jpg diff --git a/libs/msm/doc/images/completion.gif b/libs/msm/doc/images/completion.gif Binary files differnew file mode 100644 index 0000000000..f52732682c --- /dev/null +++ b/libs/msm/doc/images/completion.gif diff --git a/libs/msm/doc/images/conflict1.gif b/libs/msm/doc/images/conflict1.gif Binary files differnew file mode 100644 index 0000000000..84a589f487 --- /dev/null +++ b/libs/msm/doc/images/conflict1.gif diff --git a/libs/msm/doc/images/conflict2.gif b/libs/msm/doc/images/conflict2.gif Binary files differnew file mode 100644 index 0000000000..d76f6369ef --- /dev/null +++ b/libs/msm/doc/images/conflict2.gif diff --git a/libs/msm/doc/images/entry_point.gif b/libs/msm/doc/images/entry_point.gif Binary files differnew file mode 100644 index 0000000000..08b4de0ccb --- /dev/null +++ b/libs/msm/doc/images/entry_point.gif diff --git a/libs/msm/doc/images/entrytutorial.jpg b/libs/msm/doc/images/entrytutorial.jpg Binary files differnew file mode 100644 index 0000000000..d27a499122 --- /dev/null +++ b/libs/msm/doc/images/entrytutorial.jpg diff --git a/libs/msm/doc/images/error_no_regions.jpg b/libs/msm/doc/images/error_no_regions.jpg Binary files differnew file mode 100644 index 0000000000..20cccc5761 --- /dev/null +++ b/libs/msm/doc/images/error_no_regions.jpg diff --git a/libs/msm/doc/images/exit.gif b/libs/msm/doc/images/exit.gif Binary files differnew file mode 100644 index 0000000000..5087adcbbe --- /dev/null +++ b/libs/msm/doc/images/exit.gif diff --git a/libs/msm/doc/images/explicit.gif b/libs/msm/doc/images/explicit.gif Binary files differnew file mode 100644 index 0000000000..9c98e59c0d --- /dev/null +++ b/libs/msm/doc/images/explicit.gif diff --git a/libs/msm/doc/images/fork.gif b/libs/msm/doc/images/fork.gif Binary files differnew file mode 100644 index 0000000000..3666874e77 --- /dev/null +++ b/libs/msm/doc/images/fork.gif diff --git a/libs/msm/doc/images/history.gif b/libs/msm/doc/images/history.gif Binary files differnew file mode 100644 index 0000000000..14630bdf8a --- /dev/null +++ b/libs/msm/doc/images/history.gif diff --git a/libs/msm/doc/images/init_state.gif b/libs/msm/doc/images/init_state.gif Binary files differnew file mode 100644 index 0000000000..f3fb5508e5 --- /dev/null +++ b/libs/msm/doc/images/init_state.gif diff --git a/libs/msm/doc/images/regions.gif b/libs/msm/doc/images/regions.gif Binary files differnew file mode 100644 index 0000000000..2f9d62770b --- /dev/null +++ b/libs/msm/doc/images/regions.gif diff --git a/libs/msm/doc/images/sm.gif b/libs/msm/doc/images/sm.gif Binary files differnew file mode 100644 index 0000000000..529c0d1f65 --- /dev/null +++ b/libs/msm/doc/images/sm.gif diff --git a/libs/msm/doc/images/state.gif b/libs/msm/doc/images/state.gif Binary files differnew file mode 100644 index 0000000000..a60dd2823c --- /dev/null +++ b/libs/msm/doc/images/state.gif diff --git a/libs/msm/doc/images/terminate.gif b/libs/msm/doc/images/terminate.gif Binary files differnew file mode 100644 index 0000000000..54cea8c83c --- /dev/null +++ b/libs/msm/doc/images/terminate.gif diff --git a/libs/msm/doc/images/transition.jpg b/libs/msm/doc/images/transition.jpg Binary files differnew file mode 100644 index 0000000000..d410c84b2b --- /dev/null +++ b/libs/msm/doc/images/transition.jpg diff --git a/libs/msm/doc/src/msm.xml b/libs/msm/doc/src/msm.xml new file mode 100644 index 0000000000..65c9153a1c --- /dev/null +++ b/libs/msm/doc/src/msm.xml @@ -0,0 +1,8442 @@ +<?xml version="1.0" encoding="UTF-8"?> +<?oxygen RNGSchema="http://www.oasis-open.org/docbook/xml/5.0/rng/docbook.rng" type="xml"?> +<book xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0"> + <info> + <title>Meta State Machine (MSM)</title> + <author> + <personname>Christophe Henry</personname> + <email>christophe.j.henry@googlemail.com</email> + </author> + <copyright> + <year>2008-2010</year> + <holder> + <phrase> Distributed under the Boost Software License, Version 1.0. (See + accompanying file LICENSE_1_0.txt or copy at <link + xlink:href="http://www.boost.org/LICENSE_1_0.txt" + >http://www.boost.org/LICENSE_1_0.txt</link> ) </phrase> + </holder> + </copyright> + </info> + <preface> + <title>Preface</title> + <para>MSM is a library allowing you to easily and quickly define state machines of very high + performance. From this point, two main questions usually quickly arise, so please allow + me to try answering them upfront.</para> + <para> + <itemizedlist> + <listitem> + <para>When do I need a state machine?</para> + <para>More often that you think. Very often, one defined a state machine + informally without even noticing it. For example, one declares inside a + class some boolean attribute, say to remember that a task has been + completed. Later the boolean actually needs a third value, so it becomes an + int. A few weeks, a second attribute is needed. Then a third. Soon, you find + yourself writing:</para> + <para><code>void incoming_data(data)</code></para> + <para><code>{</code></para> + <para><code> if (data == packet_3 && flag1 == work_done && flag2 + > step3)...</code></para> + <para><code>}</code></para> + <para>This starts to look like event processing (contained inside data) if some + stage of the object life has been achieved (but is ugly).</para> + <para>This could be a protocol definition and it is a common use case for state + machines. Another common one is a user interface. The stage of the user's + interaction defines if some button is active, a functionality is available, + etc.</para> + <para>But there are many more use cases if you start looking. Actually, a whole + model-driven development method, Executable UML + (http://en.wikipedia.org/wiki/Executable_UML) specifies its complete dynamic + behavior using state machines. Class diagram, state machine diagrams, and an + action language are all you absolutely need in the Executable UML + world.</para> + </listitem> + <listitem> + <para>Another state machine library? What for?</para> + <para>True, there are many state machine libraries. This should already be an + indication that if you're not using any of them, you might be missing + something. Why should you use this one? Unfortunately, when looking for a + good state machine library, you usually pretty fast hit one or several of + the following snags:<itemizedlist> + <listitem> + <para>speed: "state machines are slow" is usually the first + criticism you might hear. While it is often an excuse not to use + any and instead resort to dirty, hand-written implementations (I + mean, no, yours are not dirty of course, I'm talking about other + developers). MSM removes this often feeble excuse because it is + blazingly fast. Most hand-written implementations will be beaten + by MSM.</para> + </listitem> + <listitem> + <para>ease of use: good argument. If you used another library, you + are probably right. Many state machine definitions will look + similar to:</para> + <para><code>state s1 = new State; // a state</code></para> + <para><code>state s2 = new State; // another state</code></para> + <para><code>event e = new Event; // event</code></para> + <para><code>s1->addTransition(e,s2); // transition s1 -> + s2</code></para> + <para>The more transitions you have, the less readable it is. A long + time ago, there was not so much Java yet, and many electronic + systems were built with a state machine defined by a simple + transition table. You could easily see the whole structure and + immediately see if you forgot some transitions. Thanks to our + new OO techniques, this ease of use was gone. MSM gives you back + the transition table and reduces the noise to the + minimum.</para> + </listitem> + <listitem> + <para>expressiveness: MSM offers several front-ends and constantly + tries to improve state machine definition techniques. For + example, you can define a transition with eUML (one of MSM's + front-ends) as:</para> + <para><code>state1 == state2 + event [condition] / + action</code></para> + <para>This is not simply syntactic sugar. Such a formalized, + readable structure allows easy communication with domain experts + of a software to be constructed. Having domain experts + understand your code will greatly reduce the number of + bugs.</para> + </listitem> + <listitem> + <para>model-driven-development: a common difficulty of a + model-driven development is the complexity of making a + round-trip (generating code from model and then model from + code). This is due to the fact that if a state machine structure + is hard for you to read, chances are that your parsing tool will + also have a hard time. MSM's syntax will hopefully help tool + writers.</para> + </listitem> + <listitem> + <para>features: most developers use only 20% of the richly defined + UML standard. Unfortunately, these are never the same 20% for + all. And so, very likely, one will need something from the + standard which is not implemented. MSM offers a very large part + of the standard, with more on the way.</para> + </listitem> + </itemizedlist></para> + <para>Let us not wait any longer, I hope you will enjoy MSM and have fun with + it!</para> + </listitem> + </itemizedlist> + </para> + </preface> + <part> + <title>User' guide</title> + <chapter> + <title>Founding idea</title> + <para>Let's start with an example taken from the C++ Template Metaprogramming + book:</para> + <programlisting>class player : public state_machine<player> +{ + // The list of FSM states enum states { Empty, Open, Stopped, Playing, Paused , initial_state = Empty }; + + // transition actions + void start_playback(play const&) { std::cout << "player::start_playback\n"; } + void open_drawer(open_close const&) { std::cout << "player::open_drawer\n"; } + // more transition actions + ... + typedef player p; // makes transition table cleaner + struct transition_table : mpl::vector11< + // Start Event Target Action + // +---------+------------+-----------+---------------------------+ + row< Stopped , play , Playing , &p::start_playback >, + row< Stopped , open_close , Open , &::open_drawer >, + // +---------+------------+-----------+---------------------------+ + row< Open , open_close , Empty , &p::close_drawer >, + // +---------+------------+-----------+---------------------------+ + row< Empty , open_close , Open , &p::open_drawer >, + row< Empty , cd_detected, Stopped , &p::store_cd_info >, + // +---------+------------+-----------+---------------------------+ + row< Playing , stop , Stopped , &p::stop_playback >, + row< Playing , pause , Paused , &p::pause_playback >, + row< Playing , open_close , Open , &p::stop_and_open >, + // +---------+------------+-----------+---------------------------+ + row< Paused , play , Playing , &p::resume_playback >, + row< Paused , stop , Stopped , &p::stop_playback >, + row< Paused , open_close , Open , &p::stop_and_open > + // +---------+------------+-----------+---------------------------+ + > {}; + // Replaces the default no-transition response. + template <class Event> + int no_transition(int state, Event const& e) + { + std::cout << "no transition from state " << state << " on event " << typeid(e).name() << std::endl; + return state; + } +}; </programlisting> + <para>This example is the foundation for the idea driving MSM: a descriptive and + expressive language based on a transition table with as little syntactic noise as + possible, all this while offering as many features from the UML 2.0 standard as + possible. MSM also offers several expressive state machine definition syntaxes with + different trade-offs.</para> + </chapter> + <chapter> + <title>UML Short Guide</title> + <sect1> + <title>What are state machines?</title> + <para>State machines are the description of a thing's lifeline. They describe the + different stages of the lifeline, the events influencing it, and what it does + when a particular event is detected at a particular stage. They offer the + complete specification of the dynamic behavior of the thing.</para> + </sect1> + <sect1> + <title>Concepts</title> + <para>Thinking in terms of state machines is a bit surprising at first, so let us + have a quick glance at the concepts.</para> + <sect2> + <title>State machine, state, transition, event </title> + <para>A state machine is a concrete model describing the behavior of a system. + It is composed of a finite number of states and transitions.</para> + <para> + <inlinemediaobject> + <imageobject> + <imagedata fileref="images/sm.gif"/> + </imageobject> + </inlinemediaobject></para> + <para>A simple state has no sub states. It can have data, entry and exit + behaviors and deferred events. One can provide entry and exit behaviors + (also called actions) to states (or state machines), which are executed + whenever a state is entered or left, no matter how. A state can also have + internal transitions which cause no entry or exit behavior to be called. A + state can mark events as deferred. This means the event cannot be processed + if this state is active, but it must be retained. Next time a state not + deferring this event is active, the event will be processed, as if it had + just been fired. </para> + <para><inlinemediaobject> + <imageobject> + <imagedata fileref="images/state.gif"/> + </imageobject> + </inlinemediaobject></para> + <para>A transition is the switching between active states, triggered by an + event. Actions and guard conditions can be attached to the transition. The + action executes when the transition fires, the guard is a Boolean operation + executed first and which can prevent the transition from firing by returning + false.</para> + <para> + <inlinemediaobject> + <imageobject> + <imagedata fileref="images/transition.jpg"/> + </imageobject> + </inlinemediaobject></para> + <para>An initial state marks the first active state of a state machine. It has + no real existence and neither has the transition originating from it.</para> + <para> + <inlinemediaobject> + <imageobject> + <imagedata fileref="images/init_state.gif"/> + </imageobject> + </inlinemediaobject></para> + </sect2> + <sect2> + <title>Submachines, orthogonal regions, pseudostates </title> + <para>A composite state is a state containing a region or decomposed in two or + more regions. A composite state contains its own set of states and regions. </para> + <para>A submachine is a state machine inserted as a state in another state + machine. The same submachine can be inserted more than once. </para> + <para>Orthogonal regions are parts of a composite state or submachine, each + having its own set of mutually exclusive set of states and transitions. </para> + <para><inlinemediaobject> + <imageobject> + <imagedata fileref="images/regions.gif" width="60%" scalefit="1"/> + </imageobject> + </inlinemediaobject></para> + <para>UML also defines a number of pseudo states, which are considered important + concepts to model, but not enough to make them first-class citizens. The + terminate pseudo state terminates the execution of a state machine (MSM + handles this slightly differently. The state machine is not destroyed but no + further event processing occurs.). </para> + <para><inlinemediaobject> + <imageobject> + <imagedata fileref="images/terminate.gif"/> + </imageobject> + </inlinemediaobject></para> + <para>An exit point pseudo state exits a composite state or a submachine and + forces termination of execution in all contained regions.</para> + <para><inlinemediaobject> + <imageobject> + <imagedata fileref="images/exit.gif" width="60%" scalefit="1"/> + </imageobject> + </inlinemediaobject></para> + <para>An entry point pseudo state allows a kind of controlled entry inside a + composite. Precisely, it connects a transition outside the composite to a + transition inside the composite. An important point is that this mechanism + only allows a single region to be entered. In the above diagram, in region1, + the initial state would become active. </para> + <para><inlinemediaobject> + <imageobject> + <imagedata fileref="images/entry_point.gif"/> + </imageobject> + </inlinemediaobject></para> + <para>There are also two more ways to enter a submachine (apart the obvious and + more common case of a transition terminating on the submachine as shown in + the region case). An explicit entry means that an inside state is the target + of a transition. Unlike with direct entry, no tentative encapsulation is + made, and only one transition is executed. An explicit exit is a transition + from an inner state to a state outside the submachine (not supported by + MSM). I would not recommend using explicit entry or exit. </para> + <para><inlinemediaobject> + <imageobject> + <imagedata fileref="images/explicit.gif"/> + </imageobject> + </inlinemediaobject></para> + <para>The last entry possibility is using fork. A fork is an explicit entry into + one or more regions. Other regions are again activated using their initial + state. </para> + <para><inlinemediaobject> + <imageobject> + <imagedata fileref="images/fork.gif" width="70%" scalefit="1"/> + </imageobject> + </inlinemediaobject></para> + </sect2> + <sect2> + <title> + <command xml:id="uml-history"/>History </title> + <para>UML defines two kinds of history, shallow history and deep history. + Shallow history is a pseudo state representing the most recent substate of a + submachine. A submachine can have at most one shallow history. A transition + with a history pseudo state as target is equivalent to a transition with the + most recent substate as target. And very importantly, only one transition + may originate from the history. Deep history is a shallow history + recursively reactivating the substates of the most recent substate. It is + represented like the shallow history with a star (H* inside a + circle).</para> + <para> + <inlinemediaobject> + <imageobject> + <imagedata fileref="images/history.gif" width="60%" scalefit="1"/> + </imageobject> + </inlinemediaobject></para> + <para>History is not a completely satisfying concept. First of all, there can be + just one history pseudo state and only one transition may originate from it. + So they do not mix well with orthogonal regions as only one region can be + “rememberedâ€. Deep history is even worse and looks like a last-minute + addition. History has to be activated by a transition and only one + transition originates from it, so how to model the transition originating + from the deep history pseudo state and pointing to the most recent substate + of the substate? As a bonus, it is also inflexible and does not accept new + types of histories. Let's face it, history sounds great and is useful in + theory, but the UML version is not quite making the cut. And therefore, MSM + provides a different version of this useful concept. </para> + </sect2> + <sect2> + <title><command xml:id="uml-anonymous"/>Completion transitions / anonymous + transitions</title> + <para>Completion events (or transitions), also called anonymous transitions, are + defined as transitions having no defined event triggering them. This means + that such transitions will immediately fire when a state being the source of + an anonymous transition becomes active, provided that a guard allows it. + They are useful in modeling algorithms as an activity diagram would normally + do. In the real-time world, they have the advantage of making it easier to + estimate how long a periodically executed action will last. For example, + consider the following diagram. </para> + <para><inlinemediaobject> + <imageobject> + <imagedata fileref="images/completion.gif"/> + </imageobject> + </inlinemediaobject></para> + <para>The designer now knows at any time that he will need a maximum of 4 + transitions. Being able to estimate how long a transition takes, he can + estimate how much of a time frame he will need to require (real-time tasks + are often executed at regular intervals). If he can also estimate the + duration of actions, he can even use graph algorithms to better estimate his + timing requirements. </para> + </sect2> + <sect2> + <title><command xml:id="UML-internal-transition"/> Internal transitions </title> + <para>Internal transitions are transitions executing in the scope of the active + state, being a simple state or a submachine. One can see them as a + self-transition of this state, without an entry or exit action + called.</para> + </sect2> + <sect2> + <title> + <command xml:id="transition-conflict"/>Conflicting transitions </title> + <para>If, for a given event, several transitions are enabled, they are said to + be in conflict. There are two kinds of conflicts: <itemizedlist> + <listitem> + <para>For a given source state, several transitions are defined, + triggered by the same event. Normally, the guard condition in + each transition defines which one is fired.</para> + </listitem> + <listitem> + <para>The source state is a submachine or simple state and the + conflict is between a transition internal to this state and a + transition triggered by the same event and having as target + another state.</para> + </listitem> + </itemizedlist>The first one is simple; one only needs to define two or more + rows in the transition table, with the same source and trigger, with a + different guard condition. Beware, however, that the UML standard wants + these conditions to be not overlapping. If they do, the standard says + nothing except that this is incorrect, so the implementer is free to + implement it the way he sees fit. In the case of MSM, the transition + appearing last in the transition table gets selected first, if it returns + false (meaning disabled), the library tries with the previous one, and so + on.</para> + <para> + <inlinemediaobject> + <imageobject> + <imagedata fileref="images/conflict1.gif"/> + </imageobject> + </inlinemediaobject></para> + <para>In the second case, UML defines that the most inner transition gets + selected first, which makes sense, otherwise no exit point pseudo state + would be possible (the inner transition brings us to the exit point, from + where the containing state machine can take over). </para> + <para><inlinemediaobject> + <imageobject> + <imagedata fileref="images/conflict2.gif" width="60%" scalefit="1"/> + </imageobject> + </inlinemediaobject></para> + <para>MSM handles both cases itself, so the designer needs only concentrate on + its state machine and the UML subtleties (not overlapping conditions), not + on implementing this behavior himself. </para> + </sect2> + </sect1> + <sect1> + <title>State machine glossary</title> + <para> + <itemizedlist> + <listitem> + <para>state machine: the life cycle of a thing. It is made of states, + regions, transitions and processes incoming events.</para> + </listitem> + <listitem> + <para>state: a stage in the life cycle of a state machine. A state (like + a submachine) can have an entry and exit behaviors.</para> + </listitem> + <listitem> + <para>event: an incident provoking (or not) a reaction of the state + machine</para> + </listitem> + <listitem> + <para>transition: a specification of how a state machine reacts to an + event. It specifies a source state, the event triggering the + transition, the target state (which will become the newly active + state if the transition is triggered), guard and actions.</para> + </listitem> + <listitem> + <para>action: an operation executed during the triggering of the + transition.</para> + </listitem> + <listitem> + <para>guard: a boolean operation being able to prevent the triggering of + a transition which would otherwise fire.</para> + </listitem> + <listitem> + <para>transition table: representation of a state machine. A state + machine diagram is a graphical, but incomplete representation of the + same model. A transition table, on the other hand, is a complete + representation.</para> + </listitem> + <listitem> + <para>initial state: The state in which the state machine starts. Having + several orthogonal regions means having as many initial + states.</para> + </listitem> + <listitem> + <para>submachine: A submachine is a state machine inserted as a state in + another state machine and can be found several times in a same state + machine.</para> + </listitem> + <listitem> + <para>orthogonal regions: (logical) parallel flow of execution of a + state machine. Every region of a state machine gets a chance to + process an incoming event.</para> + </listitem> + <listitem> + <para>terminate pseudo-state: when this state becomes active, it + terminates the execution of the whole state machine. MSM does not + destroy the state machine as required by the UML standard, however, + which lets you keep all the state machine's data.</para> + </listitem> + <listitem> + <para>entry/exit pseudo state: defined for submachines and are defined + as a connection between a transition outside of the submachine and a + transition inside the submachine. It is a way to enter or leave a + submachine through a predefined point.</para> + </listitem> + <listitem> + <para>fork: a fork allows explicit entry into several orthogonal regions + of a submachine.</para> + </listitem> + <listitem> + <para>history: a history is a way to remember the active state of a + submachine so that the submachine can proceed in its last active + state next time it becomes active.</para> + </listitem> + <listitem> + <para>completion events (also called completion/anonymous transitions): + when a transition has no named event triggering it, it automatically + fires when the source state is active, unless a guard forbids + it.</para> + </listitem> + <listitem> + <para>transition conflict: a conflict is present if for a given source + state and incoming event, several transitions are possible. UML + specifies that guard conditions have to solve the conflict.</para> + </listitem> + <listitem> + <para>internal transitions: transition from a state to itself without + having exit and entry actions being called.</para> + </listitem> + </itemizedlist> + </para> + </sect1> + </chapter> + <chapter> + <title>Tutorial</title> + <sect1> + <title>Design</title> + <para>MSM is divided between front–ends and back-ends. At the moment, there is just + one back-end. On the front-end side, you will find three of them which are as + many state machine description languages, with many more possible. For potential + language writers, this document contains a <link + xlink:href="#internals-front-back-interface">description of the interface + between front-end and back-end</link>.</para> + <para>The first front-end is an adaptation of the example provided in the <link + xlink:href="http://boostpro.com/mplbook">MPL book</link> with actions + defined as pointers to state or state machine methods. The second one is based + on functors. The third, eUML (embedded UML) is an experimental language based on + Boost.Proto and Boost.Typeof and hiding most of the metaprogramming to increase + readability. Both eUML and the functor front-end also offer a functional library + (a bit like Boost.Phoenix) for use as action language (UML defining + none).</para> + </sect1> + <sect1> + <title><command xml:id="basic-front-end"/>Basic front-end</title> + <para>This is the historical front-end, inherited from the MPL book. It provides a + transition table made of rows of different names and functionality. Actions and + guards are defined as methods and referenced through a pointer in the + transition. This front-end provides a simple interface making easy state + machines easy to define, but more complex state machines a bit harder.</para> + <sect2> + <title>A simple example</title> + <para>Let us have a look at a state machine diagram of the founding + example:</para> + <para><inlinemediaobject> + <imageobject> + <imagedata fileref="images/SimpleTutorial.jpg" width="60%" + scalefit="1"/> + </imageobject> + </inlinemediaobject></para> + <para>We are now going to build it with MSM's basic front-end. An <link + xlink:href="examples/SimpleTutorial.cpp">implementation</link> is also + provided.</para> + </sect2> + <sect2> + <title>Transition table</title> + <para>As previously stated, MSM is based on the transition table, so let us + define one:</para> + <programlisting> +struct transition_table : mpl::vector< +// Start Event Target Action Guard +// +---------+------------+-----------+---------------------------+----------------------------+ +a_row< Stopped , play , Playing , &player_::start_playback >, +a_row< Stopped , open_close , Open , &player_::open_drawer >, + _row< Stopped , stop , Stopped >, +// +---------+------------+-----------+---------------------------+----------------------------+ +a_row< Open , open_close , Empty , &player_::close_drawer >, +// +---------+------------+-----------+---------------------------+----------------------------+ +a_row< Empty , open_close , Open , &player_::open_drawer >, + row< Empty , cd_detected, Stopped , &player_::store_cd_info , &player_::good_disk_format >, + row< Empty , cd_detected, Playing , &player_::store_cd_info , &player_::auto_start >, +// +---------+------------+-----------+---------------------------+----------------------------+ +a_row< Playing , stop , Stopped , &player_::stop_playback >, +a_row< Playing , pause , Paused , &player_::pause_playback >, +a_row< Playing , open_close , Open , &player_::stop_and_open >, +// +---------+------------+-----------+---------------------------+----------------------------+ +a_row< Paused , end_pause , Playing , &player_::resume_playback >, +a_row< Paused , stop , Stopped , &player_::stop_playback >, +a_row< Paused , open_close , Open , &player_::stop_and_open > +// +---------+------------+-----------+---------------------------+----------------------------+ +> {}; + </programlisting> + <para>You will notice that this is almost exactly our founding example. The only + change in the transition table is the different types of transitions (rows). + The founding example forces one to define an action method and offers no + guards. You have 4 basic row types:<itemizedlist> + <listitem> + <para><code>row</code> takes 5 arguments: start state, event, target + state, action and guard.</para> + </listitem> + <listitem> + <para><code>a_row</code> (“a†for action) allows defining only the + action and omit the guard condition.</para> + </listitem> + <listitem> + <para><code>g_row</code> (“g†for guard) allows omitting the action + behavior and defining only the guard.</para> + </listitem> + <listitem> + <para><code>_row</code> allows omitting action and guard.</para> + </listitem> + </itemizedlist></para> + <para>The signature for an action methods is void method_name (event + const&), for example:</para> + <programlisting>void stop_playback(stop const&)</programlisting> + <para>Action methods return nothing and take the argument as const reference. Of + course nothing forbids you from using the same action for several + events:</para> + <programlisting>template <class Event> void stop_playback(Eventconst&)</programlisting> + <para>Guards have as only difference the return value, which is a + boolean:</para> + <programlisting>bool good_disk_format(cd_detected const& evt)</programlisting> + <para>The transition table is actually a MPL vector (or list), which brings the + limitation that the default maximum size of the table is 20. If you need + more transitions, overriding this default behavior is necessary, so you need + to add before any header:</para> + <programlisting>#define BOOST_MPL_CFG_NO_PREPROCESSED_HEADERS +#define BOOST_MPL_LIMIT_VECTOR_SIZE 30 //or whatever you need +#define BOOST_MPL_LIMIT_MAP_SIZE 30 //or whatever you need </programlisting> + <para>The other limitation is that the MPL types are defined only up to 50 + entries. For the moment, the only solution to achieve more is to add headers + to the MPL (luckily, this is not very complicated).</para> + </sect2> + <sect2> + <title>Defining states with entry/exit actions</title> + <para>While states were enums in the MPL book, they now are classes, which + allows them to hold data, provide entry, exit behaviors and be reusable (as + they do not know anything about the containing state machine). To define a + state, inherit from the desired state type. You will mainly use simple + states:</para> + <para>struct Empty : public msm::front::state<> {};</para> + <para>They can optionally provide entry and exit behaviors:</para> + <programlisting language="C++"> +struct Empty : public msm::front::state<> +{ + template <class Event, class Fsm> + void on_entry(Event const&, Fsm& ) + {std::cout <<"entering: Empty" << std::endl;} + template <class Event, class Fsm> + void on_exit(Event const&, Fsm& ) + {std::cout <<"leaving: Empty" << std::endl;} +}; + </programlisting> + <para>Notice how the entry and exit behaviors are templatized on the event and + state machine. Being generic facilitates reuse. There are more state types + (terminate, interrupt, pseudo states, etc.) corresponding to the UML + standard state types. These will be described in details in the next + sections.</para> + </sect2> + <sect2> + <title>What do you actually do inside actions / guards?</title> + <para>State machines define a structure and important parts of the complete + behavior, but not all. For example if you need to send a rocket to Alpha + Centauri, you can have a transition to a state "SendRocketToAlphaCentauri" + but no code actually sending the rocket. This is where you need actions. So + a simple action could be:</para> + <programlisting>template <class Fire> void send_rocket(Fire const&) +{ + fire_rocket(); +}</programlisting> + <para>Ok, this was simple. Now, we might want to give a direction. Let us suppose + this information is externally given when needed, it makes sense do use the + event for this:</para> + <programlisting>// Event +struct Fire {Direction direction;}; +template <class Fire> void send_rocket(Fire const& evt) +{ + fire_rocket(evt.direction); +}</programlisting> + <para>We might want to calculate the direction based not only on external data + but also on data accumulated during previous work. In this case, you might + want to have this data in the state machine itself. As transition actions + are members of the front-end, you can directly access the data:</para> + <programlisting>// Event +struct Fire {Direction direction;}; +//front-end definition, see down +struct launcher_ : public msm::front::state_machine_def<launcher_>{ +Data current_calculation; +template <class Fire> void send_rocket(Fire const& evt) +{ + fire_rocket(evt.direction, current_calculation); +} +... +};</programlisting> + <para>Entry and exit actions represent a behavior common to a state, no matter + through which transition it is entered or left. States being reusable, it + might make sense to locate your data there instead of in the state machine, + to maximize reuse and make code more readable. Entry and exit actions have + access to the state data (being state members) but also to the event and + state machine, like transition actions. This happens through the Event and + Fsm template parameters:</para> + <programlisting>struct Launching : public msm::front::state<> +{ + template <class Event, class Fsm> + void on_entry(Event const& evt, Fsm& fsm) + { + fire_rocket(evt.direction, fsm.current_calculation); + } +};</programlisting> + <para>Exit actions are also ideal for clanup when the state becomes + inactive.</para> + <para>Another possible use of the entry action is to pass data to substates / + submachines. Launching is a substate containing a <code>data</code> attribute:</para> + <programlisting>struct launcher_ : public msm::front::state_machine_def<launcher_>{ +Data current_calculation; +// state machines also have entry/exit actions +template <class Event, class Fsm> +void on_entry(Event const& evt, Fsm& fsm) +{ + launcher_::Launching& s = fsm.get_state<launcher_::Launching&>(); + s.data = fsm.current_calculation; +} +... +};</programlisting> + <para>The <command xlink:href="#backend-fsm-constructor-args">set_states</command> back-end method allows you to replace a complete + state.</para> + <para>The <command xlink:href="#functor-front-end-actions">functor</command> front-end and eUML offer more capabilities.</para> + <para>However, this basic front-end also has special capabilities using the row2 + / irow2 transitions.<command xlink:href="#basic-row2">_row2, a_row2, row2, + g_row2, a_irow2, irow2, g_irow2</command> let you call an action located + in any state of the current fsm or in the front-end itself, thus letting you + place useful data anywhere you see fit.</para> + <para>It is sometimes desirable to generate new events for the state machine + inside actions. Since the process_event method belongs to the back end, you + first need to gain a reference to it. The back end derives from the front + end, so one way of doing this is to use a cast:</para> + <programlisting>struct launcher_ : public msm::front::state_machine_def<launcher_>{ +template <class Fire> void send_rocket(Fire const& evt) +{ + fire_rocket(); + msm::back::state_machine<launcher_> &fsm = static_cast<msm::back::state_machine<launcher_> &>(*this); + fsm.process_event(rocket_launched()); +} +... +};</programlisting> + <para>The same can be implemented inside entry/exit actions. Admittedly, this is + a bit awkward. A more natural mechanism is available using the <command + xlink:href="#functor-front-end-actions">functor</command> + front-end.</para> + </sect2> + <sect2> + <title>Defining a simple state machine</title> + <para>Declaring a state machine is straightforward and is done with a high + signal / noise ratio. In our player example, we declare the state machine + as:</para> + <programlisting>struct player_ : public msm::front::state_machine_def<player_>{ + /* see below */}</programlisting> + <para>This declares a state machine using the basic front-end. We now declare + inside the state machine structure the initial state:</para> + <para> + <programlisting>typedef Empty initial_state;</programlisting> + </para> + <para>And that is about all of what is absolutely needed. In the example, the + states are declared inside the state machine for readability but this is not + a requirements, states can be declared wherever you like.</para> + <para>All what is left to do is to pick a back-end (which is quite simple as + there is only one at the moment):</para> + <para> + <programlisting>typedef msm::back::state_machine<player_> player;</programlisting> + </para> + <para>You now have a ready-to-use state machine with entry/exit actions, guards, + transition actions, a message queue so that processing an event can generate + another event. The state machine also adapted itself to your need and + removed almost all features we didn't use in this simple example. Note that + this is not per default the fastest possible state machine. See the section + "getting more speed" to know how to get the maximum speed. In a nutshell, + MSM cannot know about your usage of some features so you will have to + explicitly tell it.</para> + <para>State objects are built automatically with the state machine. They will + exist until state machine destruction. MSM is using Boost.Fusion behind the + hood. This unfortunately means that if you define more than 10 states, you + will need to extend the default:</para> + <para> + <programlisting>#define FUSION_MAX_VECTOR_SIZE 20 // or whatever you need + </programlisting> + </para> + <para>When an unexpected event is fired, the <code>no_transition(event, state + machine, state id)</code> method of the state machine is called . By + default, this method simply asserts when called. It is possible to overwrite + the <code>no_transition</code> method to define a different handling:</para> + <para> + <programlisting>template <class Fsm,class Event> +void no_transition(Event const& e, Fsm& ,int state){...}</programlisting> + </para> + <para><emphasis role="underline">Note</emphasis>: you might have noticed that + the tutorial calls <code>start()</code> on the state machine just after + creation. The start method will initiate the state machine, meaning it will + activate the initial state, which means in turn that the initial state's + entry behavior will be called. The reason why we need this will be explained + in the <link xlink:href="#backend-start">back-end part</link>. After a call + to start, the state machine is ready to process events. The same way, + calling <code>stop()</code> will cause the last exit actions to be called.</para> + </sect2> + <sect2> + <title>Defining a submachine</title> + <para>We now want to extend our last state machine by making the Playing state a + state machine itself (a submachine).</para> + <para><inlinemediaobject> + <imageobject> + <imagedata fileref="images/CompositeTutorial.jpg" width="60%" + scalefit="1"/> + </imageobject> + </inlinemediaobject></para> + <para>Again, an <link xlink:href="examples/CompositeTutorial.cpp">example</link> + is also provided.</para> + <para>A submachine really is a state machine itself, so we declare Playing as + such, choosing a front-end and a back-end:</para> + <para> + <programlisting>struct Playing_ : public msm::front::state_machine_def<Playing_>{...} +typedef msm::back::state_machine<Playing_> Playing;</programlisting> + </para> + <para>Like for any state machine, one also needs a transition table and an + initial state:</para> + <para> + <programlisting> +struct transition_table : mpl::vector< +// Start Event Target Action Guard +// +--------+---------+--------+---------------------------+------+ +a_row< Song1 , NextSong, Song2 , &Playing_::start_next_song >, +a_row< Song2 , NextSong, Song1 , &Playing_::start_prev_song >, +a_row< Song2 , NextSong, Song3 , &Playing_::start_next_song >, +a_row< Song3 , NextSong, Song2 , &Playing_::start_prev_song > +// +--------+---------+--------+---------------------------+------+ +> {}; + </programlisting> + </para> + <para> + <programlisting>typedef Song1 initial_state; </programlisting> + </para> + <para>This is about all you need to do. MSM will now automatically recognize + Playing as a submachine and all events handled by Playing (NextSong and + PreviousSong) will now be automatically forwarded to Playing whenever this + state is active. All other state machine features described later are also + available. You can even decide to use a state machine sometimes as + submachine or sometimes as an independent state machine.</para> + </sect2> + <sect2> + <title>Orthogonal regions, terminate state, event deferring</title> + <para>It is a very common problem in many state machines to have to handle + errors. It usually involves defining a transition from all the states to a + special error state. Translation: not fun. It is also not practical to find + from which state the error originated. The following diagram shows an + example of what clearly becomes not very readable:</para> + <para><inlinemediaobject> + <imageobject> + <imagedata fileref="images/error_no_regions.jpg" width="60%" + scalefit="1"/> + </imageobject> + </inlinemediaobject></para> + <para>This is neither very readable nor beautiful. And we do not even have any + action on the transitions yet to make it even less readable.</para> + <para>Luckily, UML provides a helpful concept, orthogonal regions. See them as + lightweight state machines running at the same time inside a common state + machine and having the capability to influence one another. The effect is + that you have several active states at any time. We can therefore keep our + state machine from the previous example and just define a new region made of + two states, AllOk and ErrorMode. AllOk is most of the time active. But the + error_found error event makes the second region move to the new active state + ErrorMode. This event does not interest the main region so it will simply be + ignored. "<code>no_transition</code>" will be called only if no region at + all handles the event. Also, as UML mandates, every region gets a chance of + handling the event, in the order as declared by the + <code>initial_state</code> type.</para> + <para>Adding an orthogonal region is easy, one only needs to declare more states + in the <code>initial_state</code> typedef. So, adding a new region with + AllOk as the region's initial state is:</para> + <para> + <programlisting>typedef mpl::vector<Empty,AllOk> initial_state;</programlisting> + </para> + <para><inlinemediaobject> + <imageobject> + <imagedata fileref="images/Orthogonal-deferred.jpg" width="60%" + scalefit="1"/> + </imageobject> + </inlinemediaobject></para> + <para>Furthermore, when you detect an error, you usually do not want events to + be further processed. To achieve this, we use another UML feature, terminate + states. When any region moves to a terminate state, the state machine + “terminates†(the state machine and all its states stay alive) and all + events are ignored. This is of course not mandatory, one can use orthogonal + regions without terminate states. MSM also provides a small extension to + UML, interrupt states. If you declare ErrorMode as interrupt state instead + of terminate state, the state machine will not handle any event other than + the one which ends the interrupt. So it's like a terminate state, with the + difference that you are allowed to resume the state machine when a condition + (like handling of the original error) is met. </para> + <para><command xml:id="basic-defer"/>Last but not least, this example also shows + here the handling of event deferring. Let's say someone puts a disc and + immediately presses play. The event cannot be handled, yet you'd want it to + be handled at a later point and not force the user to press play again. The + solution is to define it as deferred in the Empty and Open states and get it + handled in the first state where the event is not to be deferred. It can + then be handled or rejected. In this example, when Stopped becomes active, + the event will be handled because only Empty and Open defer the + event.</para> + <para>UML defines event deferring as a state property. To accommodate this, MSM + lets you specify this in states by providing a <code>deferred_events</code> + type:</para> + <programlisting>struct Empty : public msm::front::state<> +{ + // if the play event is fired while in this state, defer it until a state + // handles or rejects it + typedef mpl::vector<play> deferred_events; +... +}; </programlisting> + <para>Please have a look at the <link + xlink:href="examples/Orthogonal-deferred.cpp">complete + example</link>.</para> + <para>While this is wanted by UML and is simple, it is not always practical + because one could wish to defer only in certain conditions. One could also + want to make this be part of a transition action with the added bonus of a + guard for more sophisticated behaviors. It would also be conform to the MSM + philosophy to get as much as possible in the transition table, where you + have the whole state machine structure. This is also possible but not + practical with this front-end so we will need to pick a different row from + the functor front-end. For a complete description of the <code>Row</code> + type, please have a look at the <command xlink:href="#functor-front-end" + >functor front-end.</command></para> + <para>First, as there is no state where MSM can automatically find out the usage + of this feature, we need to require deferred events capability explicitly, + by adding a type in the state machine definition:</para> + <programlisting>struct player_ : public msm::front::state_machine_def<player_> +{ + typedef int activate_deferred_events; +... +}; </programlisting> + <para>We can now defer an event in any transition of the transition table by + using as action the predefined <code>msm::front::Defer</code> functor, for + example:</para> + <para> + <programlisting>Row < Empty , play , none , Defer , none ></programlisting> + </para> + <para>This is an internal transition row(see <command + xlink:href="#internal-transitions">internal transitions</command>) but + you can ignore this for the moment. It just means that we are not leaving + the Empty state. What matters is that we use Defer as action. This is + roughly equivalent to the previous syntax but has the advantage of giving + you all the information in the transition table with the added power of + transition behavior.</para> + <para>The second difference is that as we now have a transition defined, this + transition can play in the resolution of <command + xlink:href="#transition-conflict">transition conflicts</command>. For + example, we could model an "if (condition2) move to Playing else if + (condition1) defer play event":</para> + <para> + <programlisting>Row < Empty , play , none , Defer , condition1 >, +g_row < Empty , play , Playing , &player_::condition2 ></programlisting> + </para> + <para>Please have a look at <link xlink:href="examples/Orthogonal-deferred2.cpp" + >this possible implementation</link>.</para> + </sect2> + <sect2> + <title>History</title> + <para>UML defines two types of history, Shallow History and Deep History. In the + previous examples, if the player was playing the second song and the user + pressed pause, leaving Playing, at the next press on the play button, the + Playing state would become active and the first song would play again. Soon + would the first client complaints follow. They'd of course demand, that if + the player was paused, then it should remember which song was playing. But + it the player was stopped, then it should restart from the first song. How + can it be done? Of course, you could add a bit of programming logic and + generate extra events to make the second song start if coming from Pause. + Something like: </para> + <para> + <programlisting>if (Event == end_pause) +{ + for (int i=0;i< song number;++i) {player.process_event(NextSong()); } +} </programlisting> + </para> + <para>Not much to like in this example, isn't it? To solve this problem, you + define what is called a shallow or a deep history. A shallow history + reactivates the last active substate of a submachine when this submachine + becomes active again. The deep history does the same recursively, so if this + last active substate of the submachine was itself a submachine, its last + active substate would become active and this will continue recursively until + an active state is a normal state. For example, let us have a look at the + following UML diagram: </para> + <para><inlinemediaobject> + <imageobject> + <imagedata fileref="images/HistoryTutorial.jpg" width="60%" + scalefit="1"/> + </imageobject> + </inlinemediaobject></para> + <para>Notice that the main difference compared to previous diagrams is that the + initial state is gone and replaced by a History symbol (the H inside a + circle).</para> + <para>As explained in the <command xlink:href="#uml-history">small UML + tutorial</command>, History is a good concept with a not completely + satisfying specification. MSM kept the concept but not the specification and + goes another way by making this a policy and you can add your own history + types (the <link xlink:href="#history-interface">reference</link> explains + what needs to be done). Furthermore, History is a backend policy. This + allows you to reuse the same state machine definition with different history + policies in different contexts.</para> + <para>Concretely, your frontend stays unchanged:</para> + <para> + <programlisting>struct Playing_ : public msm::front::state_machine_def<Playing_></programlisting> + </para> + <para>You then add the policy to the backend as second parameter:</para> + <para> + <programlisting>typedef msm::back::state_machine<Playing_, + msm::back::ShallowHistory<mpl::vector<end_pause> > > Playing;</programlisting> + </para> + <para>This states that a shallow history must be activated if the Playing state + machine gets activated by the end_pause event and only this one (or any + other event added to the mpl::vector). If the state machine was in the + Stopped state and the event play was generated, the history would not be + activated and the normal initial state would become active. By default, + history is disabled. For your convenience the library provides in addition + to ShallowHistory a non-UML standard AlwaysHistory policy (likely to be your + main choice) which always activates history, whatever event triggers the + submachine activation. Deep history is not available as a policy (but could + be added). The reason is that it would conflict with policies which + submachines could define. Of course, if for example, Song1 were a state + machine itself, it could use the ShallowHistory policy itself thus creating + Deep History for itself. An <link xlink:href="examples/History.cpp" + >example</link> is also provided.</para> + </sect2> + <sect2> + <title>Completion (anonymous) transitions</title> + <para><command xml:id="anonymous-transitions"/>The following diagram shows an + example making use of this feature:</para> + <para><inlinemediaobject> + <imageobject> + <imagedata fileref="images/Anonymous.jpg" width="60%" scalefit="1"/> + </imageobject> + </inlinemediaobject></para> + <para>Anonymous transitions are transitions without a named event. This means + that the transition automatically fires when the predecessor state is + entered (to be exact, after the entry action). Otherwise it is a normal + transition with actions and guards. Why would you need something like that? + A possible case would be if a part of your state machine implements some + algorithm, where states are steps of the algorithm implementation. Then, + using several anonymous transitions with different guard conditions, you are + actually implementing some if/else statement. Another possible use would be + a real-time system called at regular intervals and always doing the same + thing, meaning implementing the same algorithm. The advantage is that once + you know how long a transition takes to execute on the system, by + calculating the longest path (the number of transitions from start to end), + you can pretty much know how long your algorithm will take in the worst + case, which in turns tells you how much of a time frame you are to request + from a scheduler. </para> + <para>If you are using Executable UML (a good book describing it is "Executable + UML, a foundation for Model-Driven Architecture"), you will notice that it + is common for a state machine to generate an event to itself only to force + leaving a state. Anonymous transitions free you from this constraint.</para> + <para>If you do not use this feature in a concrete state machine, MSM will + deactivate it and you will not pay for it. If you use it, there is however a + small performance penalty as MSM will try to fire a compound event (the + other UML name for anonymous transitions) after every taken transition. This + will therefore double the event processing cost, which is not as bad as it + sounds as MSM’s execution speed is very high anyway.</para> + <para>To define such a transition, use “none†as event in the transition table, + for example:</para> + <para> + <programlisting>row < State3 , none , State4 , &p::State3ToState4 , &p::always_true ></programlisting> + </para> + <para><link xlink:href="examples/AnonymousTutorial.cpp">An implementation</link> + of the state machine diagram is also provided.</para> + </sect2> + <sect2> + <title><command xml:id="internal-transitions"/>Internal transitions</title> + <para>Internal transitions are transitions executing in the scope of the active + state, a simple state or a submachine. One can see them as a self-transition + of this state, without an entry or exit action called. This is useful when + all you want is to execute some code for a given event in a given + state.</para> + <para>Internal transitions are specified as having a higher priority than normal + transitions. While it makes sense for a submachine with exit points, it is + surprising for a simple state. MSM lets you define the transition priority + by setting the transition’s position inside the transition table (see + <command xlink:href="#run-to-completion">internals</command> ). The + difference between "normal" and internal transitions is that internal + transitions have no target state, therefore we need new row types. We had + a_row, g_row, _row and row, we now add a_irow, g_irow, _irow and irow which + are like normal transitions but define no target state. For, example an + internal transition with a guard condition could be:</para> + <para> + <programlisting>g_irow < Empty /*state*/,cd_detected/*event*/,&p::internal_guard/* guard */></programlisting> + </para> + <para>These new row types can be placed anywhere in the transition table so that + you can still have your state machine structure grouped together. The only + difference of behavior with the UML standard is the missing notion of higher + priority for internal transitions. Please have a look at <link + xlink:href="examples/SimpleTutorialInternal.cpp">the + example</link>.</para> + <para>It is also possible to do it the UML-conform way by declaring a transition + table called <code>internal transition_table</code> inside the state itself + and using internal row types. For example:</para> + <programlisting>struct Empty : public msm::front::state<> +{ + struct internal_transition_table : mpl::vector< + a_internal < cd_detected , Empty, &Empty::internal_action > + > {}; +};</programlisting> + <para>This declares an internal transition table called + internal_transition_table and reacting on the event cd_detected by calling + internal_action on Empty. Let us note a few points:<itemizedlist> + <listitem> + <para>internal tables are NOT called transition_table but + internal_transition_table</para> + </listitem> + <listitem> + <para>they use different but similar row types: a_internal, + g_internal, _internal and internal.</para> + </listitem> + <listitem> + <para>These types take as first template argument the triggering + event and then the action and guard method. Note that the only + real difference to classical rows is the extra argument before + the function pointer. This is the type on which the function + will be called.</para> + </listitem> + <listitem> + <para>This also allows you, if you wish, to use actions and guards + from another state of the state machine or in the state machine + itself.</para> + </listitem> + <listitem> + <para>submachines can have an internal transition table and a + classical transition table.</para> + </listitem> + </itemizedlist></para> + <para>The <link xlink:href="examples/TestInternal.cpp">following example</link> + makes use of an a_internal. It also uses functor-based internal transitions + which will be explained in <command + xlink:href="#functor-internal-transitions">the functor + front-end</command>, please ignore them for the moment. Also note that + the state-defined internal transitions, having the highest priority (as + mandated by the UML standard), are tried before those defined inside the + state machine transition table.</para> + <para>Which method should you use? It depends on what you need:<itemizedlist> + <listitem> + <para>the first version (using irow) is simpler and likely to + compile faster. It also lets you choose the priority of your + internal transition.</para> + </listitem> + <listitem> + <para>the second version is more logical from a UML perspective and + lets you make states more useful and reusable. It also allows + you to call actions and guards on any state of the state + machine.</para> + </listitem> + </itemizedlist> + <command xml:id="internal-transitions-note"/><emphasis role="underline" + ><emphasis role="bold">Note</emphasis></emphasis>: There is an added + possibility coming from this feature. The + <code>internal_transition_table</code> transitions being added directly + inside the main state machine's transition table, it is possible, if it is + more to your state, to distribute your state machine definition a bit like + Boost.Statechart, leaving to the state machine itself the only task of + declaring the states it wants to use using the + <code>explicit_creation</code> type definition. While this is not the + author's favorite way, it is still possible. A simplified example using only + two states will show this possibility:<itemizedlist> + <listitem> + <para><link + xlink:href="examples/distributed_table/DistributedTable.cpp" + >state machine definition</link></para> + </listitem> + <listitem> + <para>Empty <link xlink:href="examples/distributed_table/Empty.hpp" + >header</link> and <link + xlink:href="examples/distributed_table/Empty.cpp" + >cpp</link></para> + </listitem> + <listitem> + <para>Open <link xlink:href="examples/distributed_table/Open.hpp" + >header</link> and <link + xlink:href="examples/distributed_table/Open.cpp" + >cpp</link></para> + </listitem> + <listitem> + <para><link xlink:href="examples/distributed_table/Events.hpp" + >events definition</link></para> + </listitem> + </itemizedlist></para> + <para>There is an added bonus offered for submachines, which can have both the + standard transition_table and an internal_transition_table (which has a + higher priority). This makes it easier if you decide to make a full + submachine from a state. It is also slightly faster than the standard + alternative, adding orthogonal regions, because event dispatching will, if + accepted by the internal table, not continue to the subregions. This gives + you a O(1) dispatch instead of O(number of regions). While the example is + with eUML, the same is also possible with any front-end.</para> + </sect2> + <sect2> + <title><command xml:id="basic-row2"/>more row types</title> + <para>It is also possible to write transitions using actions and guards not just + from the state machine but also from its contained states. In this case, one + must specify not just a method pointer but also the object on which to call + it. This transition row is called, not very originally, <code>row2</code>. + They come, like normal transitions in four flavors: <code>a_row2, g_row2, + _row2 and row2</code>. For example, a transition calling an action from + the state Empty could be:</para> + <para> + <programlisting>a_row2<Stopped,open_close,Open,Empty + /*action source*/,&Empty::open_drawer/*action*/></programlisting> + </para> + <para>The same capabilities are also available for internal transitions so that + we have: <code>a_irow2, g_irow2, _irow2 and row2</code>. For transitions + defined as part of the <code>internal_transition_table</code>, you can use + the <command xlink:href="#internal-transitions">a_internal, g_internal, + _internal, internal</command> row types from the previous + sections.</para> + <para>These row types allow us to distribute the state machine code among + states, making them reusable and more useful. Using transition tables inside + states also contributes to this possibility. An <link + xlink:href="examples/SimpleTutorial2.cpp">example</link> of these new + rows is also provided.</para> + </sect2> + <sect2> + <title>Explicit entry / entry and exit pseudo-state / fork</title> + <para>MSM (almost) fully supports these features, described in the <command + xlink:href="#uml-history">small UML tutorial</command>. Almost because + there are currently two limitations: <itemizedlist> + <listitem> + <para>it is only possible to explicitly enter a sub- state of the + target but not a sub-sub state.</para> + </listitem> + <listitem> + <para>it is not possible to explicitly exit. Exit points must be + used.</para> + </listitem> + </itemizedlist></para> + <para>Let us see a concrete example:</para> + <para><inlinemediaobject> + <imageobject> + <imagedata fileref="images/entrytutorial.jpg" width="60%" + scalefit="1"/> + </imageobject> + </inlinemediaobject></para> + <para>We find in this diagram:<itemizedlist> + <listitem> + <para>A “normal†activation of SubFsm2, triggered by event1. In each + region, the initial state is activated, i.e. SubState1 and + SubState1b.</para> + </listitem> + <listitem> + <para>An explicit entry into SubFsm2::SubState2 for region “1†with + event2 as trigger, meaning that in region “2†the initial state, + SubState1b, activated.</para> + </listitem> + <listitem> + <para>A fork into regions “1†and “2†to the explicit entries + SubState2 and SubState2b, triggered by event3. Both states + become active so no region is default activated (if we had a + third one, it would be).</para> + </listitem> + <listitem> + <para>A connection of two transitions through an entry pseudo state, + SubFsm2::PseudoEntry1, triggered by event4 and triggering also + the second transition on the same event (both transitions must + be triggered by the same event). Region “2†is default-activated + and SubState1b becomes active.</para> + </listitem> + <listitem> + <para>An exit from SubFsm2 using an exit pseudo-state, PseudoExit1, + triggered by event5 and connecting two transitions using the + same event. Again, the event is forwarded to the second + transition and both regions are exited, as SubFsm2 becomes + inactive. Note that if no transition is defined from + PseudoExit1, an error (as defined in the UML standard) will be + detected and no_transition called.</para> + </listitem> + </itemizedlist></para> + <para>The example is also <link xlink:href="examples/DirectEntryTutorial.cpp" + >fully implemented</link>.</para> + <para>This sounds complicated but the syntax is simple.</para> + <sect3> + <title>Explicit entry</title> + <para>First, to define that a state is an explicit entry, you have to make + it a state and mark it as explicit, giving as template parameters the + region id (the region id starts with 0 and corresponds to the first + initial state of the initial_state type sequence).</para> + <para> + <programlisting>struct SubFsm2_ : public msm::front::state_machine_def<SubFsm2_> +{ + struct SubState2 : public msm::front::state<> , + public msm::front::explicit_entry<0> + {...}; +... +};</programlisting> + </para> + <para>And define the submachine as:</para> + <para> + <programlisting>typedef msm::back::state_machine<SubFsm2_> SubFsm2;</programlisting> + </para> + <para>You can then use it as target in a transition with State1 as + source:</para> + <para> + <programlisting>_row < State1, Event2, SubFsm2::direct< SubFsm2_::SubState2> > //SubFsm2_::SubState2: complete name of SubState2 (defined within SubFsm2_)</programlisting> + </para> + <para>The syntax deserves some explanation. SubFsm2_ is a front end. + SubState2 is a nested state, therefore the SubFsm2_::SubState2 syntax. + The containing machine (containing State1 and SubFsm2) refers to the + backend instance (SubFsm2). SubFsm2::direct states that an explicit + entry is desired.</para> + <para><command xml:id="explicit-entry-no-region-id"/>Thanks to the <command xlink:href="#backend-compile-time-analysis" + >mpl_graph</command> library you can also omit to provide the region + index and let MSM find out for you. The are however two points to note:<itemizedlist> + <listitem> + <para>MSM can only find out the region index if the explicit + entry state is somehow connected to an initial state through + a transition, no matter the direction.</para> + </listitem> + <listitem> + <para>There is a compile-time cost for this feature.</para> + </listitem> + </itemizedlist></para> + <para><emphasis role="underline">Note (also valid for forks)</emphasis>: in + order to make compile time more bearable for the more standard cases, + and unlike initial states, explicit entry states which are also not + found in the transition table of the entered submachine (a rare case) do + NOT get automatically created. To explicitly create such states, you + need to add in the state machine containing the explicit states a simple + typedef giving a sequence of states to be explicitly created + like:</para> + <para> + <programlisting>typedef mpl::vector<SubState2,SubState2b> explicit_creation;</programlisting> + </para> + <para><emphasis role="underline">Note (also valid for forks)</emphasis>: At + the moment, it is not possible to use a submachine as the target of an + explicit entry. Please use entry pseudo states for an almost identical + effect.</para> + </sect3> + <sect3> + <title>Fork</title> + <para>Need a fork instead of an explicit entry? As a fork is an explicit + entry into states of different regions, we do not change the state + definition compared to the explicit entry and specify as target a list + of explicit entry states:</para> + <para> + <programlisting>_row < State1, Event3, + mpl::vector<SubFsm2::direct<SubFsm2_::SubState2>, + SubFsm2::direct <SubFsm2_::SubState2b> + ></programlisting> + </para> + <para>With SubState2 defined as before and SubState2b defined as being in + the second region (Caution: MSM does not check that the region is + correct):</para> + <para> + <programlisting>struct SubState2b : public msm::front::state<> , + public msm::front::explicit_entry<1></programlisting> + </para> + </sect3> + <sect3> + <title>Entry pseudo states</title> + <para> To define an entry pseudo state, you need derive from the + corresponding class and give the region id:</para> + <para> + <programlisting>struct PseudoEntry1 : public msm::front::entry_pseudo_state<0></programlisting> + </para> + <para>And add the corresponding transition in the top-level state machine's + transition table:</para> + <para> + <programlisting>_row < State1, Event4, SubFsm2::entry_pt<SubFsm2_::PseudoEntry1> ></programlisting> + </para> + <para>And another in the SubFsm2_ submachine definition (remember that UML + defines an entry point as a connection between two transitions), for + example this time with an action method:</para> + <para> + <programlisting>_row < PseudoEntry1, Event4, SubState3,&SubFsm2_::entry_action ></programlisting> + </para> + </sect3> + <sect3> + <title> Exit pseudo states </title> + <para>And finally, exit pseudo states are to be used almost the same way, + but defined differently: it takes as template argument the event to be + forwarded (no region id is necessary):</para> + <para> + <programlisting>struct PseudoExit1 : public exit_pseudo_state<event6></programlisting> + </para> + <para>And you need, like for entry pseudo states, two transitions, one in + the submachine:</para> + <para> + <programlisting>_row < SubState3, Event5, PseudoExit1 ></programlisting> + </para> + <para>And one in the containing state machine:</para> + <para> + <programlisting>_row < SubFsm2::exit_pt<SubFsm2_::PseudoExit1>, Event6,State2 ></programlisting> + </para> + <para><emphasis role="underline">Important note 1:</emphasis> UML defines + transiting to an entry pseudo state and having either no second + transition or one with a guard as an error but defines no error + handling. MSM will tolerate this behavior; the entry pseudo state will + simply be the newly active state.</para> + <para><emphasis role="underline">Important note 2</emphasis>: UML defines + transiting to an exit pseudo state and having no second transition as an + error, and also defines no error handling. Therefore, it was decided to + implement exit pseudo state as terminate states and the containing + composite not properly exited will stay terminated as it was technically + “exitedâ€.</para> + <para><emphasis role="underline">Important note 3:</emphasis> UML states + that for the exit point, the same event must be used in both + transitions. MSM relaxes this rule and only wants the event on the + inside transition to be convertible to the one of the outside + transition. In our case, event6 is convertible from event5. Notice that + the forwarded event must be named in the exit point definition. For + example, we could define event6 as simply as:</para> + <para> + <programlisting>struct event +{ + event(){} + template <class Event> + event(Event const&){} +}; //convertible from any event</programlisting> + <emphasis role="underline">Note</emphasis>: There is a current + limitation if you need not only convert but also get some data from the + original event. Consider:</para> + <programlisting>struct event1 +{ + event1(int val_):val(val_) {} + int val; +}; // forwarded from exit point +struct event2 +{ + template <class Event> + event2(Event const& e):val(e.val){} // compiler will complain about another event not having any val + int val; +}; // what the higher-level fsm wants to get</programlisting> + <para>The solution is to provide two constructors:</para> + <programlisting>struct event2 +{ + template <class Event> + event2(Event const& ):val(0){} // will not be used + event2(event1 const& e)):val(e.val){} // the conversion constructor + int val; +}; // what the higher-level fsm wants to get</programlisting> + </sect3> + </sect2> + <sect2> + <title>Flags</title> + <para>This <link xlink:href="examples/Flags.cpp">tutorial</link> is devoted to a + concept not defined in UML: flags. It has been added into MSM after proving + itself useful on many occasions. Please, do not be frightened as we are not + talking about ugly shortcuts made of an improbable collusion of + Booleans.</para> + <para>If you look into the Boost.Statechart documentation you'll find this + code:</para> + <programlisting>if ( ( state_downcast< const NumLockOff * >() != 0 ) && + ( state_downcast< const CapsLockOff * >() != 0 ) && + ( state_downcast< const ScrollLockOff * >() != 0 ) ) + </programlisting> + <para>While correct and found in many UML books, this can be error-prone and a + potential time-bomb when your state machine grows and you add new states or + orthogonal regions.</para> + <para>And most of all, it hides the real question, which would be “does my state + machine's current state define a special propertyâ€? In this special case + “are my keys in a lock stateâ€? So let's apply the Fundamental Theorem of + Software Engineering and move one level of abstraction higher.</para> + <para>In our player example, let's say we need to know if the player has a + loaded CD. We could do the same:</para> + <programlisting>if ( ( state_downcast< const Stopped * >() != 0 ) && + ( state_downcast< const Open * >() != 0 ) && + ( state_downcast< const Paused * >() != 0 ) && + ( state_downcast< const Playing * >() != 0 )) </programlisting> + <para>Or flag these 4 states as CDLoaded-able. You add a flag_list type into + each flagged state:</para> + <para> + <programlisting>typedef mpl::vector1<CDLoaded> flag_list;</programlisting> + </para> + <para>You can even define a list of flags, for example in Playing:</para> + <para> + <programlisting>typedef mpl::vector2<PlayingPaused,CDLoaded> flag_list;</programlisting> + </para> + <para>This means that Playing supports both properties. To check if your player + has a loaded CD, check if your flag is active in the current state:</para> + <para> + <programlisting>player p; if (p.is_flag_active<CDLoaded>()) ... </programlisting> + </para> + <para>And what if you have orthogonal regions? How to decide if a state machine + is in a flagged state? By default, you keep the same code and the current + states will be OR'ed, meaning if one of the active states has the flag, then + is_flag_active returns true. Of course, in some cases, you might want that + all of the active states are flagged for the state to be active. You can + also AND the active states:</para> + <para> + <programlisting>if (p.is_flag_active<CDLoaded,player::Flag_AND>()) ...</programlisting> + </para> + <para>The following diagram displays the flag situation in the tutorial.</para> + <para><inlinemediaobject> + <imageobject> + <imagedata fileref="images/FlagsTutorial.jpg" width="60%" + scalefit="1"/> + </imageobject> + </inlinemediaobject></para> + </sect2> + <sect2> + <title><command xml:id="event-hierarchy"/>Event Hierarchy</title> + <para>There are cases where one needs transitions based on categories of events. + An example is text parsing. Let's say you want to parse a string and use a + state machine to manage your parsing state. You want to parse 4 digits and + decide to use a state for every matched digit. Your state machine could look + like:</para> + <para><inlinemediaobject> + <imageobject> + <imagedata fileref="images/ParsingDigits.jpg" width="30%" + scalefit="1"/> + </imageobject> + </inlinemediaobject></para> + <para>But how to detect the digit event? We would like to avoid defining 10 + transitions on char_0, char_1... between two states as it would force us to + write 4 x 10 transitions and the compile-time would suffer. To solve this + problem, MSM supports the triggering of a transition on a subclass event. + For example, if we define digits as: </para> + <programlisting>struct digit {}; +struct char_0 : public digit {}; </programlisting> + <para>And to the same for other digits, we can now fire char_0, char_1 events + and this will cause a transition with "digit" as trigger to be taken.</para> + <para>An <link xlink:href="examples/ParsingDigits.cpp">example</link> with + performance measurement, taken from the documentation of Boost.Xpressive + illustrates this example. You might notice that the performance is actually + very good (in this case even better).</para> + </sect2> + <sect2> + <title>Customizing a state machine / Getting more speed</title> + <para>MSM is offering many UML features at a high-speed, but sometimes, you just + need more speed and are ready to give up some features in exchange. A + process_event is handling several tasks: <itemizedlist> + <listitem> + <para>checking for terminate/interrupt states</para> + </listitem> + <listitem> + <para>handling the message queue (for entry/exit/transition actions + generating themselves events)</para> + </listitem> + <listitem> + <para>handling deferred events</para> + </listitem> + <listitem> + <para>catching exceptions (or not)</para> + </listitem> + <listitem> + <para>handling the state switching and action calls</para> + </listitem> + </itemizedlist>Of these tasks, only the last one is absolutely necessary to + a state machine (its core job), the other ones are nice-to-haves which cost + CPU time. In many cases, it is not so important, but in embedded systems, + this can lead to ad-hoc state machine implementations. MSM detects by itself + if a concrete state machine makes use of terminate/interrupt states and + deferred events and deactivates them if not used. For the other two, if you + do not need them, you need to help by indicating it in your implementation. + This is done with two simple typedefs:<itemizedlist> + <listitem> + <para><code>no_exception_thrown</code> indicates that behaviors will + never throw and MSM does not need to catch anything</para> + </listitem> + <listitem> + <para><code>no_message_queue</code> indicates that no action will + itself generate a new event and MSM can save us the message + queue.</para> + </listitem> + </itemizedlist>The third configuration possibility, explained <link + xlink:href="#basic-defer">here</link>, is to manually activate deferred + events, using <code>activate_deferred_events</code>. For example, the + following state machine sets all three configuration types:</para> + <programlisting>struct player_ : public msm::front::state_machine_def<player_> +{ + // no need for exception handling or message queue + typedef int no_exception_thrown; + typedef int no_message_queue; + // also manually enable deferred events + typedef int activate_deferred_events + ...// rest of implementation + };</programlisting> + <para><emphasis role="underline">Important note</emphasis>: As exit pseudo + states are using the message queue to forward events out of a submachine, + the <code>no_message_queue</code> option cannot be used with state machines + containing an exit pseudo state.</para> + </sect2> + <sect2> + <title>Choosing the initial event</title> + <para>A state machine is started using the <code>start</code> method. This + causes the initial state's entry behavior to be executed. Like every entry + behavior, it becomes as parameter the event causing the state to be entered. + But when the machine starts, there was no event triggered. In this case, MSM + sends <code>msm::back::state_machine<...>::InitEvent</code>, which might + not be the default you'd want. For this special case, MSM provides a + configuration mechanism in the form of a typedef. If the state machine's + front-end definition provides an initial_event typedef set to another event, + this event will be used. For example:</para> + <programlisting>struct my_initial_event{}; +struct player_ : public msm::front::state_machine_def<player_>{ +... +typedef my_initial_event initial_event; +};</programlisting> + </sect2> + <sect2> + <title> Containing state machine (deprecated)</title> + <para>This feature is still supported in MSM for backward compatibility but made + obsolete by the fact that every guard/action/entry action/exit action get + the state machine passed as argument and might be removed at a later + time.</para> + <para>All of the states defined in the state machine are created upon state + machine construction. This has the huge advantage of a reduced syntactic + noise. The cost is a small loss of control for the user on the state + creation and access. But sometimes you needed a way for a state to get + access to its containing state machine. Basically, a state needs to change + its declaration to:</para> + <programlisting>struct Stopped : public msm::front::state<sm_ptr></programlisting> + <para>And to provide a set_sm_ptr function: <code>void set_sm_ptr(player* + pl)</code></para> + <para>to get a pointer to the containing state machine. The same applies to + terminate_state / interrupt_state and entry_pseudo_state / + exit_pseudo_state. </para> + </sect2> + </sect1> + <sect1> + <title><command xml:id="functor-front-end"/>Functor front-end</title> + <para>The functor front-end is the preferred front-end at the moment. It is more + powerful than the standard front-end and has a more readable transition table. + It also makes it easier to reuse parts of state machines. Like <command + xlink:href="#eUML-front-end">eUML</command>, it also comes with a good deal + of predefined actions. Actually, eUML generates a functor front-end through + Boost.Typeof and Boost.Proto so both offer the same functionality.</para> + <para>The rows which MSM offered in the previous front-end come in different + flavors. We saw the a_row, g_row, _row, row, not counting internal rows. This is + already much to know, so why define new rows? These types have some + disadvantages: <itemizedlist> + <listitem> + <para>They are more typing and information than we would wish. This + means syntactic noise and more to learn.</para> + </listitem> + <listitem> + <para>Function pointers are weird in C++.</para> + </listitem> + <listitem> + <para>The action/guard signature is limited and does not allow for more + variations of parameters (source state, target state, current state + machine, etc.)</para> + </listitem> + <listitem> + <para>It is not easy to reuse action code from a state machine to + another.</para> + </listitem> + </itemizedlist></para> + <sect2> + <title> Transition table </title> + <para>We can change the definition of the simple tutorial's transition table + to:</para> + <programlisting> +struct transition_table : mpl::vector< +// Start Event Target Action Guard +// +---------+------------+-----------+---------------------------+----------------------------+ +Row < Stopped , play , Playing , start_playback , none >, +Row < Stopped , open_close , Open , open_drawer , none >, +Row < Stopped , stop , Stopped , none , none >, +// +---------+------------+-----------+---------------------------+----------------------------+ +Row < Open , open_close , Empty , close_drawer , none >, +// +---------+------------+-----------+---------------------------+----------------------------+ +Row < Empty , open_close , Open , open_drawer , none >, +Row < Empty , cd_detected, Stopped , store_cd_info , good_disk_format >, +g_row< Empty , cd_detected, Playing , &player_::store_cd_info , &player_::auto_start >, +// +---------+------------+-----------+---------------------------+----------------------------+ +Row < Playing , stop , Stopped , stop_playback , none >, +Row < Playing , pause , Paused , pause_playback , none >, +Row < Playing , open_close , Open , stop_and_open , none >, +// +---------+------------+-----------+---------------------------+----------------------------+ +Row < Paused , end_pause , Playing , resume_playback , none >, +Row < Paused , stop , Stopped , stop_playback , none >, +Row < Paused , open_close , Open , stop_and_open , none > +// +---------+------------+-----------+---------------------------+----------------------------+ +> {}; + </programlisting> + <para>Transitions are now of type "Row" with exactly 5 template arguments: + source state, event, target state, action and guard. Wherever there is + nothing (for example actions and guards), write "none". Actions and guards + are no more methods but functors getting as arguments the detected event, + the state machine, source and target state:</para> + <programlisting>struct store_cd_info +{ + template <class Fsm,class Evt,class SourceState,class TargetState> + void operator()(Evt const&, Fsm& fsm, SourceState&,TargetState& ) + { + cout << "player::store_cd_info" << endl; + fsm.process_event(play()); + } +}; </programlisting> + <para>The advantage of functors compared to functions are that functors are + generic and reusable. They also allow passing more parameters than just + events. The guard functors are the same but have an operator() returning a + bool.</para> + <para>It is also possible to mix rows from different front-ends. To show this, a + g_row has been left in the transition table. <emphasis role="underline" + >Note:</emphasis> in case the action functor is used in the transition + table of a state machine contained inside a top-level state machine, the + “fsm†parameter refers to the lowest-level state machine (referencing this + action), not the top-level one.</para> + <para>To illustrate the reusable point, MSM comes with a whole set of predefined + functors. Please refer to eUML for the <link xlink:href="#Reference-begin" + >full list</link>. For example, we are now going to replace the first + action by an action sequence and the guard by a more complex functor.</para> + <para>We decide we now want to execute two actions in the first transition + (Stopped -> Playing). We only need to change the action start_playback to + <programlisting>ActionSequence_< mpl::vector<some_action, start_playback> ></programlisting>and + now will execute some_action and start_playback every time the transition is + taken. ActionSequence_ is a functor calling each action of the mpl::vector + in sequence.</para> + <para>We also want to replace good_disk_format by a condition of the type: + “good_disk_format && (some_condition || some_other_condition)â€. We + can achieve this using And_ and Or_ functors: + <programlisting>And_<good_disk_format,Or_< some_condition , some_other_condition> ></programlisting>It + even starts looking like functional programming. MSM ships with functors for + operators, state machine usage, STL algorithms or container methods.</para> + </sect2> + <sect2> + <title>Defining states with entry/exit actions</title> + <para>You probably noticed that we just showed a different transition table and + that we even mixed rows from different front-ends. This means that you can + do this and leave the definitions for states unchanged. Most examples are + doing this as it is the simplest solution. You still enjoy the simplicity of + the first front-end with the extended power of the new transition types. + This <link xlink:href="examples/SimpleWithFunctors.cpp">tutorial</link>, + adapted from the earlier example does just this.</para> + <para>Of course, it is also possible to define states where entry and exit + actions are also provided as functors as these are generated by eUML and + both front-ends are equivalent. For example, we can define a state + as:</para> + <programlisting>struct Empty_Entry +{ + template <class Event,class Fsm,class State> + void operator()(Event const&,Fsm&,State&) + { + ... + } +}; // same for Empty_Exit +struct Empty : public msm::front::euml::func_state<Empty_Entry,Empty_Exit>{};</programlisting> + <para>This also means that you can, like in the transition table, write entry / + exit actions made of more complicated action combinations. The previous + example can therefore <link xlink:href="examples/SimpleWithFunctors2.cpp">be + rewritten</link>.</para> + <para>Usually, however, one will probably use the standard state definition as + it provides the same capabilities as this front-end state definition, unless + one needs some of the shipped predefined functors or is a fan of functional + programming.</para> + </sect2> + <sect2> + <title><command xml:id="functor-front-end-actions"/>What do you actually do inside actions / guards (Part 2)?</title> + <para>Using the basic front-end, we saw how to pass data to actions through the + event, that data common to all states could be stored in the state machine, + state relevant data could be stored in the state and access as template + parameter in the entry / exit actions. What was however missing was the + capability to access relevant state data in the transition action. This is + possible with this front-end. A transition's source and target state are + also given as arguments. If the current calculation's state was to be found + in the transition's source state (whatever it is), we could access + it:</para> + <programlisting>struct send_rocket +{ + template <class Fsm,class Evt,class SourceState,class TargetState> + void operator()(Evt const&, Fsm& fsm, SourceState& src,TargetState& ) + { + fire_rocket(evt.direction, src.current_calculation); + } +}; </programlisting> + <para>It was a little awkward to generate new events inside actions with the basic + front-end. With the functor front-end it is much cleaner:</para> + <programlisting>struct send_rocket +{ + template <class Fsm,class Evt,class SourceState,class TargetState> + void operator()(Evt const& evt, Fsm& fsm, SourceState& src,TargetState&) + { + fire_rocket(evt.direction, src.current_calculation); + fsm.process_event(rocket_launched()); + } +}; </programlisting> + </sect2> + <sect2> + <title>Defining a simple state machine</title> + <para>Like states, state machines can be defined using the previous front-end, + as the previous example showed, or with the functor front-end, which allows + you to define a state machine entry and exit functions as functors, as in + <link xlink:href="examples/SimpleWithFunctors2.cpp">this + example</link>.</para> + </sect2> + <sect2> + <title>Anonymous transitions</title> + <para>Anonymous (completion) transitions are transitions without a named event. + We saw how this front-end uses <code>none</code> when no action or guard is + required. We can also use <code>none</code> instead of an event to mark an + anonymous transition. For example, the following transition makes an + immediate transition from State1 to State2:</para> + <programlisting>Row < State1 , none , State2 ></programlisting> + <para>The following transition does the same but calling an action in the + process:</para> + <programlisting>Row < State1 , none , State2 , State1ToState2, none ></programlisting> + <para>The following diagram shows an example and its <link + xlink:href="examples/AnonymousTutorialWithFunctors.cpp" + >implementation</link>:</para> + <para><inlinemediaobject> + <imageobject> + <imagedata fileref="images/Anonymous.jpg" width="70%" scalefit="1"/> + </imageobject> + </inlinemediaobject></para> + </sect2> + <sect2> + <title><command xml:id="functor-internal-transitions"/>Internal + transitions</title> + <para>The <link xlink:href="examples/SimpleTutorialInternalFunctors.cpp" + >following example</link> uses internal transitions with the functor + front-end. As for the simple standard front-end, both methods of defining + internal transitions are supported:<itemizedlist> + <listitem> + <para>providing a <code>Row</code> in the state machine's transition + table with <code>none</code> as target state defines an internal + transition.</para> + </listitem> + <listitem> + <para>providing an <code>internal_transition_table</code> made of + <code>Internal</code> rows inside a state or submachine + defines UML-conform internal transitions with higher + priority.</para> + </listitem> + <listitem> + <para>transitions defined inside + <code>internal_transition_table</code> require no source or + target state as the source state is known (<code>Internal</code> + really are <code>Row</code> without a source or target state) + .</para> + </listitem> + </itemizedlist>Like for the <command xlink:href="#internal-transitions-note" + >standard front-end internal transitions</command>, internal transition + tables are added into the main state machine's table, thus allowing you to + distribute the transition table definition and reuse states.</para> + <para>There is an added bonus offered for submachines, which can have both the + standard transition_table and an internal_transition_table (which has higher + priority). This makes it easier if you decide to make a full submachine from + a state later. It is also slightly faster than the standard alternative, + adding orthogonal regions, because event dispatching will, if accepted by + the internal table, not continue to the subregions. This gives you a O(1) + dispatch instead of O(number of regions). While the example is with eUML, + the same is also possible with this front-end.</para> + </sect2> + </sect1> + <sect1> + <title><command xml:id="eUML-front-end"/>eUML (experimental)</title> + <para><emphasis role="underline">Important note</emphasis>: eUML requires a compiler + supporting Boost.Typeof. More generally, eUML has experimental status because + some compilers will start crashing when a state machine becomes too big (usually + when you write huge actions).</para> + <para>The previous front-ends are simple to write but still force an amount of + noise, mostly MPL types, so it would be nice to write code looking like C++ + (with a C++ action language) directly inside the transition table, like UML + designers like to do on their state machine diagrams. If it were functional + programming, it would be even better. This is what eUML is for.</para> + <para>eUML is a Boost.Proto and Boost.Typeof-based compile-time domain specific + embedded language. It provides grammars which allow the definition of + actions/guards directly inside the transition table or entry/exit in the state + definition. There are grammars for actions, guards, flags, attributes, deferred + events, initial states.</para> + <para>It also relies on Boost.Typeof as a wrapper around the new decltype C++0x + feature to provide a compile-time evaluation of all the grammars. Unfortunately, + all the underlying Boost libraries are not Typeof-enabled, so for the moment, + you will need a compiler where Typeof is supported (like VC9-10, g++ >= + 4.3).</para> + <para>Examples will be provided in the next paragraphs. You need to include eUML + basic features: </para> + <para> + <programlisting>#include <msm/front/euml/euml.hpp></programlisting> + </para> + <para>To add STL support (at possible cost of longer compilation times), include: </para> + <para> + <programlisting>#include <msm/front/euml/stl.hpp></programlisting> + </para> + <para>eUML is defined in the namespace <code>msm::front::euml</code>.</para> + <sect2> + <title>Transition table</title> + <para>A transition can be defined using eUML as: </para> + <para> + <programlisting>source + event [guard] / action == target</programlisting> + </para> + <para>or as</para> + <para> + <programlisting>target == source + event [guard] / action</programlisting> + </para> + <para>The first version looks like a drawn transition in a diagram, the second + one seems natural to a C++ developer.</para> + <para>The simple transition table written with the <command + xlink:href="#functor-front-end">functor front-end</command> can now be + written as:</para> + <programlisting>BOOST_MSM_EUML_TRANSITION_TABLE(( +Stopped + play [some_guard] / (some_action , start_playback) == Playing , +Stopped + open_close/ open_drawer == Open , +Stopped + stop == Stopped , +Open + open_close / close_drawer == Empty , +Empty + open_close / open_drawer == Open , +Empty + cd_detected [good_disk_format] / store_cd_info == Stopped +),transition_table) </programlisting> + <para>Or, using the alternative notation, it can be:</para> + <programlisting>BOOST_MSM_EUML_TRANSITION_TABLE(( +Playing == Stopped + play [some_guard] / (some_action , start_playback) , +Open == Stopped + open_close/ open_drawer , +Stopped == Stopped + stop , +Empty == Open + open_close / close_drawer , +Open == Empty + open_close / open_drawer , +Stopped == Empty + cd_detected [good_disk_format] / store_cd_info +),transition_table) </programlisting> + <para>The transition table now looks like a list of (readable) rules with little + noise.</para> + <para>UML defines guards between “[ ]†and actions after a “/â€, so the chosen + syntax is already more readable for UML designers. UML also allows designers + to define several actions sequentially (our previous ActionSequence_) + separated by a comma. The first transition does just this: two actions + separated by a comma and enclosed inside parenthesis to respect C++ operator + precedence.</para> + <para>If this seems to you like it will cost you run-time performance, don't + worry, eUML is based on typeof (or decltype) which only evaluates the + parameters to BOOST_MSM_EUML_TRANSITION_TABLE and no run-time cost occurs. + Actually, eUML is only a metaprogramming layer on top of "standard" MSM + metaprogramming and this first layer generates the previously-introduced + <command xlink:href="#functor-front-end">functor + front-end</command>.</para> + <para>UML also allows designers to define more complicated guards, like + [good_disk_format && (some_condition || some_other_condition)]. This + was possible with our previously defined functors, but using a complicated + template syntax. This syntax is now possible exactly as written, which means + without any syntactic noise at all.</para> + </sect2> + <sect2> + <title>A simple example: rewriting only our transition table</title> + <para>As an introduction to eUML, we will rewrite our tutorial's transition + table using eUML. This will require two or three changes, depending on the compiler:<itemizedlist> + <listitem> + <para>events must inherit from msm::front::euml::euml_event< + event_name ></para> + </listitem> + <listitem> + <para>states must inherit from msm::front::euml::euml_state< + state_name ></para> + </listitem> + <listitem> + <para>with VC, states must be declared before the front-end</para> + </listitem> + </itemizedlist></para> + <para>We now can write the transition table like just shown, using + BOOST_MSM_EUML_DECLARE_TRANSITION_TABLE instead of + BOOST_MSM_EUML_TRANSITION_TABLE. The <link + xlink:href="examples/SimpleTutorialWithEumlTable.cpp">implementation</link> is pretty + straightforward.</para> + <para>The <link + xlink:href="examples/CompositeTutorialWithEumlTable.cpp">composite</link> implementation is slightly trickier because the submachine + has to be a msm::back::state_machine and a msm::front::euml::state. For + example:</para> + <programlisting>// front-end like always +struct front_end : public boost::msm::front::state_machine_def<front_end> +{ +... +}; +// back-end like always +typedef boost::msm::back::state_machine<front_end> back_end; +// this is new: make the submachine a eUML type +struct submachine : public back_end, + public boost::msm::front::euml::euml_state<back_end> +{ +};</programlisting> + <para>Unfortunately, there is a bug with VC, which appears from time to time and + causes in a stack overflow. If you get a warning that the program is + recursive on all paths, revert to either standard eUML or another front-end + as Microsoft doesn't seem to intend to fix it.</para> + <para>We now have a new, more readable transition table with few changes to our + example. eUML can do much more so please follow the guide.</para> + </sect2> + <sect2> + <title>Defining events, actions and states with entry/exit actions</title> + <sect3> + <title>Events</title> + <para>Events must be proto-enabled. To achieve this, they must inherit from + a proto terminal (euml_event<event-name>). eUML also provides a macro + to make this easier:</para> + <para> + <programlisting>BOOST_MSM_EUML_EVENT(play)</programlisting> + </para> + <para>This declares an event type and an instance of this type called + <code>play</code>, which is now ready to use in state or transition + behaviors.</para> + <para>There is a second macro, BOOST_MSM_EUML_EVENT_WITH_ATTRIBUTES, which + takes as second parameter the attributes an event will contain, using + the <command xlink:href="#eUML-attributes">attribute + syntax</command>.</para> + <para><emphasis role="underline">Note</emphasis>: as we now have events + defined as instances instead of just types, can we still process an + event by creating one on the fly, like: + <code>fsm.process_event(play());</code> or do we have to write: + <code>fsm.process_event(play);</code></para> + <para>The answer is you can do both. The second one is easier but unlike + other front-ends, the second uses a defined operator(), which creates an + event on the fly.</para> + </sect3> + <sect3> + <title>Actions</title> + <para>Actions (returning void) and guards (returning a bool) are defined + like previous functors, with the difference that they also must be + proto-enabled. This can be done by inheriting from euml_action< + functor-name >. eUML also provides a macro:</para> + <programlisting>BOOST_MSM_EUML_ACTION(some_condition) +{ + template <class Fsm,class Evt,class SourceState,class TargetState> + bool operator()(Evt const& ,Fsm& ,SourceState&,TargetState& ) + { return true; } +}; </programlisting> + <para>Like for events, this macro declares a functor type and an instance + for use in transition or state behaviors.</para> + <para>It is possible to use the same action grammar from the transition + table to define state entry and exit behaviors. So + <code>(action1,action2)</code> is a valid entry or exit behavior + executing both actions in turn.</para> + <para>The state functors have a slightly different signature as there is no + source and target state but only a current state (entry/exit actions are + transition-independent), for example:</para> + <programlisting>BOOST_MSM_EUML_ACTION(Empty_Entry) +{ + template <class Evt,class Fsm,class State> + void operator()(Evt const& ,Fsm& ,State& ) { ... } + }; </programlisting> + <para><command xml:id="eUML-reuse-functor"/>It is also possible to reuse the functors from the functor front-end. + The syntax is however slightly less comfortable as we need to pretend + creating one on the fly for typeof. For example:</para> + <programlisting>struct start_playback +{ + template <class Fsm,class Evt,class SourceState,class TargetState> + void operator()(Evt const& ,Fsm&,SourceState& ,TargetState& ) + { + ... + } +}; +BOOST_MSM_EUML_TRANSITION_TABLE(( +Playing == Stopped + play / start_playback() , +... +),transition_table)</programlisting> + </sect3> + <sect3> + <title>States</title> + <para>There is also a macro for states. This macro has 2 arguments, first + the expression defining the state, then the state (instance) + name:</para> + <programlisting>BOOST_MSM_EUML_STATE((),Paused)</programlisting> + <para>This defines a simple state without entry or exit action. You can + provide in the expression parameter the state behaviors (entry and exit) + using the action grammar, like in the transition table:</para> + <programlisting>BOOST_MSM_EUML_STATE(((Empty_Entry,Dummy_Entry)/*2 entryactions*/, + Empty_Exit/*1 exit action*/ ), + Empty)</programlisting> + <para>This means that Empty is defined as a state with an entry action made + of two sub-actions, Empty_Entry and Dummy_Entry (enclosed inside + parenthesis), and an exit action, Empty_Exit.</para> + <para>There are several possibilitites for the <command + xml:id="eUML-build-state"/> expression syntax:<itemizedlist> + <listitem> + <para>(): state without entry or exit action.</para> + </listitem> + <listitem> + <para>(Expr1): state with entry but no exit action.</para> + </listitem> + <listitem> + <para>(Expr1,Expr2): state with entry and exit action.</para> + </listitem> + <listitem> + <para>(Expr1,Expr2,Attributes): state with entry and exit + action, defining some attributes (read further on).</para> + </listitem> + <listitem> + <para>(Expr1,Expr2,Attributes,Configure): state with entry and + exit action, defining some attributes (read further on) and + flags (standard MSM flags) or deferred events (standard MSM + deferred events).</para> + </listitem> + <listitem> + <para>(Expr1,Expr2,Attributes,Configure,Base): state with entry + and exit action, defining some attributes (read further on), + flags and deferred events (plain msm deferred events) and a + non-default base state (as defined in standard MSM).</para> + </listitem> + </itemizedlist></para> + <para>no_action is also defined, which does, well, nothing except being a + placeholder (needed for example as entry action if we have no entry but + an exit). Expr1 and Expr2 are a sequence of actions, obeying the same + action grammar as in the transition table (following the “/†+ symbol).</para> + <para>The BOOST_MSM_EUML_STATE macro will allow you to define most common + states, but sometimes you will need more, for example provide in your + states some special behavior. In this case, you will have to do the + macro's job by hand, which is not very complicated. The state will need + to inherit from <code>msm::front::state<></code>, like any state, and + from <code>euml_state<state-name></code> to be proto-enabled. You + will then need to declare an instance for use in the transition table. + For example:</para> + <programlisting>struct Empty_impl : public msm::front::state<> , public euml_state<Empty_impl> +{ + void activate_empty() {std::cout << "switching to Empty " << std::endl;} + template <class Event,class Fsm> + void on_entry(Event const& evt,Fsm&fsm){...} + template <class Event,class Fsm> + void on_exit(Event const& evt,Fsm&fsm){...} +}; +//instance for use in the transition table +Empty_impl const Empty;</programlisting> + <para>Notice also that we defined a method named activate_empty. We would + like to call it inside a behavior. This can be done using the + BOOST_MSM_EUML_METHOD macro. </para> + <programlisting>BOOST_MSM_EUML_METHOD(ActivateEmpty_,activate_empty,activate_empty_,void,void)</programlisting> + <para>The first parameter is the name of the underlying functor, which you + could use with the functor front-end, the second is the state method + name, the third is the eUML-generated function, the fourth and fifth the + return value when used inside a transition or a state behavior. You can + now use this inside a transition:</para> + <programlisting>Empty == Open + open_close / (close_drawer,activate_empty_(target_))</programlisting> + </sect3> + </sect2> + <sect2> + <title>Wrapping up a simple state machine and first complete examples</title> + <para>You can reuse the state machine definition method from the standard + front-end and simply replace the transition table by this new one. You can + also use eUML to define a state machine "on the fly" (if, for example, you + need to provide an on_entry/on_exit for this state machine as a functor). + For this, there is also a macro, <command xml:id="eUML-build-sm" + />BOOST_MSM_EUML_DECLARE_STATE_MACHINE, which has 2 arguments, an expression + describing the state machine and the state machine name. The expression has + up to 8 arguments:<itemizedlist> + <listitem> + <para>(Stt, Init): simplest state machine where only the transition + table and initial state(s) are defined.</para> + </listitem> + <listitem> + <para>(Stt, Init, Expr1): state machine where the transition table, + initial state and entry action are defined.</para> + </listitem> + <listitem> + <para>(Stt, Init, Expr1, Expr2): state machine where the transition + table, initial state, entry and exit actions are defined.</para> + </listitem> + <listitem> + <para>(Stt, Init, Expr1, Expr2, Attributes): state machine where the + transition table, initial state, entry and exit actions are + defined. Furthermore, some attributes are added (read further + on).</para> + </listitem> + <listitem> + <para>(Stt, Init, Expr1, Expr2, Attributes, Configure): state + machine where the transition table, initial state, entry and + exit actions are defined. Furthermore, some attributes (read + further on), flags, deferred events and <link + xlink:href="#eUML-Configuration">configuration + capabilities</link> (no message queue / no exception + catching) are added.</para> + </listitem> + <listitem> + <para>(Stt, Init, Expr1, Expr2, Attributes, Flags, Deferred , Base): + state machine where the transition table, initial state, entry + and exit actions are defined. Furthermore, attributes (read + further on), flags , deferred events and configuration + capabilities (no message queue / no exception catching) are + added and a non-default base state (see the <link + xlink:href="#backend-base-state">back-end + description</link>) is defined.</para> + </listitem> + </itemizedlist>For example, a minimum state machine could be defined + as:</para> + <programlisting>BOOST_MSM_EUML_TRANSITION_TABLE(( +),transition_table) </programlisting> + <programlisting>BOOST_MSM_EUML_DECLARE_STATE_MACHINE((transition_table,init_ << Empty ), + player_)</programlisting> + <para>Please have a look at the player tutorial written using eUML's <link + xlink:href="examples/SimpleTutorialEuml2.cpp">first syntax</link> and + <link xlink:href="examples/SimpleTutorialEuml.cpp">second syntax</link>. + The BOOST_MSM_EUML_DECLARE_ATTRIBUTE macro, to which we will get back + shortly, declares attributes given to an eUML type (state or event) using + the <command xlink:href="#eUML-attributes">attribute + syntax</command>.</para> + </sect2> + <sect2> + <title>Defining a submachine</title> + <para>Defining a submachine (see <link + xlink:href="examples/CompositeTutorialEuml.cpp">tutorial</link>) with + other front-ends simply means using a state which is a state machine in the + transition table of another state machine. This is the same with eUML. One + only needs define a second state machine and reference it in the transition + table of the containing state machine.</para> + <para>Unlike the state or event definition macros, + BOOST_MSM_EUML_DECLARE_STATE_MACHINE defines a type, not an instance because + a type is what the back-end requires. This means that you will need to + declare yourself an instance to reference your submachine into another state + machine, for example:</para> + <programlisting>BOOST_MSM_EUML_DECLARE_STATE_MACHINE(...,Playing_) +typedef msm::back::state_machine<Playing_> Playing_type; +Playing_type const Playing;</programlisting> + <para>We can now use this instance inside the transition table of the containing + state machine:</para> + <programlisting>Paused == Playing + pause / pause_playback</programlisting> + </sect2> + <sect2> + <title> + <command xml:id="eUML-attributes"/>Attributes / Function call</title> + <para>We now want to make our grammar more useful. Very often, one needs only + very simple action methods, for example ++Counter or Counter > 5 where + Counter is usually defined as some attribute of the class containing the + state machine. It seems like a waste to write a functor for such a simple + action. Furthermore, states within MSM are also classes so they can have + attributes, and we would also like to provide them with attributes. </para> + <para>If you look back at our examples using the <link + xlink:href="examples/SimpleTutorialEuml2.cpp">first</link> and <link + xlink:href="examples/SimpleTutorialEuml.cpp">second</link> syntaxes, you + will find a BOOST_MSM_EUML_DECLARE_ATTRIBUTE and a BOOST_MSM_EUML_ATTRIBUTES + macro. The first one declares possible attributes:</para> + <programlisting>BOOST_MSM_EUML_DECLARE_ATTRIBUTE(std::string,cd_name) +BOOST_MSM_EUML_DECLARE_ATTRIBUTE(DiskTypeEnum,cd_type)</programlisting> + <para>This declares two attributes: cd_name of type std::string and cd_type of + type DiskTypeEnum. These attributes are not part of any event or state in + particular, we just declared a name and a type. Now, we can add attributes + to our cd_detected event using the second one:</para> + <programlisting>BOOST_MSM_EUML_ATTRIBUTES((attributes_ << cd_name << cd_type ), + cd_detected_attributes)</programlisting> + <para>This declares an attribute list which is not linked to anything in + particular yet. It can be attached to a state or an event. For example, if + we want the event cd_detected to have these defined attributes we + write:</para> + <programlisting>BOOST_MSM_EUML_EVENT_WITH_ATTRIBUTES(cd_detected,cd_detected_attributes)</programlisting> + <para>For states, we use the BOOST_MSM_EUML_STATE macro, which has an expression + form where one can provide attributes. For example:</para> + <programlisting>BOOST_MSM_EUML_STATE((no_action /*entry*/,no_action/*exit*/, + attributes_ << cd_detected_attributes), + some_state)</programlisting> + <para>OK, great, we now have a way to add attributes to a class, which we could + have done more easily, so what is the point? The point is that we can now + reference these attributes directly, at compile-time, in the transition + table. For example, in the example, you will find this transition:</para> + <programlisting>Stopped==Empty+cd_detected[good_disk_format&&(event_(cd_type)==Int_<DISK_CD>())] </programlisting> + <para>Read event_(cd_type) as event_->cd_type with event_ a type generic for + events, whatever the concrete event is (in this particular case, it happens + to be a cd_detected as the transition shows).</para> + <para>The main advantage of this feature is that you do not need to define a new + functor and you do not need to look inside the functor to know what it does, + you have all at hand.</para> + <para>MSM provides more generic objects for state machine types:<itemizedlist> + <listitem> + <para>event_ : used inside any action, the event triggering the + transition</para> + </listitem> + <listitem> + <para>state_: used inside entry and exit actions, the entered / + exited state</para> + </listitem> + <listitem> + <para>source_: used inside a transition action, the source + state</para> + </listitem> + <listitem> + <para>target_: used inside a transition action, the target + state</para> + </listitem> + <listitem> + <para>fsm_: used inside any action, the (lowest-level) state machine + processing the transition</para> + </listitem> + <listitem> + <para>Int_<int value>: a functor representing an int</para> + </listitem> + <listitem> + <para>Char_<value>: a functor representing a char</para> + </listitem> + <listitem> + <para>Size_t_<value>: a functor representing a size_t</para> + </listitem> + <listitem> + <para>String_<mpl::string> (boost >= 1.40): a functor + representing a string.</para> + </listitem> + </itemizedlist></para> + <para>These helpers can be used in two different ways:<itemizedlist> + <listitem> + <para>helper(attribute_name) returns the attribute with name + attribute_name</para> + </listitem> + <listitem> + <para>helper returns the state / event type itself.</para> + </listitem> + </itemizedlist></para> + <para>The second form is helpful if you want to provide your states with their + own methods, which you also want to use inside the transition table. In the + <link xlink:href="examples/SimpleTutorialEuml.cpp">above + tutorial</link>, we provide Empty with an activate_empty method. We would + like to create a eUML functor and call it from inside the transition table. + This is done using the MSM_EUML_METHOD / MSM_EUML_FUNCTION macros. The first + creates a functor to a method, the second to a free function. In the + tutorial, we write:</para> + <programlisting>MSM_EUML_METHOD(ActivateEmpty_,activate_empty,activate_empty_,void,void)</programlisting> + <para>The first parameter is the functor name, for use with the functor + front-end. The second is the name of the method to call. The third is the + function name for use with eUML, the fourth is the return type of the + function if used in the context of a transition action, the fifth is the + result type if used in the context of a state entry / exit action (usually + fourth and fifth are the same). We now have a new eUML function calling a + method of "something", and this "something" is one of the five previously + shown generic helpers. We can now use this in a transition, for + example:</para> + <programlisting>Empty == Open + open_close / (close_drawer,activate_empty_(target_))</programlisting> + <para>The action is now defined as a sequence of two actions: close_drawer and + activate_empty, which is called on the target itself. The target being Empty + (the state defined left), this really will call Empty::activate_empty(). + This method could also have an (or several) argument(s), for example the + event, we could then call activate_empty_(target_ , event_).</para> + <para>More examples can be found in the <link + xlink:href="examples/CompilerStressTestEuml.cpp">terrible compiler + stress test</link>, the <link xlink:href="examples/SimpleTimer.cpp" + >timer example</link> or in the <link + xlink:href="examples/iPodSearchEuml.cpp">iPodSearch with eUML</link> + (for String_ and more).</para> + </sect2> + <sect2> + <title>Orthogonal regions, flags, event deferring</title> + <para>Defining orthogonal regions really means providing more initial states. To + add more initial states, “shift left†some, for example, if we had another + initial state named AllOk :</para> + <programlisting>BOOST_MSM_EUML_DECLARE_STATE_MACHINE((transition_table, + init_ << Empty << AllOk ), + player_)</programlisting> + <para>You remember from the <command xlink:href="#eUML-build-state" + >BOOST_MSM_EUML_STATE </command> and <command + xlink:href="#eUML-build-sm" + >BOOST_MSM_EUML_DECLARE_STATE_MACHINE</command> signatures that just + after attributes, we can define flags, like in the basic MSM front-end. To + do this, we have another "shift-left" grammar, for example:</para> + <programlisting>BOOST_MSM_EUML_STATE((no_action,no_action, attributes_ <<no_attributes_, + /* flags */ configure_<< PlayingPaused << CDLoaded), + Paused)</programlisting> + <para>We now defined that Paused will get two flags, PlayingPaused and CDLoaded, + defined, with another macro:</para> + <programlisting>BOOST_MSM_EUML_FLAG(CDLoaded)</programlisting> + <para>This corresponds to the following basic front-end definition of + Paused:</para> + <programlisting>struct Paused : public msm::front::state<> +{ + typedef mpl::vector2<PlayingPaused,CDLoaded> flag_list; +};</programlisting> + <para>Under the hood, what you get really is a mpl::vector2.</para> + <para><emphasis role="underline">Note</emphasis>: As we use the version of + BOOST_MSM_EUML_STATE's expression with 4 arguments, we need to tell eUML + that we need no attributes. Similarly to a <code>cout << endl</code>, + we need a <code>attributes_ << no_attributes_</code> syntax.</para> + <para>You can use the flag with the is_flag_active method of a state machine. + You can also use the provided helper function is_flag_ (returning a bool) + for state and transition behaviors. For example, in the <link + xlink:href="examples/iPodEuml.cpp">iPod implementation with eUML</link>, + you find the following transition:</para> + <programlisting>ForwardPressed == NoForward + EastPressed[!is_flag_(NoFastFwd)]</programlisting> + <para>The function also has an optional second parameter which is the state + machine on which the function is called. By default, fsm_ is used (the + current state machine) but you could provide a functor returning a reference + to another state machine.</para> + <para>eUML also supports defining deferred events in the state (state machine) + definition. To this aim, we can reuse the flag grammar. For example:</para> + <programlisting>BOOST_MSM_EUML_STATE((Empty_Entry,Empty_Exit, attributes_ << no_attributes_, + /* deferred */ configure_<< play ),Empty) </programlisting> + <para>The configure_ left shift is also responsible for deferring events. Shift + inside configure_ a flag and the state will get a flag, shift an event and + it will get a deferred event. This replaces the basic front-end + definition:</para> + <programlisting>typedef mpl::vector<play> deferred_events;</programlisting> + <para>In <link xlink:href="examples/OrthogonalDeferredEuml.cpp">this + tutorial</link>, player is defining a second orthogonal region with + AllOk as initial state. The <code>Empty</code> and <code>Open</code> states + also defer the event <code>play</code>. <code>Open</code>, + <code>Stopped</code> and <code>Pause</code> also support the flag + <code>CDLoaded</code> using the same left shift into + <code>configure_</code>.</para> + <para>In the functor front-end, we also had the possibility to defer an event + inside a transition, which makes possible conditional deferring. This is + also possible with eUML through the use of the defer_ order, as shown in + <link xlink:href="examples/OrthogonalDeferredEuml.cpp">this + tutorial</link>. You will find the following transition:</para> + <programlisting>Open + play / defer_</programlisting> + <para>This is an <command xlink:href="#eUML-internal">internal + transition</command>. Ignore it for the moment. Interesting is, that + when the event <code>play</code> is fired and <code>Open</code> is active, + the event will be deferred. Now add a guard and you can conditionally defer + the event, for example:</para> + <programlisting>Open + play [ some_condition ] / defer_</programlisting> + <para>This is similar to what we did with the functor front-end. This means that + we have the same constraints. Using defer_ instead of a state declaration, + we need to tell MSM that we have deferred events in this state machine. We + do this (again) using a configure_ declaration in the state machine + definition in which we shift the deferred_events configuration flag:</para> + <programlisting>BOOST_MSM_EUML_DECLARE_STATE_MACHINE((transition_table, + init_ << Empty << AllOk, + Entry_Action, + Exit_Action, + attributes_ << no_attributes_, + configure_<< deferred_events ), + player_)</programlisting> + <para>A <link xlink:href="examples/OrthogonalDeferredEuml2.cpp">tutorial</link> + illustrates this possibility.</para> + </sect2> + <sect2> + <title> + <command xml:id="eUML-Configuration"/>Customizing a state machine / Getting + more speed</title> + <para>We just saw how to use configure_ to define deferred events or flags. We + can also use it to configure our state machine like we did with the other front-ends:<itemizedlist> + <listitem> + <para><code>configure_ << no_exception</code>: disables + exception handling</para> + </listitem> + <listitem> + <para><code>configure_ << no_msg_queue</code> deactivates the + message queue</para> + </listitem> + <listitem> + <para><code>configure_ << deferred_events</code> manually + enables event deferring</para> + </listitem> + </itemizedlist></para> + <para>Deactivating the first two features and not activating the third if not + needed greatly improves the event dispatching speed of your state machine. + Our <link xlink:href="examples/EumlSimple.cpp">speed testing</link> example + with eUML does this for the best performance.</para> + <para><emphasis role="underline">Important note</emphasis>: As exit pseudo + states are using the message queue to forward events out of a submachine, + the <code>no_message_queue</code> option cannot be used with state machines + containing an exit pseudo state.</para> + </sect2> + <sect2> + <title>Completion / Anonymous transitions</title> + <para>Anonymous transitions (See <command xlink:href="#uml-anonymous">UML + tutorial</command>) are transitions without a named event, which are + therefore triggered immediately when the source state becomes active, + provided a guard allows it. As there is no event, to define such a + transition, simply omit the “+†part of the transition (the event), for + example: </para> + <programlisting>State3 == State4 [always_true] / State3ToState4 +State4 [always_true] / State3ToState4 == State3</programlisting> + <para>Please have a look at <link + xlink:href="examples/AnonymousTutorialEuml.cpp">this example</link>, + which implements the <command xlink:href="#anonymous-transitions">previously + defined</command> state machine with eUML.</para> + </sect2> + <sect2> + <title><command xml:id="eUML-internal"/>Internal transitions</title> + <para>Like both other front-ends, eUML supports two ways of defining internal transitions:<itemizedlist> + <listitem> + <para>in the state machine's transition table. In this case, you + need to specify a source state, event, actions and guards but no + target state, which eUML will interpret as an internal + transition, for example this defines a transition internal to + Open, on the event open_close:</para> + <programlisting>Open + open_close [internal_guard1] / internal_action1</programlisting> + <para><link xlink:href="examples/EumlInternal.cpp">A full + example</link> is also provided.</para> + </listitem> + <listitem> + <para>in a state's <code>internal_transition_table</code>. For + example:</para> + <programlisting>BOOST_MSM_EUML_DECLARE_STATE((Open_Entry,Open_Exit),Open_def) +struct Open_impl : public Open_def +{ + BOOST_MSM_EUML_DECLARE_INTERNAL_TRANSITION_TABLE(( + open_close [internal_guard1] / internal_action1 + )) +};</programlisting> + <para>Notice how we do not need to repeat that the transition + originates from Open as we already are in Open's context. </para> + <para>The <link xlink:href="examples/EumlInternalDistributed.cpp" + >implementation</link> also shows the added bonus offered + for submachines, which can have both the standard + transition_table and an internal_transition_table (which has + higher priority). This makes it easier if you decide to make a + full submachine from a state. It is also slightly faster than + the standard alternative, adding orthogonal regions, because + event dispatching will, if accepted by the internal table, not + continue to the subregions. This gives you a O(1) dispatch + instead of O(number of regions).</para> + </listitem> + </itemizedlist></para> + </sect2> + <sect2> + <title>Other state types</title> + <para>We saw the <command xlink:href="#eUML-build-state">build_state</command> + function, which creates a simple state. Likewise, eUML provides other + state-building macros for other types of states:<itemizedlist> + <listitem> + <para>BOOST_MSM_EUML_TERMINATE_STATE takes the same arguments as + BOOST_MSM_EUML_STATE and defines, well, a terminate + state.</para> + </listitem> + <listitem> + <para>BOOST_MSM_EUML_INTERRUPT_STATE takes the same arguments as + BOOST_MSM_EUML_STATE and defines an interrupt state. However, + the expression argument must contain as first element the event + ending the interruption, for example: + <code>BOOST_MSM_EUML_INTERRUPT_STATE(( end_error /*end + interrupt event*/,ErrorMode_Entry,ErrorMode_Exit + ),ErrorMode)</code></para> + </listitem> + <listitem> + <para>BOOST_MSM_EUML_EXIT_STATE takes the same arguments as + BOOST_MSM_EUML_STATE and defines an exit pseudo state. However, + the expression argument must contain as first element the event + propagated from the exit point: + <code>BOOST_MSM_EUML_EXIT_STATE(( event6 /*propagated + event*/,PseudoExit1_Entry,PseudoExit1_Exit + ),PseudoExit1)</code></para> + </listitem> + <listitem> + <para>BOOST_MSM_EUML_EXPLICIT_ENTRY_STATE defines an entry pseudo + state. It takes 3 parameters: the region index to be entered is + defined as an int argument, followed by the configuration + expression like BOOST_MSM_EUML_STATE and the state name, so that + <code>BOOST_MSM_EUML_EXPLICIT_ENTRY_STATE(0 /*region + index*/,( SubState2_Entry,SubState2_Exit ),SubState2)</code> + defines an entry state into the first region of a + submachine.</para> + </listitem> + <listitem> + <para>BOOST_MSM_EUML_ENTRY_STATE defines an entry pseudo state. It + takes 3 parameters: the region index to be entered is defined as + an int argument, followed by the configuration expression like + BOOST_MSM_EUML_STATE and the state name, so that + <code>BOOST_MSM_EUML_ENTRY_STATE(0,( + PseudoEntry1_Entry,PseudoEntry1_Exit ),PseudoEntry1)</code> + defines a pseudo entry state into the first region of a + submachine.</para> + </listitem> + </itemizedlist></para> + <para>To use these states in the transition table, eUML offers the functions + <code>explicit_</code>, <code>exit_pt_</code> and + <code>entry_pt_</code>. For example, a direct entry into the substate + SubState2 from SubFsm2 could be:</para> + <programlisting>explicit_(SubFsm2,SubState2) == State1 + event2</programlisting> + <para>Forks being a list on direct entries, eUML supports a logical syntax + (state1, state2, ...), for example:</para> + <programlisting>(explicit_(SubFsm2,SubState2), + explicit_(SubFsm2,SubState2b), + explicit_(SubFsm2,SubState2c)) == State1 + event3 </programlisting> + <para>An entry point is entered using the same syntax as explicit entries: + <programlisting>entry_pt_(SubFsm2,PseudoEntry1) == State1 + event4</programlisting></para> + <para>For exit points, it is again the same syntax except that exit points are + used as source of the transition: + <programlisting>State2 == exit_pt_(SubFsm2,PseudoExit1) + event6 </programlisting></para> + <para>The <link xlink:href="examples/DirectEntryEuml.cpp">entry tutorial</link> + is also available with eUML.</para> + </sect2> + <sect2> + <title>Helper functions</title> + <para>We saw a few helpers but there are more, so let us have a more complete description:<itemizedlist> + <listitem> + <para>event_ : used inside any action, the event triggering the + transition</para> + </listitem> + <listitem> + <para>state_: used inside entry and exit actions, the entered / + exited state</para> + </listitem> + <listitem> + <para>source_: used inside a transition action, the source + state</para> + </listitem> + <listitem> + <para>target_: used inside a transition action, the target + state</para> + </listitem> + <listitem> + <para>fsm_: used inside any action, the (deepest-level) state + machine processing the transition</para> + </listitem> + <listitem> + <para>These objects can also be used as a function and return an + attribute, for example event_(cd_name)</para> + </listitem> + <listitem> + <para>Int_<int value>: a functor representing an int</para> + </listitem> + <listitem> + <para>Char_<value>: a functor representing a char</para> + </listitem> + <listitem> + <para>Size_t_<value>: a functor representing a size_t</para> + </listitem> + <listitem> + <para>True_ and False_ functors returning true and false + respectively</para> + </listitem> + <listitem> + <para>String_<mpl::string> (boost >= 1.40): a functor + representing a string.</para> + </listitem> + <listitem> + <para>if_then_else_(guard, action, action) where action can be an + action sequence</para> + </listitem> + <listitem> + <para>if_then_(guard, action) where action can be an action + sequence</para> + </listitem> + <listitem> + <para>while_(guard, action) where action can be an action + sequence</para> + </listitem> + <listitem> + <para>do_while_(guard, action) where action can be an action + sequence</para> + </listitem> + <listitem> + <para>for_(action, guard, action, action) where action can be an + action sequence</para> + </listitem> + <listitem> + <para>process_(some_event [, some state machine] [, some state + machine] [, some state machine] [, some state machine]) will + call process_event (some_event) on the current state machine or + on the one(s) passed as 2nd , 3rd, 4th, 5th argument. This allow + sending events to several external machines</para> + </listitem> + <listitem> + <para>process_(event_): reprocesses the event which triggered the + transition</para> + </listitem> + <listitem> + <para>reprocess_(): same as above but shorter to write</para> + </listitem> + <listitem> + <para>process2_(some_event,Value [, some state machine] [, some + state machine] [, some state machine]) will call process_event + (some_event(Value)) on the current state machine or on the + one(s) passed as 3rd, 4th, 5th argument</para> + </listitem> + <listitem> + <para>is_ flag_(some_flag[, some state machine]) will call + is_flag_active on the current state machine or on the one passed + as 2nd argument</para> + </listitem> + <listitem> + <para>Predicate_<some predicate>: Used in STL algorithms. Wraps + unary/binary functions to make them eUML-compatible so that they + can be used in STL algorithms</para> + </listitem> + </itemizedlist></para> + <para>This can be quite fun. For example, </para> + <programlisting>/( if_then_else_(--fsm_(m_SongIndex) > Int_<0>(),/*if clause*/ + show_playing_song, /*then clause*/ + (fsm_(m_SongIndex)=Int_<1>(),process_(EndPlay))/*else clause*/ + ) + )</programlisting> + <para>means: if (fsm.SongIndex > 0, call show_playing_song else + {fsm.SongIndex=1; process EndPlay on fsm;}</para> + <para>A few examples are using these features:<itemizedlist> + <listitem> + <para>the iPod example introduced at the BoostCon09 <link + xlink:href="examples/iPodEuml.cpp">has been rewritten</link> + with eUML (weak compilers please move on...)</para> + </listitem> + <listitem> + <para>the iPodSearch example also introduced at the BoostCon09 <link + xlink:href="examples/iPodSearchEuml.cpp">has been + rewritten</link> with eUML. In this example, you will also + find some examples of STL functor usage.</para> + </listitem> + <listitem> + <para><link xlink:href="examples/SimpleTimer.cpp">A simpler + timer</link> example is a good starting point. </para> + </listitem> + </itemizedlist></para> + <para>There is unfortunately a small catch. Defining a functor using + MSM_EUML_METHOD or MSM_EUML_FUNCTION will create a correct functor. Your own + eUML functors written as described at the beginning of this section will + also work well, <emphasis role="underline">except</emphasis>, for the + moment, with the while_, if_then_, if_then_else_ functions.</para> + </sect2> + <sect2> + <title>Phoenix-like STL support</title> + <para>eUML supports most C++ operators (except address-of). For example it is + possible to write event_(some_attribute)++ or [source_(some_bool) && + fsm_(some_other_bool)]. But a programmer needs more than operators in his + daily programming. The STL is clearly a must have. Therefore, eUML comes in + with a lot of functors to further reduce the need for your own functors for + the transition table. For almost every algorithm or container method of the + STL, a corresponding eUML function is defined. Like Boost.Phoenix, “.†And + “->†of call on objects are replaced by a functional programming paradigm, + for example:<itemizedlist> + <listitem> + <para>begin_(container), end_(container): return iterators of a + container.</para> + </listitem> + <listitem> + <para>empty_(container): returns container.empty()</para> + </listitem> + <listitem> + <para>clear_(container): container.clear()</para> + </listitem> + <listitem> + <para>transform_ : std::transform</para> + </listitem> + </itemizedlist></para> + <para>In a nutshell, almost every STL method or algorithm is matched by a + corresponding functor, which can then be used in the transition table or + state actions. The <link xlink:href="#Reference-begin">reference</link> + lists all eUML functions and the underlying functor (so that this + possibility is not reserved to eUML but also to the functor-based + front-end). The file structure of this Phoenix-like library matches the one + of Boost.Phoenix. All functors for STL algorithms are to be found in:</para> + <programlisting>#include <msm/front/euml/algorithm.hpp></programlisting> + <para>The algorithms are also divided into sub-headers, matching the phoenix + structure for simplicity:</para> + <programlisting>#include < msm/front/euml/iteration.hpp> +#include < msm/front/euml/transformation.hpp> +#include < msm/front/euml/querying.hpp> </programlisting> + <para>Container methods can be found in:</para> + <programlisting>#include < msm/front/euml/container.hpp></programlisting> + <para>Or one can simply include the whole STL support (you will also need to + include euml.hpp):</para> + <programlisting>#include < msm/front/euml/stl.hpp></programlisting> + <para>A few examples (to be found in <link + xlink:href="examples/iPodSearchEuml.cpp">this tutorial</link>):<itemizedlist> + <listitem> + <para><code>push_back_(fsm_(m_tgt_container),event_(m_song))</code>: + the state machine has an attribute m_tgt_container of type + std::vector<OneSong> and the event has an attribute m_song of + type OneSong. The line therefore pushes m_song at the end of + m_tgt_container</para> + </listitem> + <listitem> + <para><code>if_then_( state_(m_src_it) != + end_(fsm_(m_src_container)), + process2_(OneSong(),*(state_(m_src_it)++)) )</code>: the + current state has an attribute m_src_it (an iterator). If this + iterator != fsm.m_src_container.end(), process OneSong on fsm, + copy-constructed from state.m_src_it which we + post-increment</para> + </listitem> + </itemizedlist></para> + </sect2> + <sect2> + <title><command xml:id="eUML-phoenix"/>Writing actions with Boost.Phoenix (in development)</title> + <para> It is also possible to write actions, guards, state entry and exit + actions using a reduced set of Boost.Phoenix capabilities. This feature + is still in development stage, so you might get here and there some + surprise. Simple cases, however, should work well. What will not work + will be mixing of eUML and Phoenix functors. Writing guards in one + language and actions in another is ok though.</para> + <para>Phoenix also supports a larger syntax than what will ever be possible + with eUML, so you can only use a reduced set of phoenix's grammar. This + is due to the nature of eUML. The run-time transition table definition + is translated to a type using Boost.Typeof. The result is a "normal" MSM + transition table made of functor types. As C++ does not allow mixing + run-time and compile-time constructs, there will be some limit (trying + to instantiate a template class MyTemplateClass<i> where i is an int + will give you an idea). This means following valid Phoenix constructs + will not work:</para> + <para> + <itemizedlist> + <listitem> + <para>literals</para> + </listitem> + <listitem> + <para>function pointers</para> + </listitem> + <listitem> + <para>bind</para> + </listitem> + <listitem> + <para>->*</para> + </listitem> + </itemizedlist> + </para> + <para>MSM also provides placeholders which make more sense in its context + than arg1.. argn:</para> + <para> + <itemizedlist> + <listitem> + <para>_event: the event triggering the transition</para> + </listitem> + <listitem> + <para>_fsm: the state machine processing the event</para> + </listitem> + <listitem> + <para>_source: the source state of the transition</para> + </listitem> + <listitem> + <para>_target: the target state of the transition</para> + </listitem> + <listitem> + <para>_state: for state entry/exit actions, the entry/exit + state</para> + </listitem> + </itemizedlist> + </para> + <para>Future versions of MSM will support Phoenix better. You can contribute + by finding out cases which do not work but should, so that they can be + added.</para> + <para>Phoenix support is not activated by default. To activate it, add + before any MSM header: #define BOOST_MSM_EUML_PHOENIX_SUPPORT.</para> + <para>A <link + xlink:href="examples/SimplePhoenix.cpp">simple example</link> shows some basic capabilities.</para> + </sect2> + </sect1> + <sect1> + <title>Back-end</title> + <para>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.</para> + <sect2> + <title>Creation </title> + <para>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: + <programlisting>typedef msm::back::state_machine<my_front_end> my_fsm;</programlisting></para> + <para>We now have a fully functional state machine type. The next sections will + describe what can be done with it.</para> + </sect2> + <sect2> + <title><command xml:id="backend-start"/>Starting and stopping a state + machine</title> + <para>The <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 <link xlink:href="examples/iPodSearch.cpp" + >iPodSearch</link> example uses this possibility.</para> + <para>The <code>stop()</code> method works the same way. It will cause the exit + actions of the currently active states(s) to be called.</para> + <para>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.</para> + </sect2> + <sect2> + <title>Event dispatching</title> + <para>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: </para> + <programlisting>my_fsm fsm; fsm.process_event(some_event()); +some_event e1; fsm.process_event(e1)</programlisting> + <para>Creating an event on the fly will be optimized by the compiler so the + performance will not degrade.</para> + </sect2> + <sect2> + <title>Active state(s)</title> + <para>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, <command + xlink:href="#UML-internal-transition">internal transitions</command> or + <command xlink:href="#backend-visitor">visitors</command> 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 + <command xlink:href="#internals-state-id">internals section</command> to + know how state ids are generated.</para> + </sect2> + <sect2> + <title><command xml:id="back-end-serialization"/>Serialization</title> + <para>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>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:</para> + <para> + <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; +} </programlisting> + </para> + <para>Loading back is very similar:</para> + <para> + <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; +} </programlisting> + </para> + <para>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:</para> + <para> + <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; +}; </programlisting> + </para> + <para>You can also serialize data contained in the front-end class. Again, you + need to provide the typedef and implement serialize:</para> + <para> + <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; + } +... +}; </programlisting> + </para> + <para>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>BOOST_MSM_EUML_STATE</code> cannot be used, so the state will have + to be implemented by directly inheriting from + <code>front::euml::euml_state</code>.</para> + <para>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).</para> + <para>This <link + xlink:href="examples/Serialize.cpp">example</link> shows a state machine which we serialize after processing an + event. The <code>Empty</code> state also has some data to serialize.</para> + </sect2> + <sect2> + <title><command xml:id="backend-base-state"/>Base state type </title> + <para>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:<itemizedlist> + <listitem> + <para>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: + <programlisting>struct Empty : public msm::front::state<my_base_state></programlisting> + 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>msm::front::polymorphic_state</code> + </para> + </listitem> + <listitem> + <para>Add the user-defined base state in the state machine frontend + definition, as a second template argument, for example: + <programlisting>struct player_ : public msm::front::state_machine<player_,my_base_state> </programlisting></para> + </listitem> + </itemizedlist></para> + <para>You can also ask for a state with a given id (which you might have gotten + from current_state()) using <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.</para> + </sect2> + <sect2> + <title><command xml:id="backend-visitor"/>Visitor</title> + <para>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!</para> + <para>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>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:</para> + <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 {} +};</programlisting> + <para>This makes your states polymorphic and visitable. In this case, accept is + made const and takes no argument. It could also be:</para> + <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 {} +};</programlisting> + <para>And now, <code>accept</code> will take one argument (it could also be + non-const). By default, <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:</para> + <programlisting>#define BOOST_MSM_VISITOR_ARG_SIZE 3 +#include <boost/msm/back/state_machine.hpp></programlisting> + <para>Note that accept will be called on ALL active states <emphasis + role="underline">and also automatically on sub-states of a + submachine</emphasis>.</para> + <para><emphasis role="underline">Important warning</emphasis>: 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:</para> + <programlisting>SomeVisitor vis; sm.visit_current_states(boost::ref(vis));</programlisting> + <para>This <link xlink:href="examples/SM-2Arg.cpp">example</link> uses a + visiting function with 2 arguments.</para> + </sect2> + <sect2> + <title>Flags</title> + <para>Flags is a MSM-only concept, supported by all front-ends, which base + themselves on the functions: </para> + <programlisting>template <class Flag> bool is_flag_active() +template <class Flag,class BinaryOp> bool is_flag_active()</programlisting> + <para>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:</para> + <programlisting>my_fsm.is_flag_active<MyFlag>() +my_fsm.is_flag_active<MyFlag,my_fsm_type::Flag_OR>()</programlisting> + <para>Please refer to the front-ends sections for usage examples.</para> + </sect2> + <sect2> + <title>Getting a state</title> + <para>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: </para> + <programlisting>player::Stopped* tempstate = p.get_state<player::Stopped*>();</programlisting> + <para> or </para> + <programlisting>player::Stopped& tempstate2 = p.get_state<player::Stopped&>();</programlisting> + <para>depending on your personal taste. </para> + </sect2> + <sect2> + <title><command xml:id="backend-fsm-constructor-args"/> State machine constructor with arguments </title> + <para>You might want to define a state machine with a non-default constructor. + For example, you might want to write: </para> + <programlisting>struct player_ : public msm::front::state_machine_def<player_> +{ + player_(int some_value){…} +}; </programlisting> + <para>This is possible, using the back-end as forwarding object: </para> + <programlisting>typedef msm::back::state_machine<player_ > player; player p(3);</programlisting> + <para>The back-end will call the corresponding front-end constructor upon + creation.</para> + <para>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. </para> + <para>You can also pass arguments by reference (or const-reference) using + boost::ref (or boost::cref):</para> + <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); + </programlisting> + <para>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:</para> + <programlisting>player p( back::states_ << state_1 << ... << state_n , boost::ref(data),3);</programlisting> + <para>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:</para> + <programlisting>player p( back::states_ << Playing(back::states_ << Song1(some_Song1_data)) , + boost::ref(data),3);</programlisting> + <para>It is also possible to replace a given state by a new instance at any time + using <code>set_states()</code> and the same syntax, for example: + <programlisting>p.set_states( back::states_ << state_1 << ... << state_n );</programlisting></para> + <para>An <link xlink:href="examples/Constructor.cpp" + >example</link> making intensive use of this capability is provided.</para> + </sect2> + <sect2> + <title><command xml:id="backend-tradeof-rt-ct"/>Trading run-time speed for + better compile-time / multi-TU compilation</title> + <para>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:<itemizedlist> + <listitem> + <para>many transition conflicts</para> + </listitem> + <listitem> + <para>submachines</para> + </listitem> + </itemizedlist></para> + <para>The back-end <code>msm::back::state_machine</code> has a policy argument + (first is the front-end, then the history policy) defaulting to + <code>favor_runtime_speed</code>. To switch to + <code>favor_compile_time</code>, which is declared in + <code><msm/back/favor_compile_time.hpp></code>, you need to:<itemizedlist> + <listitem> + <para>switch the policy to <code>favor_compile_time</code> for the + main state machine (and possibly submachines)</para> + </listitem> + <listitem> + <para>move the submachine declarations into their own header which + includes + <code><msm/back/favor_compile_time.hpp></code></para> + </listitem> + <listitem> + <para>add for each submachine a cpp file including your header and + calling a macro, which generates helper code, for + example:</para> + <programlisting>#include "mysubmachine.hpp" +BOOST_MSM_BACK_GENERATE_PROCESS_EVENT(mysubmachine)</programlisting> + </listitem> + <listitem> + <para>configure your compiler for multi-core compilation</para> + </listitem> + </itemizedlist></para> + <para>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.</para> + <para>Independently, transition conflicts resolution will also be much + faster.</para> + <para>This policy uses boost.any behind the hood, which means that we will lose + one feature which MSM offers with the default policy, <link + xlink:href="#event-hierarchy">event hierarchy</link>. The following + example takes our iPod example and speeds up compile-time by using this + technique. We have:<itemizedlist> + <listitem> + <para><link xlink:href="examples/iPod_distributed/iPod.cpp">our main + state machine and main function</link></para> + </listitem> + <listitem> + <para><link xlink:href="examples/iPod_distributed/PlayingMode.hpp" + >PlayingMode moved to a separate header</link></para> + </listitem> + <listitem> + <para><link xlink:href="examples/iPod_distributed/PlayingMode.cpp">a + cpp for PlayingMode</link></para> + </listitem> + <listitem> + <para><link xlink:href="examples/iPod_distributed/MenuMode.hpp" + >MenuMode moved to a separate header</link></para> + </listitem> + <listitem> + <para><link xlink:href="examples/iPod_distributed/MenuMode.cpp">a + cpp for MenuMode</link></para> + </listitem> + <listitem> + <para><link xlink:href="examples/iPod_distributed/Events.hpp">events + move to a separate header as all machines use + it</link></para> + </listitem> + </itemizedlist> + </para> + </sect2> + <sect2> + <title><command xml:id="backend-compile-time-analysis"/>Compile-time state machine analysis </title> + <para>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 <link xlink:href="http://www.dynagraph.org/mpl_graph/">mpl_graph</link> (delivered at the moment + with MSM) from Gordon Woodhull, MSM provides several compile-time checks:<itemizedlist> + <listitem> + <para>Check that orthogonal regions ar truly orthogonal.</para> + </listitem> + <listitem> + <para>Check that all states are either reachable from the initial + states or are explicit entries / pseudo-entry states.</para> + </listitem> + </itemizedlist></para> + <para>To make use of this feature, the back-end provides a policy (default is no + analysis), <code>msm::back::mpl_graph_fsm_check</code>. For example:</para> + <programlisting> typedef msm::back::state_machine< player_,msm::back::mpl_graph_fsm_check> player; </programlisting> + <para>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>player_</code>).</para> + <para>In case an error is detected, a compile-time assertion is provoked.</para> + <para>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.</para> + <para>The same algorithm is also used in case you want to omit providing the + region index in the <command xlink:href="#explicit-entry-no-region-id">explicit entry / pseudo entry state</command> declaration.</para> + <para>The author's advice is to enable the checks after any state machine + structure change and disable it again after sucessful analysis.</para> + <para>The <link xlink:href="examples/TestErrorOrthogonality.cpp">following example</link> provokes an assertion if one of the first two lines + of the transition table is used.</para> + </sect2> + <sect2> + <title><command xml:id="backend-enqueueing"/> Enqueueing events for later + processing </title> + <para>Calling <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>enqueue_event(Event + const&)</code> instead. Calling <code>execute_queued_events()</code> will then + process all enqueued events (in FIFO order).</para> + <para>You can query the queue size by calling <code>get_message_queue_size()</code>.</para> + </sect2> + <sect2> + <title><command xml:id="backend-queues"/> Customizing the message queues </title> + <para>MSM uses by default a std::deque for its queues (one message queue for + events generated during run-to-completion or with + <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:</para> + <programlisting> typedef msm::back::state_machine< player_,msm::back::queue_container_circular> player; </programlisting> + <para>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):</para> + <programlisting> fsm.get_message_queue().set_capacity(1); </programlisting> + </sect2> + <sect2> + <title><command xml:id="backend-boost-parameter"/>Policy definition with Boost.Parameter </title> + <para>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: </para> + <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; </programlisting> + </sect2> + <sect2> + <title><command xml:id="backend-state-switch"/>Choosing when to switch active + states </title> + <para>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>Connected</code> and <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.</para> + <para>We are in state <code>Disconnected</code> and receive an event <code>connect</code>. The transition + action will ask the state machine <code>is_flag_active<is_connected></code> and will + get... false because we are still in <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.</para> + <para>MSM provides the possibility (in form of a policy) for a front-end to + decide when the target state becomes active. It can be:<itemizedlist> + <listitem> + <para>before the transition fires, if the guard will allow the + transition to fire: + <code>active_state_switch_before_transition</code></para> + </listitem> + <listitem> + <para>after calling the exit action of the source state: + <code>active_state_switch_after_exit</code></para> + </listitem> + <listitem> + <para>after the transition action is executed: + <code>active_state_switch_after_transition_action</code></para> + </listitem> + <listitem> + <para>after the entry action of the target state is executed + (default): <code>active_state_switch_after_entry</code></para> + </listitem> + </itemizedlist>The problem and the solution is shown for the + <link xlink:href="examples/ActiveStateSetBeforeTransition.cpp">functor-front-end</link> + and <link xlink:href="examples/ActivateStateBeforeTransitionEuml.cpp">eUML</link>. Removing <code>active_state_switch_before_transition</code> + will show the default state. </para> + </sect2> + </sect1> + </chapter> + <chapter> + <title> Performance / Compilers</title> + <para>Tests were made on different PCs running Windows XP and Vista and compiled with + VC9 SP1 or Ubuntu and compiled with g++ 4.2 and 4.3. For these tests, the same + player state machine was written using Boost.Statechart, as a <link + xlink:href="examples/SCSimple.cpp">state machine with only simple states</link> + and as a <link xlink:href="examples/SCComposite.cpp">state machine with a composite + state</link>. The same simple and composite state machines are implemented with + MSM with a standard frontend <link xlink:href="examples/MsmSimple.cpp" + >(simple)</link><link xlink:href="examples/MsmComposite.cpp">(composite)</link>, + the simple one also with <link xlink:href="examples/MsmSimpleFunctors.cpp" + >functors</link> and with <link xlink:href="examples/EumlSimple.cpp" + >eUML</link>. As these simple machines need no terminate/interrupt states, no + message queue and have no-throw guarantee on their actions, the MSM state machines + are defined with minimum functionality. Test machine is a Q6600 2.4GHz, Vista + 64.</para> + <sect1> + <title>Speed</title> + <para>VC9:<itemizedlist> + <listitem> + <para>The simple test completes 90 times faster with MSM than with + Boost.Statechart</para> + </listitem> + <listitem> + <para>The composite test completes 25 times faster with MSM</para> + </listitem> + </itemizedlist></para> + <para>gcc 4.2.3 (Ubuntu 8.04 in VMWare, same PC):<itemizedlist> + <listitem> + <para>The simple test completes 46 times faster with MSM</para> + </listitem> + <listitem> + <para>The composite test completes 19 times faster with Msm</para> + </listitem> + </itemizedlist></para> + </sect1> + <sect1> + <title>Executable size</title> + <para>There are some worries that MSM generates huge code. Is it true? The 2 + compilers I tested disagree with this claim. On VC9, the test state machines + used in the performance section produce executables of 14kB (for simple and + eUML) and 21kB (for the composite). This includes the test code and iostreams. + By comparison, an empty executable with iostreams generated by VC9 has a size of + 7kB. Boost.Statechart generates executables of 43kB and 54kB. As a bonus, eUML + comes for “free†in terms of executable size. You even get a speed gain. With + g++ 4.3, it strongly depends on the compiler options (much more than VC). A good + size state machine with –O3 can generate an executable of 600kB, and with eUML + you can get to 1.5MB. Trying with –Os –s I come down to 18kB and 30kB for the + test state machines, while eUML will go down to 1MB (which is still big), so in + this case eUML does not come for free.</para> + </sect1> + <sect1> + <title>Supported compilers</title> + <para>For a current status, have a look at the <link + xlink:href="http://www.boost.org/development/tests/trunk/developer/msm.html" + >regression tests</link>.</para> + <para>MSM was successfully tested with: <itemizedlist> + <listitem> + <para>VC8 (partly), VC9, VC10</para> + </listitem> + <listitem> + <para>g++ 4.0.1 and higher</para> + </listitem> + <listitem> + <para>Intel 10.1 and higher</para> + </listitem> + <listitem> + <para>Clang 2.9</para> + </listitem> + <listitem> + <para>Green Hills Software MULTI for ARM v5.0.5 patch 4416 (Simple and + Composite tutorials)</para> + </listitem> + <listitem> + <para>Partial support for IBM compiler</para> + </listitem> + </itemizedlist></para> + <para>VC8 and to some lesser extent VC9 suffer from a bug. Enabling the option + "Enable Minimal Rebuild" (/Gm) will cause much higher compile-time (up to three + times with VC8!). This option being activated per default in Debug mode, this + can be a big problem.</para> + </sect1> + <sect1> + <title> Limitations </title> + <para> + <itemizedlist> + <listitem> + <para>Compilation times of state machines with > 80 transitions that are + going to make you storm the CFO's office and make sure you get a + shiny octocore with 12GB RAM by next week, unless he's interested in + paying you watch the compiler agonize for hours... (Make sure you + ask for dual 24" as well, it doesn't hurt).</para> + </listitem> + <listitem> + <para>eUML allows very long constructs but will also quickly increase + your compile time on some compilers (VC9, VC10) with buggy decltype + support (I suspect some at least quadratic algorithms there). Even + g++ 4.4 shows some regression compared to 4.3 and will crash if the + constructs become too big.</para> + </listitem> + <listitem> + <para>Need to overwrite the mpl::vector/list default-size-limit of 20 + and fusion default vector size of 10 if more than 10 states found in + a state machine</para> + </listitem> + </itemizedlist> + </para> + </sect1> + <sect1> + <title> Compilers corner </title> + <para>Compilers are sometimes full of surprises and such strange errors happened in + the course of the development that I wanted to list the most fun for readers’ + entertainment.</para> + <para><emphasis role="underline">VC8</emphasis>: </para> + <programlisting>template <class StateType> +typename ::boost::enable_if< + typename ::boost::mpl::and_< + typename ::boost::mpl::not_< + typename has_exit_pseudo_states<StateType>::type + >::type, + typename ::boost::mpl::not_< + typename is_pseudo_exit<StateType>::type + >::type + >::type, + BaseState*>::type </programlisting> + <para>I get the following error:</para> + <para>error C2770: invalid explicit template argument(s) for '`global + namespace'::boost::enable_if<...>::...' </para> + <para>If I now remove the first “::†in ::boost::mpl , the compiler shuts up. So in + this case, it is not possible to follow Boost’s guidelines.</para> + <para><emphasis role="underline">VC9</emphasis>:<itemizedlist> + <listitem> + <para>This one is my all times’ favorite. Do you know why the exit + pseudo states are referenced in the transition table with a + “submachine::exit_pt†? Because “exit†will crash the compiler. + “Exit†is not possible either because it will crash the compiler on + one machine, but not on another (the compiler was installed from the + same disk).</para> + </listitem> + <listitem> + <para>Sometimes, removing a policy crashes the compiler, so some + versions are defining a dummy policy called WorkaroundVC9.</para> + </listitem> + <listitem> + <para>Typeof: While g++ and VC9 compile “standard†state machines in + comparable times, Typeof (while in both ways natively supported) + seems to behave in a quadratic complexity with VC9 and VC10.</para> + </listitem> + <listitem> + <para>eUML: in case of a compiler crash, changing the order of state + definitions (first states without entry or exit) sometimes solves + the problem.</para> + </listitem> + </itemizedlist></para> + <para><emphasis role="underline">g++ 4.x</emphasis>: Boring compiler, almost all is + working almost as expected. Being not a language lawyer I am unsure about the + following “Typeof problemâ€. VC9 and g++ disagree on the question if you can + derive from the BOOST_TYPEOF generated type without first defining a typedef. I + will be thankful for an answer on this. I only found two ways to break the compiler:<itemizedlist> + <listitem> + <para>Add more eUML constructs until something explodes (especially with + g++-4.4) </para> + </listitem> + <listitem> + <para>The build_terminate function uses 2 mpl::push_back instead of + mpl::insert_range because g++ would not accept insert_range.</para> + </listitem> + </itemizedlist></para> + <para>You can test your compiler’s decltype implementation with the <link + xlink:href="examples/CompilerStressTestEuml.cpp">following stress + test</link> and reactivate the commented-out code until the compiler + crashes.</para> + </sect1> + </chapter> + <chapter> + <title>Questions & Answers, tips</title> + <para><emphasis role="underline">Where should I define a state machine?</emphasis>: The + tutorials are implemented in a simple cpp source file for simplicity. I want to + model dynamic behavior of a class as a state machine, how should I define the state + machine?</para> + <para><emphasis role="underline">Answer</emphasis>: Usually you'll want to implement the + state machine as an attribute of the class. Unfortunately, a concrete state machine + is a typedef, which cannot be forward-declared. This leaves you with two + possibilities: <itemizedlist> + <listitem> + <para>Provide the state machine definition inside the header class and + contain an instance as attribute. Simple, but with several drawbacks: + using namespace directives are not advised, and compile-time cost for + all modules including the header.</para> + </listitem> + <listitem> + <para>Keep the state machine as (shared) pointer to void inside the <link + xlink:href="examples/FsmAsPtr.hpp">class definition</link>, and + implement the state machine in the <link + xlink:href="examples/FsmAsPtr.cpp">cpp file</link>. Minimum + compile-time, using directives are okay, but the state machine is now + located inside the heap.</para> + </listitem> + </itemizedlist></para> + <para><emphasis role="underline">Question</emphasis>: on_entry gets as argument, the + sent event. What event do I get when the state becomes default-activated (because it + is an initial state)?</para> + <para> + <emphasis role="underline">Answer</emphasis>: To allow you to know that the state + was default-activated, MSM generates a boost::msm::InitEvent default event. </para> + <para><emphasis role="underline">Question</emphasis>: Why do I see no call to + no_transition in my submachine? </para> + <para><emphasis role="underline">Answer</emphasis>: Because of the priority rule defined + by UML. It says that in case of transition conflict, the most inner state has a + higher priority. So after asking the inner state, the containing composite has to be + also asked to handle the transition and could find a possible transition.</para> + <para><emphasis role="underline">Question</emphasis>: Why do I get a compile error + saying the compiler cannot convert to a function ...Fsm::*(some_event)? </para> + <para><emphasis role="underline">Answer</emphasis>: You probably defined a transition + triggered by the event some_event, but used a guard/action method taking another + event. </para> + <para><emphasis role="underline">Question</emphasis>: Why do I get a compile error + saying something like “too few†or “too many†template arguments? </para> + <para><emphasis role="underline">Answer</emphasis>: You probably defined a transition in + form of a a_row or g_row where you wanted just a _row or the other way around. With + Row, it could mean that you forgot a "none". </para> + <para><emphasis role="underline">Question</emphasis>: Why do I get a very long compile + error when I define more than 20 rows in the transition table? </para> + <para><emphasis role="underline">Answer</emphasis>: MSM uses Boost.MPL under the hood + and this is the default maximum size. Please define the following 3 macros before + including any MSM headers: </para> + <programlisting>#define BOOST_MPL_CFG_NO_PREPROCESSED_HEADERS +#define BOOST_MPL_LIMIT_VECTOR_SIZE 30 // or whatever you need +#define BOOST_MPL_LIMIT_MAP_SIZE 30 // or whatever you need </programlisting> + <para><emphasis role="underline">Question</emphasis>: Why do I get this error: â€error + C2977: 'boost::mpl::vector' : too many template argumentsâ€? </para> + <para><emphasis role="underline">Answer</emphasis>: The first possibility is that you + defined a transition table as, say, vector17 and have 18 entries. The second is that + you have 17 entries and have a composite state. Under the hood, MSM adds a row for + every event in the composite transition table. The third one is that you used a + mpl::vector without the number of entries but are close to the MPL default of 50 and + have a composite, thus pushing you above 50. Then you need mpl/vector60/70….hpp and + a mpl/map60/70….hpp </para> + <para><emphasis role="underline">Question</emphasis>: Why do I get a very long compile + error when I define more than 10 states in a state machine? </para> + <para><emphasis role="underline">Answer</emphasis>: MSM uses Boost.Fusion under the hood + and this is the default maximum size. Please define the following macro before + including any MSM headers: </para> + <programlisting>#define FUSION_MAX_VECTOR_SIZE 20 // or whatever you need </programlisting> + </chapter> + <chapter> + <title>Internals</title> + <para>This chapter describes the internal machinery of the back-end, which can be useful + for UML experts but can be safely ignored for most users. For implementers, the + interface between front- and back- end is also described in detail.</para> + <sect1> + <title><command xml:id="run-to-completion"/>Backend: Run To Completion</title> + <para>The back-end implements the following run-to completion algorithm:<itemizedlist> + <listitem> + <para>Check if one region of the concrete state machine is in a + terminate or interrupt state. If yes, event processing is disabled + while the condition lasts (forever for a terminate pseudo-state, + while active for an interrupt pseudo-state).</para> + </listitem> + <listitem> + <para>If the message queue feature is enabled and if the state machine + is already processing an event, push the currently processed event + into the queue and end processing. Otherwise, remember that the + state machine is now processing an event and continue.</para> + </listitem> + <listitem> + <para>If the state machine detected that no deferred event is used, skip + this step. Otherwise, mark the first deferred event from the + deferred queue as active.</para> + </listitem> + <listitem> + <para>Now start the core of event dispatching. If exception handling is + activated, this will happen inside a try/catch block and the + front-end <code>exception_caught</code> is called if an exception + occurs. </para> + </listitem> + <listitem> + <para>The event is now dispatched in turn to every region, in the order + defined by the initial state front-end definition. This will, for + every region, call the corresponding front-end transition definition + (the "row" or "Row" of the transition table).</para> + </listitem> + <listitem> + <para>Without transition conflict, if for a given region a transition is + possible, the guard condition is checked. If it returns + <code>true</code>, the transition processing continues and the + current state's exit action is called, followed by the transition + action behavior and the new active state's entry behavior.</para> + </listitem> + <listitem> + <para>With transition conflicts (several possible transitions, + disambiguated by mutually exclusive guard conditions), the guard + conditions are tried in reverse order of their transition definition + in the transition table. The first one returning <code>true</code> + selects its transition. Note that this is not defined by the UML + standard, which simply specifies that if the guard conditions are + not mutually exclusive, the state machine is ill-formed and the + behaviour undefined. Relying on this implementation-specific + behaviour will make it harder for the developer to support another + state machine framework.</para> + </listitem> + <listitem> + <para>If at least one region processes the event, this event is seen as + having been accepted. If not, the library calls + <code>no_transition</code> on the state machine for every + contained region.</para> + </listitem> + <listitem> + <para>If the currently active state is a submachine, the behaviour is + slightly different. The UML standard specifies that internal + transitions have to be tried first, so the event is first dispatched + to the submachine. Only if the submachine does not accept the event + are other (non internal) transitions tried.</para> + </listitem> + <listitem> + <para>This back-end supports simple states' and submachines' internal + transitions. These are provided in the state's + <code>internal_transition_table</code> type. Transitions defined + in this table are added at the end of the main state machine's + transition table, but with a lesser priority than the submachine's + transitions (defined in <code>transition_table</code>). This means, + for simple states, that these transitions have higher priority than + non-internal transitions, conform to the UML standard which gives + higher priority to deeper-level transitions. For submachines, this + is a non-standard addition which can help make event processing + faster by giving a chance to bypass subregion processing. With + standard UML, one would need to add a subregion only to process + these internal transitions, which would be slower.</para> + </listitem> + <listitem> + <para>After the dispatching itself, the deferred event marked in step 3 + (if any) now gets a chance of processing.</para> + </listitem> + <listitem> + <para>Then, events queued in the message queue also get a dispatching + chance</para> + </listitem> + <listitem> + <para>Finally, completion / anonymous transitions, if to be found in the + transition table, also get their dispatching chance.</para> + </listitem> + </itemizedlist></para> + <para>This algorithm illustrates how the back-end configures itself at compile-time + as much as possible. Every feature not found in a given state machine definition + is deactivated and has therefore no runtime cost. Completion events, deferred + events, terminate states, dispatching to several regions, internal transitions + are all deactivated if not used. User configuration is only for exception + handling and message queue necessary.</para> + </sect1> + <sect1> + <title><command xml:id="internals-front-back-interface"/>Frontend / Backend + interface</title> + <para>The design of MSM tries to make front-ends and back-ends (later) to be as + interchangeable as possible. Of course, no back-end will ever implement every + feature defined by any possible front-end and inversely, but the goal is to make + it as easy as possible to extend the current state of the library.</para> + <para>To achieve this, MSM divides the functionality between both sides: the + front-end is a sort of user interface and is descriptive, the back-end + implements the state machine engine.</para> + <para>MSM being based on a transition table, a concrete state machine (or a given + front-end) must provide a transition_table. This transition table must be made + of rows. And each row must tell what kind of transition it is and implement the + calls to the actions and guards. A state machine must also define its regions + (marked by initial states) And that is about the only constraints for + front-ends. How the rows are described is implementer's choice. </para> + <para>Every row must provide:</para> + <itemizedlist> + <listitem> + <para>A <code>Source</code> typedef indicating, well, the type of the source + state.</para> + </listitem> + <listitem> + <para>A <code>Target</code> typedef indicating, well, the type of the target + state.</para> + </listitem> + <listitem> + <para>A <code>Evt</code> typedef indicating the type of the event triggering + the transition.</para> + </listitem> + <listitem> + <para>A <code>row_type_tag</code> typedef indicating the type of the + transition.</para> + </listitem> + <listitem> + <para>Rows having a type requiring transition actions must provide a static + function <code>action_call</code> with the following signature: <code> + template <class Fsm,class SourceState,class TargetState,class + AllStates> </code></para> + <para><code>static void action_call (Fsm& fsm, Event const& evt, + SourceState&, TargetState&, AllStates&) </code></para> + <para>The function gets as parameters the (back-end) state machine, the + event, source and target states and a container (in the current + back-end, a fusion::set) of all the states defined in the state machine. + For example, as the back-end has the front-end as basic class, + <code>action_call</code> is simply defined as + <code>(fsm.*action)(evt)</code>.</para> + </listitem> + <listitem> + <para>Rows having a type requiring a guard must provide a static function + <code>guard_call</code> with the following signature:<code + > </code></para> + <para><code>template <class Fsm,class SourceState,class TargetState,class + AllStates></code></para> + <para><code>static bool guard_call (Fsm&, Event const&, + SourceState&, TargetState&, AllStates&)</code></para> + </listitem> + <listitem> + <para>The possible transition (row) types are:<itemizedlist> + <listitem> + <para>a_row_tag: a transition with actions and no guard</para> + </listitem> + <listitem> + <para>g_row_type: a transition with a guard and no + actions</para> + </listitem> + <listitem> + <para>_row_tag: a transition without actions or guard</para> + </listitem> + <listitem> + <para>row_tag: a transition with guard and actions</para> + </listitem> + <listitem> + <para>a_irow_tag: an internal transition (defined inside the + <code>transition_table</code>) with actions</para> + </listitem> + <listitem> + <para>g_irow_tag: an internal transition (defined inside the + <code>transition_table</code>) with guard</para> + </listitem> + <listitem> + <para>irow_tag: an internal transition (defined inside the + <code>transition_table</code>) with actions and + guards</para> + </listitem> + <listitem> + <para>_irow_tag: an internal transition (defined inside the + <code>transition_table</code>) without action or guard. + Due to higher priority for internal transitions, this is + equivalent to a "ignore event"</para> + </listitem> + <listitem> + <para>sm_a_i_row_tag: an internal transition (defined inside the + <code>internal_transition_table</code>) with + actions</para> + </listitem> + <listitem> + <para>sm_g_i_row_tag: an internal transition (defined inside the + <code>internal_transition_table</code>) with + guard</para> + </listitem> + <listitem> + <para>sm_i_row_tag: an internal transition (defined inside the + <code>internal_transition_table</code>) with actions and + guards</para> + </listitem> + <listitem> + <para>sm__i_row_tag: an internal transition (defined inside the + <code>internal_transition_table</code>) without action + or guard. Due to higher priority for internal transitions, + this is quivalent to a "ignore event"</para> + </listitem> + </itemizedlist></para> + </listitem> + </itemizedlist> + <para>Furthermore, a front-end must provide the definition of states and state + machines. State machine definitions must provide (the implementer is free to + provide it or let it be done by every concrete state machine. Different MSM + front-ends took one or the other approach):<itemizedlist> + <listitem> + <para><code>initial_state</code>: This typedef can be a single state or + a mpl container and provides the initial states defining one or + several orthogonal regions.</para> + </listitem> + <listitem> + <para><code>transition_table</code>: This typedef is a MPL sequence of + transition rows.</para> + </listitem> + <listitem> + <para><code>configuration</code>: this typedef is a MPL sequence of + known types triggering special behavior in the back-end, for example + if a concrete fsm requires a message queue or exception + catching.</para> + </listitem> + </itemizedlist></para> + <para>States and state machines must both provide a (possibly empty) definition of:<itemizedlist> + <listitem> + <para><code>flag_list</code>: the flags being active when this state or + state machine become the current state of the fsm.</para> + </listitem> + <listitem> + <para><code>deferred_events</code>: events being automatically deferred + when the state is the current state of the fsm.</para> + </listitem> + <listitem> + <para><code>internal_transition_table</code>: the internal transitions + of this state.</para> + </listitem> + <listitem> + <para><code>on_entry</code> and <code>on_exit</code> methods.</para> + </listitem> + </itemizedlist></para> + </sect1> + <sect1> + <title><command xml:id="internals-state-id"/> Generated state ids </title> + <para>Normally, one does not need to know the ids are generated for all the states + of a state machine, unless for debugging purposes, like the pstate function does + in the tutorials in order to display the name of the current state. This section + will show how to automatically display typeid-generated names, but these are not + very readable on all platforms, so it can help to know how the ids are + generated. The ids are generated using the transition table, from the “Start†+ column up to down, then from the “Next†column, up to down, as shown in the next + image: </para> + <para><inlinemediaobject> + <imageobject> + <imagedata fileref="images/AnnexA.jpg" width="90%" scalefit="1"/> + </imageobject> + </inlinemediaobject></para> + <para>Stopped will get id 0, Open id 1, ErrorMode id 6 and SleepMode (seen only in + the “Next†column) id 7. If you have some implicitly created states, like + transition-less initial states or states created using the explicit_creation + typedef, these will be added as a source at the end of the transition table. If + you have submachine states, a row will be added for them at the end of the + table, after the automatically or explicitly created states, which can change + their id. The next help you will need for debugging would be to call the + current_state method of the state_machine class, then the display_type helper to + generate a readable name from the id. If you do not want to go through the + transition table to fill an array of names, the library provides another helper, + fill_state_names, which, given an array of sufficient size (please see next + section to know how many states are defined in the state machine), will fill it + with typeid-generated names. </para> + </sect1> + <sect1> + <title>Metaprogramming tools</title> + <para>We can find for the transition table more uses than what we have seen so far. + Let's suppose you need to write a coverage tool. A state machine would be + perfect for such a job, if only it could provide some information about its + structure. Thanks to the transition table and Boost.MPL, it does.</para> + <para>What is needed for a coverage tool? You need to know how many states are + defined in the state machine, and how many events can be fired. This way you can + log the fired events and the states visited in the life of a concrete machine + and be able to perform some coverage analysis, like “fired 65% of all possible + events and visited 80% of the states defined in the state machineâ€. To achieve + this, MSM provides a few useful tools:<itemizedlist> + <listitem> + <para>generate_state_set<transition table>: returns a mpl::set of all + the states defined in the table.</para> + </listitem> + <listitem> + <para>generate_event_set<transition table>: returns a mpl::set of all + the events defined in the table.</para> + </listitem> + <listitem> + <para>using mpl::size<>::value you can get the number of elements in + the set.</para> + </listitem> + <listitem> + <para>display_type defines an operator() sending typeid(Type).name() to + cout.</para> + </listitem> + <listitem> + <para>fill_state_names fills an array of char const* with names of all + states (found by typeid)</para> + </listitem> + <listitem> + <para>using mpl::for_each on the result of generate_state_set and + generate_event_set passing display_type as argument will display all + the states of the state machine.</para> + </listitem> + <listitem> + <para>let's suppose you need to recursively find the states and events + defined in the composite states and thus also having a transition + table. Calling recursive_get_transition_table<Composite> will + return you the transition table of the composite state, recursively + adding the transition tables of all sub-state machines and + sub-sub...-sub-state machines. Then call generate_state_set or + generate_event_set on the result to get the full list of states and + events. </para> + </listitem> + </itemizedlist></para> + <para> An <link xlink:href="examples/BoostCon09Full.cpp">example</link> shows the + tools in action. </para> + </sect1> + </chapter> + <chapter> + <title>Acknowledgements</title> + <para>I am in debt to the following people who helped MSM along the way.</para> + <sect1> + <title>MSM v2</title> + <para> + <itemizedlist> + <listitem> + <para>Thanks to Dave Abrahams for managing the review</para> + </listitem> + <listitem> + <para>Thanks to Eric Niebler for his patience correcting my grammar + errors</para> + </listitem> + <listitem> + <para>Special thanks to Joel de Guzman who gave me very good ideas at + the BoostCon09. These ideas were the starting point of the redesign. + Any time again, Joel ☺</para> + </listitem> + <listitem> + <para>Thanks to Richard O’Hara for making Green Hills bring a patch in + less than 1 week, thus adding one more compiler to the supported + list.</para> + </listitem> + <listitem> + <para>Big thanks to those who took the time to write a review: Franz + Alt, David Bergman, Michael Caisse, Barend Gehrels, Darryl Greene, + Juraj Ivancic, Erik Nelson, Kenny Riddile.</para> + </listitem> + <listitem> + <para>Thanks to Matt Calabrese, Juraj Ivancic, Adam Merz and Joseph Wu + for reporting bugs.</para> + </listitem> + <listitem> + <para>Thanks to Thomas Mistretta for providing an addition to the + section "What do you actually do inside actions / guards".</para> + </listitem> + </itemizedlist> + </para> + </sect1> + <sect1> + <title> MSM v1</title> + <para> + <itemizedlist> + <listitem> + <para>The original version of this framework is based on the brilliant + work of David Abrahams and Aleksey Gurtovoy who laid down the base + and the principles of the framework in their excellent book, “C++ + template Metaprogrammingâ€. The implementation also makes heavy use + of the boost::mpl.</para> + </listitem> + <listitem> + <para>Thanks to Jeff Flinn for his idea of the user-defined base state + and his review which allowed MSM to be presented at the + BoostCon09.</para> + </listitem> + <listitem> + <para>Thanks to my MSM v1 beta testers, Christoph Woskowski and Franz + Alt for using the framework with little documentation and to my + private reviewer, Edouard Alligand</para> + </listitem> + </itemizedlist> + </para> + </sect1> + </chapter> + <chapter> + <title>Version history</title> + <sect1> + <title>From V2.21 to V2.22 (Boost 1.48)</title> + <para> + <itemizedlist> + <listitem> + <para>eUML: added easier event reprocessing: + <code>process(event_)</code> and <code>reprocess()</code></para> + </listitem> + <listitem> + <para>Rewrite of internal transition tables. There were a few bugs + (failing recursivity in internal transition tables of sub-sub + machines) and a missing feature (unused internal transition table of + the main state machine).</para> + </listitem> + <listitem> + <para>Bugfixes<itemizedlist> + <listitem> + <para>Reverted favor_compile_time policy to Boost 1.46 + state</para> + </listitem> + <listitem> + <para><code>none</code> event now is convertible from any + other event </para> + </listitem> + <listitem> + <para>eUML and pseudo exit states</para> + </listitem> + <listitem> + <para>Fixed not working Flag_AND</para> + </listitem> + <listitem> + <para>Fixed rare bugs causing multiple processing of the + same event in a submachine whose transition table + contains this event and a base event of it.</para> + </listitem> + <listitem> + <para>gcc warnings about unused variables</para> + </listitem> + </itemizedlist></para> + </listitem> + <listitem> + <para>Breaking change: the new internal transition table feature causes + a minor breaking change. In a submachine, the "Fsm" template + parameter for guards / actions of an internal table declared using + <code>internal_transition_table</code> now is the submachine, + not the higher-level state machine. Internal transitions declared + using internal rows in the higher-level state machine keep their + behavior (the "Fsm" parameter is the higher-level state machine). To + sum up, the internal transition "Fsm" parameter is the closest state + machine containing this transition.</para> + </listitem> + </itemizedlist> + </para> + </sect1> + <sect1> + <title>From V2.20 to V2.21 (Boost 1.47)</title> + <para> + <itemizedlist> + <listitem> + <para>Added a <command xlink:href="#backend-start">stop()</command> + method in the back-end.</para> + </listitem> + <listitem> + <para><command xlink:href="#eUML-phoenix">Added partial support for + Boost.Phoenix functors in eUML</command></para> + </listitem> + <listitem> + <para>Added the possibility to choose when <command xlink:href="#backend-state-switch">state switching</command> + occurs.</para> + </listitem> + <listitem> + <para>Bugfixes<itemizedlist> + <listitem> + <para>Trac 5117, 5253, 5533, 5573</para> + </listitem> + <listitem> + <para>gcc warnings about unused variables</para> + </listitem> + <listitem> + <para>better implemenation of favor_compile_time back-end + policy</para> + </listitem> + <listitem> + <para>bug with eUML and state construction</para> + </listitem> + <listitem> + <para>incorrect eUML event and state macros</para> + </listitem> + <listitem> + <para>incorrect event type passed to a direct entry state's + on_entry action</para> + </listitem> + <listitem> + <para>more examples</para> + </listitem> + </itemizedlist></para> + </listitem> + </itemizedlist> + </para> + </sect1> + <sect1> + <title>From V2.12 to V2.20 (Boost 1.46)</title> + <para> + <itemizedlist> + <listitem> + <para>Compile-time state machine analysis using mpl_graph:</para> + <itemizedlist> + <listitem> + <para><command xlink:href="#backend-compile-time-analysis">checking of region orthogonality</command>.</para> + </listitem> + <listitem> + <para><command xlink:href="#backend-compile-time-analysis">search for unreachable states</command>.</para> + </listitem> + <listitem> + <para><command xlink:href="#explicit-entry-no-region-id">automatic region index search for pseudo entry or explicit + entry states</command>.</para> + </listitem> + </itemizedlist> + </listitem> + <listitem> + <para><command xlink:href="#backend-boost-parameter">Boost.Parameter interface definition</command> for + msm::back::state_machine<> template arguments.</para> + </listitem> + <listitem> + <para><command xlink:href="#backend-queues">Possibility to provide a + container</command> for the event and deferred event queues. A + policy implementation based on a more efficient Boost.CircularBuffer + is provided.</para> + </listitem> + <listitem> + <para>msm::back::state_machine<>::is_flag_active method made + const.</para> + </listitem> + <listitem> + <para>added possibility to <command xlink:href="#backend-enqueueing" + >enqueue events</command> for delayed processing.</para> + </listitem> + <listitem> + <para>Bugfixes<itemizedlist> + <listitem> + <para>Trac 4926</para> + </listitem> + <listitem> + <para>stack overflow using the Defer functor</para> + </listitem> + <listitem> + <para>anonymous transition of a submachine not called for + the initial state</para> + </listitem> + </itemizedlist></para> + </listitem> + </itemizedlist> + </para> + </sect1> + <sect1> + <title>From V2.10 to V2.12 (Boost 1.45)</title> + <para> + <itemizedlist> + <listitem> + <para>Support for <command xlink:href="#back-end-serialization">serialization</command></para> + </listitem> + <listitem> + <para><command xlink:href="#eUML-reuse-functor">Possibility to use + normal functors</command> (from functor front-end) in + eUML.</para> + </listitem> + <listitem> + <para><command xlink:href="#backend-fsm-constructor-args">New constructors</command> where substates / submachines can be taken as + arguments. This allows passing arguments to the constructor of a + submachine.</para> + </listitem> + <listitem> + <para>Bugfixes</para> + </listitem> + </itemizedlist> + </para> + </sect1> + <sect1> + <title>From V2.0 to V2.12 (Boost 1.44)</title> + <para> + <itemizedlist> + <listitem> + <para>New documentation</para> + </listitem> + <listitem> + <para>Internal transitions. Either as part of the transition table or + using a state's internal transition table</para> + </listitem> + <listitem> + <para>increased dispatch and copy speed</para> + </listitem> + <listitem> + <para><command xlink:href="#basic-row2">new row types</command> for the + basic front-end</para> + </listitem> + <listitem> + <para>new eUML syntax, better attribute support, macros to ease + developer's life. Even VC8 seems to like it better.</para> + </listitem> + <listitem> + <para>New policy for reduced compile-time at the cost of dispatch + speed</para> + </listitem> + <listitem> + <para>Support for base events</para> + </listitem> + <listitem> + <para>possibility to choose the initial event</para> + </listitem> + </itemizedlist> + </para> + </sect1> + </chapter> + </part> + <part> + <title><command xml:id="Reference-begin"/>Reference</title> + <chapter> + <title>eUML operators and basic helpers</title> + <para>The following table lists the supported operators: </para> + <para> + <table frame="all"> + <title>Operators and state machine helpers</title> + <tgroup cols="3"> + <colspec colname="c1" colnum="1"/> + <colspec colname="c2" colnum="2"/> + <colspec colname="c3" colnum="3"/> + <thead> + <row> + <entry>eUML function / operator</entry> + <entry>Description</entry> + <entry>Functor</entry> + </row> + </thead> + <tbody> + <row> + <entry>&&</entry> + <entry>Calls lazily Action1&& Action2</entry> + <entry>And_</entry> + </row> + <row> + <entry>||</entry> + <entry>Calls lazily Action1|| Action2</entry> + <entry>Or_</entry> + </row> + <row> + <entry>!</entry> + <entry>Calls lazily !Action1</entry> + <entry>Not_</entry> + </row> + <row> + <entry>!=</entry> + <entry>Calls lazily Action1 != Action2</entry> + <entry>NotEqualTo_</entry> + </row> + <row> + <entry>==</entry> + <entry>Calls lazily Action1 == Action2</entry> + <entry>EqualTo_</entry> + </row> + <row> + <entry>></entry> + <entry>Calls lazily Action1 > Action2</entry> + <entry>Greater_</entry> + </row> + <row> + <entry>>=</entry> + <entry>Calls lazily Action1 >= Action2</entry> + <entry>Greater_Equal_</entry> + </row> + <row> + <entry><</entry> + <entry>Calls lazily Action1 < Action2</entry> + <entry>Less_</entry> + </row> + <row> + <entry><=</entry> + <entry>Calls lazily Action1 <= Action2</entry> + <entry>Less_Equal_</entry> + </row> + <row> + <entry>&</entry> + <entry>Calls lazily Action1 & Action2</entry> + <entry>Bitwise_And_</entry> + </row> + <row> + <entry>|</entry> + <entry>Calls lazily Action1 | Action2</entry> + <entry>Bitwise_Or_</entry> + </row> + <row> + <entry>^</entry> + <entry>Calls lazily Action1 ^ Action2</entry> + <entry>Bitwise_Xor_</entry> + </row> + <row> + <entry>--</entry> + <entry>Calls lazily --Action1 / Action1--</entry> + <entry>Pre_Dec_ / Post_Dec_</entry> + </row> + <row> + <entry>++</entry> + <entry>Calls lazily ++Action1 / Action1++</entry> + <entry>Pre_Inc_ / Post_Inc_</entry> + </row> + <row> + <entry>/</entry> + <entry>Calls lazily Action1 / Action2</entry> + <entry>Divides_</entry> + </row> + <row> + <entry>/=</entry> + <entry>Calls lazily Action1 /= Action2</entry> + <entry>Divides_Assign_</entry> + </row> + <row> + <entry>*</entry> + <entry>Calls lazily Action1 * Action2</entry> + <entry>Multiplies_</entry> + </row> + <row> + <entry>*=</entry> + <entry>Calls lazily Action1 *= Action2</entry> + <entry>Multiplies_Assign_</entry> + </row> + <row> + <entry>+ (binary)</entry> + <entry>Calls lazily Action1 + Action2</entry> + <entry>Plus_</entry> + </row> + <row> + <entry>+ (unary)</entry> + <entry>Calls lazily +Action1</entry> + <entry>Unary_Plus_</entry> + </row> + <row> + <entry>+=</entry> + <entry>Calls lazily Action1 += Action2</entry> + <entry>Plus_Assign_</entry> + </row> + <row> + <entry>- (binary)</entry> + <entry>Calls lazily Action1 - Action2</entry> + <entry>Minus_</entry> + </row> + <row> + <entry>- (unary)</entry> + <entry>Calls lazily -Action1</entry> + <entry>Unary_Minus_</entry> + </row> + <row> + <entry>-=</entry> + <entry>Calls lazily Action1 -= Action2</entry> + <entry>Minus_Assign_</entry> + </row> + <row> + <entry>%</entry> + <entry>Calls lazily Action1 % Action2</entry> + <entry>Modulus_</entry> + </row> + <row> + <entry>%=</entry> + <entry>Calls lazily Action1 %= Action2</entry> + <entry>Modulus_Assign_</entry> + </row> + <row> + <entry>>></entry> + <entry>Calls lazily Action1 >> Action2</entry> + <entry>ShiftRight_</entry> + </row> + <row> + <entry>>>=</entry> + <entry>Calls lazily Action1 >>= Action2</entry> + <entry>ShiftRight_Assign_</entry> + </row> + <row> + <entry><<</entry> + <entry>Calls lazily Action1 << Action2</entry> + <entry>ShiftLeft_</entry> + </row> + <row> + <entry><<=</entry> + <entry>Calls lazily Action1 <<= Action2</entry> + <entry>ShiftLeft_Assign_</entry> + </row> + <row> + <entry>[] (works on vector, map, arrays)</entry> + <entry>Calls lazily Action1 [Action2]</entry> + <entry>Subscript_</entry> + </row> + <row> + <entry>if_then_else_(Condition,Action1,Action2)</entry> + <entry>Returns either the result of calling Action1 or the result of + calling Action2</entry> + <entry>If_Else_</entry> + </row> + <row> + <entry>if_then_(Condition,Action)</entry> + <entry>Returns the result of calling Action if Condition</entry> + <entry>If_Then_</entry> + </row> + <row> + <entry>while_(Condition, Body)</entry> + <entry>While Condition(), calls Body(). Returns nothing</entry> + <entry>While_Do_</entry> + </row> + <row> + <entry>do_while_(Condition, Body)</entry> + <entry>Calls Body() while Condition(). Returns nothing</entry> + <entry>Do_While_</entry> + </row> + <row> + <entry>for_(Begin,Stop,EndLoop,Body)</entry> + <entry>Calls for(Begin;Stop;EndLoop){Body;}</entry> + <entry>For_Loop_</entry> + </row> + <row> + <entry>process_(Event [,fsm1] [,fsm2] [,fsm3] [,fsm4])</entry> + <entry>Processes Event on the current state machine (if no fsm + specified) or on up to 4 state machines returned by an + appropriate functor.</entry> + <entry>Process_</entry> + </row> + <row> + <entry>process2_(Event, Data [,fsm1] [,fsm2] [,fsm3])</entry> + <entry>Processes Event on the current state machine (if no fsm + specified) or on up to 2 state machines returned by an + appropriate functor. The event is copy-constructed from what + Data() returns.</entry> + <entry>Process2_</entry> + </row> + <row> + <entry>is_flag_(Flag [,fsm])</entry> + <entry>Calls is_flag_active() on the current state machine or the + one returned by calling fsm.</entry> + <entry>Get_Flag_</entry> + </row> + <row> + <entry>event_ [(attribute name)]</entry> + <entry>Returns the current event (as const reference)</entry> + <entry>GetEvent_</entry> + </row> + <row> + <entry>source_ [(attribute name)]</entry> + <entry>Returns the source state of the currently triggered + transition (as reference). If an attribute name is provided, + returns the attribute by reference.</entry> + <entry>GetSource_</entry> + </row> + <row> + <entry>target_ [(attribute name)]</entry> + <entry>Returns the target state of the currently triggered + transition (as reference). If an attribute name is provided, + returns the attribute by reference.</entry> + <entry>GetTarget_</entry> + </row> + <row> + <entry>state_ [(attribute name)]</entry> + <entry>Returns the source state of the currently active state (as + reference). Valid inside a state entry/exit action. If an + attribute name is provided, returns the attribute by + reference.</entry> + <entry>GetState_</entry> + </row> + <row> + <entry>fsm_ [(attribute name)]</entry> + <entry>Returns the current state machine (as reference). Valid + inside a state entry/exit action or a transition. If an + attribute name is provided, returns the attribute by + reference.</entry> + <entry>GetFsm_</entry> + </row> + <row> + <entry>substate_(state_name [,fsm])</entry> + <entry>Returns (as reference) the state state_name referenced in the + current state machine or the one given as argument.</entry> + <entry>SubState_</entry> + </row> + </tbody> + </tgroup> + </table> + </para> + <para>To use these functions, you need to include: </para> + <para><code>#include <msm/front/euml/euml.hpp></code></para> + </chapter> + <chapter> + <title> + <command xml:id="eUML-STL-all"/>Functional programming </title> + <para>To use these functions, you need to include: </para> + <para><code>#include <msm/front/euml/stl.hpp></code></para> + <para>or the specified header in the following tables.</para> + <para>The following tables list the supported STL algorithms: </para> + <para> + <command xml:id="eUML-STL-querying"/> + <table frame="all"> + <title>STL algorithms</title> + <tgroup cols="2"> + <colspec colname="c2" colnum="1"/> + <colspec colname="c3" colnum="2"/> + <thead> + <row> + <entry>STL algorithms in querying.hpp</entry> + <entry>Functor</entry> + </row> + </thead> + <tbody> + <row> + <entry>find_(first, last, value)</entry> + <entry>Find_</entry> + </row> + <row> + <entry>find_if_(first, last, value)</entry> + <entry>FindIf_</entry> + </row> + <row> + <entry>lower_bound_(first, last, value [,opᵃ])</entry> + <entry>LowerBound_</entry> + </row> + <row> + <entry>upper_bound_(first, last, value [,opᵃ])</entry> + <entry>UpperBound_</entry> + </row> + <row> + <entry>equal_range_(first, last, value [,opᵃ])</entry> + <entry>EqualRange_</entry> + </row> + <row> + <entry>binary_search_(first, last, value [,opᵃ])</entry> + <entry>BinarySearch_</entry> + </row> + <row> + <entry>min_element_(first, last[,opᵃ])</entry> + <entry>MinElement_</entry> + </row> + <row> + <entry>max_element_(first, last[,opᵃ])</entry> + <entry>MaxElement_</entry> + </row> + <row> + <entry>adjacent_find_(first, last[,opᵃ])</entry> + <entry>AdjacentFind_</entry> + </row> + <row> + <entry>find_end_( first1, last1, first2, last2 [,op ᵃ])</entry> + <entry>FindEnd_</entry> + </row> + <row> + <entry>find_first_of_( first1, last1, first2, last2 [,op ᵃ])</entry> + <entry>FindFirstOf_</entry> + </row> + <row> + <entry>equal_( first1, last1, first2 [,op ᵃ])</entry> + <entry>Equal_</entry> + </row> + <row> + <entry>search_( first1, last1, first2, last2 [,op ᵃ])</entry> + <entry>Search_</entry> + </row> + <row> + <entry>includes_( first1, last1, first2, last2 [,op ᵃ])</entry> + <entry>Includes_</entry> + </row> + <row> + <entry>lexicographical_compare_ ( first1, last1, first2, last2 [,op + ᵃ]) </entry> + <entry>LexicographicalCompare_</entry> + </row> + <row> + <entry>count_(first, last, value [,size])</entry> + <entry>Count_</entry> + </row> + <row> + <entry>count_if_(first, last, op ᵃ [,size])</entry> + <entry>CountIf_</entry> + </row> + <row> + <entry>distance_(first, last)</entry> + <entry>Distance_</entry> + </row> + <row> + <entry>mismatch _( first1, last1, first2 [,op ᵃ])</entry> + <entry>Mismatch_</entry> + </row> + </tbody> + </tgroup> + </table> + </para> + <para> + <command xml:id="eUML-STL-iteration"/> + <table frame="all"> + <title>STL algorithms</title> + <tgroup cols="2"> + <colspec colname="c2" colnum="1"/> + <colspec colname="c3" colnum="2"/> + <thead> + <row> + <entry>STL algorithms in iteration.hpp</entry> + <entry>Functor</entry> + </row> + </thead> + <tbody> + <row> + <entry>for_each_(first,last, unary opᵃ)</entry> + <entry>ForEach_</entry> + </row> + <row> + <entry>accumulate_first, last, init [,opᵃ])</entry> + <entry>Accumulate_</entry> + </row> + </tbody> + </tgroup> + </table> + </para> + <para> + <command xml:id="eUML-STL-transformation"/> + <table> + <title>STL algorithms</title> + <tgroup cols="2"> + <colspec colname="c2" colnum="1"/> + <colspec colname="c3" colnum="2"/> + <thead> + <row> + <entry>STL algorithms in transformation.hpp</entry> + <entry>Functor</entry> + </row> + </thead> + <tbody> + <row> + <entry>copy_(first, last, result)</entry> + <entry>Copy_</entry> + </row> + <row> + <entry>copy_backward_(first, last, result)</entry> + <entry>CopyBackward_</entry> + </row> + <row> + <entry>reverse_(first, last)</entry> + <entry>Reverse_</entry> + </row> + <row> + <entry>reverse_copy_(first, last , result)</entry> + <entry>ReverseCopy_</entry> + </row> + <row> + <entry>remove_(first, last, value)</entry> + <entry>Remove_</entry> + </row> + <row> + <entry>remove_if_(first, last , opᵃ)</entry> + <entry>RemoveIf_</entry> + </row> + <row> + <entry>remove_copy_(first, last , output, value)</entry> + <entry>RemoveCopy_</entry> + </row> + <row> + <entry>remove_copy_if_(first, last, output, opᵃ)</entry> + <entry>RemoveCopyIf_</entry> + </row> + <row> + <entry>fill_(first, last, value)</entry> + <entry>Fill_</entry> + </row> + <row> + <entry>fill_n_(first, size, value)ᵇ</entry> + <entry>FillN_</entry> + </row> + <row> + <entry>generate_(first, last, generatorᵃ)</entry> + <entry>Generate_</entry> + </row> + <row> + <entry>generate_(first, size, generatorᵃ)ᵇ</entry> + <entry>GenerateN_</entry> + </row> + <row> + <entry>unique_(first, last [,opᵃ])</entry> + <entry>Unique_</entry> + </row> + <row> + <entry>unique_copy_(first, last, output [,opᵃ])</entry> + <entry>UniqueCopy_</entry> + </row> + <row> + <entry>random_shuffle_(first, last [,opᵃ])</entry> + <entry>RandomShuffle_</entry> + </row> + <row> + <entry>rotate_copy_(first, middle, last, output)</entry> + <entry>RotateCopy_</entry> + </row> + <row> + <entry>partition_ (first, last [,opᵃ])</entry> + <entry>Partition_</entry> + </row> + <row> + <entry>stable_partition_ (first, last [,opᵃ])</entry> + <entry>StablePartition_</entry> + </row> + <row> + <entry>stable_sort_(first, last [,opᵃ])</entry> + <entry>StableSort_</entry> + </row> + <row> + <entry>sort_(first, last [,opᵃ])</entry> + <entry>Sort_</entry> + </row> + <row> + <entry>partial_sort_(first, middle, last [,opᵃ])</entry> + <entry>PartialSort_</entry> + </row> + <row> + <entry>partial_sort_copy_ (first, last, res_first, res_last [,opᵃ]) </entry> + <entry>PartialSortCopy_</entry> + </row> + <row> + <entry>nth_element_(first, nth, last [,opᵃ])</entry> + <entry>NthElement_</entry> + </row> + <row> + <entry>merge_( first1, last1, first2, last2, output [,op ᵃ])</entry> + <entry>Merge_</entry> + </row> + <row> + <entry>inplace_merge_(first, middle, last [,opᵃ])</entry> + <entry>InplaceMerge_</entry> + </row> + <row> + <entry>set_union_(first1, last1, first2, last2, output [,op + ᵃ])</entry> + <entry>SetUnion_</entry> + </row> + <row> + <entry>push_heap_(first, last [,op ᵃ])</entry> + <entry>PushHeap_</entry> + </row> + <row> + <entry>pop_heap_(first, last [,op ᵃ])</entry> + <entry>PopHeap_</entry> + </row> + <row> + <entry>make_heap_(first, last [,op ᵃ])</entry> + <entry>MakeHeap_</entry> + </row> + <row> + <entry>sort_heap_(first, last [,op ᵃ])</entry> + <entry>SortHeap_</entry> + </row> + <row> + <entry>next_permutation_(first, last [,op ᵃ])</entry> + <entry>NextPermutation_</entry> + </row> + <row> + <entry>prev_permutation_(first, last [,op ᵃ])</entry> + <entry>PrevPermutation_</entry> + </row> + <row> + <entry>inner_product_(first1, last1, first2, init [,op1ᵃ] [,op2ᵃ]) </entry> + <entry>InnerProduct_</entry> + </row> + <row> + <entry>partial_sum_(first, last, output [,opᵃ])</entry> + <entry>PartialSum_</entry> + </row> + <row> + <entry>adjacent_difference_(first, last, output [,opᵃ])</entry> + <entry>AdjacentDifference_</entry> + </row> + <row> + <entry>replace_(first, last, old_value, new_value)</entry> + <entry>Replace_</entry> + </row> + <row> + <entry>replace_if_(first, last, opᵃ, new_value)</entry> + <entry>ReplaceIf_</entry> + </row> + <row> + <entry>replace_copy_(first, last, result, old_value, + new_value)</entry> + <entry>ReplaceCopy_</entry> + </row> + <row> + <entry>replace_copy_if_(first, last, result, opᵃ, new_value)</entry> + <entry>ReplaceCopyIf_</entry> + </row> + <row> + <entry>rotate_(first, middle, last)ᵇ</entry> + <entry>Rotate_</entry> + </row> + </tbody> + </tgroup> + </table> + </para> + <para> + <command xml:id="eUML-STL-container"/> + <table> + <title>STL container methods</title> + <tgroup cols="2"> + <colspec colname="c2" colnum="1"/> + <colspec colname="c3" colnum="2"/> + <thead> + <row> + <entry>STL container methods(common) in container.hpp</entry> + <entry>Functor</entry> + </row> + </thead> + <tbody> + <row> + <entry>container::reference front_(container)</entry> + <entry>Front_</entry> + </row> + <row> + <entry>container::reference back_(container)</entry> + <entry>Back_</entry> + </row> + <row> + <entry>container::iterator begin_(container)</entry> + <entry>Begin_</entry> + </row> + <row> + <entry>container::iterator end_(container)</entry> + <entry>End_</entry> + </row> + <row> + <entry>container::reverse_iterator rbegin_(container)</entry> + <entry>RBegin_</entry> + </row> + <row> + <entry>container::reverse_iterator rend_(container)</entry> + <entry>REnd_</entry> + </row> + <row> + <entry>void push_back_(container, value)</entry> + <entry>Push_Back_</entry> + </row> + <row> + <entry>void pop_back_(container, value)</entry> + <entry>Pop_Back_</entry> + </row> + <row> + <entry>void push_front_(container, value)</entry> + <entry>Push_Front_</entry> + </row> + <row> + <entry>void pop_front_(container, value)</entry> + <entry>Pop_Front_</entry> + </row> + <row> + <entry>void clear_(container)</entry> + <entry>Clear_</entry> + </row> + <row> + <entry>size_type capacity_(container)</entry> + <entry>Capacity_</entry> + </row> + <row> + <entry>size_type size_(container)</entry> + <entry>Size_</entry> + </row> + <row> + <entry>size_type max_size_(container)</entry> + <entry>Max_Size_</entry> + </row> + <row> + <entry>void reserve_(container, value)</entry> + <entry>Reserve _</entry> + </row> + <row> + <entry>void resize_(container, value)</entry> + <entry>Resize _</entry> + </row> + <row> + <entry>iterator insert_(container, pos, value)</entry> + <entry>Insert_</entry> + </row> + <row> + <entry>void insert_( container , pos, first, last)</entry> + <entry>Insert_</entry> + </row> + <row> + <entry>void insert_( container , pos, number, value)</entry> + <entry>Insert_</entry> + </row> + <row> + <entry>void swap_( container , other_container)</entry> + <entry>Swap_</entry> + </row> + <row> + <entry>void erase_( container , pos)</entry> + <entry>Erase_</entry> + </row> + <row> + <entry>void erase_( container , first, last) </entry> + <entry>Erase_</entry> + </row> + <row> + <entry>bool empty_( container)</entry> + <entry>Empty_</entry> + </row> + </tbody> + </tgroup> + </table> + </para> + <para> + <table> + <title>STL list methods</title> + <tgroup cols="2"> + <colspec colname="c2" colnum="1"/> + <colspec colname="c3" colnum="2"/> + <thead> + <row> + <entry>std::list methods in container.hpp</entry> + <entry>Functor</entry> + </row> + </thead> + <tbody> + <row> + <entry>void list_remove_(container, value)</entry> + <entry>ListRemove_</entry> + </row> + <row> + <entry>void list_remove_if_(container, opᵃ)</entry> + <entry>ListRemove_If_</entry> + </row> + <row> + <entry>void list_merge_(container, other_list)</entry> + <entry>ListMerge_</entry> + </row> + <row> + <entry>void list_merge_(container, other_list, opᵃ)</entry> + <entry>ListMerge_</entry> + </row> + <row> + <entry>void splice_(container, iterator, other_list)</entry> + <entry>Splice_</entry> + </row> + <row> + <entry>void splice_(container, iterator, other_list, + iterator)</entry> + <entry>Splice_</entry> + </row> + <row> + <entry>void splice_(container, iterator, other_list, first, + last)</entry> + <entry>Splice_</entry> + </row> + <row> + <entry>void list_reverse_(container)</entry> + <entry>ListReverse_</entry> + </row> + <row> + <entry>void list_unique_(container)</entry> + <entry>ListUnique_</entry> + </row> + <row> + <entry>void list_unique_(container, opᵃ)</entry> + <entry>ListUnique_</entry> + </row> + <row> + <entry>void list_sort_(container)</entry> + <entry>ListSort_</entry> + </row> + <row> + <entry>void list_sort_(container, opᵃ)</entry> + <entry>ListSort_</entry> + </row> + </tbody> + </tgroup> + </table> + </para> + <para> + <table> + <title>STL associative container methods </title> + <tgroup cols="2"> + <colspec colname="c2" colnum="1"/> + <colspec colname="c3" colnum="2"/> + <thead> + <row> + <entry>Associative container methods in container.hpp</entry> + <entry>Functor</entry> + </row> + </thead> + <tbody> + <row> + <entry>iterator insert_(container, pos, value)</entry> + <entry>Insert_</entry> + </row> + <row> + <entry>void insert_( container , first, last)</entry> + <entry>Insert_</entry> + </row> + <row> + <entry>pair<iterator, bool> insert_( container , value)</entry> + <entry>Insert_</entry> + </row> + <row> + <entry>void associative_erase_( container , pos)</entry> + <entry>Associative_Erase_</entry> + </row> + <row> + <entry>void associative_erase_( container , first, last)</entry> + <entry>Associative_Erase_</entry> + </row> + <row> + <entry>size_type associative_erase_( container , key)</entry> + <entry>Associative_Erase_</entry> + </row> + <row> + <entry>iterator associative_find_( container , key)</entry> + <entry>Associative_Find_</entry> + </row> + <row> + <entry>size_type associative_count_( container , key)</entry> + <entry>AssociativeCount_</entry> + </row> + <row> + <entry>iterator associative_lower_bound_( container , key)</entry> + <entry>Associative_Lower_Bound_</entry> + </row> + <row> + <entry>iterator associative_upper_bound_( container , key)</entry> + <entry>Associative_Upper_Bound_</entry> + </row> + <row> + <entry>pair<iterator, iterator> associative_equal_range_( + container , key)</entry> + <entry>Associative_Equal_Range_</entry> + </row> + </tbody> + </tgroup> + </table> + </para> + <para> + <table> + <title>STL pair</title> + <tgroup cols="2"> + <colspec colname="c2" colnum="1"/> + <colspec colname="c3" colnum="2"/> + <thead> + <row> + <entry>std::pair in container.hpp</entry> + <entry>Functor</entry> + </row> + </thead> + <tbody> + <row> + <entry>first_type first_(pair<T1, T2>)</entry> + <entry>First_</entry> + </row> + <row> + <entry>second_type second_(pair<T1, T2>)</entry> + <entry>Second_</entry> + </row> + </tbody> + </tgroup> + </table> + </para> + <para> + <table> + <title>STL string</title> + <tgroup cols="3"> + <colspec colname="newCol1" colnum="1"/> + <colspec colname="c2" colnum="2"/> + <colspec colname="c3" colnum="3"/> + <thead> + <row> + <entry>STL string method</entry> + <entry>std::string method in container.hpp</entry> + <entry>Functor</entry> + </row> + </thead> + <tbody> + <row> + <entry>substr (size_type pos, size_type size)</entry> + <entry>string substr_(container, pos, length)</entry> + <entry>Substr_</entry> + </row> + <row> + <entry>int compare(string)</entry> + <entry>int string_compare_(container, another_string)</entry> + <entry>StringCompare_</entry> + </row> + <row> + <entry>int compare(char*)</entry> + <entry>int string_compare_(container, another_string)</entry> + <entry>StringCompare_</entry> + </row> + <row> + <entry>int compare(size_type pos, size_type size, string)</entry> + <entry>int string_compare_(container, pos, size, + another_string)</entry> + <entry>StringCompare_</entry> + </row> + <row> + <entry>int compare (size_type pos, size_type size, string, size_type + length)</entry> + <entry>int string_compare_(container, pos, size, another_string, + length)</entry> + <entry>StringCompare_</entry> + </row> + <row> + <entry>string& append(const string&)</entry> + <entry>string& append_(container, another_string)</entry> + <entry>Append_</entry> + </row> + <row> + <entry>string& append (charT*)</entry> + <entry>string& append_(container, another_string)</entry> + <entry>Append_</entry> + </row> + <row> + <entry>string& append (string , size_type pos, size_type + size)</entry> + <entry>string& append_(container, other_string, pos, + size)</entry> + <entry>Append_</entry> + </row> + <row> + <entry>string& append (charT*, size_type size)</entry> + <entry>string& append_(container, another_string, + length)</entry> + <entry>Append_</entry> + </row> + <row> + <entry>string& append (size_type size, charT)</entry> + <entry>string& append_(container, size, char)</entry> + <entry>Append_</entry> + </row> + <row> + <entry>string& append (iterator begin, iterator end)</entry> + <entry>string& append_(container, begin, end)</entry> + <entry>Append_</entry> + </row> + <row> + <entry>string& insert (size_type pos, charT*)</entry> + <entry>string& string_insert_(container, pos, + other_string)</entry> + <entry>StringInsert_</entry> + </row> + <row> + <entry>string& insert(size_type pos, charT*,size_type n)</entry> + <entry>string& string_insert_(container, pos, other_string, + n)</entry> + <entry>StringInsert_</entry> + </row> + <row> + <entry>string& insert(size_type pos,size_type n, charT + c)</entry> + <entry>string& string_insert_(container, pos, n, c)</entry> + <entry>StringInsert_</entry> + </row> + <row> + <entry>string& insert (size_type pos, const string&)</entry> + <entry>string& string_insert_(container, pos, + other_string)</entry> + <entry>StringInsert_</entry> + </row> + <row> + <entry>string& insert (size_type pos, const string&, + size_type pos1, size_type n)</entry> + <entry>string& string_insert_(container, pos, other_string, + pos1, n)</entry> + <entry>StringInsert_</entry> + </row> + <row> + <entry>string& erase(size_type pos=0, size_type n=npos)</entry> + <entry>string& string_erase_(container, pos, n)</entry> + <entry>StringErase_</entry> + </row> + <row> + <entry>string& assign(const string&)</entry> + <entry>string& string_assign_(container, another_string)</entry> + <entry>StringAssign_</entry> + </row> + <row> + <entry>string& assign(const charT*)</entry> + <entry>string& string_assign_(container, another_string)</entry> + <entry>StringAssign_</entry> + </row> + <row> + <entry>string& assign(const string&, size_type pos, + size_type n)</entry> + <entry>string& string_assign_(container, another_string, pos, + n)</entry> + <entry>StringAssign_</entry> + </row> + <row> + <entry>string& assign(const charT*, size_type n)</entry> + <entry>string& string_assign_(container, another_string, + n)</entry> + <entry>StringAssign_</entry> + </row> + <row> + <entry>string& assign(size_type n, charT c)</entry> + <entry>string& string_assign_(container, n, c)</entry> + <entry>StringAssign_</entry> + </row> + <row> + <entry>string& assign(iterator first, iterator last)</entry> + <entry>string& string_assign_(container, first, last)</entry> + <entry>StringAssign_</entry> + </row> + <row> + <entry>string& replace(size_type pos, size_type n, const + string&)</entry> + <entry>string& string_replace_(container, pos, n, + another_string)</entry> + <entry>StringReplace_</entry> + </row> + <row> + <entry>string& replace(size_type pos, size_type n, const charT*, + size_type n1)</entry> + <entry>string& string_replace_(container, pos, n, + another_string, n1)</entry> + <entry>StringReplace_</entry> + </row> + <row> + <entry>string& replace(size_type pos, size_type n, const + charT*)</entry> + <entry>string& string_replace_(container, pos, n, + another_string)</entry> + <entry>StringReplace_</entry> + </row> + <row> + <entry>string& replace(size_type pos, size_type n, size_type n1, + charT c)</entry> + <entry>string& string_replace_(container, pos, n, n1, c)</entry> + <entry>StringReplace_</entry> + </row> + <row> + <entry>string& replace(iterator first, iterator last, const + string&)</entry> + <entry>string& string_replace_(container, first, last, + another_string)</entry> + <entry>StringReplace_</entry> + </row> + <row> + <entry>string& replace(iterator first, iterator last, const + charT*, size_type n)</entry> + <entry>string& string_replace_(container, first, last, + another_string, n)</entry> + <entry>StringReplace_</entry> + </row> + <row> + <entry>string& replace(iterator first, iterator last, const + charT*)</entry> + <entry>string& string_replace_(container, first, last, + another_string)</entry> + <entry>StringReplace_</entry> + </row> + <row> + <entry>string& replace(iterator first, iterator last, size_type + n, charT c)</entry> + <entry>string& string_replace_(container, first, last, n, + c)</entry> + <entry>StringReplace_</entry> + </row> + <row> + <entry>string& replace(iterator first, iterator last, iterator + f, iterator l)</entry> + <entry>string& string_replace_(container, first, last, f, + l)</entry> + <entry>StringReplace_</entry> + </row> + <row> + <entry>const charT* c_str()</entry> + <entry>const charT* c_str_(container)</entry> + <entry>CStr_</entry> + </row> + <row> + <entry>const charT* data()</entry> + <entry>const charT* string_data_(container)</entry> + <entry>StringData_</entry> + </row> + <row> + <entry>size_type copy(charT* buf, size_type n, size_type pos = + 0)</entry> + <entry>size_type string_copy_(container, buf, n, pos); size_type + string_copy_(container, buf, n) </entry> + <entry>StringCopy_</entry> + </row> + <row> + <entry>size_type find(charT* s, size_type pos, size_type n)</entry> + <entry>size_type string_find_(container, s, pos, n)</entry> + <entry>StringFind_</entry> + </row> + <row> + <entry>size_type find(charT* s, size_type pos=0)</entry> + <entry>size_type string_find_(container, s, pos); size_type + string_find_(container, s) </entry> + <entry>StringFind_</entry> + </row> + <row> + <entry>size_type find(const string& s, size_type pos=0)</entry> + <entry>size_type string_find_(container, s, pos) size_type + string_find_(container, s) </entry> + <entry>StringFind_</entry> + </row> + <row> + <entry>size_type find(charT c, size_type pos=0)</entry> + <entry>size_type string_find_(container, c, pos) size_type + string_find_(container, c) </entry> + <entry>StringFind_</entry> + </row> + <row> + <entry>size_type rfind(charT* s, size_type pos, size_type n)</entry> + <entry>size_type string_rfind_(container, s, pos, n)</entry> + <entry>StringRFind_</entry> + </row> + <row> + <entry>size_type rfind(charT* s, size_type pos=npos)</entry> + <entry>size_type string_rfind_(container, s, pos); size_type + string_rfind_(container, s) </entry> + <entry>StringRFind_</entry> + </row> + <row> + <entry>size_type rfind(const string& s, size_type + pos=npos)</entry> + <entry>size_type string_rfind_(container, s, pos); size_type + string_rfind_(container, s) </entry> + <entry>StringRFind_</entry> + </row> + <row> + <entry>size_type rfind(charT c, size_type pos=npos)</entry> + <entry>size_type string_rfind_(container, c, pos) size_type + string_rfind_(container, c) </entry> + <entry>StringRFind_</entry> + </row> + <row> + <entry>size_type find_first_of(charT* s, size_type pos, size_type + n)</entry> + <entry>size_type find_first_of_(container, s, pos, n)</entry> + <entry>StringFindFirstOf_</entry> + </row> + <row> + <entry>size_type find_first_of (charT* s, size_type pos=0)</entry> + <entry>size_type find_first_of_(container, s, pos); size_type + find_first_of_(container, s) </entry> + <entry>StringFindFirstOf_</entry> + </row> + <row> + <entry>size_type find_first_of (const string& s, size_type + pos=0)</entry> + <entry>size_type find_first_of_(container, s, pos); size_type + find_first_of_(container, s) </entry> + <entry>StringFindFirstOf_</entry> + </row> + <row> + <entry>size_type find_first_of (charT c, size_type pos=0)</entry> + <entry>size_type find_first_of_(container, c, pos) size_type + find_first_of_(container, c) </entry> + <entry>StringFindFirstOf_</entry> + </row> + <row> + <entry>size_type find_first_not_of(charT* s, size_type pos, + size_type n)</entry> + <entry>size_type find_first_not_of_(container, s, pos, n)</entry> + <entry>StringFindFirstNotOf_</entry> + </row> + <row> + <entry>size_type find_first_not_of (charT* s, size_type + pos=0)</entry> + <entry>size_type find_first_not_of_(container, s, pos); size_type + find_first_not_of_(container, s) </entry> + <entry>StringFindFirstNotOf_</entry> + </row> + <row> + <entry>size_type find_first_not_of (const string& s, size_type + pos=0)</entry> + <entry>size_type find_first_not_of_(container, s, pos); size_type + find_first_not_of_(container, s) </entry> + <entry>StringFindFirstNotOf_</entry> + </row> + <row> + <entry>size_type find_first_not_of (charT c, size_type + pos=0)</entry> + <entry>size_type find_first_not_of_(container, c, pos); size_type + find_first_not_of_(container, c) </entry> + <entry>StringFindFirstNotOf_</entry> + </row> + <row> + <entry>size_type find_last_of(charT* s, size_type pos, size_type + n)</entry> + <entry>size_type find_last_of_(container, s, pos, n)</entry> + <entry>StringFindLastOf_</entry> + </row> + <row> + <entry>size_type find_last_of (charT* s, size_type pos=npos)</entry> + <entry>size_type find_last_of_(container, s, pos); size_type + find_last_of_(container, s) </entry> + <entry>StringFindLastOf_</entry> + </row> + <row> + <entry>size_type find_last_of (const string& s, size_type + pos=npos)</entry> + <entry>size_type find_last_of_(container, s, pos); size_type + find_last_of_(container, s) </entry> + <entry>StringFindLastOf_</entry> + </row> + <row> + <entry>size_type find_last_of (charT c, size_type pos=npos)</entry> + <entry>size_type find_last_of_(container, c, pos); size_type + find_last_of_(container, c) </entry> + <entry>StringFindLastOf_</entry> + </row> + <row> + <entry>size_type find_last_not_of(charT* s, size_type pos, size_type + n)</entry> + <entry>size_type find_last_not_of_(container, s, pos, n)</entry> + <entry>StringFindLastNotOf_</entry> + </row> + <row> + <entry>size_type find_last_not_of (charT* s, size_type + pos=npos)</entry> + <entry>size_type find_last_not_of_(container, s, pos); size_type + find_last_of_(container, s) </entry> + <entry>StringFindLastNotOf_</entry> + </row> + <row> + <entry>size_type find_last_not_of (const string& s, size_type + pos=npos)</entry> + <entry>size_type find_last_not_of_(container, s, pos); size_type + find_last_not_of_(container, s) </entry> + <entry>StringFindLastNotOf_</entry> + </row> + <row> + <entry>size_type find_last_not_of (charT c, size_type + pos=npos)</entry> + <entry>size_type find_last_not_of_(container, c, pos); size_type + find_last_not_of_(container, c) </entry> + <entry>StringFindLastNotOf_</entry> + </row> + </tbody> + </tgroup> + </table> + </para> + <para><emphasis role="underline">Notes</emphasis>: <itemizedlist> + <listitem> + <para>ᵃ: algorithms requiring a predicate need to make them eUML compatible + by wrapping them inside a Predicate_ functor. For example, + std::less<int> => Predicate_<std::less<int> >()</para> + </listitem> + <listitem> + <para>ᵇ: If using the SGI STL implementation, these functors use the SGI + return value</para> + </listitem> + </itemizedlist> + </para> + </chapter> + <refentry> + <refnamediv> + <refname>Common headers</refname> + <refpurpose>The common types used by front- and back-ends</refpurpose> + </refnamediv> + <refsect1> + <title>msm/common.hpp</title> + <para>This header provides one type, wrap, which is an empty type whose only reason + to exist is to be cheap to construct, so that it can be used with mpl::for_each, + as shown in the Metaprogramming book, chapter 9.</para> + <classsynopsis> + <ooclass> + <classname>template <class Dummy> wrap{};</classname> + </ooclass> + </classsynopsis> + </refsect1> + <refsect1> + <title>msm/row_tags.hpp</title> + <para>This header contains the row type tags which front-ends can support partially + or totally. Please see the <command xlink:href="#internals-front-back-interface" + >Internals</command> section for a description of the different + types.</para> + </refsect1> + </refentry> + <refentry> + <refnamediv> + <refname>Back-end</refname> + <refpurpose>The back-end headers</refpurpose> + </refnamediv> + <refsect1> + <title>msm/back/state_machine.hpp</title> + <para> This header provides one type, state_machine, MSM's state machine engine + implementation.</para> + <classsynopsis> + <ooclass> + <classname>template <class Derived,class HistoryPolicy=NoHistory,class + CompilePolicy=favor_runtime_speed> state_machine</classname> + </ooclass> + </classsynopsis> + <refsect2> + <title> Template arguments </title> + <refsect3> + <title> Derived </title> + <para>The name of the front-end state machine definition. All three + front-ends are possible.</para> + </refsect3> + <refsect3> + <title> HistoryPolicy </title> + <para>The desired history. This can be: AlwaysHistory, NoHistory, + ShallowHistory. Default is NoHistory.</para> + </refsect3> + <refsect3> + <title> CompilePolicy </title> + <para>The trade-off performance / compile-time. There are two predefined + policies, favor_runtime_speed and favor_compile_time. Default is + favor_runtime_speed, best performance, longer compile-time. See <link + xlink:href="#backend-tradeof-rt-ct">the backend</link>.</para> + </refsect3> + </refsect2> + <refsect2> + <title> methods </title> + <refsect3> + <title>start</title> + <para> The start methods must be called before any call to process_event. It + activates the entry action of the initial state(s). This allows you to + choose when a state machine can start. See <link + xlink:href="#backend-start">backend</link>.</para> + <methodsynopsis> + <methodname>void start</methodname> + <methodparam> + <funcparams/> + </methodparam> + </methodsynopsis> + </refsect3> + <refsect3> + <title>process_event</title> + <para>The event processing method implements the double-dispatch. Each call + to this function with a new event type instantiates a new dispatch + algorithm and increases compile-time.</para> + <methodsynopsis> + <methodname>template <class Event> HandledEnum + process_event</methodname> + <methodparam> + <funcparams>Event const&</funcparams> + </methodparam> + </methodsynopsis> + </refsect3> + <refsect3> + <title>current_state</title> + <para>Returns the ids of currently active states. You will typically need it + only for debugging or logging purposes.</para> + <methodsynopsis> + <methodname>const int* current_state const</methodname> + <methodparam> + <funcparams/> + </methodparam> + </methodsynopsis> + </refsect3> + <refsect3> + <title>get_state_by_id</title> + <para>Returns the state whose id is given. As all states of a concrete state + machine share a common base state, the return value is a base state. If + the id corresponds to no state, a null pointer is returned.</para> + <methodsynopsis> + <methodname>const BaseState* get_state_by_id const</methodname> + <methodparam> + <funcparams>int id</funcparams> + </methodparam> + </methodsynopsis> + </refsect3> + <refsect3> + <title>is_contained</title> + <para>Helper returning true if the state machine is contained as a + submachine of another state machine.</para> + <methodsynopsis> + <methodname>bool is_contained const</methodname> + <methodparam> + <funcparams/> + </methodparam> + </methodsynopsis> + </refsect3> + <refsect3> + <title>get_state</title> + <para>Returns the required state of the state machine as a pointer. A + compile error will occur if the state is not to be found in the state + machine.</para> + <methodsynopsis> + <methodname>template <class State> State* get_state</methodname> + <methodparam> + <funcparams/> + </methodparam> + </methodsynopsis> + </refsect3> + <refsect3> + <title>get_state</title> + <para>Returns the required state of the state machine as a reference. A + compile error will occur if the state is not to be found in the state + machine.</para> + <methodsynopsis> + <methodname>template <class State> State& get_state</methodname> + <methodparam> + <funcparams/> + </methodparam> + </methodsynopsis> + </refsect3> + <refsect3> + <title>is_flag_active</title> + <para>Returns true if the given flag is currently active. A flag is active + if the active state of one region is tagged with this flag (using OR as + BinaryOp) or active states of <emphasis role="underline">all</emphasis> + regions (using AND as BinaryOp)</para> + <methodsynopsis> + <methodname>template <class Flag,class BinaryOp> bool + is_flag_active</methodname> + <methodparam> + <funcparams/> + </methodparam> + </methodsynopsis> + </refsect3> + <refsect3> + <title>is_flag_active</title> + <para>Returns true if the given flag is currently active. A flag is active + if the active state of one region is tagged with this flag.</para> + <methodsynopsis> + <methodname>template <class Flag> bool is_flag_active</methodname> + <methodparam> + <funcparams/> + </methodparam> + </methodsynopsis> + </refsect3> + <refsect3> + <title>visit_current_states</title> + <para>Visits all active states and their substates. A state is visited using + the <code>accept</code> method without argument. The base class of all + states must provide an <code>accept_sig</code> type.</para> + <methodsynopsis> + <methodname>void visit_current_states</methodname> + <methodparam> + <funcparams/> + </methodparam> + </methodsynopsis> + </refsect3> + <refsect3> + <title>visit_current_states</title> + <para>Visits all active states and their substates. A state is visited using + the <code>accept</code> method with arguments. The base class of all + states must provide an <code>accept_sig</code> type defining the + signature and thus the number and type of the parameters.</para> + <methodsynopsis> + <methodname>void visit_current_states</methodname> + <methodparam> + <funcparams>any-type param1, any-type param2,...</funcparams> + </methodparam> + </methodsynopsis> + </refsect3> + <refsect3> + <title>defer_event</title> + <para> Defers the provided event. This method can be called only if at least + one state defers an event or if the state machine provides the + <code>activate_deferred_events</code>(see <link + xlink:href="examples/Orthogonal-deferred2.cpp">example</link>) type + either directly or using the deferred_events configuration of eUML + (<code>configure_ << deferred_events</code>)</para> + <methodsynopsis> + <methodname>template <class Event> void defer_event</methodname> + <methodparam> + <funcparams>Event const&</funcparams> + </methodparam> + </methodsynopsis> + </refsect3> + </refsect2> + <refsect2> + <title>Types</title> + <refsect3> + <title>nr_regions </title> + <para>The number of orthogonal regions contained in the state machine</para> + </refsect3> + <refsect3> + <title>entry_pt</title> + <para>This nested type provides the necessary typedef for entry point + pseudostates. + <code>state_machine<...>::entry_pt<state_name></code> is a + transition's valid target inside the containing state machine's + transition table.</para> + <classsynopsis> + <ooclass> + <classname>entry_pt</classname> + </ooclass> + </classsynopsis> + </refsect3> + <refsect3> + <title>exit_pt</title> + <para>This nested type provides the necessary typedef for exit point + pseudostates. <code>state_machine<...>::exit_pt<state_name></code> + is a transition's valid source inside the containing state machine's + transition table.</para> + <classsynopsis> + <ooclass> + <classname>exit_pt</classname> + </ooclass> + </classsynopsis> + </refsect3> + <refsect3> + <title>direct</title> + <para>This nested type provides the necessary typedef for an explicit entry + inside a submachine. + <code>state_machine<...>::direct<state_name></code> is a + transition's valid target inside the containing state machine's + transition table.</para> + <classsynopsis> + <ooclass> + <classname>direct</classname> + </ooclass> + </classsynopsis> + </refsect3> + <refsect3> + <title>stt</title> + <para>Calling state_machine<frontend>::stt returns a mpl::vector + containing the transition table of the state machine. This type can then + be used with generate_state_set or generate_event_set.</para> + </refsect3> + </refsect2> + </refsect1> + <refsect1> + <title>args.hpp</title> + <para>This header provides one type, args. which provides the necessary types for a + visitor implementation.</para> + </refsect1> + <refsect1> + <title><command xml:id="history-interface"/>msm/back/history_policies.hpp</title> + <para>This header provides the out-of-the-box history policies supported by MSM. + There are 3 such policies.</para> + <refsect2> + <title>Every history policy must implement the following methods: </title> + <refsect3> + <title> set_initial_states </title> + <para> This method is called by msm::back::state_machine when constructed. + It gives the policy a chance to save the ids of all initial states + (passed as array).</para> + <funcsynopsis> + <funcprototype> + <funcdef>void set_initial_states</funcdef> + <paramdef> + <funcparams>int* const</funcparams> + </paramdef> + </funcprototype> + </funcsynopsis> + </refsect3> + <refsect3> + <title> history_exit </title> + <para>This method is called by msm::back::state_machine when the submachine + is exited. It gives the policy a chance to remember the ids of the last + active substates of this submachine (passed as array).</para> + <funcsynopsis> + <funcprototype> + <funcdef>void history_exit</funcdef> + <paramdef> + <funcparams>int* const</funcparams> + </paramdef> + </funcprototype> + </funcsynopsis> + </refsect3> + <refsect3> + <title> history_entry </title> + <para>This method is called by msm::back::state_machine when the submachine + is entered. It gives the policy a chance to set the active states + according to the policy's aim. The policy gets as parameter the event + which activated the submachine and returns an array of active states + ids.</para> + <funcsynopsis> + <funcprototype> + <funcdef>template <class Event> int* const history_exit</funcdef> + <paramdef> + <funcparams>Event const&</funcparams> + </paramdef> + </funcprototype> + </funcsynopsis> + </refsect3> + </refsect2> + <refsect2> + <title>Out-of-the-box policies: </title> + <refsect3> + <title>NoHistory</title> + <para>This policy is the default used by state_machine. No active state of a + submachine is remembered and at every new activation of the submachine, + the initial state(s) are activated. </para> + </refsect3> + <refsect3> + <title>AlwaysHistory</title> + <para>This policy is a non-UML-standard extension. The active state(s) of a + submachine is (are) always remembered at every new activation of the + submachine. </para> + </refsect3> + <refsect3> + <title>ShallowHistory</title> + <para>This policy activates the active state(s) of a submachine if the event + is found in the policy's event list. </para> + </refsect3> + </refsect2> + </refsect1> + <refsect1> + <title>msm/back/default_compile_policy.hpp</title> + <para>This header contains the definition of favor_runtime_speed. This policy has + two settings:<itemizedlist> + <listitem> + <para>Submachines dispatch faster because their transitions are added + into their containing machine's transition table instead of simply + forwarding events.</para> + </listitem> + <listitem> + <para>It solves transition conflicts at compile-time</para> + </listitem> + </itemizedlist></para> + </refsect1> + <refsect1> + <title>msm/back/favor_compile_time.hpp</title> + <para>This header contains the definition of favor_compile_time. This policy has two settings:<itemizedlist> + <listitem> + <para>Submachines dispatch is slower because all events, even those with + no dispatch chance, are forwarded to submachines. In exchange, no + row is added into the containing machine's transition table, which + reduces compile-time.</para> + </listitem> + <listitem> + <para>It solves transition conflicts at run-time.</para> + </listitem> + </itemizedlist></para> + </refsect1> + <refsect1> + <title>msm/back/metafunctions.hpp </title> + <para>This header contains metafunctions for use by the library. Three metafunctions + can be useful for the user:<itemizedlist> + <listitem> + <para><code>generate_state_set< stt ></code>: generates the list of + all states referenced by the transition table stt. If stt is a + recursive table (generated by + <code>recursive_get_transition_table</code>), the metafunction + finds recursively all states of the submachines. A non-recursive + table can be obtained with some_backend_fsm::stt.</para> + </listitem> + <listitem> + <para><code>generate_event_set< stt></code>: generates the list of + all events referenced by the transition table stt. If stt is a + recursive table (generated by + <code>recursive_get_transition_table</code>), the metafunction + finds recursively all events of the submachines. A non-recursive + table can be obtained with some_backend_fsm::stt.</para> + </listitem> + <listitem> + <para><code>recursive_get_transition_table<fsm></code>: recursively + extends the transition table of the state machine fsm with tables + from the submachines.</para> + </listitem> + </itemizedlist></para> + </refsect1> + <refsect1> + <title>msm/back/tools.hpp </title> + <para> This header contains a few metaprogramming tools to get some information out + of a state machine.</para> + <refsect2> + <title>fill_state_names </title> + <refsect3> + <title>attributes </title> + <para> fill_state_names has for attribute:<itemizedlist> + <listitem> + <para><code>char const** m_names</code>: an already allocated + array of const char* where the typeid-generated names of a + state machine states will be witten.</para> + </listitem> + </itemizedlist></para> + </refsect3> + <refsect3> + <title>constructor </title> + <constructorsynopsis> + <methodparam> + <funcparams>char const** names_to_fill</funcparams> + </methodparam> + </constructorsynopsis> + </refsect3> + <refsect3> + <title>usage</title> + <para> fill_state_names is made for use in a mpl::for_each iterating on a + state list and writing inside a pre-allocated array the state names. + Example:</para> + <programlisting>typedef some_fsm::stt Stt; +typedef msm::back::generate_state_set<Stt>::type all_states; //states +static char const* state_names[mpl::size<all_states>::value]; +// array to fill with names +// fill the names of the states defined in the state machine +mpl::for_each<all_states,boost::msm::wrap<mpl::placeholders::_1> > + (msm::back::fill_state_names<Stt>(state_names)); +// display all active states +for (unsigned int i=0;i<some_fsm::nr_regions::value;++i) +{ + std::cout << " -> " + << state_names[my_fsm_instance.current_state()[i]] + << std::endl; +}</programlisting> + </refsect3> + </refsect2> + <refsect2> + <title>get_state_name </title> + <refsect3> + <title> attributes </title> + <para>get_state_name has for attributes:<itemizedlist> + <listitem> + <para>std::string& m_name: the return value of the + iteration</para> + </listitem> + <listitem> + <para>int m_state_id: the searched state's id</para> + </listitem> + </itemizedlist></para> + </refsect3> + <refsect3> + <title>constructor</title> + <para>The constructor takes as argument a reference to the string to fill + with the state name and the id which must be searched.</para> + <constructorsynopsis> + <methodparam> + <funcparams>string& name_to_fill,int state_id</funcparams> + </methodparam> + </constructorsynopsis> + </refsect3> + <refsect3> + <title> usage</title> + <para>This type is made for the same search as in the previous example, + using a mpl::for_each to iterate on states. After the iteration, the + state name reference has been set.</para> + </refsect3> + </refsect2> + <refsect2> + <title>display_type </title> + <refsect3> + <title> attributes </title> + <para>none</para> + </refsect3> + <refsect3> + <title> usage</title> + <para>Reusing the state list from the previous example, we can output all + state names:</para> + <para><code>mpl::for_each<all_states,boost::msm::wrap<mpl::placeholders::_1> + >(msm::back::display_type ());</code></para> + </refsect3> + </refsect2> + </refsect1> + </refentry> + <refentry> + <refnamediv> + <refname>Front-end</refname> + <refpurpose>The front-end headers</refpurpose> + </refnamediv> + <refsect1> + <title>msm/front/common_states.hpp</title> + <para>This header contains the predefined types to serve as base for states or state machines:<itemizedlist> + <listitem> + <para>default_base_state: non-polymorphic empty type.</para> + </listitem> + <listitem> + <para>polymorphic_state: type with a virtual destructor, which makes all + states polymorphic.</para> + </listitem> + </itemizedlist></para> + </refsect1> + <refsect1> + <title>msm/front/completion_event.hpp</title> + <para>This header contains one type, <code>none</code>. This type has several + meanings inside a transition table:<itemizedlist> + <listitem> + <para>as action or guard: that there is no action or guard</para> + </listitem> + <listitem> + <para>as target state: that the transition is an internal + transition</para> + </listitem> + <listitem> + <para>as event: the transition is an anonymous (completion) + transition</para> + </listitem> + </itemizedlist></para> + </refsect1> + <refsect1> + <title>msm/front/functor_row.hpp</title> + <para>This header implements the functor front-end's transitions and helpers.</para> + <refsect2> + <title>Row</title> + <refsect3> + <title>definition</title> + <classsynopsis> + <ooclass> + <classname>template <class Source,class Event,class Target,class + Action,class Guard> Row</classname> + </ooclass> + </classsynopsis> + </refsect3> + <refsect3> + <title>tags</title> + <para>row_type_tag is defined differently for every specialization:<itemizedlist> + <listitem> + <para>all 5 template parameters means a normal transition with + action and guard: <code>typedef row_tag + row_type_tag;</code></para> + </listitem> + <listitem> + <para>Row<Source,Event,Target,none,none> a normal transition + without action or guard: <code>typedef _row_tag + row_type_tag;</code></para> + </listitem> + <listitem> + <para>Row<Source,Event,Target,Action,none> a normal + transition without guard: <code>typedef a_row_tag + row_type_tag;</code></para> + </listitem> + <listitem> + <para>Row<Source,Event,Target,none,Guard> a normal transition + without action: <code>typedef g_row_tag + row_type_tag;</code></para> + </listitem> + <listitem> + <para>Row<Source,Event,none,Action,none> an internal + transition without guard: <code>typedef a_irow_tag + row_type_tag;</code></para> + </listitem> + <listitem> + <para>Row<Source,Event,none,none,Guard> an internal + transition without action: <code>typedef g_irow_tag + row_type_tag;</code></para> + </listitem> + <listitem> + <para>Row<Source,Event,none,none,Guard> an internal + transition with action and guard: <code>typedef irow_tag + row_type_tag;</code></para> + </listitem> + <listitem> + <para>Row<Source,Event,none,none,none> an internal transition + without action or guard: <code>typedef _irow_tag + row_type_tag;</code></para> + </listitem> + </itemizedlist></para> + </refsect3> + <refsect3> + <title>methods</title> + <para>Like any other front-end, Row implements the two necessary static + functions for action and guard call. Each function receives as parameter + the (deepest-level) state machine processsing the event, the event + itself, the source and target states and all the states contained in a + state machine.</para> + <funcsynopsis> + <funcprototype> + <funcdef>template <class Fsm,class SourceState,class TargetState, + class AllStates> static void action_call</funcdef> + <paramdef> + <funcparams>Fsm& fsm,Event const& + evt,SourceState&,TargetState,AllStates&</funcparams> + </paramdef> + </funcprototype> + </funcsynopsis> + <funcsynopsis> + <funcprototype> + <funcdef>template <class Fsm,class SourceState,class TargetState, + class AllStates> static bool guard_call</funcdef> + <paramdef> + <funcparams>Fsm& fsm,Event const& + evt,SourceState&,TargetState,AllStates&</funcparams> + </paramdef> + </funcprototype> + </funcsynopsis> + </refsect3> + </refsect2> + <refsect2> + <title>Internal</title> + <refsect3> + <title>definition</title> + <classsynopsis> + <ooclass> + <classname>template <class Event,class Action,class Guard> + Internal</classname> + </ooclass> + </classsynopsis> + </refsect3> + <refsect3> + <title>tags</title> + <para>row_type_tag is defined differently for every specialization:<itemizedlist> + <listitem> + <para>all 3 template parameters means an internal transition + with action and guard: <code>typedef sm_i_row_tag + row_type_tag;</code></para> + </listitem> + <listitem> + <para>Internal<Event,none,none> an internal transition + without action or guard: <code>typedef sm__i_row_tag + row_type_tag;</code></para> + </listitem> + <listitem> + <para>Internal<Event,Action,none> an internal transition + without guard: <code>typedef sm_a_i_row_tag + row_type_tag;</code></para> + </listitem> + <listitem> + <para>Internal<Event,none,Guard> an internal transition + without action: <code>typedef sm_g_i_row_tag + row_type_tag;</code></para> + </listitem> + </itemizedlist></para> + </refsect3> + <refsect3> + <title>methods</title> + <para>Like any other front-end, Internal implements the two necessary static + functions for action and guard call. Each function receives as parameter + the (deepest-level) state machine processsing the event, the event + itself, the source and target states and all the states contained in a + state machine.</para> + <funcsynopsis> + <funcprototype> + <funcdef>template <class Fsm,class SourceState,class TargetState, + class AllStates> static void action_call</funcdef> + <paramdef> + <funcparams>Fsm& fsm,Event const& + evt,SourceState&,TargetState,AllStates&</funcparams> + </paramdef> + </funcprototype> + </funcsynopsis> + <funcsynopsis> + <funcprototype> + <funcdef>template <class Fsm,class SourceState,class TargetState, + class AllStates> static bool guard_call</funcdef> + <paramdef> + <funcparams>Fsm& fsm,Event const& + evt,SourceState&,TargetState,AllStates&</funcparams> + </paramdef> + </funcprototype> + </funcsynopsis> + </refsect3> + </refsect2> + <refsect2> + <title>ActionSequence_</title> + <para>This functor calls every element of the template Sequence (which are also + callable functors) in turn. It is also the underlying implementation of the + eUML sequence grammar (action1,action2,...).</para> + <refsect3> + <title>definition</title> + <classsynopsis> + <ooclass> + <classname>template <class Sequence> ActionSequence_</classname> + </ooclass> + </classsynopsis> + </refsect3> + <refsect3> + <title>methods</title> + <para>This helper functor is made for use in a transition table and in a + state behavior and therefore implements an operator() with 3 and with 4 + arguments:</para> + <para> + <funcsynopsis> + <funcprototype> + <funcdef>template <class Evt,class Fsm,class + SourceState,class TargetState> operator()</funcdef> + <paramdef>Evt const& ,Fsm& ,SourceState& + ,TargetState& </paramdef> + </funcprototype> + </funcsynopsis> + </para> + <para> + <funcsynopsis> + <funcprototype> + <funcdef>template <class Evt,class Fsm,class State> + operator()</funcdef> + <paramdef>Evt const&, Fsm&, State&</paramdef> + </funcprototype> + </funcsynopsis> + </para> + </refsect3> + </refsect2> + <refsect2> + <title>Defer</title> + <refsect3> + <title>definition</title> + <classsynopsis> + <ooclass> + <classname>Defer</classname> + </ooclass> + </classsynopsis> + </refsect3> + <refsect3> + <title>methods</title> + <para>This helper functor is made for use in a transition table and + therefore implements an operator() with 4 arguments:</para> + <funcsynopsis> + <funcprototype> + <funcdef>template <class Evt,class Fsm,class SourceState,class + TargetState> operator()</funcdef> + <paramdef>Evt const&, Fsm& , SourceState&, + TargetState&</paramdef> + </funcprototype> + </funcsynopsis> + </refsect3> + </refsect2> + </refsect1> + <refsect1> + <title>msm/front/internal_row.hpp</title> + <para>This header implements the internal transition rows for use inside an + internal_transition_table. All these row types have no source or target state, + as the backend will recognize internal transitions from this + internal_transition_table.</para> + <refsect2> + <title>methods</title> + <para>Like any other front-end, the following transition row types implements + the two necessary static functions for action and guard call. Each function + receives as parameter the (deepest-level) state machine processsing the + event, the event itself, the source and target states and all the states + contained in a state machine.</para> + <funcsynopsis> + <funcprototype> + <funcdef>template <class Fsm,class SourceState,class TargetState, + class AllStates> static void action_call</funcdef> + <paramdef> + <funcparams>Fsm& fsm,Event const& + evt,SourceState&,TargetState,AllStates&</funcparams> + </paramdef> + </funcprototype> + </funcsynopsis> + <funcsynopsis> + <funcprototype> + <funcdef>template <class Fsm,class SourceState,class TargetState, + class AllStates> static bool guard_call</funcdef> + <paramdef> + <funcparams>Fsm& fsm,Event const& + evt,SourceState&,TargetState,AllStates&</funcparams> + </paramdef> + </funcprototype> + </funcsynopsis> + </refsect2> + <refsect2> + <title>a_internal</title> + <refsect3> + <title>definition</title> + <para>This is an internal transition with an action called during the + transition.</para> + <classsynopsis> + <ooclass> + <classname>template< class Event, class CalledForAction, void + (CalledForAction::*action)(Event const&)> + a_internal</classname> + </ooclass> + </classsynopsis> + </refsect3> + <refsect3> + <title>template parameters</title> + <para> + <itemizedlist> + <listitem> + <para>Event: the event triggering the internal + transition.</para> + </listitem> + <listitem> + <para>CalledForAction: the type on which the action method will + be called. It can be either a state of the containing state + machine or the state machine itself.</para> + </listitem> + <listitem> + <para>action: a pointer to the method which CalledForAction + provides.</para> + </listitem> + </itemizedlist> + </para> + </refsect3> + </refsect2> + <refsect2> + <title>g_internal</title> + <para>This is an internal transition with a guard called before the transition + and allowing the transition if returning true.</para> + <refsect3> + <title>definition</title> + <classsynopsis> + <ooclass> + <classname>template< class Event, class CalledForGuard, bool + (CalledForGuard::*guard)(Event const&)> + g_internal</classname> + </ooclass> + </classsynopsis> + </refsect3> + <refsect3> + <title>template parameters</title> + <para> + <itemizedlist> + <listitem> + <para>Event: the event triggering the internal + transition.</para> + </listitem> + <listitem> + <para>CalledForGuard: the type on which the guard method will be + called. It can be either a state of the containing state + machine or the state machine itself.</para> + </listitem> + <listitem> + <para>guard: a pointer to the method which CalledForGuard + provides.</para> + </listitem> + </itemizedlist> + </para> + </refsect3> + </refsect2> + <refsect2> + <title>internal</title> + <para>This is an internal transition with a guard called before the transition + and allowing the transition if returning true. It also calls an action + called during the transition.</para> + <refsect3> + <title>definition</title> + <classsynopsis> + <ooclass> + <classname>template< class Event, class CalledForAction, void + (CalledForAction::*action)(Event const&), class + CalledForGuard, bool (CalledForGuard::*guard)(Event const&)> + internal</classname> + </ooclass> + </classsynopsis> + </refsect3> + <refsect3> + <title>template parameters</title> + <para> + <itemizedlist> + <listitem> + <para>Event: the event triggering the internal transition</para> + </listitem> + <listitem> + <para>CalledForAction: the type on which the action method will + be called. It can be either a state of the containing state + machine or the state machine itself.</para> + </listitem> + <listitem> + <para>action: a pointer to the method which CalledForAction + provides.</para> + </listitem> + <listitem> + <para>CalledForGuard: the type on which the guard method will be + called. It can be either a state of the containing state + machine or the state machine itself.</para> + </listitem> + <listitem> + <para>guard: a pointer to the method which CalledForGuard + provides.</para> + </listitem> + </itemizedlist> + </para> + </refsect3> + </refsect2> + <refsect2> + <title>_internal</title> + <para>This is an internal transition without action or guard. This is equivalent + to an explicit "ignore event".</para> + <refsect3> + <title>definition</title> + <classsynopsis> + <ooclass> + <classname>template< class Event > _internal</classname> + </ooclass> + </classsynopsis> + </refsect3> + <refsect3> + <title>template parameters</title> + <para> + <itemizedlist> + <listitem> + <para>Event: the event triggering the internal + transition.</para> + </listitem> + </itemizedlist> + </para> + </refsect3> + </refsect2> + </refsect1> + <refsect1> + <title>msm/front/row2.hpp</title> + <para>This header contains the variants of row2, which are an extension of the + standard row transitions for use in the transition table. They offer the + possibility to define action and guard not only in the state machine, but in any + state of the state machine. They can also be used in internal transition tables + through their irow2 variants.</para> + <refsect2> + <title>methods</title> + <para>Like any other front-end, the following transition row types implements + the two necessary static functions for action and guard call. Each function + receives as parameter the (deepest-level) state machine processsing the + event, the event itself, the source and target states and all the states + contained in a state machine.</para> + <funcsynopsis> + <funcprototype> + <funcdef>template <class Fsm,class SourceState,class TargetState, + class AllStates> static void action_call</funcdef> + <paramdef> + <funcparams>Fsm& fsm,Event const& + evt,SourceState&,TargetState,AllStates&</funcparams> + </paramdef> + </funcprototype> + </funcsynopsis> + <funcsynopsis> + <funcprototype> + <funcdef>template <class Fsm,class SourceState,class TargetState, + class AllStates> static bool guard_call</funcdef> + <paramdef> + <funcparams>Fsm& fsm,Event const& + evt,SourceState&,TargetState,AllStates&</funcparams> + </paramdef> + </funcprototype> + </funcsynopsis> + </refsect2> + <refsect2> + <title>_row2</title> + <para>This is a transition without action or guard. The state machine only + changes active state.</para> + <refsect3> + <title>definition</title> + <classsynopsis> + <ooclass> + <classname>template< class Source, class Event, class Target > + _row2</classname> + </ooclass> + </classsynopsis> + </refsect3> + <refsect3> + <title>template parameters</title> + <para> + <itemizedlist> + <listitem> + <para>Event: the event triggering the transition.</para> + </listitem> + <listitem> + <para>Source: the source state of the transition.</para> + </listitem> + <listitem> + <para>Target: the target state of the transition.</para> + </listitem> + </itemizedlist> + </para> + </refsect3> + </refsect2> + <refsect2> + <title>a_row2</title> + <para>This is a transition with action and without guard.</para> + <refsect3> + <title>definition</title> + <classsynopsis> + <ooclass> + <classname>template< class Source, class Event, class Target, + </classname> + </ooclass> + </classsynopsis> + <classsynopsis> + <ooclass> + <classname>class CalledForAction, void + (CalledForAction::*action)(Event const&) > _row2</classname> + </ooclass> + </classsynopsis> + </refsect3> + <refsect3> + <title>template parameters</title> + <para> + <itemizedlist> + <listitem> + <para>Event: the event triggering the transition.</para> + </listitem> + <listitem> + <para>Source: the source state of the transition.</para> + </listitem> + <listitem> + <para>Target: the target state of the transition.</para> + </listitem> + <listitem> + <para>CalledForAction: the type on which the action method will + be called. It can be either a state of the containing state + machine or the state machine itself.</para> + </listitem> + <listitem> + <para>action: a pointer to the method which CalledForAction + provides.</para> + </listitem> + </itemizedlist> + </para> + </refsect3> + </refsect2> + <refsect2> + <title>g_row2</title> + <para>This is a transition with guard and without action.</para> + <refsect3> + <title>definition</title> + <classsynopsis> + <ooclass> + <classname>template< class Source, class Event, class Target, + </classname> + </ooclass> + </classsynopsis> + <classsynopsis> + <ooclass> + <classname>class CalledForGuard, bool (CalledForGuard::*guard)(Event + const&) > _row2</classname> + </ooclass> + </classsynopsis> + </refsect3> + <refsect3> + <title>template parameters</title> + <para> + <itemizedlist> + <listitem> + <para>Event: the event triggering the transition.</para> + </listitem> + <listitem> + <para>Source: the source state of the transition.</para> + </listitem> + <listitem> + <para>Target: the target state of the transition.</para> + </listitem> + <listitem> + <para>CalledForGuard: the type on which the guard method will be + called. It can be either a state of the containing state + machine or the state machine itself.</para> + </listitem> + <listitem> + <para>guard: a pointer to the method which CalledForGuard + provides.</para> + </listitem> + </itemizedlist> + </para> + </refsect3> + </refsect2> + <refsect2> + <title>row2</title> + <para>This is a transition with guard and action.</para> + <refsect3> + <title>definition</title> + <classsynopsis> + <ooclass> + <classname>template< class Source, class Event, class Target, + </classname> + </ooclass> + </classsynopsis> + <classsynopsis> + <ooclass> + <classname>class CalledForAction, void + (CalledForAction::*action)(Event const&), </classname> + </ooclass> + </classsynopsis> + <classsynopsis> + <ooclass> + <classname>class CalledForGuard, bool (CalledForGuard::*guard)(Event + const&) > _row2</classname> + </ooclass> + </classsynopsis> + </refsect3> + <refsect3> + <title>template parameters</title> + <para> + <itemizedlist> + <listitem> + <para>Event: the event triggering the transition.</para> + </listitem> + <listitem> + <para>Source: the source state of the transition.</para> + </listitem> + <listitem> + <para>Target: the target state of the transition.</para> + </listitem> + <listitem> + <para>CalledForAction: the type on which the action method will + be called. It can be either a state of the containing state + machine or the state machine itself.</para> + </listitem> + <listitem> + <para>action: a pointer to the method which CalledForAction + provides.</para> + </listitem> + <listitem> + <para>CalledForGuard: the type on which the guard method will be + called. It can be either a state of the containing state + machine or the state machine itself.</para> + </listitem> + <listitem> + <para>guard: a pointer to the method which CalledForGuard + provides.</para> + </listitem> + </itemizedlist> + </para> + </refsect3> + </refsect2> + <refsect2> + <title>a_irow2</title> + <para>This is an internal transition for use inside a transition table, with + action and without guard.</para> + <refsect3> + <title>definition</title> + <classsynopsis> + <ooclass> + <classname>template< class Source, class Event, </classname> + </ooclass> + </classsynopsis> + <classsynopsis> + <ooclass> + <classname>class CalledForAction, void + (CalledForAction::*action)(Event const&) > _row2</classname> + </ooclass> + </classsynopsis> + </refsect3> + <refsect3> + <title>template parameters</title> + <para> + <itemizedlist> + <listitem> + <para>Event: the event triggering the transition.</para> + </listitem> + <listitem> + <para>Source: the source state of the transition.</para> + </listitem> + <listitem> + <para>CalledForAction: the type on which the action method will + be called. It can be either a state of the containing state + machine or the state machine itself.</para> + </listitem> + <listitem> + <para>action: a pointer to the method which CalledForAction + provides.</para> + </listitem> + </itemizedlist> + </para> + </refsect3> + </refsect2> + <refsect2> + <title>g_irow2</title> + <para>This is an internal transition for use inside a transition table, with + guard and without action.</para> + <refsect3> + <title>definition</title> + <classsynopsis> + <ooclass> + <classname>template< class Source, class Event, </classname> + </ooclass> + </classsynopsis> + <classsynopsis> + <ooclass> + <classname>class CalledForGuard, bool (CalledForGuard::*guard)(Event + const&) > _row2</classname> + </ooclass> + </classsynopsis> + </refsect3> + <refsect3> + <title>template parameters</title> + <para> + <itemizedlist> + <listitem> + <para>Event: the event triggering the transition.</para> + </listitem> + <listitem> + <para>Source: the source state of the transition.</para> + </listitem> + <listitem> + <para>CalledForGuard: the type on which the guard method will be + called. It can be either a state of the containing state + machine or the state machine itself.</para> + </listitem> + <listitem> + <para>guard: a pointer to the method which CalledForGuard + provides.</para> + </listitem> + </itemizedlist> + </para> + </refsect3> + </refsect2> + <refsect2> + <title>irow2</title> + <para>This is an internal transition for use inside a transition table, with + guard and action.</para> + <refsect3> + <title>definition</title> + <classsynopsis> + <ooclass> + <classname>template< class Source, class Event, </classname> + </ooclass> + </classsynopsis> + <classsynopsis> + <ooclass> + <classname>class CalledForAction, void + (CalledForAction::*action)(Event const&), </classname> + </ooclass> + </classsynopsis> + <classsynopsis> + <ooclass> + <classname>class CalledForGuard, bool (CalledForGuard::*guard)(Event + const&) > _row2</classname> + </ooclass> + </classsynopsis> + </refsect3> + <refsect3> + <title>template parameters</title> + <para> + <itemizedlist> + <listitem> + <para>Event: the event triggering the transition.</para> + </listitem> + <listitem> + <para>Source: the source state of the transition.</para> + </listitem> + <listitem> + <para>CalledForAction: the type on which the action method will + be called. It can be either a state of the containing state + machine or the state machine itself.</para> + </listitem> + <listitem> + <para>action: a pointer to the method which CalledForAction + provides.</para> + </listitem> + <listitem> + <para>CalledForGuard: the type on which the guard method will be + called. It can be either a state of the containing state + machine or the state machine itself.</para> + </listitem> + <listitem> + <para>guard: a pointer to the method which CalledForGuard + provides.</para> + </listitem> + </itemizedlist> + </para> + </refsect3> + </refsect2> + </refsect1> + <refsect1> + <title>msm/front/state_machine_def.hpp</title> + <para>This header provides the implementation of the <command + xlink:href="#basic-front-end">basic front-end</command>. It contains one + type, <code>state_machine_def</code></para> + <refsect2> + <title>state_machine_def definition</title> + <para>This type is the basic class for a basic (or possibly any other) + front-end. It provides the standard row types (which includes internal + transitions) and a default implementation of the required methods and + typedefs.</para> + <classsynopsis> + <ooclass> + <classname>template <class Derived,class BaseState = + default_base_state> state_machine_def</classname> + </ooclass> + </classsynopsis> + <refsect3> + <title>typedefs</title> + <para> + <itemizedlist> + <listitem> + <para>flag_list: by default, no flag is set in the state + machine</para> + </listitem> + <listitem> + <para>deferred_events: by default, no event is deferred.</para> + </listitem> + <listitem> + <para>configuration: by default, no configuration customization + is done.</para> + </listitem> + </itemizedlist> + </para> + </refsect3> + <refsect3> + <title>row methods</title> + <para>Like any other front-end, the following transition row types + implements the two necessary static functions for action and guard call. + Each function receives as parameter the (deepest-level) state machine + processsing the event, the event itself, the source and target states + and all the states contained in a state machine (ignored).</para> + <funcsynopsis> + <funcprototype> + <funcdef>template <class Fsm,class SourceState,class TargetState, + class AllStates> static void action_call</funcdef> + <paramdef> + <funcparams>Fsm& fsm,Event const& + evt,SourceState&,TargetState,AllStates&</funcparams> + </paramdef> + </funcprototype> + </funcsynopsis> + <funcsynopsis> + <funcprototype> + <funcdef>template <class Fsm,class SourceState,class TargetState, + class AllStates> static bool guard_call</funcdef> + <paramdef> + <funcparams>Fsm& fsm,Event const& + evt,SourceState&,TargetState,AllStates&</funcparams> + </paramdef> + </funcprototype> + </funcsynopsis> + </refsect3> + <refsect3> + <title>a_row</title> + <para>This is a transition with action and without guard.</para> + <para><ooclass> + <classname>template< class Source, class Event, class Target, + void (Derived::*action)(Event const&) > a_row</classname> + </ooclass><itemizedlist> + <listitem> + <para>Event: the event triggering the transition.</para> + </listitem> + <listitem> + <para>Source: the source state of the transition.</para> + </listitem> + <listitem> + <para>Target: the target state of the transition.</para> + </listitem> + <listitem> + <para>action: a pointer to the method provided by the concrete + front-end (represented by <code>Derived</code>).</para> + </listitem> + </itemizedlist></para> + </refsect3> + <refsect3> + <title>g_row</title> + <para>This is a transition with guard and without action.</para> + <para><ooclass> + <classname>template< class Source, class Event, class Target, + bool (Derived::*guard)(Event const&) > g_row</classname> + </ooclass><itemizedlist> + <listitem> + <para>Event: the event triggering the transition.</para> + </listitem> + <listitem> + <para>Source: the source state of the transition.</para> + </listitem> + <listitem> + <para>Target: the target state of the transition.</para> + </listitem> + <listitem> + <para>guard: a pointer to the method provided by the concrete + front-end (represented by <code>Derived</code>).</para> + </listitem> + </itemizedlist></para> + </refsect3> + <refsect3> + <title>row</title> + <para>This is a transition with guard and action.</para> + <para><ooclass> + <classname>template< class Source, class Event, class Target, + void (Derived::*action)(Event const&), bool + (Derived::*guard)(Event const&) > row</classname> + </ooclass><itemizedlist> + <listitem> + <para>Event: the event triggering the transition.</para> + </listitem> + <listitem> + <para>Source: the source state of the transition.</para> + </listitem> + <listitem> + <para>Target: the target state of the transition.</para> + </listitem> + <listitem> + <para>action: a pointer to the method provided by the concrete + front-end (represented by <code>Derived</code>).</para> + </listitem> + <listitem> + <para>guard: a pointer to the method provided by the concrete + front-end (represented by <code>Derived</code>).</para> + </listitem> + </itemizedlist></para> + </refsect3> + <refsect3> + <title>_row</title> + <para>This is a transition without action or guard. The state machine only + changes active state.</para> + <para><ooclass> + <classname>template< class Source, class Event, class Target > + _row</classname> + </ooclass><itemizedlist> + <listitem> + <para>Event: the event triggering the transition.</para> + </listitem> + <listitem> + <para>Source: the source state of the transition.</para> + </listitem> + <listitem> + <para>Target: the target state of the transition.</para> + </listitem> + </itemizedlist></para> + </refsect3> + <refsect3> + <title>a_irow</title> + <para>This is an internal transition for use inside a transition table, with + action and without guard.</para> + <para><ooclass> + <classname>template< class Source, class Event, void + (Derived::*action)(Event const&) > a_irow</classname> + </ooclass><itemizedlist> + <listitem> + <para>Event: the event triggering the transition.</para> + </listitem> + <listitem> + <para>Source: the source state of the transition.</para> + </listitem> + <listitem> + <para>action: a pointer to the method provided by the concrete + front-end (represented by <code>Derived</code>).</para> + </listitem> + </itemizedlist></para> + </refsect3> + <refsect3> + <title>g_irow</title> + <para>This is an internal transition for use inside a transition table, with + guard and without action.</para> + <para><ooclass> + <classname>template< class Source, class Event, bool + (Derived::*guard)(Event const&) > g_irow</classname> + </ooclass><itemizedlist> + <listitem> + <para>Event: the event triggering the transition.</para> + </listitem> + <listitem> + <para>Source: the source state of the transition.</para> + </listitem> + <listitem> + <para>guard: a pointer to the method provided by the concrete + front-end (represented by <code>Derived</code>).</para> + </listitem> + </itemizedlist></para> + </refsect3> + <refsect3> + <title>irow</title> + <para>This is an internal transition for use inside a transition table, with + guard and action.</para> + <para><ooclass> + <classname>template< class Source, class Event, void + (Derived::*action)(Event const&), bool + (Derived::*guard)(Event const&) > irow</classname> + </ooclass><itemizedlist> + <listitem> + <para>Event: the event triggering the transition.</para> + </listitem> + <listitem> + <para>Source: the source state of the transition.</para> + </listitem> + <listitem> + <para>action: a pointer to the method provided by the concrete + front-end (represented by <code>Derived</code>).</para> + </listitem> + <listitem> + <para>guard: a pointer to the method provided by the concrete + front-end (represented by <code>Derived</code>).</para> + </listitem> + </itemizedlist></para> + </refsect3> + <refsect3> + <title>_irow</title> + <para>This is an internal transition without action or guard. As it does + nothing, it means "ignore event".</para> + <para><ooclass> + <classname>template< class Source, class Event > + _irow</classname> + </ooclass><itemizedlist> + <listitem> + <para>Event: the event triggering the transition.</para> + </listitem> + <listitem> + <para>Source: the source state of the transition.</para> + </listitem> + </itemizedlist></para> + </refsect3> + <refsect3> + <title>methods</title> + <para><code>state_machine_def</code> provides a default implementation in + case of an event which cannot be processed by a state machine (no + transition found). The implementation is using a + <code>BOOST_ASSERT</code> so that the error will only be noticed in + debug mode. Overwrite this method in your implementation to change the + behavior.</para> + <para> + <funcsynopsis> + <funcprototype> + <funcdef>template <class Fsm,class Event> static void + no_transition</funcdef> + <paramdef> + <funcparams>Event const& ,Fsm&, int + state</funcparams> + </paramdef> + </funcprototype> + </funcsynopsis> + </para> + <para><code>state_machine_def</code> provides a default implementation in + case an exception is thrown by a state (entry/exit) or transition + (action/guard) behavior. The implementation is using a + <code>BOOST_ASSERT</code> so that the error will only be noticed in + debug mode. Overwrite this method in your implementation to change the + behavior. This method will be called only if exception handling is not + deactivated (default) by defining + <code>has_no_message_queue</code>.</para> + <para> + <funcsynopsis> + <funcprototype> + <funcdef>template <class Fsm,class Event> static void + exception_caught</funcdef> + <paramdef> + <funcparams>Event const& ,Fsm&, + std::exception&</funcparams> + </paramdef> + </funcprototype> + </funcsynopsis> + </para> + </refsect3> + </refsect2> + </refsect1> + <refsect1> + <title>msm/front/states.hpp </title> + <para>This header provides the different states (except state machines) for the + basic front-end (or mixed with other front-ends).</para> + <refsect2> + <title>types</title> + <para>This header provides the following types:</para> + <refsect3> + <title>no_sm_ptr</title> + <para>deprecated: default policy for states. It means that states do not + need to save a pointer to their containing state machine.</para> + </refsect3> + <refsect3> + <title>sm_ptr</title> + <para>deprecated: state policy. It means that states need to save a pointer + to their containing state machine. When seeing this flag, the back-end + will call set_sm_ptr(fsm*) and give itself as argument.</para> + </refsect3> + <refsect3> + <title>state</title> + <para>Basic type for simple states. Inherit from this type to define a + simple state. The first argument is needed if you want your state (and + all others used in a concrete state machine) to inherit a basic type for + logging or providing a common behavior.</para> + <classsynopsis> + <ooclass> + <classname>template<class Base = default_base_state,class + SMPtrPolicy = no_sm_ptr> state</classname> + </ooclass> + </classsynopsis> + </refsect3> + <refsect3> + <title>terminate_state</title> + <para>Basic type for terminate states. Inherit from this type to define a + terminate state. The first argument is needed if you want your state + (and all others used in a concrete state machine) to inherit a basic + type for logging or providing a common behavior.</para> + <classsynopsis> + <ooclass> + <classname>template<class Base = default_base_state,class + SMPtrPolicy = no_sm_ptr> terminate_state</classname> + </ooclass> + </classsynopsis> + </refsect3> + <refsect3> + <title>interrupt_state</title> + <para>Basic type for interrupt states. Interrupt states prevent any further + event handling until EndInterruptEvent is sent. Inherit from this type + to define a terminate state. The first argument is the name of the event + ending the interrupt. The second argument is needed if you want your + state (and all others used in a concrete state machine) to inherit a + basic type for logging or providing a common behavior.</para> + <classsynopsis> + <ooclass> + <classname>template<class EndInterruptEvent,class Base = + default_base_state,</classname> + </ooclass> + </classsynopsis> + <classsynopsis> + <ooclass> + <classname>class SMPtrPolicy = no_sm_ptr> + interrupt_state</classname> + </ooclass> + </classsynopsis> + </refsect3> + <refsect3> + <title>explicit_entry</title> + <para>Inherit from this type <emphasis role="underline">in + addition</emphasis> to the desired state type to enable this state + for direct entering. The template parameter gives the region id of the + state (regions are numbered in the order of the + <code>initial_state</code> typedef).</para> + <classsynopsis> + <ooclass> + <classname>template <int ZoneIndex=-1> explicit_entry</classname> + </ooclass> + </classsynopsis> + </refsect3> + <refsect3> + <title>entry_pseudo_state</title> + <para>Basic type for entry pseudo states. Entry pseudo states are an + predefined entry into a submachine and connect two transitions. The + first argument is the id of the region entered by this state (regions + are numbered in the order of the <code>initial_state</code> typedef). + The second argument is needed if you want your state (and all others + used in a concrete state machine) to inherit a basic type for logging or + providing a common behavior.</para> + <classsynopsis> + <ooclass> + <classname>template<int RegionIndex=-1,class Base = + default_base_state,</classname> + </ooclass> + </classsynopsis> + <classsynopsis> + <ooclass> + <classname>class SMPtrPolicy = no_sm_ptr> + entry_pseudo_state</classname> + </ooclass> + </classsynopsis> + </refsect3> + <refsect3> + <title>exit_pseudo_state</title> + <para>Basic type for exit pseudo states. Exit pseudo states are an + predefined exit from a submachine and connect two transitions. The first + argument is the name of the event which will be "thrown" out of the exit + point. This event does not need to be the same as the one sent by the + inner region but must be convertible from it. The second argument is + needed if you want your state (and all others used in a concrete state + machine) to inherit a basic type for logging or providing a common + behavior.</para> + <classsynopsis> + <ooclass> + <classname>template<class Event,class Base = + default_base_state,</classname> + </ooclass> + </classsynopsis> + <classsynopsis> + <ooclass> + <classname>class SMPtrPolicy = no_sm_ptr> + exit_pseudo_state</classname> + </ooclass> + </classsynopsis> + </refsect3> + </refsect2> + </refsect1> + <refsect1> + <title>msm/front/euml/euml.hpp</title> + <para>This header includes all of eUML except the STL functors.</para> + </refsect1> + <refsect1> + <title>msm/front/euml/stl.hpp</title> + <para>This header includes all the functors for STL support in eUML. These <command + xlink:href="#eUML-STL-all">tables</command> show a full description.</para> + </refsect1> + <refsect1> + <title>msm/front/euml/algorithm.hpp</title> + <para>This header includes all the functors for STL algorithms support in eUML. + These <command xlink:href="#eUML-STL-all">tables</command> show a full + description.</para> + </refsect1> + <refsect1> + <title>msm/front/euml/iteration.hpp</title> + <para>This header includes iteration functors for STL support in eUML. This <command + xlink:href="#eUML-STL-iteration">tables</command> shows a full + description.</para> + </refsect1> + <refsect1> + <title>msm/front/euml/querying.hpp</title> + <para>This header includes querying functors for STL support in eUML. This <command + xlink:href="#eUML-STL-querying">tables</command> shows a full + description.</para> + </refsect1> + <refsect1> + <title>msm/front/euml/transformation.hpp</title> + <para>This header includes transformation functors for STL support in eUML. This + <command xlink:href="#eUML-STL-transformation">tables</command> shows a full + description.</para> + </refsect1> + <refsect1> + <title>msm/front/euml/container.hpp</title> + <para>This header includes container functors for STL support in eUML (functors + calling container methods). This <command xlink:href="#eUML-STL-container" + >tables</command> shows a full description. It also provides npos for + strings.</para> + <refsect2> + <title>Npos_<container type></title> + <para>Functor returning npos for transition or state behaviors. Like all + constants, only the functor form exists, so parenthesis are necessary. + Example:</para> + <para><code>string_find_(event_(m_song),Char_<'S'>(),Size_t_<0>()) != + Npos_<string>() // compare result of string::find with + npos</code></para> + </refsect2> + </refsect1> + <refsect1> + <title>msm/front/euml/stt_grammar.hpp</title> + <para>This header provides the transition table grammars. This includes internal + transition tables.</para> + <refsect2> + <title>functions</title> + <refsect3> + <title>build_stt</title> + <para>The function build_stt evaluates the grammar-conform expression as + parameter. It returns a transition table, which is a mpl::vector of + transitions (rows) or, if the expression is ill-formed (does not match + the grammar), the type <code>invalid_type</code>, which will lead to a + compile-time static assertion when this transition table is passed to a + state machine. </para> + <funcsynopsis> + <funcprototype> + <funcdef>template<class Expr> [mpl::vector<...> / + msm::front::euml::invalid_type] build_stt</funcdef> + <paramdef>Expr const& expr</paramdef> + </funcprototype> + </funcsynopsis> + </refsect3> + <refsect3> + <title>build_internal_stt</title> + <para>The function build_internal_stt evaluates the grammar-conform + expression as parameter. It returns a transition table, which is a + mpl::vector of transitions (rows) or, if the expression is ill-formed + (does not match the grammar), the type <code>invalid_type</code>, which + will lead to a compile-time static assertion when this transition table + is passed to a state machine. </para> + <funcsynopsis> + <funcprototype> + <funcdef>template<class Expr> [mpl::vector<...> / + msm::front::euml::invalid_type] build_internal_stt</funcdef> + <paramdef>Expr const& expr</paramdef> + </funcprototype> + </funcsynopsis> + </refsect3> + </refsect2> + <refsect2> + <title>grammars</title> + <refsect3> + <title><command xml:id="reference-stt-grammar">transition + table</command></title> + <para>The transition table accepts the following grammar:</para> + <programlisting>Stt := Row | (Stt ',' Stt) +Row := (Target '==' (SourcePlusEvent)) /* first syntax*/ + | ( (SourcePlusEvent) '==' Target ) /* second syntax*/ + | (SourcePlusEvent) /* internal transitions */ +SourcePlusEvent := (BuildSource '+' BuildEvent)/* standard transition*/ + | (BuildSource) /* anonymous transition */ +BuildSource := state_tag | (state_tag '/' Action) | (state_tag '[' Guard ']') + | (state_tag '[' Guard ']' '/' Action) +BuildEvent := event_tag | (event_tag '/' Action) | (event_tag '[' Guard ']') + | (event_tag '[' Guard ']' '/' Action)</programlisting> + <para>The grammars Action and Guard are defined in state_grammar.hpp and + guard_grammar.hpp respectively. state_tag and event_tag are inherited + from euml_state (or other state variants) and euml_event respectively. + For example, following declarations are possible:</para> + <programlisting>target == source + event [guard] / action, +source + event [guard] / action == target, +source + event [guard] / (action1,action2) == target, +target == source + event [guard] / (action1,action2), +target == source + event, +source + event == target, +target == source + event [guard], +source + event [guard] == target, +target == source + event / action, +source + event /action == target, +source / action == target, /*anonymous transition*/ +target == source / action, /*anonymous transition*/ +source + event /action, /* internal transition*/</programlisting> + </refsect3> + <refsect3> + <title>internal transition table</title> + <para>The internal transition table accepts the following grammar:</para> + <programlisting>IStt := BuildEvent | (IStt ',' IStt)</programlisting> + <para>BuildEvent being defined for both internal and standard transition + tables.</para> + </refsect3> + </refsect2> + </refsect1> + <refsect1> + <title>msm/front/euml/guard_grammar.hpp</title> + <para>This header contains the <code>Guard</code> grammar used in the previous + section. This grammar is long but pretty simple:</para> + <programlisting>Guard := action_tag | (Guard '&&' Guard) + | (Guard '||' Guard) | ... /* operators*/ + | (if_then_else_(Guard,Guard,Guard)) | (function (Action,...Action))</programlisting> + <para>Most C++ operators are supported (address-of is not). With + <code>function</code> is meant any eUML predefined function or any self-made + (using <code>MSM_EUML_METHOD</code> or <code>MSM_EUML_FUNCTION</code>). Action + is a grammar defined in state_grammar.hpp.</para> + </refsect1> + <refsect1> + <title>msm/front/euml/state_grammar.hpp</title> + <para>This header provides the grammar for actions and the different grammars and + functions to build states using eUML.</para> + <refsect2> + <title>action grammar</title> + <para>Like the guard grammar, this grammar supports relevant C++ operators and + eUML functions:</para> + <programlisting>Action := action_tag | (Action '+' Action) + | ('--' Action) | ... /* operators*/ + | if_then_else_(Guard,Action,Action) | if_then_(Action) + | while_(Guard,Action) + | do_while_(Guard,Action) | for_(Action,Guard,Action,Action) + | (function(Action,...Action)) +ActionSequence := Action | (Action ',' Action)</programlisting> + <para>Relevant operators are: ++ (post/pre), -- (post/pre), dereferencing, + + (unary/binary), - (unary/binary), *, /, %, &(bitwise), | (bitwise), + ^(bitwise), +=, -=, *=, /=, %=, <<=, >>=, <<, >>, =, [].</para> + </refsect2> + <refsect2> + <title>attributes</title> + <para>This grammar is used to add attributes to states (or state machines) or + events: It evaluates to a fusion::map. You can use two forms:<itemizedlist> + <listitem> + <para><code>attributes_ << no_attributes_</code></para> + </listitem> + <listitem> + <para><code>attributes_ << attribute_1 << ... << + attribute_n</code></para> + </listitem> + </itemizedlist></para> + <para>Attributes can be of any default-constructible type (fusion + requirement).</para> + </refsect2> + <refsect2> + <title>configure</title> + <para>This grammar also has two forms:<itemizedlist> + <listitem> + <para><code>configure_ << no_configure_</code></para> + </listitem> + <listitem> + <para><code>configure_ << type_1 << ... << + type_n</code></para> + </listitem> + </itemizedlist></para> + <para>This grammar is used to create inside one syntax:<itemizedlist> + <listitem> + <para>flags: <code>configure_ << some_flag</code> where + some_flag inherits from <code>euml_flag<some_flag></code> or + is defined using BOOST_MSM_EUML_FLAG.</para> + </listitem> + <listitem> + <para>deferred events: <code>configure_ << some_event</code> + where some_event inherits from + <code>euml_event<some_event></code> or is defined using + BOOST_MSM_EUML_EVENT or + BOOST_MSM_EUML_EVENT_WITH_ATTRIBUTES.</para> + </listitem> + <listitem> + <para>configuration (message queue, manual deferring, exception + handling): <code>configure_ << some_config</code> where + some_config inherits from + <code>euml_config<some_config></code>. At the moment, + three predefined objects exist (in msm//front/euml/common.hpp):<itemizedlist> + <listitem> + <para>no_exception: disable catching exceptions</para> + </listitem> + <listitem> + <para>no_msg_queue: disable message queue</para> + </listitem> + <listitem> + <para>deferred_events: manually enable handling of + deferred events</para> + </listitem> + </itemizedlist></para> + </listitem> + </itemizedlist></para> + </refsect2> + <refsect2> + <title>initial states</title> + <para>The grammar to define initial states for a state machine is: <code>init_ + << state_1 << ... << state_n</code> where + state_1...state_n inherit from euml_state or is defined using + BOOST_MSM_EUML_STATE, BOOST_MSM_EUML_INTERRUPT_STATE, + BOOST_MSM_EUML_TERMINATE_STATE, BOOST_MSM_EUML_EXPLICIT_ENTRY_STATE, + BOOST_MSM_EUML_ENTRY_STATE or BOOST_MSM_EUML_EXIT_STATE.</para> + </refsect2> + <refsect2> + <title>functions</title> + <refsect3> + <title>build_sm</title> + <para>This function has several overloads. The return type is not relevant + to you as only decltype (return type) is what one needs.</para> + <para>Defines a state machine without entry or exit:</para> + <funcsynopsis> + <funcprototype> + <funcdef>template <class StateNameTag,class Stt,class Init> + func_state_machine<...> build_sm</funcdef> + <paramdef>Stt ,Init</paramdef> + </funcprototype> + </funcsynopsis> + <para>Defines a state machine with entry behavior:</para> + <funcsynopsis> + <funcprototype> + <funcdef>template <class StateNameTag,class Stt,class Init,class + Expr1> func_state_machine<...> build_sm</funcdef> + <paramdef>Stt ,Init,Expr1 const&</paramdef> + </funcprototype> + </funcsynopsis> + <para>Defines a state machine with entry and exit behaviors:</para> + <funcsynopsis> + <funcprototype> + <funcdef>template <class StateNameTag,class Stt,class Init,class + Expr1, class Expr2> func_state_machine<...> + build_sm</funcdef> + <paramdef>Stt ,Init,Expr1 const&,Expr2 const&</paramdef> + </funcprototype> + </funcsynopsis> + <para>Defines a state machine with entry, exit behaviors and + attributes:</para> + <funcsynopsis> + <funcprototype> + <funcdef>template <class StateNameTag,class Stt,class Init,class + Expr1, class Expr2, class Attributes> func_state_machine<...> + build_sm</funcdef> + <paramdef>Stt ,Init,Expr1 const&, Expr2 const&, Attributes + const&</paramdef> + </funcprototype> + </funcsynopsis> + <para>Defines a state machine with entry, exit behaviors, attributes and + configuration (deferred events, flags):</para> + <funcsynopsis> + <funcprototype> + <funcdef>template <class StateNameTag,class Stt,class Init,class + Expr1, class Expr2, class Attributes, class Configure> + func_state_machine<...> build_sm</funcdef> + <paramdef>Stt ,Init,Expr1 const&, Expr2 const&, Attributes + const&, Configure const&</paramdef> + </funcprototype> + </funcsynopsis> + <para>Defines a state machine with entry, exit behaviors, attributes, + configuration (deferred events, flags) and a base state:</para> + <funcsynopsis> + <funcprototype> + <funcdef>template <class StateNameTag,class Stt,class Init,class + Expr1, class Expr2, class Attributes, class Configure, class + Base> func_state_machine<...> build_sm</funcdef> + <paramdef>Stt ,Init,Expr1 const&, Expr2 const&, Attributes + const&, Configure const&, Base</paramdef> + </funcprototype> + </funcsynopsis> + <para>Notice that this function requires the extra parameter class + StateNameTag to disambiguate state machines having the same parameters + but still being different.</para> + </refsect3> + <refsect3> + <title>build_state</title> + <para>This function has several overloads. The return type is not relevant + to you as only decltype (return type) is what one needs.</para> + <para>Defines a simple state without entry or exit:</para> + <funcsynopsis> + <funcprototype> + <funcdef>func_state<class StateNameTag,...> build_state</funcdef> + <paramdef/> + </funcprototype> + </funcsynopsis> + <para>Defines a simple state with entry behavior:</para> + <funcsynopsis> + <funcprototype> + <funcdef>template <class StateNameTag,class Expr1> + func_state<...> build_state</funcdef> + <paramdef>Expr1 const&</paramdef> + </funcprototype> + </funcsynopsis> + <para>Defines a simple state with entry and exit behaviors:</para> + <funcsynopsis> + <funcprototype> + <funcdef>template <class StateNameTag,class Expr1, class Expr2> + func_state<...> build_state</funcdef> + <paramdef>Expr1 const&,Expr2 const&</paramdef> + </funcprototype> + </funcsynopsis> + <para>Defines a simple state with entry, exit behaviors and + attributes:</para> + <funcsynopsis> + <funcprototype> + <funcdef>template <class StateNameTag,class Expr1, class Expr2, + class Attributes> func_state<...> build_state</funcdef> + <paramdef>Expr1 const&, Expr2 const&, Attributes + const&</paramdef> + </funcprototype> + </funcsynopsis> + <para>Defines a simple state with entry, exit behaviors, attributes and + configuration (deferred events, flags):</para> + <funcsynopsis> + <funcprototype> + <funcdef>template <class StateNameTag,class Expr1, class Expr2, + class Attributes, class Configure> func_state<...> + build_state</funcdef> + <paramdef>Expr1 const&, Expr2 const&, Attributes const&, + Configure const&</paramdef> + </funcprototype> + </funcsynopsis> + <para>Defines a simple state with entry, exit behaviors, attributes, + configuration (deferred events, flags) and a base state:</para> + <funcsynopsis> + <funcprototype> + <funcdef>template <class StateNameTag,class Expr1, class Expr2, + class Attributes, class Configure, class Base> + func_state<...> build_state</funcdef> + <paramdef>Expr1 const&, Expr2 const&, Attributes const&, + Configure const&, Base</paramdef> + </funcprototype> + </funcsynopsis> + <para>Notice that this function requires the extra parameter class + StateNameTag to disambiguate states having the same parameters but still + being different.</para> + </refsect3> + <refsect3> + <title>build_terminate_state</title> + <para>This function has the same overloads as build_state.</para> + </refsect3> + <refsect3> + <title>build_interrupt_state</title> + <para>This function has several overloads. The return type is not relevant + to you as only decltype (return type) is what one needs.</para> + <para>Defines an interrupt state without entry or exit:</para> + <funcsynopsis> + <funcprototype> + <funcdef>template <class StateNameTag,class EndInterruptEvent> + func_state<...> build_interrupt_state</funcdef> + <paramdef>EndInterruptEvent const&</paramdef> + </funcprototype> + </funcsynopsis> + <para>Defines an interrupt state with entry behavior:</para> + <funcsynopsis> + <funcprototype> + <funcdef>template <class StateNameTag,class + EndInterruptEvent,class Expr1> func_state<...> + build_interrupt_state</funcdef> + <paramdef>EndInterruptEvent const&,Expr1 const&</paramdef> + </funcprototype> + </funcsynopsis> + <para>Defines an interrupt state with entry and exit behaviors:</para> + <funcsynopsis> + <funcprototype> + <funcdef>template <class StateNameTag,class + EndInterruptEvent,class Expr1, class Expr2> func_state<...> + build_interrupt_state</funcdef> + <paramdef>EndInterruptEvent const&,Expr1 const&,Expr2 + const&</paramdef> + </funcprototype> + </funcsynopsis> + <para>Defines an interrupt state with entry, exit behaviors and + attributes:</para> + <funcsynopsis> + <funcprototype> + <funcdef>template <class StateNameTag,class + EndInterruptEvent,class Expr1, class Expr2, class Attributes> + func_state<...> build_interrupt_state</funcdef> + <paramdef>EndInterruptEvent const&,Expr1 const&, Expr2 + const&, Attributes const&</paramdef> + </funcprototype> + </funcsynopsis> + <para>Defines an interrupt state with entry, exit behaviors, attributes and + configuration (deferred events, flags):</para> + <funcsynopsis> + <funcprototype> + <funcdef>template <class StateNameTag,class + EndInterruptEvent,class Expr1, class Expr2, class Attributes, + class Configure> func_state<...> + build_interrupt_state</funcdef> + <paramdef>EndInterruptEvent const&,Expr1 const&, Expr2 + const&, Attributes const&, Configure + const&</paramdef> + </funcprototype> + </funcsynopsis> + <para>Defines an interrupt state with entry, exit behaviors, attributes, + configuration (deferred events, flags) and a base state:</para> + <funcsynopsis> + <funcprototype> + <funcdef>template <class StateNameTag,class + EndInterruptEvent,class Expr1, class Expr2, class Attributes, + class Configure, class Base> func_state<...> + build_interrupt_state</funcdef> + <paramdef>EndInterruptEvent const&,Expr1 const&, Expr2 + const&, Attributes const&, Configure const&, + Base</paramdef> + </funcprototype> + </funcsynopsis> + <para>Notice that this function requires the extra parameter class + StateNameTag to disambiguate states having the same parameters but still + being different.</para> + </refsect3> + <refsect3> + <title>build_entry_state</title> + <para>This function has several overloads. The return type is not relevant + to you as only decltype (return type) is what one needs.</para> + <para>Defines an entry pseudo state without entry or exit:</para> + <funcsynopsis> + <funcprototype> + <funcdef>template <class StateNameTag,int RegionIndex> + entry_func_state<...> build_entry_state</funcdef> + <paramdef/> + </funcprototype> + </funcsynopsis> + <para>Defines an entry pseudo state with entry behavior:</para> + <funcsynopsis> + <funcprototype> + <funcdef>template <class StateNameTag,int RegionIndex,class + Expr1> entry_func_state<...> build_entry_state</funcdef> + <paramdef>Expr1 const&</paramdef> + </funcprototype> + </funcsynopsis> + <para>Defines an entry pseudo state with entry and exit behaviors:</para> + <funcsynopsis> + <funcprototype> + <funcdef>template <class StateNameTag,int RegionIndex,class + Expr1, class Expr2> entry_func_state<...> + build_entry_state</funcdef> + <paramdef>Expr1 const&,Expr2 const&</paramdef> + </funcprototype> + </funcsynopsis> + <para>Defines an entry pseudo state with entry, exit behaviors and + attributes:</para> + <funcsynopsis> + <funcprototype> + <funcdef>template <class StateNameTag,int RegionIndex,class + Expr1, class Expr2, class Attributes> entry_func_state<...> + build_entry_state</funcdef> + <paramdef>Expr1 const&, Expr2 const&, Attributes + const&</paramdef> + </funcprototype> + </funcsynopsis> + <para>Defines an entry pseudo state with entry, exit behaviors, attributes + and configuration (deferred events, flags):</para> + <funcsynopsis> + <funcprototype> + <funcdef>template <class StateNameTag,int RegionIndex,class + Expr1, class Expr2, class Attributes, class Configure> + entry_func_state<...> build_entry_state</funcdef> + <paramdef>Expr1 const&, Expr2 const&, Attributes const&, + Configure const&</paramdef> + </funcprototype> + </funcsynopsis> + <para>Defines an entry pseudo state with entry, exit behaviors, attributes, + configuration (deferred events, flags) and a base state:</para> + <funcsynopsis> + <funcprototype> + <funcdef>template <class StateNameTag,int RegionIndex,class + Expr1, class Expr2, class Attributes, class Configure, class + Base> entry_func_state<...> build_entry_state</funcdef> + <paramdef>Expr1 const&, Expr2 const&, Attributes const&, + Configure const&, Base</paramdef> + </funcprototype> + </funcsynopsis> + <para>Notice that this function requires the extra parameter class + StateNameTag to disambiguate states having the same parameters but still + being different.</para> + </refsect3> + <refsect3> + <title>build_exit_state</title> + <para>This function has several overloads. The return type is not relevant + to you as only decltype (return type) is what one needs.</para> + <para>Defines an exit pseudo state without entry or exit:</para> + <funcsynopsis> + <funcprototype> + <funcdef>template <class StateNameTag,class Event> + exit_func_state<...> build_exit_state</funcdef> + <paramdef>Event const&</paramdef> + </funcprototype> + </funcsynopsis> + <para>Defines an exit pseudo state with entry behavior:</para> + <funcsynopsis> + <funcprototype> + <funcdef>template <class StateNameTag,class Event,class Expr1> + exit_func_state<...> build_exit_state</funcdef> + <paramdef>Event const&,Expr1 const&</paramdef> + </funcprototype> + </funcsynopsis> + <para>Defines an exit pseudo state with entry and exit behaviors:</para> + <funcsynopsis> + <funcprototype> + <funcdef>template <class StateNameTag,class Event,class Expr1, + class Expr2> exit_func_state<...> build_exit_state</funcdef> + <paramdef>Event const&,Expr1 const&,Expr2 + const&</paramdef> + </funcprototype> + </funcsynopsis> + <para>Defines an exit pseudo state with entry, exit behaviors and + attributes:</para> + <funcsynopsis> + <funcprototype> + <funcdef>template <class StateNameTag,class Event,class Expr1, + class Expr2, class Attributes> exit_func_state<...> + build_exit_state</funcdef> + <paramdef>Event const&,Expr1 const&, Expr2 const&, + Attributes const&</paramdef> + </funcprototype> + </funcsynopsis> + <para>Defines an exit pseudo state with entry, exit behaviors, attributes + and configuration (deferred events, flags):</para> + <funcsynopsis> + <funcprototype> + <funcdef>template <class StateNameTag,class Event,class Expr1, + class Expr2, class Attributes, class Configure> + exit_func_state<...> build_exit_state</funcdef> + <paramdef>Event const&,Expr1 const&, Expr2 const&, + Attributes const&, Configure const&</paramdef> + </funcprototype> + </funcsynopsis> + <para>Defines an exit pseudo state with entry, exit behaviors, attributes, + configuration (deferred events, flags) and a base state:</para> + <funcsynopsis> + <funcprototype> + <funcdef>template <class StateNameTag,class Event,class Expr1, + class Expr2, class Attributes, class Configure, class Base> + exit_func_state<...> build_exit_state</funcdef> + <paramdef>Event const&,Expr1 const&, Expr2 const&, + Attributes const&, Configure const&, Base</paramdef> + </funcprototype> + </funcsynopsis> + <para>Notice that this function requires the extra parameter class + StateNameTag to disambiguate states having the same parameters but still + being different.</para> + </refsect3> + <refsect3> + <title>build_explicit_entry_state</title> + <para>This function has the same overloads as build_entry_state and + explicit_entry_func_state as return type.</para> + </refsect3> + </refsect2> + </refsect1> + <refsect1> + <title>msm/front/euml/common.hpp</title> + <refsect2> + <title>types</title> + <refsect3> + <title>euml_event</title> + <para>The basic type for events with eUML.</para> + <classsynopsis> + <ooclass> + <classname>template <class EventName> euml_event;</classname> + </ooclass> + </classsynopsis> + <programlisting>struct play : euml_event<play>{};</programlisting> + </refsect3> + <refsect3> + <title>euml_state</title> + <para>The basic type for states with eUML. You will usually not use this + type directly as it is easier to use BOOST_MSM_EUML_STATE, + BOOST_MSM_EUML_INTERRUPT_STATE, BOOST_MSM_EUML_TERMINATE_STATE, + BOOST_MSM_EUML_EXPLICIT_ENTRY_STATE, BOOST_MSM_EUML_ENTRY_STATE or + BOOST_MSM_EUML_EXIT_STATE.</para> + <classsynopsis> + <ooclass> + <classname>template <class StateName> euml_state;</classname> + </ooclass> + </classsynopsis> + <para>You can however use this type directly if you want to provide your + state with extra functions or provide entry or exit behaviors without + functors, for example:</para> + <programlisting>struct Empty : public msm::front::state<> , public euml_state<Empty> +{ + void foo() {...} + template <class Event,class Fsm> + void on_entry(Event const& evt,Fsm& fsm){...} +};</programlisting> + </refsect3> + <refsect3> + <title>euml_flag</title> + <para>The basic type for flags with eUML.</para> + <classsynopsis> + <ooclass> + <classname>template <class FlagName> euml_flag;</classname> + </ooclass> + </classsynopsis> + <programlisting>struct PlayingPaused: euml_flag<PlayingPaused>{};</programlisting> + </refsect3> + <refsect3> + <title>euml_action</title> + <para>The basic type for state or transition behaviors and guards with + eUML.</para> + <classsynopsis> + <ooclass> + <classname>template <class AcionName> euml_action;</classname> + </ooclass> + </classsynopsis> + <programlisting>struct close_drawer : euml_action<close_drawer> +{ + template <class Fsm,class Evt,class SourceState,class TargetState> + void operator()(Evt const& , Fsm&, SourceState& ,TargetState& ) {...} +};</programlisting> + <para>Or, as state entry or exit behavior:</para> + <programlisting>struct Playing_Entry : euml_action<Playing_Entry> +{ + template <class Event,class Fsm,class State> + void operator()(Event const&,Fsm& fsm,State& ){...} +};</programlisting> + </refsect3> + <refsect3> + <title>euml_config</title> + <para>The basic type for configuration possibilities with eUML.</para> + <classsynopsis> + <ooclass> + <classname>template <class ConfigName> euml_config;</classname> + </ooclass> + </classsynopsis> + <para>You normally do not use this type directly but instead the instances + of predefined configuration:<itemizedlist> + <listitem> + <para>no_exception: disable catching exceptions</para> + </listitem> + <listitem> + <para>no_msg_queue: disable message queue. The message queue + allows you to send an event for procesing while in an event + processing.</para> + </listitem> + <listitem> + <para>deferred_events: manually enable handling of deferred + events</para> + </listitem> + </itemizedlist></para> + </refsect3> + <refsect3> + <title>invalid_type</title> + <para>Type returned by grammar parsers if the grammar is invalid. Seeing + this type will result in a static assertion.</para> + </refsect3> + <refsect3> + <title>no_action</title> + <para>Placeholder type for use in entry/exit or transition behaviors, which + does absolutely nothing.</para> + </refsect3> + <refsect3> + <title>source_</title> + <para>Generic object or function for the source state of a given transition:<itemizedlist> + <listitem> + <para>as object: returns by reference the source state of a + transition, usually to be used by another function (usually + one created by MSM_EUML_METHOD or MSM_EUML_FUNCTION).</para> + <para>Example: + <programlisting>some_user_function_(source_)</programlisting></para> + </listitem> + <listitem> + <para>as function: returns by reference the attribute passed as + parameter.</para> + <para>Example: + <programlisting>source_(m_counter)++</programlisting></para> + </listitem> + </itemizedlist></para> + </refsect3> + <refsect3> + <title>target_</title> + <para>Generic object or function for the target state of a given transition:<itemizedlist> + <listitem> + <para>as object: returns by reference the target state of a + transition, usually to be used by another function (usually + one created by MSM_EUML_METHOD or MSM_EUML_FUNCTION).</para> + <para>Example: + <programlisting>some_user_function_(target_)</programlisting></para> + </listitem> + <listitem> + <para>as function: returns by reference the attribute passed as + parameter.</para> + <para>Example: + <programlisting>target_(m_counter)++</programlisting></para> + </listitem> + </itemizedlist></para> + </refsect3> + <refsect3> + <title>state_</title> + <para>Generic object or function for the state of a given entry / exit + behavior. state_ means source_ while in the context of an exit behavior + and target_ in the context of an entry behavior:<itemizedlist> + <listitem> + <para>as object: returns by reference the current state, usually + to be used by another function (usually one created by + MSM_EUML_METHOD or MSM_EUML_FUNCTION).</para> + <para>Example: + <programlisting>some_user_function_(state_) // calls some_user_function on the current state</programlisting></para> + </listitem> + <listitem> + <para>as function: returns by reference the attribute passed as + parameter.</para> + <para>Example: + <programlisting>state_(m_counter)++</programlisting></para> + </listitem> + </itemizedlist></para> + </refsect3> + <refsect3> + <title>event_</title> + <para>Generic object or function for the event triggering a given transition + (valid in a transition behavior, as well as in state entry/exit behaviors):<itemizedlist> + <listitem> + <para>as object: returns by reference the event of a transition, + usually to be used by another function (usually one created + by MSM_EUML_METHOD or MSM_EUML_FUNCTION).</para> + <para>Example: + <programlisting>some_user_function_(event_)</programlisting></para> + </listitem> + <listitem> + <para>as function: returns by reference the attribute passed as + parameter.</para> + <para>Example: + <programlisting>event_(m_counter)++</programlisting></para> + </listitem> + </itemizedlist></para> + </refsect3> + <refsect3> + <title>fsm_</title> + <para>Generic object or function for the state machine containing a given transition:<itemizedlist> + <listitem> + <para>as object: returns by reference the event of a transition, + usually to be used by another function (usually one created + by MSM_EUML_METHOD or MSM_EUML_FUNCTION).</para> + <para>Example: + <programlisting>some_user_function_(fsm_)</programlisting></para> + </listitem> + <listitem> + <para>as function: returns by reference the attribute passed as + parameter.</para> + <para>Example: + <programlisting>fsm_(m_counter)++</programlisting></para> + </listitem> + </itemizedlist></para> + </refsect3> + <refsect3> + <title>substate_</title> + <para>Generic object or function returning a state of a given state machine:<itemizedlist> + <listitem> + <para>with 1 parameter: returns by reference the state passed as + parameter, usually to be used by another function (usually + one created by MSM_EUML_METHOD or MSM_EUML_FUNCTION).</para> + <para>Example: + <programlisting>some_user_function_(substate_(my_state))</programlisting></para> + </listitem> + <listitem> + <para>with 2 parameters: returns by reference the state passed + as first parameter from the state machine passed as second + parameter, usually to be used by another function (usually + one created by MSM_EUML_METHOD or MSM_EUML_FUNCTION). This + makes sense when used in combination with attribute_.</para> + <para>Example (equivalent to the previous example): + <programlisting>some_user_function_(substate_(my_state,fsm_))</programlisting></para> + </listitem> + </itemizedlist></para> + </refsect3> + <refsect3> + <title>attribute_</title> + <para>Generic object or function returning the attribute passed (by name) as + second parameter of the thing passed as first (a state, event or state + machine). Example: </para> + <para> + <programlisting>attribute_(substate_(my_state),cd_name_attribute)++</programlisting> + </para> + </refsect3> + <refsect3> + <title>True_</title> + <para>Functor returning true for transition or state behaviors. Like all + constants, only the functor form exists, so parenthesis are necessary. + Example:</para> + <para> + <programlisting>if_then_(True_(),/* some action always called*/)</programlisting> + </para> + </refsect3> + <refsect3> + <title>False_</title> + <para>Functor returning false for transition or state behaviors. Like all + constants, only the functor form exists, so parenthesis are necessary. + Example:</para> + <para> + <programlisting>if_then_(False_(),/* some action never called */)</programlisting> + </para> + </refsect3> + <refsect3> + <title>Int_<int value></title> + <para>Functor returning an integer value for transition or state behaviors. + Like all constants, only the functor form exists, so parenthesis are + necessary. Example:</para> + <para> + <programlisting>target_(m_ringing_cpt) = Int_<RINGING_TIME>() // RINGING_TIME is a constant</programlisting> + </para> + </refsect3> + <refsect3> + <title>Char_<char value></title> + <para>Functor returning a char value for transition or state behaviors. Like + all constants, only the functor form exists, so parenthesis are + necessary. Example:</para> + <para> + <programlisting>// look for 'S' in event.m_song +[string_find_(event_(m_song),Char_<'S'>(),Size_t_<0>()) != Npos_<string>()]</programlisting> + </para> + </refsect3> + <refsect3> + <title>Size_t_<size_t value></title> + <para>Functor returning a size_t value for transition or state behaviors. + Like all constants, only the functor form exists, so parenthesis are + necessary. Example:</para> + <para> + <programlisting>substr_(event_(m_song),Size_t_<1>()) // returns a substring of event.m_song</programlisting> + </para> + </refsect3> + <refsect3> + <title>String_ < mpl::string ></title> + <para>Functor returning a string for transition or state behaviors. Like all + constants, only the functor form exists, so parenthesis are necessary. + Requires boost >= 1.40 for mpl::string.</para> + <para>Example:</para> + <para> + <programlisting>// adds "Let it be" to fsm.m_src_container +push_back_(fsm_(m_src_container), String_<mpl::string<'Let','it ','be'> >())</programlisting> + </para> + </refsect3> + <refsect3> + <title>Predicate_ < some_stl_compatible_functor ></title> + <para>This functor eUML-enables a STL functor (for use in an algorithm). + This is necessary because all what is in the transition table must be a + eUML terminal.</para> + <para>Example:</para> + <programlisting>//equivalent to: +//std::accumulate(fsm.m_vec.begin(),fsm.m_vec.end(),1,std::plus<int>())== 1 +accumulate_(begin_(fsm_(m_vec)),end_(fsm_(m_vec)),Int_<1>(), + Predicate_<std::plus<int> >()) == Int_<1>())</programlisting> + </refsect3> + <refsect3> + <title>process_</title> + <para>This function sends an event to up to 4 state machines by calling + <code>process_event</code> on them:<itemizedlist> + <listitem> + <para><code>process_(some_event)</code> : processes an event in + the current (containing) state machine.</para> + </listitem> + <listitem> + <para><code>process_(some_event [,fsm1...fsm4] )</code> : + processes the same event in the 1-4 state machines passed as + argument.</para> + </listitem> + </itemizedlist></para> + </refsect3> + <refsect3> + <title>process2_</title> + <para>This function sends an event to up to 3 state machines by calling + <code>process_event</code> on them and copy-constructing the event + from the data passed as second parameter:<itemizedlist> + <listitem> + <para><code>process2_(some_event, some_data)</code> : processes + an event in the current (containing) state machine.</para> + </listitem> + <listitem> + <para><code>process2_(some_event, some_data [,fsm1...fsm3] + )</code> : processes the same event in the 1-3 state + machines passed as argument.</para> + </listitem> + </itemizedlist></para> + <para>Example: </para> + <para> + <programlisting>// processes NotFound on current state machine, +// copy-constructed with event.m_song +process2_(NotFound,event_(m_song))</programlisting> + </para> + <para>With the following definitions:</para> + <programlisting>BOOST_MSM_EUML_DECLARE_ATTRIBUTE(std::string,m_song)//declaration of m_song +NotFound (const string& data) // copy-constructor of NotFound</programlisting> + </refsect3> + <refsect3> + <title>is_flag_</title> + <para>This function tells if a flag is active by calling + <code>is_flag_active</code> on the current state machine or one + passed as parameter:<itemizedlist> + <listitem> + <para><code>is_flag_(some_flag)</code> : calls + <code>is_flag_active</code> on the current (containing) + state machine.</para> + </listitem> + <listitem> + <para><code>is_flag_(some_flag, some_fsm)</code> :calls + <code>is_flag_active</code> on the state machine.passed + as argument.</para> + </listitem> + </itemizedlist></para> + </refsect3> + <refsect3> + <title>defer_</title> + <para>This object defers the current event by calling + <code>defer_event</code> on the current state machine. + Example:</para> + <programlisting>Empty() + play() / defer_</programlisting> + </refsect3> + <refsect3> + <title>explicit_(submachine-name,state-name)</title> + <para>Used as transition's target, causes an explicit entry into the given + state from the given submachine. Several explicit_ as targets, separated + by commas, means a fork. The state must have been declared as such using + BOOST_MSM_EUML_EXPLICIT_ENTRY_STATE.</para> + </refsect3> + <refsect3> + <title>entry_pt_(submachine-name,state-name)</title> + <para>Used as transition's target from a containing state machine, causes + submachine-name to be entered using the given entry pseudo-state. This + state must have been declared as pseudo entry using + BOOST_MSM_EUML_ENTRY_STATE.</para> + </refsect3> + <refsect3> + <title>exit_pt_(submachine-name,state-name)</title> + <para>Used as transition's source from a containing state machine, causes + submachine-name to be left using the given exit pseudo-state. This state + must have been declared as pseudo exit using + BOOST_MSM_EUML_EXIT_STATE.</para> + </refsect3> + <refsect3> + <title>MSM_EUML_FUNCTION</title> + <para>This macro creates a eUML function and a functor for use with the + functor front-end, based on a free function:<itemizedlist> + <listitem> + <para>first parameter: the name of the functor</para> + </listitem> + <listitem> + <para>second parameter: the underlying function</para> + </listitem> + <listitem> + <para>third parameter: the eUML function name</para> + </listitem> + <listitem> + <para>fourth parameter: the return type if used in a transition + behavior</para> + </listitem> + <listitem> + <para>fifth parameter: the return type if used in a state + behavior (entry/exit)</para> + </listitem> + </itemizedlist> Note that the function itself can take up to 5 + arguments.</para> + <para>Example:</para> + <para> + <programlisting>MSM_EUML_FUNCTION(BinarySearch_,std::binary_search,binary_search_,bool,bool)</programlisting> + </para> + <para>Can be used like:</para> + <para> + <programlisting>binary_search_(begin_(fsm_(m_var)),end_(fsm_(m_var)),Int_<9>())</programlisting> + </para> + </refsect3> + <refsect3> + <title>MSM_EUML_METHOD</title> + <para>This macro creates a eUML function and a functor for use with the + functor front-end, based on a method:<itemizedlist> + <listitem> + <para>first parameter: the name of the functor</para> + </listitem> + <listitem> + <para>second parameter: the underlying function</para> + </listitem> + <listitem> + <para>third parameter: the eUML function name</para> + </listitem> + <listitem> + <para>fourth parameter: the return type if used in a transition + behavior</para> + </listitem> + <listitem> + <para>fifth parameter: the return type if used in a state + behavior (entry/exit)</para> + </listitem> + </itemizedlist> Note that the method itself can take up to 4 arguments + (5 like for a free function - 1 for the object on which the method is + called).</para> + <para>Example:</para> + <programlisting>struct Empty : public msm::front::state<> , public euml_state<Empty> +{ + void activate_empty() {std::cout << "switching to Empty " << std::endl;} +... +}; +MSM_EUML_METHOD(ActivateEmpty_,activate_empty,activate_empty_,void,void)</programlisting> + <para>Can be used like:</para> + <para> + <programlisting>Empty == Open + open_close / (close_drawer , activate_empty_(target_))</programlisting> + </para> + </refsect3> + <refsect3> + <title>BOOST_MSM_EUML_ACTION(action-instance-name)</title> + <para>This macro declares a behavior type and a const instance for use in + state or transition behaviors. The action implementation itself follows + the macro declaration, for example:</para> + <programlisting>BOOST_MSM_EUML_ACTION(good_disk_format) +{ + template <class Fsm,class Evt,class SourceState,class TargetState> + void/bool operator()(Evt const& evt,Fsm&,SourceState& ,TargetState& ){...} +};</programlisting> + </refsect3> + <refsect3> + <title>BOOST_MSM_EUML_FLAG(flag-instance-name)</title> + <para>This macro declares a flag type and a const instance for use in + behaviors.</para> + </refsect3> + <refsect3> + <title>BOOST_MSM_EUML_FLAG_NAME(flag-instance-name)</title> + <para>This macro returns the name of the flag type generated by + BOOST_MSM_EUML_FLAG. You need this where the type is required (usually + with the back-end method is_flag_active). For example:</para> + <programlisting>fsm.is_flag_active<BOOST_MSM_EUML_FLAG_NAME(CDLoaded)>()</programlisting> + </refsect3> + <refsect3> + <title>BOOST_MSM_EUML_DECLARE_ATTRIBUTE(event-type,event-name)</title> + <para>This macro declares an attribute called event-name of type event-type. + This attribute can then be made part of an attribute list using + BOOST_MSM_EUML_ATTRIBUTES.</para> + </refsect3> + <refsect3> + <title>BOOST_MSM_EUML_ATTRIBUTES(attributes-expression,attributes-name)</title> + <para>This macro declares an attribute list called attributes-name based on + the expression as first argument. These attributes can then be made part + of an event using BOOST_MSM_EUML_EVENT_WITH_ATTRIBUTES, of a state as + 3rd parameter of BOOST_MSM_EUML_STATE or of a state machine as 5th + parameter of BOOST_MSM_EUML_DECLARE_STATE_MACHINE.</para> + <para>Attributes are added using left-shift, for example:</para> + <programlisting>// m_song is of type std::string +BOOST_MSM_EUML_DECLARE_ATTRIBUTE(std::string,m_song) +// contains one attribute, m_song +BOOST_MSM_EUML_ATTRIBUTES((attributes_ << m_song ), FoundDef)</programlisting> + </refsect3> + <refsect3> + <title>BOOST_MSM_EUML_EVENT(event-instance name)</title> + <para>This macro defines an event type (event-instance-name_helper) and + declares a const instance of this event type called event-instance-name + for use in a transition table or state behaviors.</para> + </refsect3> + <refsect3> + <title>BOOST_MSM_EUML_EVENT_WITH_ATTRIBUTES(event-instance-name,attributes)</title> + <para>This macro defines an event type (event-instance-name_helper) and + declares a const instance of this event type called event-instance-name + for use in a transition table or state behaviors. The event will have as + attributes the ones passed by the second argument:</para> + <para><code>BOOST_MSM_EUML_EVENT_WITH_ATTRIBUTES(Found,FoundDef)</code> + </para> + <para>The created event instance supports operator()(attributes) so that + <programlisting>my_back_end.process_event(Found(some_string))</programlisting> + is possible.</para> + </refsect3> + <refsect3> + <title>BOOST_MSM_EUML_EVENT_NAME(event-instance-name)</title> + <para>This macro returns the name of the event type generated by + BOOST_MSM_EUML_EVENT or BOOST_MSM_EUML_EVENT_WITH_ATTRIBUTES. You need + this where the type is required (usually inside a back-end definition). + For example:</para> + <para> + <programlisting>typedef msm::back::state_machine<Playing_, +msm::back::ShallowHistory<mpl::vector<BOOST_MSM_EUML_EVENT_NAME(end_pause) +> > > Playing_type;</programlisting> + </para> + </refsect3> + <refsect3> + <title>BOOST_MSM_EUML_STATE(build-expression,state-instance-name)</title> + <para>This macro defines a state type (state-instance-name_helper) and + declares a const instance of this state type called state-instance-name + for use in a transition table or state behaviors.</para> + <para>There are several possibilitites for the expression syntax:<itemizedlist> + <listitem> + <para>(): state without entry or exit action.</para> + </listitem> + <listitem> + <para>(Expr1): state with entry but no exit action.</para> + </listitem> + <listitem> + <para>(Expr1,Expr2): state with entry and exit action.</para> + </listitem> + <listitem> + <para>(Expr1,Expr2,Attributes): state with entry and exit + action, defining some attributes.</para> + </listitem> + <listitem> + <para>(Expr1,Expr2,Attributes,Configure): state with entry and + exit action, defining some attributes and flags (standard + MSM flags) or deferred events (standard MSM deferred + events).</para> + </listitem> + <listitem> + <para>(Expr1,Expr2,Attributes,Configure,Base): state with entry + and exit action, defining some attributes, flags and + deferred events (plain msm deferred events) and a + non-default base state (as defined in standard MSM).</para> + </listitem> + </itemizedlist></para> + </refsect3> + <refsect3> + <title>BOOST_MSM_EUML_INTERRUPT_STATE(build-expression,state-instance-name)</title> + <para>This macro defines an interrupt state type + (state-instance-name_helper) and declares a const instance of this state + type called state-instance-name for use in a transition table or state + behaviors.</para> + <para>There are several possibilitites for the expression syntax. In all of + them, the first argument is the name of the event (generated by one of + the previous macros) ending the interrupt:<itemizedlist> + <listitem> + <para>(end_interrupt_event): interrupt state without entry or + exit action.</para> + </listitem> + <listitem> + <para>(end_interrupt_event,Expr1): interrupt state with entry + but no exit action.</para> + </listitem> + <listitem> + <para>(end_interrupt_event,Expr1,Expr2): interrupt state with + entry and exit action.</para> + </listitem> + <listitem> + <para>(end_interrupt_event,Expr1,Expr2,Attributes): interrupt + state with entry and exit action, defining some + attributes.</para> + </listitem> + <listitem> + <para>(end_interrupt_event,Expr1,Expr2,Attributes,Configure): + interrupt state with entry and exit action, defining some + attributes and flags (standard MSM flags) or deferred events + (standard MSM deferred events).</para> + </listitem> + <listitem> + <para>(end_interrupt_event,Expr1,Expr2,Attributes,Configure,Base): + interrupt state with entry and exit action, defining some + attributes, flags and deferred events (plain msm deferred + events) and a non-default base state (as defined in standard + MSM).</para> + </listitem> + </itemizedlist></para> + </refsect3> + <refsect3> + <title>BOOST_MSM_EUML_TERMINATE_STATE(build-expression,state-instance-name)</title> + <para>This macro defines a terminate pseudo-state type + (state-instance-name_helper) and declares a const instance of this state + type called state-instance-name for use in a transition table or state + behaviors.</para> + <para>There are several possibilitites for the expression syntax:<itemizedlist> + <listitem> + <para>(): terminate pseudo-state without entry or exit + action.</para> + </listitem> + <listitem> + <para>(Expr1): terminate pseudo-state with entry but no exit + action.</para> + </listitem> + <listitem> + <para>(Expr1,Expr2): terminate pseudo-state with entry and exit + action.</para> + </listitem> + <listitem> + <para>(Expr1,Expr2,Attributes): terminate pseudo-state with + entry and exit action, defining some attributes.</para> + </listitem> + <listitem> + <para>(Expr1,Expr2,Attributes,Configure): terminate pseudo-state + with entry and exit action, defining some attributes and + flags (standard MSM flags) or deferred events (standard MSM + deferred events).</para> + </listitem> + <listitem> + <para>(Expr1,Expr2,Attributes,Configure,Base): terminate + pseudo-state with entry and exit action, defining some + attributes, flags and deferred events (plain msm deferred + events) and a non-default base state (as defined in standard + MSM).</para> + </listitem> + </itemizedlist></para> + </refsect3> + <refsect3> + <title>BOOST_MSM_EUML_EXIT_STATE(build-expression,state-instance-name)</title> + <para>This macro defines an exit pseudo-state type + (state-instance-name_helper) and declares a const instance of this state + type called state-instance-name for use in a transition table or state + behaviors.</para> + <para>There are several possibilitites for the expression syntax:<itemizedlist> + <listitem> + <para>(forwarded_event):exit pseudo-state without entry or exit + action.</para> + </listitem> + <listitem> + <para>(forwarded_event,Expr1): exit pseudo-state with entry but + no exit action.</para> + </listitem> + <listitem> + <para>(forwarded_event,Expr1,Expr2): exit pseudo-state with + entry and exit action.</para> + </listitem> + <listitem> + <para>(forwarded_event,Expr1,Expr2,Attributes): exit + pseudo-state with entry and exit action, defining some + attributes.</para> + </listitem> + <listitem> + <para>(forwarded_event,Expr1,Expr2,Attributes,Configure): exit + pseudo-state with entry and exit action, defining some + attributes and flags (standard MSM flags) or deferred events + (standard MSM deferred events).</para> + </listitem> + <listitem> + <para>(forwarded_event,Expr1,Expr2,Attributes,Configure,Base): + exit pseudo-state with entry and exit action, defining some + attributes, flags and deferred events (plain msm deferred + events) and a non-default base state (as defined in standard + MSM).</para> + </listitem> + </itemizedlist></para> + <para>Note that the forwarded_event must be constructible from the event + sent by the submachine containing the exit point.</para> + </refsect3> + <refsect3> + <title>BOOST_MSM_EUML_ENTRY_STATE(int + region-index,build-expression,state-instance-name)</title> + <para>This macro defines an entry pseudo-state type + (state-instance-name_helper) and declares a const instance of this state + type called state-instance-name for use in a transition table or state + behaviors.</para> + <para>There are several possibilitites for the expression syntax:<itemizedlist> + <listitem> + <para>(): entry pseudo-state without entry or exit + action.</para> + </listitem> + <listitem> + <para>(Expr1): entry pseudo-state with entry but no exit + action.</para> + </listitem> + <listitem> + <para>(Expr1,Expr2): entry pseudo-state with entry and exit + action.</para> + </listitem> + <listitem> + <para>(Expr1,Expr2,Attributes): entry pseudo-state with entry + and exit action, defining some attributes.</para> + </listitem> + <listitem> + <para>(Expr1,Expr2,Attributes,Configure): entry pseudo-state + with entry and exit action, defining some attributes and + flags (standard MSM flags) or deferred events (standard MSM + deferred events).</para> + </listitem> + <listitem> + <para>(Expr1,Expr2,Attributes,Configure,Base): entry + pseudo-state with entry and exit action, defining some + attributes, flags and deferred events (plain msm deferred + events) and a non-default base state (as defined in standard + MSM).</para> + </listitem> + </itemizedlist></para> + </refsect3> + <refsect3> + <title>BOOST_MSM_EUML_EXPLICIT_ENTRY_STATE(int + region-index,build-expression,state-instance-name)</title> + <para>This macro defines a submachine's substate type + (state-instance-name_helper), which can be explicitly entered and also + declares a const instance of this state type called state-instance-name + for use in a transition table or state behaviors.</para> + <para>There are several possibilitites for the expression syntax:<itemizedlist> + <listitem> + <para>(): state without entry or exit action.</para> + </listitem> + <listitem> + <para>(Expr1): state with entry but no exit action.</para> + </listitem> + <listitem> + <para>(Expr1,Expr2): state with entry and exit action.</para> + </listitem> + <listitem> + <para>(Expr1,Expr2,Attributes): state with entry and exit + action, defining some attributes.</para> + </listitem> + <listitem> + <para>(Expr1,Expr2,Attributes,Configure): state with entry and + exit action, defining some attributes and flags (standard + MSM flags) or deferred events (standard MSM deferred + events).</para> + </listitem> + <listitem> + <para>(Expr1,Expr2,Attributes,Configure,Base): state with entry + and exit action, defining some attributes, flags and + deferred events (plain msm deferred events) and a + non-default base state (as defined in standard MSM).</para> + </listitem> + </itemizedlist></para> + </refsect3> + <refsect3> + <title>BOOST_MSM_EUML_STATE_NAME(state-instance-name)</title> + <para>This macro returns the name of the state type generated by + BOOST_MSM_EUML_STATE or other state macros. You need this where the type + is required (usually using a backend function). For example:</para> + <para> + <programlisting>fsm.get_state<BOOST_MSM_EUML_STATE_NAME(StringFind)&>().some_state_function();</programlisting> + </para> + </refsect3> + <refsect3> + <title>BOOST_MSM_EUML_DECLARE_STATE(build-expression,state-instance-name)</title> + <para>Like BOOST_MSM_EUML_STATE but does not provide an instance, simply a + type declaration.</para> + </refsect3> + <refsect3> + <title>BOOST_MSM_EUML_DECLARE_INTERRUPT_STATE(build-expression,state-instance-name)</title> + <para>Like BOOST_MSM_EUML_INTERRUPT_STATE but does not provide an instance, + simply a type declaration.</para> + </refsect3> + <refsect3> + <title>BOOST_MSM_EUML_DECLARE_TERMINATE_STATE(build-expression,state-instance-name)</title> + <para>Like BOOST_MSM_EUML_TERMINATE_STATE but does not provide an instance, + simply a type declaration.</para> + </refsect3> + <refsect3> + <title>BOOST_MSM_EUML_DECLARE_EXIT_STATE(build-expression,state-instance-name)</title> + <para>Like BOOST_MSM_EUML_EXIT_STATE but does not provide an instance, + simply a type declaration.</para> + </refsect3> + <refsect3> + <title>BOOST_MSM_EUML_DECLARE_ENTRY_STATE(int + region-index,build-expression,state-instance-name)</title> + <para>Like BOOST_MSM_EUML_ENTRY_STATE but does not provide an instance, + simply a type declaration.</para> + </refsect3> + <refsect3> + <title>BOOST_MSM_EUML_DECLARE_EXPLICIT_ENTRY_STATE(int + region-index,build-expression,state-instance-name)</title> + <para>Like BOOST_MSM_EUML_EXPLICIT_ENTRY_STATE but does not provide an + instance, simply a type declaration.</para> + </refsect3> + <refsect3> + <title>BOOST_MSM_EUML_TRANSITION_TABLE(expression, + table-instance-name)</title> + <para>This macro declares a transition table type and also declares a const + instance of the table which can then be used in a state machine + declaration (see BOOST_MSM_EUML_DECLARE_STATE_MACHINE).The expression + must follow the <command xlink:href="#reference-stt-grammar">transition + table grammar</command>.</para> + </refsect3> + <refsect3> + <title>BOOST_MSM_EUML_DECLARE_TRANSITION_TABLE(iexpression,table-instance-name)</title> + <para>Like BOOST_MSM_EUML_TRANSITION_TABLE but does not provide an instance, + simply a type declaration.</para> + </refsect3> + <refsect3> + <title>BOOST_MSM_EUML_INTERNAL_TRANSITION_TABLE(expression, + table-instance-name)</title> + <para>This macro declares a transition table type and also declares a const + instance of the table.The expression must follow the <command + xlink:href="#reference-stt-grammar">transition table + grammar</command>. For the moment, this macro is not used.</para> + </refsect3> + <refsect3> + <title>BOOST_MSM_EUML_DECLARE_INTERNAL_TRANSITION_TABLE(iexpression,table-instance-name)</title> + <para>Like BOOST_MSM_EUML_TRANSITION_TABLE but does not provide an instance, + simply a type declaration. This is currently the only way to declare an + internal transition table with eUML. For example:</para> + <programlisting>BOOST_MSM_EUML_DECLARE_STATE((Open_Entry,Open_Exit),Open_def) +struct Open_impl : public Open_def +{ + BOOST_MSM_EUML_DECLARE_INTERNAL_TRANSITION_TABLE(( + open_close [internal_guard1] / internal_action1 , + open_close [internal_guard2] / internal_action2 + )) +}; </programlisting> + </refsect3> + </refsect2> + </refsect1> + </refentry> + </part> +</book> diff --git a/libs/msm/example/mpl_graph/Jamfile.v2 b/libs/msm/example/mpl_graph/Jamfile.v2 new file mode 100644 index 0000000000..cace3af358 --- /dev/null +++ b/libs/msm/example/mpl_graph/Jamfile.v2 @@ -0,0 +1,34 @@ +# msm/example/mpl_graph/Jamfile.v2 tests the mpl_graph examples +# +# Copyright (c) 2010 Gordon Woodhull +# +# Use, modification and distribution is subject to the Boost Software License, +# Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) + +import testing ; + +project msm + : + requirements + <include>. + <toolset>gcc:<cxxflags>"-ftemplate-depth-300 -g0" + <toolset>darwin:<cxxflags>"-ftemplate-depth-300 -g0" + <toolset>intel:<cxxflags>"-g0" + <toolset>gcc:<optimization>off + <toolset>darwin:<optimization>off + <toolset>intel:<optimization>off + <library>/boost/test//boost_unit_test_framework/<link>static + <library>/boost/serialization//boost_serialization/<link>static + ; + + +test-suite msm-unit-tests + : + [ compile adjacency_list_graph.cpp ] + [ compile depth_first_search.cpp ] + [ compile breadth_first_search.cpp ] + [ compile incidence_list_graph.cpp ] + [ compile msm_adaptor.cpp ] + ; + diff --git a/libs/msm/example/mpl_graph/adjacency_list_graph.cpp b/libs/msm/example/mpl_graph/adjacency_list_graph.cpp new file mode 100755 index 0000000000..fcf8f2584e --- /dev/null +++ b/libs/msm/example/mpl_graph/adjacency_list_graph.cpp @@ -0,0 +1,82 @@ +// Copyright 2008-2010 Gordon Woodhull +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// mplgraph.cpp : Defines the entry point for the console application. +// + +#include <boost/msm/mpl_graph/adjacency_list_graph.hpp> +#include <boost/msm/mpl_graph/mpl_utils.hpp> +#include <boost/mpl/equal.hpp> + + +namespace mpl_graph = boost::msm::mpl_graph; +namespace mpl_utils = mpl_graph::mpl_utils; +namespace mpl = boost::mpl; + +/* + test graph and tests are almost identical to incidence_list_graph.cpp + A -> B -> C -\--> D + \ |--> E + \ \--> F + \-----/ + G // except this +*/ + +// vertices +struct A{}; struct B{}; struct C{}; struct D{}; struct E{}; struct F{}; struct G{}; + +// edges +struct A_B{}; struct B_C{}; struct C_D{}; struct C_E{}; struct C_F{}; struct B_F{}; + +typedef mpl::vector< + mpl::pair<A, mpl::vector<mpl::pair<A_B, B> > >, + mpl::pair<B, mpl::vector<mpl::pair<B_C, C>, + mpl::pair<B_F, F> > >, + mpl::pair<C, mpl::vector<mpl::pair<C_D, D>, + mpl::pair<C_E, E>, + mpl::pair<C_F, F> > >, + mpl::pair<G, mpl::vector<> > > + some_adjacency_list; +typedef mpl_graph::adjacency_list_graph<some_adjacency_list> some_graph; + +BOOST_MPL_ASSERT(( boost::is_same<mpl_graph::source<B_C,some_graph>::type, B> )); +BOOST_MPL_ASSERT(( boost::is_same<mpl_graph::source<C_D,some_graph>::type, C> )); + +BOOST_MPL_ASSERT(( boost::is_same<mpl_graph::target<C_D,some_graph>::type, D> )); +BOOST_MPL_ASSERT(( boost::is_same<mpl_graph::target<B_F,some_graph>::type, F> )); + + +// shouldn't assume the order but this seems to work +BOOST_MPL_ASSERT(( mpl_utils::set_equal<mpl_graph::out_edges<C,some_graph>::type, mpl::vector<C_D,C_E,C_F> > )); +BOOST_MPL_ASSERT(( mpl_utils::set_equal<mpl_graph::out_edges<B,some_graph>::type, mpl::vector<B_C,B_F> > )); + +BOOST_MPL_ASSERT_RELATION( (mpl_graph::out_degree<B,some_graph>::value), ==, 2 ); +BOOST_MPL_ASSERT_RELATION( (mpl_graph::out_degree<C,some_graph>::value), ==, 3 ); + + +BOOST_MPL_ASSERT(( mpl_utils::set_equal<mpl_graph::in_edges<C,some_graph>::type, mpl::vector<B_C> > )); +BOOST_MPL_ASSERT(( mpl_utils::set_equal<mpl_graph::in_edges<F,some_graph>::type, mpl::vector<B_F,C_F> > )); + +BOOST_MPL_ASSERT_RELATION( (mpl_graph::in_degree<A,some_graph>::value), ==, 0 ); +BOOST_MPL_ASSERT_RELATION( (mpl_graph::in_degree<F,some_graph>::value), ==, 2 ); + + +BOOST_MPL_ASSERT_RELATION( (mpl_graph::degree<A,some_graph>::value), ==, 1 ); +BOOST_MPL_ASSERT_RELATION( (mpl_graph::degree<C,some_graph>::value), ==, 4 ); + +BOOST_MPL_ASSERT(( mpl_utils::set_equal<mpl_graph::adjacent_vertices<A,some_graph>::type, mpl::vector<B> > )); +BOOST_MPL_ASSERT(( mpl_utils::set_equal<mpl_graph::adjacent_vertices<C,some_graph>::type, mpl::vector<D,E,F> > )); + +BOOST_MPL_ASSERT(( mpl_utils::set_equal<mpl_graph::vertices<some_graph>::type, mpl::vector<A,B,C,D,E,F,G> > )); + +BOOST_MPL_ASSERT_RELATION( mpl_graph::num_vertices<some_graph>::value, ==, 7 ); + +BOOST_MPL_ASSERT_RELATION( mpl_graph::num_edges<some_graph>::value, ==, 6 ); + + +int main(int argc, char* argv[]) +{ + return 0; +} + diff --git a/libs/msm/example/mpl_graph/breadth_first_search.cpp b/libs/msm/example/mpl_graph/breadth_first_search.cpp new file mode 100644 index 0000000000..3b73651ff7 --- /dev/null +++ b/libs/msm/example/mpl_graph/breadth_first_search.cpp @@ -0,0 +1,211 @@ +// Copyright 2008-2010 Gordon Woodhull +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include <boost/msm/mpl_graph/breadth_first_search.hpp> +#include <boost/msm/mpl_graph/adjacency_list_graph.hpp> +#include <boost/msm/mpl_graph/incidence_list_graph.hpp> + +#include <iostream> + +namespace mpl_graph = boost::msm::mpl_graph; +namespace mpl = boost::mpl; + +// vertices +struct A{}; struct B{}; struct C{}; struct D{}; struct E{}; struct F{}; struct G{}; + +// edges +struct A_B{}; struct B_C{}; struct C_D{}; struct C_E{}; struct C_F{}; struct B_F{}; + + + +/* + incidence list test graph: + A -> B -> C -\--> D + \ |--> E + \ \--> F + \-----/ +*/ + +typedef mpl::vector<mpl::vector<A_B,A,B>, + mpl::vector<B_C,B,C>, + mpl::vector<C_D,C,D>, + mpl::vector<C_E,C,E>, + mpl::vector<C_F,C,F>, + mpl::vector<B_F,B,F> > + some_incidence_list; +typedef mpl_graph::incidence_list_graph<some_incidence_list> some_incidence_list_graph; + + + +/* + adjacency list test graph: + A -> B -> C -\--> D + \ |--> E + \ \--> F + \-----/ + G +*/ + +typedef mpl::vector< + mpl::pair<A, mpl::vector<mpl::pair<A_B, B> > >, + mpl::pair<B, mpl::vector<mpl::pair<B_C, C>, + mpl::pair<B_F, F> > >, + mpl::pair<C, mpl::vector<mpl::pair<C_D, D>, + mpl::pair<C_E, E>, + mpl::pair<C_F, F> > >, + mpl::pair<G, mpl::vector<> > > + some_adjacency_list; +typedef mpl_graph::adjacency_list_graph<some_adjacency_list> some_adjacency_list_graph; + + +struct preordering_visitor : mpl_graph::bfs_default_visitor_operations { + template<typename Vertex, typename Graph, typename State> + struct discover_vertex : + mpl::push_back<State, Vertex> + {}; +}; + +struct postordering_visitor : mpl_graph::bfs_default_visitor_operations { + template<typename Vertex, typename Graph, typename State> + struct finish_vertex : + mpl::push_back<State, Vertex> + {}; +}; + +struct examine_edge_visitor : mpl_graph::bfs_default_visitor_operations { + template<typename Edge, typename Graph, typename State> + struct examine_edge : + mpl::push_back<State, Edge> + {}; +}; + +struct tree_edge_visitor : mpl_graph::bfs_default_visitor_operations { + template<typename Edge, typename Graph, typename State> + struct tree_edge : + mpl::push_back<State, Edge> + {}; +}; + +// adjacency list tests + +// preordering, start from A +typedef mpl::first<mpl_graph:: + breadth_first_search<some_adjacency_list_graph, + preordering_visitor, + mpl::vector<>, + A>::type>::type + preorder_adj_a; +BOOST_MPL_ASSERT(( mpl::equal<preorder_adj_a::type, mpl::vector<A,B,C,F,D,E> > )); + +// examine edges, start from A +typedef mpl::first<mpl_graph:: + breadth_first_search<some_adjacency_list_graph, + examine_edge_visitor, + mpl::vector<>, + A>::type>::type + ex_edges_adj_a; +BOOST_MPL_ASSERT(( mpl::equal<ex_edges_adj_a::type, mpl::vector<A_B,B_C,B_F,C_D,C_E,C_F> > )); + +// tree edges, start from A +typedef mpl::first<mpl_graph:: + breadth_first_search<some_adjacency_list_graph, + tree_edge_visitor, + mpl::vector<>, + A>::type>::type + tree_edges_adj_a; +BOOST_MPL_ASSERT(( mpl::equal<tree_edges_adj_a::type, mpl::vector<A_B,B_C,B_F,C_D,C_E> > )); + +// preordering, search all, default start node (first) +typedef mpl::first<mpl_graph:: + breadth_first_search_all<some_adjacency_list_graph, + preordering_visitor, + mpl::vector<> >::type>::type + preorder_adj; +BOOST_MPL_ASSERT(( mpl::equal<preorder_adj::type, mpl::vector<A,B,C,F,D,E,G> > )); + +// postordering, starting at A (same as preordering because BFS fully processes one vertex b4 moving to next) +typedef mpl::first<mpl_graph:: + breadth_first_search<some_adjacency_list_graph, + postordering_visitor, + mpl::vector<>, + A>::type>::type + postorder_adj_a; +BOOST_MPL_ASSERT(( mpl::equal<postorder_adj_a::type, mpl::vector<A,B,C,F,D,E> > )); + +// postordering, default start node (same as preordering because BFS fully processes one vertex b4 moving to next) +typedef mpl::first<mpl_graph:: + breadth_first_search_all<some_adjacency_list_graph, + postordering_visitor, + mpl::vector<> >::type>::type + postorder_adj; +BOOST_MPL_ASSERT(( mpl::equal<postorder_adj::type, mpl::vector<A,B,C,F,D,E,G> > )); + +// preordering starting at C +typedef mpl::first<mpl_graph:: + breadth_first_search<some_adjacency_list_graph, + preordering_visitor, + mpl::vector<>, + C>::type>::type + preorder_adj_from_c; +BOOST_MPL_ASSERT(( mpl::equal<preorder_adj_from_c::type, mpl::vector<C,D,E,F> > )); + +// preordering, search all, starting at C +typedef mpl::first<mpl_graph:: + breadth_first_search_all<some_adjacency_list_graph, + preordering_visitor, + mpl::vector<>, + C>::type>::type + preorder_adj_from_c_all; +BOOST_MPL_ASSERT(( mpl::equal<preorder_adj_from_c_all::type, mpl::vector<C,D,E,F,A,B,G> > )); + + +// incidence list tests + +// preordering, start from A +typedef mpl::first<mpl_graph:: + breadth_first_search<some_incidence_list_graph, + preordering_visitor, + mpl::vector<>, + A>::type>::type + preorder_inc_a; +BOOST_MPL_ASSERT(( mpl::equal<preorder_inc_a::type, mpl::vector<A,B,C,F,D,E> > )); + +// preordering, start from C +typedef mpl::first<mpl_graph:: + breadth_first_search<some_incidence_list_graph, + preordering_visitor, + mpl::vector<>, + C>::type>::type + preorder_inc_c; +BOOST_MPL_ASSERT(( mpl::equal<preorder_inc_c::type, mpl::vector<C,D,E,F> > )); + +// preordering, default start node (first) +typedef mpl::first<mpl_graph:: + breadth_first_search_all<some_incidence_list_graph, + preordering_visitor, + mpl::vector<> >::type>::type + preorder_inc; +BOOST_MPL_ASSERT(( mpl::equal<preorder_inc::type, mpl::vector<A,B,C,F,D,E> > )); + +// postordering, default start node +typedef mpl::first<mpl_graph:: + breadth_first_search_all<some_incidence_list_graph, + postordering_visitor, + mpl::vector<> >::type>::type + postorder_inc; +BOOST_MPL_ASSERT(( mpl::equal<postorder_inc::type, mpl::vector<A,B,C,F,D,E> > )); + +// preordering, search all, starting at C +typedef mpl::first<mpl_graph:: + breadth_first_search_all<some_incidence_list_graph, + preordering_visitor, + mpl::vector<>, + C>::type>::type + preorder_inc_from_c; +BOOST_MPL_ASSERT(( mpl::equal<preorder_inc_from_c::type, mpl::vector<C,D,E,F,A,B> > )); + + +int main() { + return 0; +}
\ No newline at end of file diff --git a/libs/msm/example/mpl_graph/depth_first_search.cpp b/libs/msm/example/mpl_graph/depth_first_search.cpp new file mode 100644 index 0000000000..2d6f36e626 --- /dev/null +++ b/libs/msm/example/mpl_graph/depth_first_search.cpp @@ -0,0 +1,153 @@ +// Copyright 2008-2010 Gordon Woodhull +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include <boost/msm/mpl_graph/depth_first_search.hpp> +#include <boost/msm/mpl_graph/adjacency_list_graph.hpp> +#include <boost/msm/mpl_graph/incidence_list_graph.hpp> + +#include <iostream> + +namespace mpl_graph = boost::msm::mpl_graph; +namespace mpl = boost::mpl; + +// vertices +struct A{}; struct B{}; struct C{}; struct D{}; struct E{}; struct F{}; struct G{}; + +// edges +struct A_B{}; struct B_C{}; struct C_D{}; struct C_E{}; struct C_F{}; struct B_F{}; + + + +/* + incidence list test graph: + A -> B -> C -\--> D + \ |--> E + \ \--> F + \-----/ + G +*/ + +typedef mpl::vector<mpl::vector<A_B,A,B>, + mpl::vector<B_C,B,C>, + mpl::vector<C_D,C,D>, + mpl::vector<C_E,C,E>, + mpl::vector<C_F,C,F>, + mpl::vector<B_F,B,F> > + some_incidence_list; +typedef mpl_graph::incidence_list_graph<some_incidence_list> some_incidence_list_graph; + + + +/* + adjacency list test graph: + A -> B -> C -\--> D + \ |--> E + \ \--> F + \-----/ + G +*/ + +typedef mpl::vector< + mpl::pair<A, mpl::vector<mpl::pair<A_B, B> > >, + mpl::pair<B, mpl::vector<mpl::pair<B_C, C>, + mpl::pair<B_F, F> > >, + mpl::pair<C, mpl::vector<mpl::pair<C_D, D>, + mpl::pair<C_E, E>, + mpl::pair<C_F, F> > >, + mpl::pair<G, mpl::vector<> > > + some_adjacency_list; +typedef mpl_graph::adjacency_list_graph<some_adjacency_list> some_adjacency_list_graph; + + +struct preordering_visitor : mpl_graph::dfs_default_visitor_operations { + template<typename Node, typename Graph, typename State> + struct discover_vertex : + mpl::push_back<State, Node> + {}; +}; + +struct postordering_visitor : mpl_graph::dfs_default_visitor_operations { + template<typename Node, typename Graph, typename State> + struct finish_vertex : + mpl::push_back<State, Node> + {}; +}; + +// adjacency list tests + +// preordering, default start node (first) +typedef mpl::first<mpl_graph:: + depth_first_search_all<some_adjacency_list_graph, + preordering_visitor, + mpl::vector<> >::type>::type + preorder_adj; +BOOST_MPL_ASSERT(( mpl::equal<preorder_adj::type, mpl::vector<A,B,C,D,E,F,G> > )); + +// postordering, default start node +typedef mpl::first<mpl_graph:: + depth_first_search_all<some_adjacency_list_graph, + postordering_visitor, + mpl::vector<> >::type>::type + postorder_adj; +BOOST_MPL_ASSERT(( mpl::equal<postorder_adj::type, mpl::vector<D,E,F,C,B,A,G> > )); + +// preordering all starting at C +typedef mpl::first<mpl_graph:: + depth_first_search_all<some_adjacency_list_graph, + preordering_visitor, + mpl::vector<>, + C>::type>::type + preorder_adj_all_from_c; +BOOST_MPL_ASSERT(( mpl::equal<preorder_adj_all_from_c::type, mpl::vector<C,D,E,F,A,B,G> > )); + +// preordering just those starting at C +typedef mpl::first<mpl_graph:: + depth_first_search<some_adjacency_list_graph, + preordering_visitor, + mpl::vector<>, + C>::type>::type + preorder_adj_from_c; +BOOST_MPL_ASSERT(( mpl::equal<preorder_adj_from_c::type, mpl::vector<C,D,E,F> > )); + + +// incidence list tests + +// preordering, default start node (first) +typedef mpl::first<mpl_graph:: + depth_first_search_all<some_incidence_list_graph, + preordering_visitor, + mpl::vector<> >::type>::type + preorder_inc; +BOOST_MPL_ASSERT(( mpl::equal<preorder_inc::type, mpl::vector<A,B,C,D,E,F> > )); + +// postordering, default start node +typedef mpl::first<mpl_graph:: + depth_first_search_all<some_incidence_list_graph, + postordering_visitor, + mpl::vector<> >::type>::type + postorder_inc; +BOOST_MPL_ASSERT(( mpl::equal<postorder_inc::type, mpl::vector<D,E,F,C,B,A> > )); + +// preordering starting at C +typedef mpl::first<mpl_graph:: + depth_first_search_all<some_incidence_list_graph, + preordering_visitor, + mpl::vector<>, + C>::type>::type + preorder_inc_all_from_c; +BOOST_MPL_ASSERT(( mpl::equal<preorder_inc_all_from_c::type, mpl::vector<C,D,E,F,A,B> > )); + +// preordering starting at B +typedef mpl::first<mpl_graph:: + depth_first_search<some_incidence_list_graph, + preordering_visitor, + mpl::vector<>, + B>::type>::type + preorder_inc_from_b; +BOOST_MPL_ASSERT(( mpl::equal<preorder_inc_from_b::type, mpl::vector<B,C,D,E,F> > )); + + +int main() { + return 0; +}
\ No newline at end of file diff --git a/libs/msm/example/mpl_graph/incidence_list_graph.cpp b/libs/msm/example/mpl_graph/incidence_list_graph.cpp new file mode 100755 index 0000000000..b8ec78f360 --- /dev/null +++ b/libs/msm/example/mpl_graph/incidence_list_graph.cpp @@ -0,0 +1,72 @@ +// Copyright 2008-2010 Gordon Woodhull +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// mplgraph.cpp : Defines the entry point for the console application. +// + +#include <boost/msm/mpl_graph/incidence_list_graph.hpp> +#include <boost/msm/mpl_graph/mpl_utils.hpp> + +namespace mpl_graph = boost::msm::mpl_graph; +namespace mpl_utils = mpl_graph::mpl_utils; +namespace mpl = boost::mpl; +/* + test graph: + A -> B -> C -\--> D + \ |--> E + \ \--> F + \-----/ +*/ + +// vertices +struct A{}; struct B{}; struct C{}; struct D{}; struct E{}; struct F{}; + +// edges +struct A_B{}; struct B_C{}; struct C_D{}; struct C_E{}; struct C_F{}; struct B_F{}; + +typedef mpl::vector<mpl::vector<A_B,A,B>, + mpl::vector<B_C,B,C>, + mpl::vector<C_D,C,D>, + mpl::vector<C_E,C,E>, + mpl::vector<C_F,C,F>, + mpl::vector<B_F,B,F> > + some_incidence_list; +typedef mpl_graph::incidence_list_graph<some_incidence_list> some_graph; + +BOOST_MPL_ASSERT(( boost::is_same<mpl_graph::source<B_C,some_graph>::type, B> )); +BOOST_MPL_ASSERT(( boost::is_same<mpl_graph::source<C_D,some_graph>::type, C> )); + +BOOST_MPL_ASSERT(( boost::is_same<mpl_graph::target<C_D,some_graph>::type, D> )); +BOOST_MPL_ASSERT(( boost::is_same<mpl_graph::target<B_F,some_graph>::type, F> )); + +BOOST_MPL_ASSERT(( mpl_utils::set_equal<mpl_graph::out_edges<C,some_graph>::type, mpl::vector<C_D,C_E,C_F> > )); +BOOST_MPL_ASSERT(( mpl_utils::set_equal<mpl_graph::out_edges<B,some_graph>::type, mpl::vector<B_F,B_C> > )); + +BOOST_MPL_ASSERT_RELATION( (mpl_graph::out_degree<B,some_graph>::value), ==, 2 ); +BOOST_MPL_ASSERT_RELATION( (mpl_graph::out_degree<C,some_graph>::value), ==, 3 ); + +BOOST_MPL_ASSERT(( mpl_utils::set_equal<mpl_graph::in_edges<C,some_graph>::type, mpl::vector<B_C> > )); +BOOST_MPL_ASSERT(( mpl_utils::set_equal<mpl_graph::in_edges<F,some_graph>::type, mpl::vector<C_F,B_F> > )); + +BOOST_MPL_ASSERT_RELATION( (mpl_graph::in_degree<A,some_graph>::value), ==, 0 ); +BOOST_MPL_ASSERT_RELATION( (mpl_graph::in_degree<F,some_graph>::value), ==, 2 ); + +BOOST_MPL_ASSERT_RELATION( (mpl_graph::degree<A,some_graph>::value), ==, 1 ); +BOOST_MPL_ASSERT_RELATION( (mpl_graph::degree<C,some_graph>::value), ==, 4 ); + +BOOST_MPL_ASSERT(( mpl_utils::set_equal<mpl_graph::adjacent_vertices<A,some_graph>::type, mpl::vector<B> > )); +BOOST_MPL_ASSERT(( mpl_utils::set_equal<mpl_graph::adjacent_vertices<C,some_graph>::type, mpl::vector<D,E,F> > )); + +BOOST_MPL_ASSERT(( mpl_utils::set_equal<mpl_graph::vertices<some_graph>::type, mpl::vector<A,B,C,D,E,F> > )); + +BOOST_MPL_ASSERT_RELATION( mpl_graph::num_vertices<some_graph>::value, ==, 6 ); + +BOOST_MPL_ASSERT_RELATION( mpl_graph::num_edges<some_graph>::value, ==, 6 ); + + +int main(int argc, char* argv[]) +{ + return 0; +} + diff --git a/libs/msm/example/mpl_graph/msm_adaptor.cpp b/libs/msm/example/mpl_graph/msm_adaptor.cpp new file mode 100644 index 0000000000..a7edf7651b --- /dev/null +++ b/libs/msm/example/mpl_graph/msm_adaptor.cpp @@ -0,0 +1,264 @@ +// Copyright 2010 Gordon Woodhull +// modified from MSMv2.10/libs/msm/doc/HTML/examples/SimpleTutorial.cpp +// for the purpose of showing how to run mpl_graph algorithms on MSMs +// and distributed same license as source +// Copyright 2008 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> + +// mpl_graph graph implementation and depth first search +#include <boost/msm/mpl_graph/incidence_list_graph.hpp> +#include <boost/msm/mpl_graph/depth_first_search.hpp> +#include <boost/msm/mpl_graph/breadth_first_search.hpp> + +namespace msm = boost::msm; +namespace mpl = boost::mpl; + +namespace mpl_graph = boost::msm::mpl_graph; + +namespace +{ + // events + struct play {}; + struct end_pause {}; + struct stop {}; + struct pause {}; + struct open_close {}; + + // A "complicated" event type that carries some data. + enum DiskTypeEnum + { + DISK_CD=0, + DISK_DVD=1 + }; + struct cd_detected + { + cd_detected(std::string name, DiskTypeEnum diskType) + : name(name), + disc_type(diskType) + {} + + std::string name; + DiskTypeEnum disc_type; + }; + + // front-end: define the FSM structure + struct player_ : public msm::front::state_machine_def<player_> + { + // The list of FSM states + struct Empty : public msm::front::state<> + { + // every (optional) entry/exit methods get the event passed. + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Empty" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Empty" << std::endl;} + }; + struct Open : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) {std::cout << "entering: Open" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Open" << std::endl;} + }; + + // sm_ptr still supported but deprecated as functors are a much better way to do the same thing + struct Stopped : public msm::front::state<msm::front::default_base_state,msm::front::sm_ptr> + { + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) {std::cout << "entering: Stopped" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Stopped" << std::endl;} + void set_sm_ptr(player_* pl) + { + m_player=pl; + } + player_* m_player; + }; + + struct Playing : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Playing" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Playing" << std::endl;} + }; + + // state not defining any entry or exit + struct Paused : public msm::front::state<> + { + }; + + // the initial state of the player SM. Must be defined + typedef Empty initial_state; + + // transition actions + void start_playback(play const&) { std::cout << "player::start_playback\n"; } + void open_drawer(open_close const&) { std::cout << "player::open_drawer\n"; } + void close_drawer(open_close const&) { std::cout << "player::close_drawer\n"; } + void store_cd_info(cd_detected const&) { std::cout << "player::store_cd_info\n"; } + void stop_playback(stop const&) { std::cout << "player::stop_playback\n"; } + void pause_playback(pause const&) { std::cout << "player::pause_playback\n"; } + void resume_playback(end_pause const&) { std::cout << "player::resume_playback\n"; } + void stop_and_open(open_close const&) { std::cout << "player::stop_and_open\n"; } + void stopped_again(stop const&) {std::cout << "player::stopped_again\n";} + // guard conditions + bool good_disk_format(cd_detected const& evt) + { + // to test a guard condition, let's say we understand only CDs, not DVD + if (evt.disc_type != DISK_CD) + { + std::cout << "wrong disk, sorry" << std::endl; + return false; + } + return true; + } + // used to show a transition conflict. This guard will simply deactivate one transition and thus + // solve the conflict + bool auto_start(cd_detected const&) + { + return false; + } + + typedef player_ p; // makes transition table cleaner + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Stopped , play , Playing , &p::start_playback >, + a_row < Stopped , open_close , Open , &p::open_drawer >, + _row < Stopped , stop , Stopped >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Open , open_close , Empty , &p::close_drawer >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Empty , open_close , Open , &p::open_drawer >, + row < Empty , cd_detected , Stopped , &p::store_cd_info ,&p::good_disk_format >, + row < Empty , cd_detected , Playing , &p::store_cd_info ,&p::auto_start >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Playing , stop , Stopped , &p::stop_playback >, + a_row < Playing , pause , Paused , &p::pause_playback >, + a_row < Playing , open_close , Open , &p::stop_and_open >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Paused , end_pause , Playing , &p::resume_playback >, + a_row < Paused , stop , Stopped , &p::stop_playback >, + a_row < Paused , open_close , Open , &p::stop_and_open > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + + + // transition table is already an incidence list; + // select Edge, Source, Target = pair<Start,Event>, Start, Next + // making Start part of Edge is necessary because Edge tags have to be unique + + template<typename Row> + struct row_to_incidence : + mpl::vector<mpl::pair<typename Row::Target, typename Row::Evt>, typename Row::Source, typename Row::Target> {}; + + typedef mpl::fold<player_::transition_table, + mpl::vector<>, + mpl::push_back<mpl::_1, row_to_incidence<mpl::_2> > >::type + transition_incidence_list; + + typedef mpl_graph::incidence_list_graph<transition_incidence_list> + transition_graph; + + struct preordering_dfs_visitor : mpl_graph::dfs_default_visitor_operations { + template<typename Node, typename Graph, typename State> + struct discover_vertex : + mpl::push_back<State, Node> + {}; + }; + + typedef mpl::first<mpl_graph:: + depth_first_search<transition_graph, + preordering_dfs_visitor, + mpl::vector<>, + player_::initial_state>::type>::type + dfs_from_initial_state; + + BOOST_MPL_ASSERT(( mpl::equal<dfs_from_initial_state, + mpl::vector<Empty,Open,Stopped,Playing,Paused> > )); + + struct preordering_bfs_visitor : mpl_graph::bfs_default_visitor_operations { + template<typename Node, typename Graph, typename State> + struct discover_vertex : + mpl::push_back<State, Node> + {}; + }; + typedef mpl::first<mpl_graph:: + breadth_first_search<transition_graph, + preordering_bfs_visitor, + mpl::vector<>, + player_::initial_state>::type>::type + bfs_from_initial_state; + + // yawn, BFS happens to produce the same result as DFS for this example + BOOST_MPL_ASSERT(( mpl::equal<bfs_from_initial_state, + mpl::vector<Empty,Open,Stopped,Playing,Paused> > )); + + }; + // Pick a back-end + typedef msm::back::state_machine<player_> player; + + // + // Testing utilities. + // + static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" }; + void pstate(player const& p) + { + std::cout << " -> " << state_names[p.current_state()[0]] << std::endl; + } + + + void test() + { + player p; + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + p.start(); + // go to Open, call on_exit on Empty, then action, then on_entry on Open + p.process_event(open_close()); pstate(p); + p.process_event(open_close()); pstate(p); + // will be rejected, wrong disk type + p.process_event( + cd_detected("louie, louie",DISK_DVD)); pstate(p); + p.process_event( + cd_detected("louie, louie",DISK_CD)); pstate(p); + p.process_event(play()); + + // at this point, Play is active + p.process_event(pause()); pstate(p); + // go back to Playing + p.process_event(end_pause()); pstate(p); + p.process_event(pause()); pstate(p); + p.process_event(stop()); pstate(p); + // event leading to the same state + // no action method called as it is not present in the transition table + p.process_event(stop()); pstate(p); + } +} + +int main() +{ + test(); + return 0; +} diff --git a/libs/msm/index.html b/libs/msm/index.html new file mode 100644 index 0000000000..2cf67d38df --- /dev/null +++ b/libs/msm/index.html @@ -0,0 +1,22 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> + +<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"> +<head> + <title>Boost.MSM Documentation</title> + <meta http-equiv="Content-Type" content="text/html; charset=us-ascii" /> + <meta http-equiv="refresh" content="0; URL=doc/HTML/index.html" /> +</head> + +<body> + Automatic redirection failed, please go to <a href= + "doc/HTML/index.html">doc/HTML/index.html</a> +<p>© Copyright Christophe Henry, 2010</p> + <p>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">www.boost.org/LICENSE_1_0.txt</a>)</p> + +</body> +</html> + diff --git a/libs/msm/test/Anonymous.cpp b/libs/msm/test/Anonymous.cpp new file mode 100644 index 0000000000..945d3a061e --- /dev/null +++ b/libs/msm/test/Anonymous.cpp @@ -0,0 +1,182 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> + +#define BOOST_TEST_MODULE MyTest +#include <boost/test/unit_test.hpp> + +namespace msm = boost::msm; +namespace mpl = boost::mpl; +using namespace boost::msm::front; + +namespace +{ + // events + struct event1 {}; + + + // front-end: define the FSM structure + struct my_machine_ : public msm::front::state_machine_def<my_machine_> + { + unsigned int state2_to_state3_counter; + unsigned int state3_to_state4_counter; + unsigned int always_true_counter; + unsigned int always_false_counter; + + my_machine_(): + state2_to_state3_counter(0), + state3_to_state4_counter(0), + always_true_counter(0), + always_false_counter(0) + {} + + // The list of FSM states + struct State1 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct State2 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + struct State3 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + struct State4 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + // the initial state of the player SM. Must be defined + typedef State1 initial_state; + + // transition actions + void State2ToState3(none const&) { ++state2_to_state3_counter; } + void State3ToState4(none const&) { ++state3_to_state4_counter; } + // guard conditions + bool always_true(none const& evt) + { + ++always_true_counter; + return true; + } + bool always_false(none const& evt) + { + ++always_false_counter; + return false; + } + + typedef my_machine_ p; // makes transition table cleaner + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + _row < State1 , none , State2 >, + a_row < State2 , none , State3 , &p::State2ToState3 >, + // +---------+-------------+---------+---------------------+----------------------+ + row < State3 , none , State4 , &p::State3ToState4 , &p::always_true >, + g_row < State3 , none , State4 , &p::always_false >, + _row < State4 , event1 , State1 > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const&, FSM&,int) + { + BOOST_FAIL("no_transition called!"); + } + // init counters + template <class Event,class FSM> + void on_entry(Event const&,FSM& fsm) + { + fsm.template get_state<my_machine_::State1&>().entry_counter=0; + fsm.template get_state<my_machine_::State1&>().exit_counter=0; + fsm.template get_state<my_machine_::State2&>().entry_counter=0; + fsm.template get_state<my_machine_::State2&>().exit_counter=0; + fsm.template get_state<my_machine_::State3&>().entry_counter=0; + fsm.template get_state<my_machine_::State3&>().exit_counter=0; + fsm.template get_state<my_machine_::State4&>().entry_counter=0; + fsm.template get_state<my_machine_::State4&>().exit_counter=0; + } + }; + // Pick a back-end + typedef msm::back::state_machine<my_machine_> my_machine; + + //static char const* const state_names[] = { "State1", "State2", "State3", "State4" }; + + + BOOST_AUTO_TEST_CASE( my_test ) + { + my_machine p; + + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + // in this case it will also immediately trigger all anonymous transitions + p.start(); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"State4 should be active"); //State4 + BOOST_CHECK_MESSAGE(p.get_state<my_machine_::State1&>().exit_counter == 1,"State1 exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<my_machine_::State1&>().entry_counter == 1,"State1 entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<my_machine_::State2&>().exit_counter == 1,"State2 exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<my_machine_::State2&>().entry_counter == 1,"State2 entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<my_machine_::State3&>().exit_counter == 1,"State3 exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<my_machine_::State3&>().entry_counter == 1,"State3 entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<my_machine_::State4&>().entry_counter == 1,"State4 entry not called correctly"); + BOOST_CHECK_MESSAGE(p.always_true_counter == 1,"guard not called correctly"); + BOOST_CHECK_MESSAGE(p.always_false_counter == 1,"guard not called correctly"); + BOOST_CHECK_MESSAGE(p.state2_to_state3_counter == 1,"action not called correctly"); + BOOST_CHECK_MESSAGE(p.state3_to_state4_counter == 1,"action not called correctly"); + + + // this event will bring us back to the initial state and thus, a new "loop" will be started + p.process_event(event1()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"State4 should be active"); //State4 + BOOST_CHECK_MESSAGE(p.get_state<my_machine_::State1&>().exit_counter == 2,"State1 exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<my_machine_::State1&>().entry_counter == 2,"State1 entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<my_machine_::State2&>().exit_counter == 2,"State2 exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<my_machine_::State2&>().entry_counter == 2,"State2 entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<my_machine_::State3&>().exit_counter == 2,"State3 exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<my_machine_::State3&>().entry_counter == 2,"State3 entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<my_machine_::State4&>().entry_counter == 2,"State4 entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<my_machine_::State4&>().exit_counter == 1,"State4 exit not called correctly"); + BOOST_CHECK_MESSAGE(p.always_true_counter == 2,"guard not called correctly"); + BOOST_CHECK_MESSAGE(p.always_false_counter == 2,"guard not called correctly"); + BOOST_CHECK_MESSAGE(p.state2_to_state3_counter == 2,"action not called correctly"); + BOOST_CHECK_MESSAGE(p.state3_to_state4_counter == 2,"action not called correctly"); + + } +} + diff --git a/libs/msm/test/AnonymousEuml.cpp b/libs/msm/test/AnonymousEuml.cpp new file mode 100644 index 0000000000..97241ff15e --- /dev/null +++ b/libs/msm/test/AnonymousEuml.cpp @@ -0,0 +1,165 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +#include <boost/msm/front/euml/euml.hpp> + +#define BOOST_TEST_MODULE MyTest +#include <boost/test/unit_test.hpp> + +namespace msm = boost::msm; +using namespace boost::msm::front::euml; + +namespace +{ + // events + BOOST_MSM_EUML_EVENT(event1) + + // The list of FSM states + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,entry_counter) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,exit_counter) + + BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),State1) + BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),State2) + BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),State3) + BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),State4) + + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,state2_to_state3_counter) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,state3_to_state4_counter) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,always_true_counter) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,always_false_counter) + + // transition actions + BOOST_MSM_EUML_ACTION(State2ToState3) + { + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& fsm,SourceState& ,TargetState& ) + { + ++fsm.get_attribute(state2_to_state3_counter); + } + }; + BOOST_MSM_EUML_ACTION(State3ToState4) + { + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& fsm,SourceState& ,TargetState& ) + { + ++fsm.get_attribute(state3_to_state4_counter); + } + }; + // guard conditions + BOOST_MSM_EUML_ACTION(always_true) + { + template <class FSM,class EVT,class SourceState,class TargetState> + bool operator()(EVT const& evt,FSM& fsm,SourceState& ,TargetState& ) + { + ++fsm.get_attribute(always_true_counter); + return true; + } + }; + BOOST_MSM_EUML_ACTION(always_false) + { + template <class FSM,class EVT,class SourceState,class TargetState> + bool operator()(EVT const& evt,FSM& fsm,SourceState& ,TargetState& ) + { + ++fsm.get_attribute(always_false_counter); + return false; + } + }; + + BOOST_MSM_EUML_ACTION(No_Transition) + { + template <class FSM,class Event> + void operator()(Event const&,FSM&,int) + { + BOOST_FAIL("no_transition called!"); + } + }; + + BOOST_MSM_EUML_TRANSITION_TABLE(( + State2 == State1 , + State3 == State2 / State2ToState3, + State4 == State3 [always_true] / State3ToState4, + State4 == State3 [always_false], + State1 == State4 + event1 + // +------------------------------------------------------------------------------+ + ),transition_table) + + // create a state machine "on the fly" + BOOST_MSM_EUML_DECLARE_STATE_MACHINE(( transition_table, //STT + init_ << State1, // Init State + no_action, // Entry + no_action, // Exit + attributes_ << state2_to_state3_counter << state3_to_state4_counter + << always_true_counter << always_false_counter, // Attributes + configure_ << no_configure_, // configuration + No_Transition // no_transition handler + ), + my_machine_) //fsm name + + // Pick a back-end + typedef msm::back::state_machine<my_machine_> my_machine; + + //static char const* const state_names[] = { "State1", "State2", "State3", "State4" }; + + BOOST_AUTO_TEST_CASE( my_test ) + { + my_machine p; + + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + // in this case it will also immediately trigger all anonymous transitions + p.start(); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"State4 should be active"); //State4 + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(State1)&>().get_attribute(exit_counter) == 1, + "State1 exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(State1)&>().get_attribute(entry_counter) == 1, + "State1 entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(State2)&>().get_attribute(exit_counter) == 1, + "State2 exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(State2)&>().get_attribute(entry_counter) == 1, + "State2 entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(State3)&>().get_attribute(exit_counter) == 1, + "State3 exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(State3)&>().get_attribute(entry_counter) == 1, + "State3 entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(State4)&>().get_attribute(entry_counter)== 1, + "State4 entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_attribute(always_true_counter) == 1,"guard not called correctly"); + BOOST_CHECK_MESSAGE(p.get_attribute(always_false_counter) == 1,"guard not called correctly"); + BOOST_CHECK_MESSAGE(p.get_attribute(state2_to_state3_counter) == 1,"action not called correctly"); + BOOST_CHECK_MESSAGE(p.get_attribute(state3_to_state4_counter) == 1,"action not called correctly"); + + + // this event will bring us back to the initial state and thus, a new "loop" will be started + p.process_event(event1); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"State4 should be active"); //State4 + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(State1)&>().get_attribute(exit_counter) == 2, + "State1 exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(State1)&>().get_attribute(entry_counter) == 2, + "State1 entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(State2)&>().get_attribute(exit_counter) == 2, + "State2 exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(State2)&>().get_attribute(entry_counter) == 2, + "State2 entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(State3)&>().get_attribute(exit_counter) == 2, + "State3 exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(State3)&>().get_attribute(entry_counter) == 2, + "State3 entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(State4)&>().get_attribute(entry_counter)== 2, + "State4 entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_attribute(always_true_counter) == 2,"guard not called correctly"); + BOOST_CHECK_MESSAGE(p.get_attribute(always_false_counter) == 2,"guard not called correctly"); + BOOST_CHECK_MESSAGE(p.get_attribute(state2_to_state3_counter) == 2,"action not called correctly"); + BOOST_CHECK_MESSAGE(p.get_attribute(state3_to_state4_counter) == 2,"action not called correctly"); + + } +} + diff --git a/libs/msm/test/CompositeEuml.cpp b/libs/msm/test/CompositeEuml.cpp new file mode 100644 index 0000000000..ce5a2885fe --- /dev/null +++ b/libs/msm/test/CompositeEuml.cpp @@ -0,0 +1,278 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <iostream> +#include <boost/msm/back/state_machine.hpp> +#include <boost/msm/front/euml/euml.hpp> + +#define BOOST_TEST_MODULE MyTest +#include <boost/test/unit_test.hpp> + +using namespace std; +using namespace boost::msm::front::euml; +namespace msm = boost::msm; + +namespace +{ + // A "complicated" event type that carries some data. + enum DiskTypeEnum + { + DISK_CD=0, + DISK_DVD=1 + }; + // events + BOOST_MSM_EUML_EVENT(play) + BOOST_MSM_EUML_EVENT(end_pause) + BOOST_MSM_EUML_EVENT(stop) + BOOST_MSM_EUML_EVENT(pause) + BOOST_MSM_EUML_EVENT(open_close) + BOOST_MSM_EUML_EVENT(next_song) + BOOST_MSM_EUML_EVENT(previous_song) + BOOST_MSM_EUML_EVENT(region2_evt) + // A "complicated" event type that carries some data. + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(std::string,cd_name) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(DiskTypeEnum,cd_type) + BOOST_MSM_EUML_ATTRIBUTES((attributes_ << cd_name << cd_type ), cd_detected_attributes) + BOOST_MSM_EUML_EVENT_WITH_ATTRIBUTES(cd_detected,cd_detected_attributes) + + //states + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,entry_counter) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,exit_counter) + + BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Empty) + BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Open) + BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Stopped) + BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Paused) + + // Playing is now a state machine itself. + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,start_next_song_counter) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,start_prev_song_guard_counter) + // It has 5 substates + BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Song1) + BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Song2) + BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Song3) + BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Region2State1) + BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Region2State2) + + // Playing has a transition table + BOOST_MSM_EUML_TRANSITION_TABLE(( + // +------------------------------------------------------------------------------+ + Song2 == Song1 + next_song , + Song1 == Song2 + previous_song [True_()] / ++fsm_(start_prev_song_guard_counter) , + Song3 == Song2 + next_song / ++fsm_(start_next_song_counter) , + Song2 == Song3 + previous_song [True_()] / ++fsm_(start_prev_song_guard_counter) , + Region2State2 == Region2State1 + region2_evt + // +------------------------------------------------------------------------------+ + ),playing_transition_table ) + + BOOST_MSM_EUML_DECLARE_STATE_MACHINE( (playing_transition_table, //STT + init_ << Song1 << Region2State1, // Init State + ++state_(entry_counter), // Entry + ++state_(exit_counter), // Exit + attributes_ << entry_counter << exit_counter + << start_next_song_counter + << start_prev_song_guard_counter // Attributes + ),Playing_) + // choice of back-end + typedef msm::back::state_machine<Playing_> Playing_type; + Playing_type const Playing; + + + //fsm + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,start_playback_counter) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,can_close_drawer_counter) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,test_fct_counter) + BOOST_MSM_EUML_ACTION(No_Transition) + { + template <class FSM,class Event> + void operator()(Event const&,FSM&,int) + { + BOOST_FAIL("no_transition called!"); + } + }; + BOOST_MSM_EUML_ACTION(good_disk_format) + { + template <class FSM,class EVT,class SourceState,class TargetState> + bool operator()(EVT const& evt,FSM&,SourceState& ,TargetState& ) + { + if (evt.get_attribute(cd_type)!=DISK_CD) + { + return false; + } + return true; + } + }; + BOOST_MSM_EUML_TRANSITION_TABLE(( + Playing == Stopped + play / (++fsm_(start_playback_counter),++fsm_(test_fct_counter) ), + Playing == Paused + end_pause , + // +------------------------------------------------------------------------------+ + Empty == Open + open_close / ++fsm_(can_close_drawer_counter), + // +------------------------------------------------------------------------------+ + Open == Empty + open_close , + Open == Paused + open_close , + Open == Stopped + open_close , + Open == Playing + open_close , + // +------------------------------------------------------------------------------+ + Paused == Playing + pause , + // +------------------------------------------------------------------------------+ + Stopped == Playing + stop , + Stopped == Paused + stop , + Stopped == Empty + cd_detected [good_disk_format || + (event_(cd_type)==Int_<DISK_CD>())] / process_(play) , + Stopped == Stopped + stop + // +------------------------------------------------------------------------------+ + ),transition_table) + + BOOST_MSM_EUML_DECLARE_STATE_MACHINE(( transition_table, //STT + init_ << Empty, // Init State + no_action, // Entry + no_action, // Exit + attributes_ << start_playback_counter + << can_close_drawer_counter << test_fct_counter, // Attributes + configure_ << no_configure_, // configuration + No_Transition // no_transition handler + ), + player_) //fsm name + + typedef msm::back::state_machine<player_> player; + +// static char const* const state_names[] = { "Stopped", "Paused", "Open", "Empty", "Playing" }; + + + BOOST_AUTO_TEST_CASE( my_test ) + { + player p; + + p.start(); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Empty)&>().get_attribute(entry_counter) == 1, + "Empty entry not called correctly"); + + p.process_event(open_close()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 2,"Open should be active"); //Open + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Empty)&>().get_attribute(exit_counter) == 1, + "Empty exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Open)&>().get_attribute(entry_counter) == 1, + "Open entry not called correctly"); + + p.process_event(open_close()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Empty should be active"); //Empty + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Open)&>().get_attribute(exit_counter) == 1, + "Open exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Empty)&>().get_attribute(entry_counter) == 2, + "Empty entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_attribute(can_close_drawer_counter) == 1,"guard not called correctly"); + + p.process_event( + cd_detected("louie, louie",DISK_DVD)); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Empty should be active"); //Empty + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Open)&>().get_attribute(exit_counter) == 1, + "Open exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Empty)&>().get_attribute(entry_counter) == 2, + "Empty entry not called correctly"); + + p.process_event( + cd_detected("louie, louie",DISK_CD)); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Empty)&>().get_attribute(exit_counter) == 2, + "Empty exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(entry_counter) == 1, + "Stopped entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(exit_counter) == 1, + "Stopped exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<Playing_type&>().get_attribute(entry_counter) == 1, + "Playing entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_attribute(start_playback_counter) == 1,"action not called correctly"); + BOOST_CHECK_MESSAGE(p.get_attribute(test_fct_counter) == 1,"action not called correctly"); + + p.process_event(next_song); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<Playing_type&>().current_state()[0] == 1,"Song2 should be active"); + BOOST_CHECK_MESSAGE(p.get_state<Playing_type&>().current_state()[1] == 3,"Region2State1 should be active"); + BOOST_CHECK_MESSAGE( + p.get_state<Playing_type&>().get_state<BOOST_MSM_EUML_STATE_NAME(Region2State1)&>().get_attribute(entry_counter) == 1, + "Region2State1 entry not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<Playing_type&>().get_state<BOOST_MSM_EUML_STATE_NAME(Song2)&>().get_attribute(entry_counter) == 1, + "Song2 entry not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<Playing_type&>().get_state<BOOST_MSM_EUML_STATE_NAME(Song1)&>().get_attribute(exit_counter) == 1, + "Song1 exit not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<Playing_type&>().get_attribute(start_next_song_counter) == 0, + "submachine action not called correctly"); + + p.process_event(next_song); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<Playing_type&>().current_state()[0] == 2,"Song3 should be active"); + BOOST_CHECK_MESSAGE( + p.get_state<Playing_type&>().get_state<BOOST_MSM_EUML_STATE_NAME(Song3)&>().get_attribute(entry_counter) == 1, + "Song3 entry not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<Playing_type&>().get_state<BOOST_MSM_EUML_STATE_NAME(Song2)&>().get_attribute(exit_counter) == 1, + "Song2 exit not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<Playing_type&>().get_attribute(start_next_song_counter) == 1, + "submachine action not called correctly"); + + p.process_event(previous_song); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<Playing_type&>().current_state()[0] == 1,"Song2 should be active"); + BOOST_CHECK_MESSAGE( + p.get_state<Playing_type&>().get_state<BOOST_MSM_EUML_STATE_NAME(Song2)&>().get_attribute(entry_counter) == 2, + "Song2 entry not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<Playing_type&>().get_state<BOOST_MSM_EUML_STATE_NAME(Song3)&>().get_attribute(exit_counter) == 1, + "Song3 exit not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<Playing_type&>().get_attribute(start_prev_song_guard_counter) == 1, + "submachine guard not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<Playing_type&>().get_state<BOOST_MSM_EUML_STATE_NAME(Region2State2)&>().get_attribute(entry_counter) == 0, + "Region2State2 entry not called correctly"); + + + p.process_event(pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 1,"Paused should be active"); //Paused + BOOST_CHECK_MESSAGE(p.get_state<Playing_type&>().get_attribute(exit_counter) == 1, + "Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Paused)&>().get_attribute(entry_counter) == 1, + "Paused entry not called correctly"); + + // go back to Playing + p.process_event(end_pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Paused)&>().get_attribute(exit_counter) == 1, + "Paused exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<Playing_type&>().get_attribute(entry_counter) == 2, + "Playing entry not called correctly"); + + p.process_event(pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 1,"Paused should be active"); //Paused + BOOST_CHECK_MESSAGE(p.get_state<Playing_type&>().get_attribute(exit_counter) == 2, + "Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Paused)&>().get_attribute(entry_counter) == 2, + "Paused entry not called correctly"); + + p.process_event(stop()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Paused)&>().get_attribute(exit_counter) == 2, + "Paused exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(entry_counter) == 2, + "Stopped entry not called correctly"); + + p.process_event(stop()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(exit_counter) == 2, + "Stopped exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(entry_counter) == 3, + "Stopped entry not called correctly"); + } +} + diff --git a/libs/msm/test/CompositeMachine.cpp b/libs/msm/test/CompositeMachine.cpp new file mode 100644 index 0000000000..9d6591b8a8 --- /dev/null +++ b/libs/msm/test/CompositeMachine.cpp @@ -0,0 +1,343 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> +#define BOOST_TEST_MODULE MyTest +#include <boost/test/unit_test.hpp> + +namespace msm = boost::msm; +namespace mpl = boost::mpl; + +namespace +{ + // events + struct play {}; + struct end_pause {}; + struct stop {}; + struct pause {}; + struct open_close {}; + struct NextSong {}; + struct PreviousSong {}; + struct cd_detected + { + cd_detected(std::string name) + : name(name) + {} + std::string name; + }; + + // front-end: define the FSM structure + struct player_ : public msm::front::state_machine_def<player_> + { + unsigned int start_playback_counter; + unsigned int can_close_drawer_counter; + + player_(): + start_playback_counter(0), + can_close_drawer_counter(0) + {} + // The list of FSM states + struct Empty : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct Open : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + // sm_ptr still supported but deprecated as functors are a much better way to do the same thing + struct Stopped : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + struct Playing_ : public msm::front::state_machine_def<Playing_> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + unsigned int start_next_song_counter; + unsigned int start_prev_song_guard_counter; + + Playing_(): + start_next_song_counter(0), + start_prev_song_guard_counter(0) + {} + + // The list of FSM states + struct Song1 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct Song2 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct Song3 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + // the initial state. Must be defined + typedef Song1 initial_state; + // transition actions + void start_next_song(NextSong const&) {++start_next_song_counter; } + void start_prev_song(PreviousSong const&) { } + // guard conditions + bool start_prev_song_guard(PreviousSong const&) {++start_prev_song_guard_counter;return true; } + + typedef Playing_ pl; // makes transition table cleaner + // Transition table for Playing + struct transition_table : mpl::vector4< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + _row < Song1 , NextSong , Song2 >, + row < Song2 , PreviousSong, Song1 , &pl::start_prev_song,&pl::start_prev_song_guard>, + a_row < Song2 , NextSong , Song3 , &pl::start_next_song >, + g_row < Song3 , PreviousSong, Song2 ,&pl::start_prev_song_guard> + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const&, FSM&,int) + { + BOOST_FAIL("no_transition called!"); + } + }; + // back-end + typedef msm::back::state_machine<Playing_> Playing; + + // state not defining any entry or exit + struct Paused : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + // the initial state of the player SM. Must be defined + typedef Empty initial_state; + + // transition actions + void start_playback(play const&) {++start_playback_counter; } + void open_drawer(open_close const&) { } + void store_cd_info(cd_detected const&) { } + void stop_playback(stop const&) { } + void pause_playback(pause const&) { } + void resume_playback(end_pause const&) { } + void stop_and_open(open_close const&) { } + void stopped_again(stop const&){} + //guards + bool can_close_drawer(open_close const&) + { + ++can_close_drawer_counter; + return true; + } + + + typedef player_ p; // makes transition table cleaner + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Stopped , play , Playing , &p::start_playback >, + a_row < Stopped , open_close , Open , &p::open_drawer >, + _row < Stopped , stop , Stopped >, + // +---------+-------------+---------+---------------------+----------------------+ + g_row < Open , open_close , Empty , &p::can_close_drawer >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Empty , open_close , Open , &p::open_drawer >, + a_row < Empty , cd_detected , Stopped , &p::store_cd_info >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Playing , stop , Stopped , &p::stop_playback >, + a_row < Playing , pause , Paused , &p::pause_playback >, + a_row < Playing , open_close , Open , &p::stop_and_open >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Paused , end_pause , Playing , &p::resume_playback >, + a_row < Paused , stop , Stopped , &p::stop_playback >, + a_row < Paused , open_close , Open , &p::stop_and_open > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + BOOST_FAIL("no_transition called!"); + } + // init counters + template <class Event,class FSM> + void on_entry(Event const&,FSM& fsm) + { + fsm.template get_state<player_::Stopped&>().entry_counter=0; + fsm.template get_state<player_::Stopped&>().exit_counter=0; + fsm.template get_state<player_::Open&>().entry_counter=0; + fsm.template get_state<player_::Open&>().exit_counter=0; + fsm.template get_state<player_::Empty&>().entry_counter=0; + fsm.template get_state<player_::Empty&>().exit_counter=0; + fsm.template get_state<player_::Playing&>().entry_counter=0; + fsm.template get_state<player_::Playing&>().exit_counter=0; + fsm.template get_state<player_::Playing&>().template get_state<player_::Playing::Song1&>().entry_counter=0; + fsm.template get_state<player_::Playing&>().template get_state<player_::Playing::Song1&>().exit_counter=0; + fsm.template get_state<player_::Playing&>().template get_state<player_::Playing::Song2&>().entry_counter=0; + fsm.template get_state<player_::Playing&>().template get_state<player_::Playing::Song2&>().exit_counter=0; + fsm.template get_state<player_::Playing&>().template get_state<player_::Playing::Song3&>().entry_counter=0; + fsm.template get_state<player_::Playing&>().template get_state<player_::Playing::Song3&>().exit_counter=0; + fsm.template get_state<player_::Paused&>().entry_counter=0; + fsm.template get_state<player_::Paused&>().exit_counter=0; + } + + }; + // Pick a back-end + typedef msm::back::state_machine<player_> player; + +// static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" }; + + + BOOST_AUTO_TEST_CASE( my_test ) + { + player p; + + p.start(); + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().entry_counter == 1,"Empty entry not called correctly"); + + p.process_event(open_close()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 1,"Open should be active"); //Open + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().exit_counter == 1,"Empty exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().entry_counter == 1,"Open entry not called correctly"); + + p.process_event(open_close()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 2,"Empty should be active"); //Empty + BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().exit_counter == 1,"Open exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().entry_counter == 2,"Empty entry not called correctly"); + BOOST_CHECK_MESSAGE(p.can_close_drawer_counter == 1,"guard not called correctly"); + + p.process_event(cd_detected("louie, louie")); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().exit_counter == 2,"Empty exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 1,"Stopped entry not called correctly"); + + p.process_event(play()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().exit_counter == 1,"Stopped exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().entry_counter == 1,"Playing entry not called correctly"); + BOOST_CHECK_MESSAGE(p.start_playback_counter == 1,"action not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().current_state()[0] == 0,"Song1 should be active"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().get_state<player_::Playing::Song1&>().entry_counter == 1, + "Song1 entry not called correctly"); + + p.process_event(NextSong()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().current_state()[0] == 1,"Song2 should be active"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().get_state<player_::Playing::Song2&>().entry_counter == 1, + "Song2 entry not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().get_state<player_::Playing::Song1&>().exit_counter == 1, + "Song1 exit not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().start_next_song_counter == 0, + "submachine action not called correctly"); + + p.process_event(NextSong()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().current_state()[0] == 2,"Song3 should be active"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().get_state<player_::Playing::Song3&>().entry_counter == 1, + "Song3 entry not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().get_state<player_::Playing::Song2&>().exit_counter == 1, + "Song2 exit not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().start_next_song_counter == 1, + "submachine action not called correctly"); + + p.process_event(PreviousSong()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().current_state()[0] == 1,"Song2 should be active"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().get_state<player_::Playing::Song2&>().entry_counter == 2, + "Song2 entry not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().get_state<player_::Playing::Song3&>().exit_counter == 1, + "Song3 exit not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().start_prev_song_guard_counter == 1, + "submachine guard not called correctly"); + + p.process_event(pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Paused should be active"); //Paused + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().exit_counter == 1,"Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().entry_counter == 1,"Paused entry not called correctly"); + + // go back to Playing + p.process_event(end_pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().exit_counter == 1,"Paused exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().entry_counter == 2,"Playing entry not called correctly"); + + p.process_event(pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Paused should be active"); //Paused + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().exit_counter == 2,"Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().entry_counter == 2,"Paused entry not called correctly"); + + p.process_event(stop()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().exit_counter == 2,"Paused exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 2,"Stopped entry not called correctly"); + + p.process_event(stop()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().exit_counter == 2,"Stopped exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 3,"Stopped entry not called correctly"); + } +} + diff --git a/libs/msm/test/Constructor.cpp b/libs/msm/test/Constructor.cpp new file mode 100644 index 0000000000..1753339f2f --- /dev/null +++ b/libs/msm/test/Constructor.cpp @@ -0,0 +1,208 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> +#define BOOST_TEST_MODULE MyTest +#include <boost/test/unit_test.hpp> + +namespace msm = boost::msm; +namespace mpl = boost::mpl; + +namespace +{ + // events + struct play {}; + struct end_pause {}; + struct stop {}; + struct pause {}; + struct open_close {}; + + // A "complicated" event type that carries some data. + enum DiskTypeEnum + { + DISK_CD=0, + DISK_DVD=1 + }; + struct cd_detected + { + cd_detected(std::string name, DiskTypeEnum diskType) + : name(name), + disc_type(diskType) + {} + + std::string name; + DiskTypeEnum disc_type; + }; + struct SomeExternalContext + { + SomeExternalContext(int b):bla(b){} + int bla; + }; + // front-end: define the FSM structure + struct player_ : public msm::front::state_machine_def<player_> + { + player_(SomeExternalContext& context,int someint): + context_(context), + someint_(someint) + {} + + SomeExternalContext& context_; + int someint_; + + // The list of FSM states + struct Empty : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct Open : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + // sm_ptr still supported but deprecated as functors are a much better way to do the same thing + struct Stopped : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + struct Playing : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + // state not defining any entry or exit + struct Paused : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + // the initial state of the player SM. Must be defined + typedef Empty initial_state; + + // transition actions + void start_playback(play const&) {} + void open_drawer(open_close const&) { } + void store_cd_info(cd_detected const&) { } + void stop_playback(stop const&) { } + void pause_playback(pause const&) { } + void resume_playback(end_pause const&) { } + void stop_and_open(open_close const&) { } + void stopped_again(stop const&){} + // guard conditions + bool good_disk_format(cd_detected const& evt) + { + // to test a guard condition, let's say we understand only CDs, not DVD + if (evt.disc_type != DISK_CD) + { + return false; + } + return true; + } + bool can_close_drawer(open_close const&) + { + return true; + } + + typedef player_ p; // makes transition table cleaner + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Stopped , play , Playing , &p::start_playback >, + a_row < Stopped , open_close , Open , &p::open_drawer >, + _row < Stopped , stop , Stopped >, + // +---------+-------------+---------+---------------------+----------------------+ + g_row < Open , open_close , Empty , &p::can_close_drawer >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Empty , open_close , Open , &p::open_drawer >, + row < Empty , cd_detected , Stopped , &p::store_cd_info ,&p::good_disk_format >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Playing , stop , Stopped , &p::stop_playback >, + a_row < Playing , pause , Paused , &p::pause_playback >, + a_row < Playing , open_close , Open , &p::stop_and_open >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Paused , end_pause , Playing , &p::resume_playback >, + a_row < Paused , stop , Stopped , &p::stop_playback >, + a_row < Paused , open_close , Open , &p::stop_and_open > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const&, FSM&,int) + { + BOOST_FAIL("no_transition called!"); + } + // init counters + template <class Event,class FSM> + void on_entry(Event const&,FSM& fsm) + { + fsm.template get_state<player_::Stopped&>().entry_counter=0; + fsm.template get_state<player_::Stopped&>().exit_counter=0; + fsm.template get_state<player_::Open&>().entry_counter=0; + fsm.template get_state<player_::Open&>().exit_counter=0; + fsm.template get_state<player_::Empty&>().entry_counter=0; + fsm.template get_state<player_::Empty&>().exit_counter=0; + fsm.template get_state<player_::Playing&>().entry_counter=0; + fsm.template get_state<player_::Playing&>().exit_counter=0; + fsm.template get_state<player_::Paused&>().entry_counter=0; + fsm.template get_state<player_::Paused&>().exit_counter=0; + fsm.context_ = 20; + } + + }; + // Pick a back-end + typedef msm::back::state_machine<player_> player; + +// static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" }; + + + BOOST_AUTO_TEST_CASE( my_test ) + { + SomeExternalContext ctx(3); + player p(boost::ref(ctx),5); + BOOST_CHECK_MESSAGE(p.context_.bla == 3,"wrong value passed"); + BOOST_CHECK_MESSAGE(p.someint_ == 5,"wrong value passed"); + ctx.bla = 10; + BOOST_CHECK_MESSAGE(p.context_.bla == 10,"error with reference"); + p.start(); + BOOST_CHECK_MESSAGE(p.context_.bla == 20,"error with fsm entry behavior"); + } +} + diff --git a/libs/msm/test/Entries.cpp b/libs/msm/test/Entries.cpp new file mode 100644 index 0000000000..e18fc731bb --- /dev/null +++ b/libs/msm/test/Entries.cpp @@ -0,0 +1,295 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> +#define BOOST_TEST_MODULE MyTest +#include <boost/test/unit_test.hpp> + +namespace msm = boost::msm; +namespace mpl = boost::mpl; + +namespace +{ + // events + struct event1 {}; + struct event2 {}; + struct event3 {}; + struct event4 {}; + struct event5 {}; + struct event6 + { + event6(){} + template <class Event> + event6(Event const&){} + }; + // front-end: define the FSM structure + struct Fsm_ : public msm::front::state_machine_def<Fsm_> + { + // The list of FSM states + struct State1 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct State2 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct SubFsm2_ : public msm::front::state_machine_def<SubFsm2_> + { + typedef msm::back::state_machine<SubFsm2_> SubFsm2; + + unsigned int entry_action_counter; + + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + + struct SubState1 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct SubState1b : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct SubState2 : public msm::front::state<> , public msm::front::explicit_entry<0> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct SubState2b : public msm::front::state<> , public msm::front::explicit_entry<1> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + // test with a pseudo entry + struct PseudoEntry1 : public msm::front::entry_pseudo_state<0> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct SubState3 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct PseudoExit1 : public msm::front::exit_pseudo_state<event6> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + // action methods + void entry_action(event4 const&) + { + ++entry_action_counter; + } + // the initial state. Must be defined + typedef mpl::vector<SubState1,SubState1b> initial_state; + + typedef mpl::vector<SubState2b> explicit_creation; + + // Transition table for SubFsm2 + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +--------------+-------------+------------+------------------------+----------------------+ + a_row < PseudoEntry1 , event4 , SubState3 ,&SubFsm2_::entry_action >, + _row < SubState2 , event6 , SubState1 >, + _row < SubState3 , event5 , PseudoExit1 > + // +--------------+-------------+------------+------------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + BOOST_FAIL("no_transition called!"); + } + }; + typedef msm::back::state_machine<SubFsm2_> SubFsm2; + + // the initial state of the player SM. Must be defined + typedef State1 initial_state; + + // transition actions + // guard conditions + + // Transition table for Fsm + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------------------+--------+------------------------------------+-------+--------+ + _row < State1 , event1 , SubFsm2 >, + _row < State1 , event2 , SubFsm2::direct<SubFsm2_::SubState2> >, + _row < State1 , event3 , mpl::vector<SubFsm2::direct<SubFsm2_::SubState2>, + SubFsm2::direct<SubFsm2_::SubState2b> > >, + _row < State1 , event4 , SubFsm2::entry_pt + <SubFsm2_::PseudoEntry1> >, + // +---------------------+--------+------------------------------------+-------+--------+ + _row < SubFsm2 , event1 , State1 >, + _row < SubFsm2::exit_pt + <SubFsm2_::PseudoExit1>, event6 , State2 > + // +---------------------+--------+------------------------------------+-------+--------+ + > {}; + + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + BOOST_FAIL("no_transition called!"); + } + // init counters + template <class Event,class FSM> + void on_entry(Event const&,FSM& fsm) + { + fsm.template get_state<Fsm_::State1&>().entry_counter=0; + fsm.template get_state<Fsm_::State1&>().exit_counter=0; + fsm.template get_state<Fsm_::State2&>().entry_counter=0; + fsm.template get_state<Fsm_::State2&>().exit_counter=0; + fsm.template get_state<Fsm_::SubFsm2&>().entry_counter=0; + fsm.template get_state<Fsm_::SubFsm2&>().exit_counter=0; + fsm.template get_state<Fsm_::SubFsm2&>().entry_action_counter=0; + fsm.template get_state<Fsm_::SubFsm2&>().template get_state<Fsm_::SubFsm2::SubState1&>().entry_counter=0; + fsm.template get_state<Fsm_::SubFsm2&>().template get_state<Fsm_::SubFsm2::SubState1&>().exit_counter=0; + fsm.template get_state<Fsm_::SubFsm2&>().template get_state<Fsm_::SubFsm2::SubState1b&>().entry_counter=0; + fsm.template get_state<Fsm_::SubFsm2&>().template get_state<Fsm_::SubFsm2::SubState1b&>().exit_counter=0; + fsm.template get_state<Fsm_::SubFsm2&>().template get_state<Fsm_::SubFsm2::SubState2&>().entry_counter=0; + fsm.template get_state<Fsm_::SubFsm2&>().template get_state<Fsm_::SubFsm2::SubState2&>().exit_counter=0; + fsm.template get_state<Fsm_::SubFsm2&>().template get_state<Fsm_::SubFsm2::SubState2b&>().entry_counter=0; + fsm.template get_state<Fsm_::SubFsm2&>().template get_state<Fsm_::SubFsm2::SubState2b&>().exit_counter=0; + fsm.template get_state<Fsm_::SubFsm2&>().template get_state<Fsm_::SubFsm2::SubState3&>().entry_counter=0; + fsm.template get_state<Fsm_::SubFsm2&>().template get_state<Fsm_::SubFsm2::SubState3&>().exit_counter=0; + fsm.template get_state<Fsm_::SubFsm2&>().template get_state<Fsm_::SubFsm2::PseudoEntry1&>().entry_counter=0; + fsm.template get_state<Fsm_::SubFsm2&>().template get_state<Fsm_::SubFsm2::PseudoEntry1&>().exit_counter=0; + fsm.template get_state<Fsm_::SubFsm2&>().template get_state<Fsm_::SubFsm2::exit_pt<SubFsm2_::PseudoExit1>&>().entry_counter=0; + fsm.template get_state<Fsm_::SubFsm2&>().template get_state<Fsm_::SubFsm2::exit_pt<SubFsm2_::PseudoExit1>&>().exit_counter=0; + + } + }; + typedef msm::back::state_machine<Fsm_> Fsm; +// static char const* const state_names[] = { "State1", "SubFsm2","State2" }; + + + BOOST_AUTO_TEST_CASE( my_test ) + { + Fsm p; + + p.start(); + BOOST_CHECK_MESSAGE(p.get_state<Fsm_::State1&>().entry_counter == 1,"State1 entry not called correctly"); + + p.process_event(event1()); + p.process_event(event1()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"State1 should be active"); + BOOST_CHECK_MESSAGE(p.get_state<Fsm_::State1&>().exit_counter == 1,"State1 exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<Fsm_::State1&>().entry_counter == 2,"State1 entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<Fsm_::SubFsm2&>().exit_counter == 1,"SubFsm2 exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<Fsm_::SubFsm2&>().entry_counter == 1,"SubFsm2 entry not called correctly"); + + p.process_event(event2()); + p.process_event(event6()); + p.process_event(event1()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"State1 should be active"); + BOOST_CHECK_MESSAGE(p.get_state<Fsm_::State1&>().exit_counter == 2,"State1 exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<Fsm_::State1&>().entry_counter == 3,"State1 entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<Fsm_::SubFsm2&>().exit_counter == 2,"SubFsm2 exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<Fsm_::SubFsm2&>().entry_counter == 2,"SubFsm2 entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<Fsm_::SubFsm2&>().get_state<Fsm_::SubFsm2_::SubState2&>().entry_counter == 1, + "SubFsm2::SubState2 entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<Fsm_::SubFsm2&>().get_state<Fsm_::SubFsm2_::SubState2&>().exit_counter == 1, + "SubFsm2::SubState2 exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<Fsm_::SubFsm2&>().get_state<Fsm_::SubFsm2_::SubState1&>().entry_counter == 2, + "SubFsm2::SubState1 entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<Fsm_::SubFsm2&>().get_state<Fsm_::SubFsm2_::SubState1&>().exit_counter == 2, + "SubFsm2::SubState1 exit not called correctly"); + + p.process_event(event3()); + p.process_event(event1()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"State1 should be active"); + BOOST_CHECK_MESSAGE(p.get_state<Fsm_::State1&>().exit_counter == 3,"State1 exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<Fsm_::State1&>().entry_counter == 4,"State1 entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<Fsm_::SubFsm2&>().exit_counter == 3,"SubFsm2 exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<Fsm_::SubFsm2&>().entry_counter == 3,"SubFsm2 entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<Fsm_::SubFsm2&>().get_state<Fsm_::SubFsm2_::SubState2&>().entry_counter == 2, + "SubFsm2::SubState2 entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<Fsm_::SubFsm2&>().get_state<Fsm_::SubFsm2_::SubState2&>().exit_counter == 2, + "SubFsm2::SubState2 exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<Fsm_::SubFsm2&>().get_state<Fsm_::SubFsm2_::SubState2b&>().entry_counter == 1, + "SubFsm2::SubState2b entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<Fsm_::SubFsm2&>().get_state<Fsm_::SubFsm2_::SubState2b&>().exit_counter == 1, + "SubFsm2::SubState2b exit not called correctly"); + + p.process_event(event4()); + p.process_event(event5()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 2,"State2 should be active"); + BOOST_CHECK_MESSAGE(p.get_state<Fsm_::State1&>().exit_counter == 4,"State1 exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<Fsm_::State2&>().entry_counter == 1,"State2 entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<Fsm_::SubFsm2&>().exit_counter == 4,"SubFsm2 exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<Fsm_::SubFsm2&>().entry_counter == 4,"SubFsm2 entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<Fsm_::SubFsm2&>().get_state<Fsm_::SubFsm2_::PseudoEntry1&>().entry_counter == 1, + "SubFsm2::PseudoEntry1 entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<Fsm_::SubFsm2&>().get_state<Fsm_::SubFsm2_::PseudoEntry1&>().exit_counter == 1, + "SubFsm2::PseudoEntry1 exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<Fsm_::SubFsm2&>().get_state<Fsm_::SubFsm2_::SubState3&>().entry_counter == 1, + "SubFsm2::SubState3 entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<Fsm_::SubFsm2&>().get_state<Fsm_::SubFsm2_::SubState3&>().exit_counter == 1, + "SubFsm2::SubState3 exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<Fsm_::SubFsm2&>().get_state<Fsm_::SubFsm2::exit_pt<Fsm_::SubFsm2_::PseudoExit1>&>().entry_counter == 1, + "SubFsm2::PseudoExit1 entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<Fsm_::SubFsm2&>().get_state<Fsm_::SubFsm2::exit_pt<Fsm_::SubFsm2_::PseudoExit1>&>().exit_counter == 1, + "SubFsm2::PseudoExit1 exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<Fsm_::SubFsm2&>().entry_action_counter == 1,"Action not called correctly"); + + } +} + diff --git a/libs/msm/test/History.cpp b/libs/msm/test/History.cpp new file mode 100644 index 0000000000..927720cf8d --- /dev/null +++ b/libs/msm/test/History.cpp @@ -0,0 +1,354 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> +#define BOOST_TEST_MODULE MyTest +#include <boost/test/unit_test.hpp> + +namespace msm = boost::msm; +namespace mpl = boost::mpl; + +namespace +{ + // events + struct play {}; + struct end_pause {}; + struct stop {}; + struct pause {}; + struct open_close {}; + struct NextSong {}; + struct PreviousSong {}; + struct cd_detected + { + cd_detected(std::string name) + : name(name) + {} + std::string name; + }; + + // front-end: define the FSM structure + struct player_ : public msm::front::state_machine_def<player_> + { + unsigned int start_playback_counter; + unsigned int can_close_drawer_counter; + + player_(): + start_playback_counter(0), + can_close_drawer_counter(0) + {} + // The list of FSM states + struct Empty : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct Open : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + // sm_ptr still supported but deprecated as functors are a much better way to do the same thing + struct Stopped : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + struct Playing_ : public msm::front::state_machine_def<Playing_> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + unsigned int start_next_song_counter; + unsigned int start_prev_song_guard_counter; + + Playing_(): + start_next_song_counter(0), + start_prev_song_guard_counter(0) + {} + + // The list of FSM states + struct Song1 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct Song2 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct Song3 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + // the initial state. Must be defined + typedef Song1 initial_state; + // transition actions + void start_next_song(NextSong const&) {++start_next_song_counter; } + void start_prev_song(PreviousSong const&) { } + // guard conditions + bool start_prev_song_guard(PreviousSong const&) {++start_prev_song_guard_counter;return true; } + + typedef Playing_ pl; // makes transition table cleaner + // Transition table for Playing + struct transition_table : mpl::vector4< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + _row < Song1 , NextSong , Song2 >, + row < Song2 , PreviousSong, Song1 , &pl::start_prev_song,&pl::start_prev_song_guard>, + a_row < Song2 , NextSong , Song3 , &pl::start_next_song >, + g_row < Song3 , PreviousSong, Song2 ,&pl::start_prev_song_guard> + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const&, FSM&,int) + { + BOOST_FAIL("no_transition called!"); + } + }; + // back-end + typedef msm::back::state_machine<Playing_,msm::back::ShallowHistory<mpl::vector<end_pause> > > Playing; + + // state not defining any entry or exit + struct Paused : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + // the initial state of the player SM. Must be defined + typedef Empty initial_state; + + // transition actions + void start_playback(play const&) {++start_playback_counter; } + void open_drawer(open_close const&) { } + void store_cd_info(cd_detected const&) { } + void stop_playback(stop const&) { } + void pause_playback(pause const&) { } + void resume_playback(end_pause const&) { } + void stop_and_open(open_close const&) { } + void stopped_again(stop const&){} + //guards + bool can_close_drawer(open_close const&) + { + ++can_close_drawer_counter; + return true; + } + + + typedef player_ p; // makes transition table cleaner + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Stopped , play , Playing , &p::start_playback >, + a_row < Stopped , open_close , Open , &p::open_drawer >, + _row < Stopped , stop , Stopped >, + // +---------+-------------+---------+---------------------+----------------------+ + g_row < Open , open_close , Empty , &p::can_close_drawer >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Empty , open_close , Open , &p::open_drawer >, + a_row < Empty , cd_detected , Stopped , &p::store_cd_info >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Playing , stop , Stopped , &p::stop_playback >, + a_row < Playing , pause , Paused , &p::pause_playback >, + a_row < Playing , open_close , Open , &p::stop_and_open >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Paused , end_pause , Playing , &p::resume_playback >, + a_row < Paused , stop , Stopped , &p::stop_playback >, + a_row < Paused , open_close , Open , &p::stop_and_open > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + BOOST_FAIL("no_transition called!"); + } + // init counters + template <class Event,class FSM> + void on_entry(Event const&,FSM& fsm) + { + fsm.template get_state<player_::Stopped&>().entry_counter=0; + fsm.template get_state<player_::Stopped&>().exit_counter=0; + fsm.template get_state<player_::Open&>().entry_counter=0; + fsm.template get_state<player_::Open&>().exit_counter=0; + fsm.template get_state<player_::Empty&>().entry_counter=0; + fsm.template get_state<player_::Empty&>().exit_counter=0; + fsm.template get_state<player_::Playing&>().entry_counter=0; + fsm.template get_state<player_::Playing&>().exit_counter=0; + fsm.template get_state<player_::Playing&>().template get_state<player_::Playing::Song1&>().entry_counter=0; + fsm.template get_state<player_::Playing&>().template get_state<player_::Playing::Song1&>().exit_counter=0; + fsm.template get_state<player_::Playing&>().template get_state<player_::Playing::Song2&>().entry_counter=0; + fsm.template get_state<player_::Playing&>().template get_state<player_::Playing::Song2&>().exit_counter=0; + fsm.template get_state<player_::Playing&>().template get_state<player_::Playing::Song3&>().entry_counter=0; + fsm.template get_state<player_::Playing&>().template get_state<player_::Playing::Song3&>().exit_counter=0; + fsm.template get_state<player_::Paused&>().entry_counter=0; + fsm.template get_state<player_::Paused&>().exit_counter=0; + } + + }; + // Pick a back-end + typedef msm::back::state_machine<player_> player; + +// static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" }; + + + BOOST_AUTO_TEST_CASE( my_test ) + { + player p; + + p.start(); + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().entry_counter == 1,"Empty entry not called correctly"); + + p.process_event(open_close()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 1,"Open should be active"); //Open + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().exit_counter == 1,"Empty exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().entry_counter == 1,"Open entry not called correctly"); + + p.process_event(open_close()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 2,"Empty should be active"); //Empty + BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().exit_counter == 1,"Open exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().entry_counter == 2,"Empty entry not called correctly"); + BOOST_CHECK_MESSAGE(p.can_close_drawer_counter == 1,"guard not called correctly"); + + p.process_event(cd_detected("louie, louie")); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().exit_counter == 2,"Empty exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 1,"Stopped entry not called correctly"); + + p.process_event(play()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().exit_counter == 1,"Stopped exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().entry_counter == 1,"Playing entry not called correctly"); + BOOST_CHECK_MESSAGE(p.start_playback_counter == 1,"action not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().current_state()[0] == 0,"Song1 should be active"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().get_state<player_::Playing::Song1&>().entry_counter == 1, + "Song1 entry not called correctly"); + + p.process_event(NextSong()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().current_state()[0] == 1,"Song2 should be active"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().get_state<player_::Playing::Song2&>().entry_counter == 1, + "Song2 entry not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().get_state<player_::Playing::Song1&>().exit_counter == 1, + "Song1 exit not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().start_next_song_counter == 0, + "submachine action not called correctly"); + + p.process_event(NextSong()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().current_state()[0] == 2,"Song3 should be active"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().get_state<player_::Playing::Song3&>().entry_counter == 1, + "Song3 entry not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().get_state<player_::Playing::Song2&>().exit_counter == 1, + "Song2 exit not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().start_next_song_counter == 1, + "submachine action not called correctly"); + + p.process_event(PreviousSong()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().current_state()[0] == 1,"Song2 should be active"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().get_state<player_::Playing::Song2&>().entry_counter == 2, + "Song2 entry not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().get_state<player_::Playing::Song3&>().exit_counter == 1, + "Song3 exit not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().start_prev_song_guard_counter == 1, + "submachine guard not called correctly"); + + p.process_event(pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Paused should be active"); //Paused + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().exit_counter == 1,"Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().entry_counter == 1,"Paused entry not called correctly"); + + // go back to Playing + p.process_event(end_pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().exit_counter == 1,"Paused exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().entry_counter == 2,"Playing entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().current_state()[0] == 1,"Song2 should be active"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().get_state<player_::Playing::Song2&>().entry_counter == 3, + "Song2 entry not called correctly"); + + p.process_event(pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Paused should be active"); //Paused + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().exit_counter == 2,"Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().entry_counter == 2,"Paused entry not called correctly"); + + p.process_event(stop()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().exit_counter == 2,"Paused exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 2,"Stopped entry not called correctly"); + + p.process_event(stop()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().exit_counter == 2,"Stopped exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 3,"Stopped entry not called correctly"); + + p.process_event(play()); + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().current_state()[0] == 0,"Song1 should be active"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().get_state<player_::Playing::Song1&>().entry_counter == 2, + "Song1 entry not called correctly"); + + } +} + diff --git a/libs/msm/test/Jamfile.v2 b/libs/msm/test/Jamfile.v2 new file mode 100644 index 0000000000..af7aad0c6a --- /dev/null +++ b/libs/msm/test/Jamfile.v2 @@ -0,0 +1,49 @@ +# test/Jamfile.v2 controls building of MSM Library unit tests +# +# Copyright (c) 2010 Christophe Henry +# +# Use, modification and distribution is subject to the Boost Software License, +# Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) + +import testing ; + +project msm + : + requirements + <include>. + <toolset>gcc:<cxxflags>"-ftemplate-depth-300 -g0" + <toolset>darwin:<cxxflags>"-ftemplate-depth-300 -g0" + <toolset>intel:<cxxflags>"-g0" + <toolset>gcc:<optimization>off + <toolset>darwin:<optimization>off + <toolset>intel:<optimization>off + <library>/boost/test//boost_unit_test_framework/<link>static + <library>/boost/serialization//boost_serialization/<link>static + ; + +test-suite msm-unit-tests + : + [ run Anonymous.cpp ] + [ run AnonymousEuml.cpp ] + [ run CompositeEuml.cpp ] + [ run CompositeMachine.cpp ] + [ run Constructor.cpp ] + [ run Entries.cpp ] + [ run History.cpp ] + [ run OrthogonalDeferred.cpp ] + [ run OrthogonalDeferred2.cpp ] + [ run OrthogonalDeferredEuml.cpp ] + [ run SimpleEuml.cpp ] + [ run SimpleEuml2.cpp ] + [ run SimpleInternal.cpp ] + [ run SimpleInternalEuml.cpp ] + [ run SimpleInternalFunctors.cpp ] + [ run SimpleMachine.cpp ] + [ run SimpleWithFunctors.cpp ] + [ run Serialize.cpp ] + [ run SerializeWithHistory.cpp ] + [ run SerializeSimpleEuml.cpp ] + [ run TestConstructor.cpp ] + ; + diff --git a/libs/msm/test/OrthogonalDeferred.cpp b/libs/msm/test/OrthogonalDeferred.cpp new file mode 100644 index 0000000000..f184de89b6 --- /dev/null +++ b/libs/msm/test/OrthogonalDeferred.cpp @@ -0,0 +1,486 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> +#define BOOST_TEST_MODULE MyTest +#include <boost/test/unit_test.hpp> + +namespace msm = boost::msm; +namespace mpl = boost::mpl; + +namespace +{ + // events + struct play {}; + struct end_pause {}; + struct stop {}; + struct pause {}; + struct open_close {}; + struct NextSong {}; + struct PreviousSong {}; + struct error_found {}; + struct end_error {}; + struct do_terminate {}; + + // Flags. Allow information about a property of the current state + struct PlayingPaused{}; + struct CDLoaded {}; + struct FirstSongPlaying {}; + + // A "complicated" event type that carries some data. + struct cd_detected + { + cd_detected(std::string name) + : name(name) + {} + + std::string name; + }; + + // front-end: define the FSM structure + struct player_ : public msm::front::state_machine_def<player_> + { + unsigned int start_playback_counter; + unsigned int can_close_drawer_counter; + unsigned int report_error_counter; + unsigned int report_end_error_counter; + + player_(): + start_playback_counter(0), + can_close_drawer_counter(0), + report_error_counter(0), + report_end_error_counter(0) + {} + // The list of FSM states + struct Empty : public msm::front::state<> + { + typedef mpl::vector<play> deferred_events; + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct Open : public msm::front::state<> + { + typedef mpl::vector<play> deferred_events; + typedef mpl::vector1<CDLoaded> flag_list; + + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + struct Stopped : public msm::front::state<> + { + typedef mpl::vector1<CDLoaded> flag_list; + + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + // the player state machine contains a state which is himself a state machine + // as you see, no need to declare it anywhere so Playing can be developed separately + // by another team in another module. For simplicity I just declare it inside player + struct Playing_ : public msm::front::state_machine_def<Playing_> + { + // when playing, the CD is loaded and we are in either pause or playing (duh) + typedef mpl::vector2<PlayingPaused,CDLoaded> flag_list; + + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + unsigned int start_next_song_counter; + unsigned int start_prev_song_guard_counter; + + Playing_(): + start_next_song_counter(0), + start_prev_song_guard_counter(0) + {} + + // The list of FSM states + struct Song1 : public msm::front::state<> + { + typedef mpl::vector1<FirstSongPlaying> flag_list; + + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct Song2 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct Song3 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + // the initial state. Must be defined + typedef Song1 initial_state; + // transition actions + void start_next_song(NextSong const&) {++start_next_song_counter; } + void start_prev_song(PreviousSong const&) { } + // guard conditions + bool start_prev_song_guard(PreviousSong const&) {++start_prev_song_guard_counter;return true; } + + typedef Playing_ pl; // makes transition table cleaner + // Transition table for Playing + struct transition_table : mpl::vector4< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + _row < Song1 , NextSong , Song2 >, + row < Song2 , PreviousSong, Song1 , &pl::start_prev_song,&pl::start_prev_song_guard>, + a_row < Song2 , NextSong , Song3 , &pl::start_next_song >, + g_row < Song3 , PreviousSong, Song2 ,&pl::start_prev_song_guard> + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const&, FSM&,int) + { + BOOST_FAIL("no_transition called!"); + } + }; + // back-end + typedef msm::back::state_machine<Playing_> Playing; + + // state not defining any entry or exit + struct Paused : public msm::front::state<> + { + typedef mpl::vector2<PlayingPaused,CDLoaded> flag_list; + + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct AllOk : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + // this state is also made terminal so that all the events are blocked + struct ErrorMode : //public msm::front::terminate_state<> // ErrorMode terminates the state machine + public msm::front::interrupt_state<end_error> // ErroMode just interrupts. Will resume if + // the event end_error is generated + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct ErrorTerminate : public msm::front::terminate_state<> // terminates the state machine + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + // the initial state of the player SM. Must be defined + typedef mpl::vector<Empty,AllOk> initial_state; + + // transition actions + void start_playback(play const&) {++start_playback_counter; } + void open_drawer(open_close const&) { } + void store_cd_info(cd_detected const&) { } + void stop_playback(stop const&) { } + void pause_playback(pause const&) { } + void resume_playback(end_pause const&) { } + void stop_and_open(open_close const&) { } + void stopped_again(stop const&){} + void report_error(error_found const&) {++report_error_counter;} + void report_end_error(end_error const&) {++report_end_error_counter;} + + //guards + bool can_close_drawer(open_close const&) + { + ++can_close_drawer_counter; + return true; + } + typedef player_ p; // makes transition table cleaner + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Stopped , play , Playing , &p::start_playback >, + a_row < Stopped , open_close , Open , &p::open_drawer >, + _row < Stopped , stop , Stopped >, + // +---------+-------------+---------+---------------------+----------------------+ + g_row < Open , open_close , Empty , &p::can_close_drawer >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Empty , open_close , Open , &p::open_drawer >, + a_row < Empty , cd_detected , Stopped , &p::store_cd_info >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Playing , stop , Stopped , &p::stop_playback >, + a_row < Playing , pause , Paused , &p::pause_playback >, + a_row < Playing , open_close , Open , &p::stop_and_open >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Paused , end_pause , Playing , &p::resume_playback >, + a_row < Paused , stop , Stopped , &p::stop_playback >, + a_row < Paused , open_close , Open , &p::stop_and_open >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < AllOk , error_found ,ErrorMode, &p::report_error >, + a_row <ErrorMode, end_error ,AllOk , &p::report_end_error >, + _row < AllOk , do_terminate,ErrorTerminate > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + BOOST_FAIL("no_transition called!"); + } + // init counters + template <class Event,class FSM> + void on_entry(Event const&,FSM& fsm) + { + fsm.template get_state<player_::Stopped&>().entry_counter=0; + fsm.template get_state<player_::Stopped&>().exit_counter=0; + fsm.template get_state<player_::Open&>().entry_counter=0; + fsm.template get_state<player_::Open&>().exit_counter=0; + fsm.template get_state<player_::Empty&>().entry_counter=0; + fsm.template get_state<player_::Empty&>().exit_counter=0; + fsm.template get_state<player_::Playing&>().entry_counter=0; + fsm.template get_state<player_::Playing&>().exit_counter=0; + fsm.template get_state<player_::Playing&>().template get_state<player_::Playing::Song1&>().entry_counter=0; + fsm.template get_state<player_::Playing&>().template get_state<player_::Playing::Song1&>().exit_counter=0; + fsm.template get_state<player_::Playing&>().template get_state<player_::Playing::Song2&>().entry_counter=0; + fsm.template get_state<player_::Playing&>().template get_state<player_::Playing::Song2&>().exit_counter=0; + fsm.template get_state<player_::Playing&>().template get_state<player_::Playing::Song3&>().entry_counter=0; + fsm.template get_state<player_::Playing&>().template get_state<player_::Playing::Song3&>().exit_counter=0; + fsm.template get_state<player_::Paused&>().entry_counter=0; + fsm.template get_state<player_::Paused&>().exit_counter=0; + fsm.template get_state<player_::AllOk&>().entry_counter=0; + fsm.template get_state<player_::AllOk&>().exit_counter=0; + fsm.template get_state<player_::ErrorMode&>().entry_counter=0; + fsm.template get_state<player_::ErrorMode&>().exit_counter=0; + fsm.template get_state<player_::ErrorTerminate&>().entry_counter=0; + fsm.template get_state<player_::ErrorTerminate&>().exit_counter=0; + } + }; + // Pick a back-end + typedef msm::back::state_machine<player_> player; + + //static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused","AllOk","ErrorMode" }; + + BOOST_AUTO_TEST_CASE( my_test ) + { + player p; + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + p.start(); + // test deferred event + // deferred in Empty and Open, will be handled only after event cd_detected + p.process_event(play()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 2,"Empty should be active"); //Empty + BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().exit_counter == 0,"Open exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().entry_counter == 0,"Playing entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().entry_counter == 1,"Empty entry not called correctly"); + //flags + BOOST_CHECK_MESSAGE(p.is_flag_active<CDLoaded>() == false,"CDLoaded should not be active"); + + p.process_event(open_close()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 1,"Open should be active"); //Open + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().exit_counter == 1,"Empty exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().entry_counter == 1,"Open entry not called correctly"); + + p.process_event(open_close()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 2,"Empty should be active"); //Empty + BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().exit_counter == 1,"Open exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().entry_counter == 2,"Empty entry not called correctly"); + BOOST_CHECK_MESSAGE(p.can_close_drawer_counter == 1,"guard not called correctly"); + + //deferred event should have been processed + p.process_event(cd_detected("louie, louie")); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().exit_counter == 2,"Empty exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 1,"Stopped entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().exit_counter == 1,"Stopped exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().entry_counter == 1,"Playing entry not called correctly"); + BOOST_CHECK_MESSAGE(p.start_playback_counter == 1,"action not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().current_state()[0] == 0,"Song1 should be active"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().get_state<player_::Playing::Song1&>().entry_counter == 1, + "Song1 entry not called correctly"); + + //flags + BOOST_CHECK_MESSAGE(p.is_flag_active<PlayingPaused>() == true,"PlayingPaused should be active"); + BOOST_CHECK_MESSAGE(p.is_flag_active<FirstSongPlaying>() == true,"FirstSongPlaying should be active"); + + + p.process_event(NextSong()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().current_state()[0] == 1,"Song2 should be active"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().get_state<player_::Playing::Song2&>().entry_counter == 1, + "Song2 entry not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().get_state<player_::Playing::Song1&>().exit_counter == 1, + "Song1 exit not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().start_next_song_counter == 0, + "submachine action not called correctly"); + + p.process_event(NextSong()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().current_state()[0] == 2,"Song3 should be active"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().get_state<player_::Playing::Song3&>().entry_counter == 1, + "Song3 entry not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().get_state<player_::Playing::Song2&>().exit_counter == 1, + "Song2 exit not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().start_next_song_counter == 1, + "submachine action not called correctly"); + + p.process_event(PreviousSong()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().current_state()[0] == 1,"Song2 should be active"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().get_state<player_::Playing::Song2&>().entry_counter == 2, + "Song2 entry not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().get_state<player_::Playing::Song3&>().exit_counter == 1, + "Song3 exit not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().start_prev_song_guard_counter == 1, + "submachine guard not called correctly"); + //flags + BOOST_CHECK_MESSAGE(p.is_flag_active<PlayingPaused>() == true,"PlayingPaused should be active"); + BOOST_CHECK_MESSAGE(p.is_flag_active<FirstSongPlaying>() == false,"FirstSongPlaying should not be active"); + + p.process_event(pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Paused should be active"); //Paused + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().exit_counter == 1,"Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().entry_counter == 1,"Paused entry not called correctly"); + //flags + BOOST_CHECK_MESSAGE(p.is_flag_active<PlayingPaused>() == true,"PlayingPaused should be active"); + + // go back to Playing + p.process_event(end_pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().exit_counter == 1,"Paused exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().entry_counter == 2,"Playing entry not called correctly"); + + p.process_event(pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Paused should be active"); //Paused + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().exit_counter == 2,"Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().entry_counter == 2,"Paused entry not called correctly"); + + p.process_event(stop()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().exit_counter == 2,"Paused exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 2,"Stopped entry not called correctly"); + //flags + BOOST_CHECK_MESSAGE(p.is_flag_active<PlayingPaused>() == false,"PlayingPaused should not be active"); + BOOST_CHECK_MESSAGE(p.is_flag_active<CDLoaded>() == true,"CDLoaded should be active"); + //BOOST_CHECK_MESSAGE(p.is_flag_active<CDLoaded,player::Flag_AND>() == false,"CDLoaded with AND should not be active"); + + p.process_event(stop()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().exit_counter == 2,"Stopped exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 3,"Stopped entry not called correctly"); + + //test interrupt + BOOST_CHECK_MESSAGE(p.current_state()[1] == 5,"AllOk should be active"); //AllOk + p.process_event(error_found()); + BOOST_CHECK_MESSAGE(p.current_state()[1] == 6,"ErrorMode should be active"); //ErrorMode + BOOST_CHECK_MESSAGE(p.get_state<player_::AllOk&>().exit_counter == 1,"AllOk exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::ErrorMode&>().entry_counter == 1,"ErrorMode entry not called correctly"); + + // try generating more events + p.process_event(play()); + BOOST_CHECK_MESSAGE(p.current_state()[1] == 6,"ErrorMode should be active"); //ErrorMode + BOOST_CHECK_MESSAGE(p.get_state<player_::AllOk&>().exit_counter == 1,"AllOk exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::ErrorMode&>().entry_counter == 1,"ErrorMode entry not called correctly"); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().exit_counter == 2,"Stopped exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 3,"Stopped entry not called correctly"); + + p.process_event(end_error()); + BOOST_CHECK_MESSAGE(p.current_state()[1] == 5,"AllOk should be active"); //AllOk + BOOST_CHECK_MESSAGE(p.get_state<player_::ErrorMode&>().exit_counter == 1,"ErrorMode exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::AllOk&>().entry_counter == 2,"AllOk entry not called correctly"); + + p.process_event(play()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().exit_counter == 3,"Stopped exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().entry_counter == 3,"Playing entry not called correctly"); + + //test terminate + BOOST_CHECK_MESSAGE(p.current_state()[1] == 5,"AllOk should be active"); //AllOk + p.process_event(do_terminate()); + BOOST_CHECK_MESSAGE(p.current_state()[1] == 7,"ErrorTerminate should be active"); //ErrorTerminate + BOOST_CHECK_MESSAGE(p.get_state<player_::AllOk&>().exit_counter == 2,"AllOk exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::ErrorTerminate&>().entry_counter == 1,"ErrorTerminate entry not called correctly"); + + // try generating more events + p.process_event(stop()); + BOOST_CHECK_MESSAGE(p.current_state()[1] == 7,"ErrorTerminate should be active"); //ErrorTerminate + BOOST_CHECK_MESSAGE(p.get_state<player_::ErrorTerminate&>().exit_counter == 0,"ErrorTerminate exit not called correctly"); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().exit_counter == 2,"Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 3,"Stopped entry not called correctly"); + + p.process_event(end_error()); + BOOST_CHECK_MESSAGE(p.current_state()[1] == 7,"ErrorTerminate should be active"); //ErrorTerminate + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().exit_counter == 2,"Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 3,"Stopped entry not called correctly"); + + p.process_event(stop()); + BOOST_CHECK_MESSAGE(p.current_state()[1] == 7,"ErrorTerminate should be active"); //ErrorTerminate + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().exit_counter == 2,"Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 3,"Stopped entry not called correctly"); + + } +} + diff --git a/libs/msm/test/OrthogonalDeferred2.cpp b/libs/msm/test/OrthogonalDeferred2.cpp new file mode 100644 index 0000000000..8486b3ca54 --- /dev/null +++ b/libs/msm/test/OrthogonalDeferred2.cpp @@ -0,0 +1,493 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> +#include <boost/msm/front/functor_row.hpp> + +#define BOOST_TEST_MODULE MyTest +#include <boost/test/unit_test.hpp> + +namespace msm = boost::msm; +namespace mpl = boost::mpl; +using namespace boost::msm::front; + +namespace +{ + // events + struct play {}; + struct end_pause {}; + struct stop {}; + struct pause {}; + struct open_close {}; + struct NextSong {}; + struct PreviousSong {}; + struct error_found {}; + struct end_error {}; + struct do_terminate {}; + + // Flags. Allow information about a property of the current state + struct PlayingPaused{}; + struct CDLoaded {}; + struct FirstSongPlaying {}; + + // A "complicated" event type that carries some data. + struct cd_detected + { + cd_detected(std::string name) + : name(name) + {} + + std::string name; + }; + + // front-end: define the FSM structure + struct player_ : public msm::front::state_machine_def<player_> + { + // we want deferred events and no state requires deferred events (only the fsm in the + // transition table), so the fsm does. + typedef int activate_deferred_events; + + unsigned int start_playback_counter; + unsigned int can_close_drawer_counter; + unsigned int report_error_counter; + unsigned int report_end_error_counter; + + player_(): + start_playback_counter(0), + can_close_drawer_counter(0), + report_error_counter(0), + report_end_error_counter(0) + {} + // The list of FSM states + struct Empty : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct Open : public msm::front::state<> + { + typedef mpl::vector1<CDLoaded> flag_list; + + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + struct Stopped : public msm::front::state<> + { + typedef mpl::vector1<CDLoaded> flag_list; + + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + // the player state machine contains a state which is himself a state machine + // as you see, no need to declare it anywhere so Playing can be developed separately + // by another team in another module. For simplicity I just declare it inside player + struct Playing_ : public msm::front::state_machine_def<Playing_> + { + // when playing, the CD is loaded and we are in either pause or playing (duh) + typedef mpl::vector2<PlayingPaused,CDLoaded> flag_list; + + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + unsigned int start_next_song_counter; + unsigned int start_prev_song_guard_counter; + + Playing_(): + start_next_song_counter(0), + start_prev_song_guard_counter(0) + {} + + // The list of FSM states + struct Song1 : public msm::front::state<> + { + typedef mpl::vector1<FirstSongPlaying> flag_list; + + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct Song2 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct Song3 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + // the initial state. Must be defined + typedef Song1 initial_state; + // transition actions + void start_next_song(NextSong const&) {++start_next_song_counter; } + void start_prev_song(PreviousSong const&) { } + // guard conditions + bool start_prev_song_guard(PreviousSong const&) {++start_prev_song_guard_counter;return true; } + + typedef Playing_ pl; // makes transition table cleaner + // Transition table for Playing + struct transition_table : mpl::vector4< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + _row < Song1 , NextSong , Song2 >, + row < Song2 , PreviousSong, Song1 , &pl::start_prev_song,&pl::start_prev_song_guard>, + a_row < Song2 , NextSong , Song3 , &pl::start_next_song >, + g_row < Song3 , PreviousSong, Song2 ,&pl::start_prev_song_guard> + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const&, FSM&,int) + { + BOOST_FAIL("no_transition called!"); + } + }; + // back-end + typedef msm::back::state_machine<Playing_> Playing; + + // state not defining any entry or exit + struct Paused : public msm::front::state<> + { + typedef mpl::vector2<PlayingPaused,CDLoaded> flag_list; + + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct AllOk : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + // this state is also made terminal so that all the events are blocked + struct ErrorMode : //public msm::front::terminate_state<> // ErrorMode terminates the state machine + public msm::front::interrupt_state<end_error> // ErroMode just interrupts. Will resume if + // the event end_error is generated + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct ErrorTerminate : public msm::front::terminate_state<> // terminates the state machine + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + // the initial state of the player SM. Must be defined + typedef mpl::vector<Empty,AllOk> initial_state; + + // transition actions + void start_playback(play const&) {++start_playback_counter; } + void open_drawer(open_close const&) { } + void store_cd_info(cd_detected const&) { } + void stop_playback(stop const&) { } + void pause_playback(pause const&) { } + void resume_playback(end_pause const&) { } + void stop_and_open(open_close const&) { } + void stopped_again(stop const&){} + void report_error(error_found const&) {++report_error_counter;} + void report_end_error(end_error const&) {++report_end_error_counter;} + + //guards + bool can_close_drawer(open_close const&) + { + ++can_close_drawer_counter; + return true; + } + typedef player_ p; // makes transition table cleaner + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Stopped , play , Playing , &p::start_playback >, + a_row < Stopped , open_close , Open , &p::open_drawer >, + _row < Stopped , stop , Stopped >, + // +---------+-------------+---------+---------------------+----------------------+ + g_row < Open , open_close , Empty , &p::can_close_drawer >, + Row < Open , play , none , Defer , none >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Empty , open_close , Open , &p::open_drawer >, + a_row < Empty , cd_detected , Stopped , &p::store_cd_info >, + Row < Empty , play , none , Defer , none >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Playing , stop , Stopped , &p::stop_playback >, + a_row < Playing , pause , Paused , &p::pause_playback >, + a_row < Playing , open_close , Open , &p::stop_and_open >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Paused , end_pause , Playing , &p::resume_playback >, + a_row < Paused , stop , Stopped , &p::stop_playback >, + a_row < Paused , open_close , Open , &p::stop_and_open >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < AllOk , error_found ,ErrorMode, &p::report_error >, + a_row <ErrorMode, end_error ,AllOk , &p::report_end_error >, + _row < AllOk , do_terminate,ErrorTerminate > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + BOOST_FAIL("no_transition called!"); + } + // init counters + template <class Event,class FSM> + void on_entry(Event const&,FSM& fsm) + { + fsm.template get_state<player_::Stopped&>().entry_counter=0; + fsm.template get_state<player_::Stopped&>().exit_counter=0; + fsm.template get_state<player_::Open&>().entry_counter=0; + fsm.template get_state<player_::Open&>().exit_counter=0; + fsm.template get_state<player_::Empty&>().entry_counter=0; + fsm.template get_state<player_::Empty&>().exit_counter=0; + fsm.template get_state<player_::Playing&>().entry_counter=0; + fsm.template get_state<player_::Playing&>().exit_counter=0; + fsm.template get_state<player_::Playing&>().template get_state<player_::Playing::Song1&>().entry_counter=0; + fsm.template get_state<player_::Playing&>().template get_state<player_::Playing::Song1&>().exit_counter=0; + fsm.template get_state<player_::Playing&>().template get_state<player_::Playing::Song2&>().entry_counter=0; + fsm.template get_state<player_::Playing&>().template get_state<player_::Playing::Song2&>().exit_counter=0; + fsm.template get_state<player_::Playing&>().template get_state<player_::Playing::Song3&>().entry_counter=0; + fsm.template get_state<player_::Playing&>().template get_state<player_::Playing::Song3&>().exit_counter=0; + fsm.template get_state<player_::Paused&>().entry_counter=0; + fsm.template get_state<player_::Paused&>().exit_counter=0; + fsm.template get_state<player_::AllOk&>().entry_counter=0; + fsm.template get_state<player_::AllOk&>().exit_counter=0; + fsm.template get_state<player_::ErrorMode&>().entry_counter=0; + fsm.template get_state<player_::ErrorMode&>().exit_counter=0; + fsm.template get_state<player_::ErrorTerminate&>().entry_counter=0; + fsm.template get_state<player_::ErrorTerminate&>().exit_counter=0; + } + }; + // Pick a back-end + typedef msm::back::state_machine<player_> player; + + //static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused","AllOk","ErrorMode" }; + + BOOST_AUTO_TEST_CASE( my_test ) + { + player p; + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + p.start(); + // test deferred event + // deferred in Empty and Open, will be handled only after event cd_detected + p.process_event(play()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 2,"Empty should be active"); //Empty + BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().exit_counter == 0,"Open exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().entry_counter == 0,"Playing entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().entry_counter == 1,"Empty entry not called correctly"); + //flags + BOOST_CHECK_MESSAGE(p.is_flag_active<CDLoaded>() == false,"CDLoaded should not be active"); + + p.process_event(open_close()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 1,"Open should be active"); //Open + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().exit_counter == 1,"Empty exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().entry_counter == 1,"Open entry not called correctly"); + + p.process_event(open_close()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 2,"Empty should be active"); //Empty + BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().exit_counter == 1,"Open exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().entry_counter == 2,"Empty entry not called correctly"); + BOOST_CHECK_MESSAGE(p.can_close_drawer_counter == 1,"guard not called correctly"); + + //deferred event should have been processed + p.process_event(cd_detected("louie, louie")); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().exit_counter == 2,"Empty exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 1,"Stopped entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().exit_counter == 1,"Stopped exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().entry_counter == 1,"Playing entry not called correctly"); + BOOST_CHECK_MESSAGE(p.start_playback_counter == 1,"action not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().current_state()[0] == 0,"Song1 should be active"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().get_state<player_::Playing::Song1&>().entry_counter == 1, + "Song1 entry not called correctly"); + + //flags + BOOST_CHECK_MESSAGE(p.is_flag_active<PlayingPaused>() == true,"PlayingPaused should be active"); + BOOST_CHECK_MESSAGE(p.is_flag_active<FirstSongPlaying>() == true,"FirstSongPlaying should be active"); + + + p.process_event(NextSong()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().current_state()[0] == 1,"Song2 should be active"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().get_state<player_::Playing::Song2&>().entry_counter == 1, + "Song2 entry not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().get_state<player_::Playing::Song1&>().exit_counter == 1, + "Song1 exit not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().start_next_song_counter == 0, + "submachine action not called correctly"); + + p.process_event(NextSong()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().current_state()[0] == 2,"Song3 should be active"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().get_state<player_::Playing::Song3&>().entry_counter == 1, + "Song3 entry not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().get_state<player_::Playing::Song2&>().exit_counter == 1, + "Song2 exit not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().start_next_song_counter == 1, + "submachine action not called correctly"); + + p.process_event(PreviousSong()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().current_state()[0] == 1,"Song2 should be active"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().get_state<player_::Playing::Song2&>().entry_counter == 2, + "Song2 entry not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().get_state<player_::Playing::Song3&>().exit_counter == 1, + "Song3 exit not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().start_prev_song_guard_counter == 1, + "submachine guard not called correctly"); + //flags + BOOST_CHECK_MESSAGE(p.is_flag_active<PlayingPaused>() == true,"PlayingPaused should be active"); + BOOST_CHECK_MESSAGE(p.is_flag_active<FirstSongPlaying>() == false,"FirstSongPlaying should not be active"); + + p.process_event(pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Paused should be active"); //Paused + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().exit_counter == 1,"Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().entry_counter == 1,"Paused entry not called correctly"); + //flags + BOOST_CHECK_MESSAGE(p.is_flag_active<PlayingPaused>() == true,"PlayingPaused should be active"); + + // go back to Playing + p.process_event(end_pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().exit_counter == 1,"Paused exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().entry_counter == 2,"Playing entry not called correctly"); + + p.process_event(pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Paused should be active"); //Paused + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().exit_counter == 2,"Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().entry_counter == 2,"Paused entry not called correctly"); + + p.process_event(stop()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().exit_counter == 2,"Paused exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 2,"Stopped entry not called correctly"); + //flags + BOOST_CHECK_MESSAGE(p.is_flag_active<PlayingPaused>() == false,"PlayingPaused should not be active"); + BOOST_CHECK_MESSAGE(p.is_flag_active<CDLoaded>() == true,"CDLoaded should be active"); + //BOOST_CHECK_MESSAGE(p.is_flag_active<CDLoaded,player::Flag_AND>() == false,"CDLoaded with AND should not be active"); + + p.process_event(stop()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().exit_counter == 2,"Stopped exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 3,"Stopped entry not called correctly"); + + //test interrupt + BOOST_CHECK_MESSAGE(p.current_state()[1] == 5,"AllOk should be active"); //AllOk + p.process_event(error_found()); + BOOST_CHECK_MESSAGE(p.current_state()[1] == 6,"ErrorMode should be active"); //ErrorMode + BOOST_CHECK_MESSAGE(p.get_state<player_::AllOk&>().exit_counter == 1,"AllOk exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::ErrorMode&>().entry_counter == 1,"ErrorMode entry not called correctly"); + + // try generating more events + p.process_event(play()); + BOOST_CHECK_MESSAGE(p.current_state()[1] == 6,"ErrorMode should be active"); //ErrorMode + BOOST_CHECK_MESSAGE(p.get_state<player_::AllOk&>().exit_counter == 1,"AllOk exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::ErrorMode&>().entry_counter == 1,"ErrorMode entry not called correctly"); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().exit_counter == 2,"Stopped exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 3,"Stopped entry not called correctly"); + + p.process_event(end_error()); + BOOST_CHECK_MESSAGE(p.current_state()[1] == 5,"AllOk should be active"); //AllOk + BOOST_CHECK_MESSAGE(p.get_state<player_::ErrorMode&>().exit_counter == 1,"ErrorMode exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::AllOk&>().entry_counter == 2,"AllOk entry not called correctly"); + + p.process_event(play()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().exit_counter == 3,"Stopped exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().entry_counter == 3,"Playing entry not called correctly"); + + //test terminate + BOOST_CHECK_MESSAGE(p.current_state()[1] == 5,"AllOk should be active"); //AllOk + p.process_event(do_terminate()); + BOOST_CHECK_MESSAGE(p.current_state()[1] == 7,"ErrorTerminate should be active"); //ErrorTerminate + BOOST_CHECK_MESSAGE(p.get_state<player_::AllOk&>().exit_counter == 2,"AllOk exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::ErrorTerminate&>().entry_counter == 1,"ErrorTerminate entry not called correctly"); + + // try generating more events + p.process_event(stop()); + BOOST_CHECK_MESSAGE(p.current_state()[1] == 7,"ErrorTerminate should be active"); //ErrorTerminate + BOOST_CHECK_MESSAGE(p.get_state<player_::ErrorTerminate&>().exit_counter == 0,"ErrorTerminate exit not called correctly"); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().exit_counter == 2,"Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 3,"Stopped entry not called correctly"); + + p.process_event(end_error()); + BOOST_CHECK_MESSAGE(p.current_state()[1] == 7,"ErrorTerminate should be active"); //ErrorTerminate + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().exit_counter == 2,"Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 3,"Stopped entry not called correctly"); + + p.process_event(stop()); + BOOST_CHECK_MESSAGE(p.current_state()[1] == 7,"ErrorTerminate should be active"); //ErrorTerminate + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().exit_counter == 2,"Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 3,"Stopped entry not called correctly"); + + } +} + diff --git a/libs/msm/test/OrthogonalDeferredEuml.cpp b/libs/msm/test/OrthogonalDeferredEuml.cpp new file mode 100644 index 0000000000..63f40737be --- /dev/null +++ b/libs/msm/test/OrthogonalDeferredEuml.cpp @@ -0,0 +1,358 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> +#include <boost/msm/front/euml/euml.hpp> + +#define BOOST_TEST_MODULE MyTest +#include <boost/test/unit_test.hpp> + +namespace msm = boost::msm; +namespace mpl = boost::mpl; +using namespace boost::msm::front::euml; + +namespace +{ + // events + BOOST_MSM_EUML_EVENT(play) + BOOST_MSM_EUML_EVENT(end_pause) + BOOST_MSM_EUML_EVENT(stop) + BOOST_MSM_EUML_EVENT(pause) + BOOST_MSM_EUML_EVENT(open_close) + BOOST_MSM_EUML_EVENT(next_song) + BOOST_MSM_EUML_EVENT(previous_song) + BOOST_MSM_EUML_EVENT(error_found) + BOOST_MSM_EUML_EVENT(end_error) + BOOST_MSM_EUML_EVENT(do_terminate) + + // Flags. Allow information about a property of the current state + BOOST_MSM_EUML_FLAG(PlayingPaused) + BOOST_MSM_EUML_FLAG(CDLoaded) + BOOST_MSM_EUML_FLAG(FirstSongPlaying) + + // A "complicated" event type that carries some data. + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(std::string,cd_name) + BOOST_MSM_EUML_ATTRIBUTES((attributes_ << cd_name ), cd_detected_attributes) + BOOST_MSM_EUML_EVENT_WITH_ATTRIBUTES(cd_detected,cd_detected_attributes) + + //states + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,entry_counter) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,exit_counter) + + BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter), + attributes_ << entry_counter << exit_counter, configure_ << play),Empty) + BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter), + attributes_ << entry_counter << exit_counter, configure_<< CDLoaded << play),Open) + BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter), + attributes_ << entry_counter << exit_counter, configure_<< CDLoaded),Stopped) + BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter), + attributes_ << entry_counter << exit_counter, configure_<< CDLoaded << PlayingPaused),Paused) + + BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),AllOk) + BOOST_MSM_EUML_TERMINATE_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter), + ErrorTerminate) + BOOST_MSM_EUML_INTERRUPT_STATE(( end_error,++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter), + ErrorMode) + + // Playing is now a state machine itself. + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,start_next_song_counter) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,start_prev_song_guard_counter) + + BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter), + attributes_ << entry_counter << exit_counter, configure_<< FirstSongPlaying ),Song1) + BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Song2) + BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Song3) + + // Playing has a transition table + BOOST_MSM_EUML_TRANSITION_TABLE(( + // +------------------------------------------------------------------------------+ + Song2 == Song1 + next_song , + Song1 == Song2 + previous_song [True_()] / ++fsm_(start_prev_song_guard_counter) , + Song3 == Song2 + next_song / ++fsm_(start_next_song_counter) , + Song2 == Song3 + previous_song [True_()] / ++fsm_(start_prev_song_guard_counter) + // +------------------------------------------------------------------------------+ + ),playing_transition_table ) + + BOOST_MSM_EUML_DECLARE_STATE_MACHINE( (playing_transition_table, //STT + init_ << Song1, // Init State + ++state_(entry_counter), // Entry + ++state_(exit_counter), // Exit + attributes_ << entry_counter << exit_counter + << start_next_song_counter + << start_prev_song_guard_counter, // Attributes + configure_<< PlayingPaused << CDLoaded //flags + ),Playing_) + + // back-end + typedef msm::back::state_machine<Playing_> Playing_type; + Playing_type const Playing; + + //fsm + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,start_playback_counter) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,can_close_drawer_counter) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,report_error_counter) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,report_end_error_counter) + BOOST_MSM_EUML_ACTION(No_Transition) + { + template <class FSM,class Event> + void operator()(Event const&,FSM&,int) + { + BOOST_FAIL("no_transition called!"); + } + }; + + BOOST_MSM_EUML_TRANSITION_TABLE(( + Playing == Stopped + play / ++fsm_(start_playback_counter), + Playing == Paused + end_pause , + // +------------------------------------------------------------------------------+ + Empty == Open + open_close / ++fsm_(can_close_drawer_counter), + // +------------------------------------------------------------------------------+ + Open == Empty + open_close , + Open == Paused + open_close , + Open == Stopped + open_close , + Open == Playing + open_close , + // +------------------------------------------------------------------------------+ + Paused == Playing + pause , + // +------------------------------------------------------------------------------+ + Stopped == Playing + stop , + Stopped == Paused + stop , + Stopped == Empty + cd_detected , + Stopped == Stopped + stop , + ErrorMode == AllOk + error_found / ++fsm_(report_error_counter), + AllOk == ErrorMode+ end_error / ++fsm_(report_end_error_counter), + ErrorTerminate== AllOk +do_terminate + // +------------------------------------------------------------------------------+ + ),transition_table) + + BOOST_MSM_EUML_DECLARE_STATE_MACHINE(( transition_table, //STT + init_ << Empty << AllOk, // Init State + no_action, // Entry + no_action, // Exit + attributes_ << start_playback_counter << can_close_drawer_counter + << report_error_counter << report_end_error_counter, // Attributes + configure_ << no_configure_, // configuration + No_Transition // no_transition handler + ), + player_) //fsm name + + // Pick a back-end + typedef msm::back::state_machine<player_> player; + + //static char const* const state_names[] = { "Stopped", "Paused","Open", "Empty", "Playing" ,"AllOk","ErrorMode","ErrorTerminate" }; + + BOOST_AUTO_TEST_CASE( my_test ) + { + player p; + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + p.start(); + // test deferred event + // deferred in Empty and Open, will be handled only after event cd_detected + p.process_event(play); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Empty should be active"); //Empty + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Open)&>().get_attribute(exit_counter) == 0, + "Open exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<Playing_type&>().get_attribute(entry_counter) == 0,"Playing entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Empty)&>().get_attribute(entry_counter) == 1, + "Empty entry not called correctly"); + //flags + BOOST_CHECK_MESSAGE(p.is_flag_active<BOOST_MSM_EUML_FLAG_NAME(CDLoaded)>() == false,"CDLoaded should not be active"); + + p.process_event(open_close); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 2,"Open should be active"); //Open + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Empty)&>().get_attribute(exit_counter) == 1, + "Empty exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Open)&>().get_attribute(entry_counter) == 1, + "Open entry not called correctly"); + + p.process_event(open_close); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Empty should be active"); //Empty + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Open)&>().get_attribute(exit_counter) == 1, + "Open exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Empty)&>().get_attribute(entry_counter) == 2, + "Empty entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_attribute(can_close_drawer_counter) == 1,"guard not called correctly"); + + //deferred event should have been processed + p.process_event(cd_detected("louie, louie")); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Empty)&>().get_attribute(exit_counter) == 2, + "Empty exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(entry_counter) == 1, + "Stopped entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(exit_counter) == 1, + "Stopped exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<Playing_type&>().get_attribute(entry_counter) == 1,"Playing entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_attribute(start_playback_counter) == 1,"action not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<Playing_type&>().current_state()[0] == 0,"Song1 should be active"); + BOOST_CHECK_MESSAGE( + p.get_state<Playing_type&>().get_state<BOOST_MSM_EUML_STATE_NAME(Song1)&>().get_attribute(entry_counter) == 1, + "Song1 entry not called correctly"); + + //flags + BOOST_CHECK_MESSAGE(p.is_flag_active<BOOST_MSM_EUML_FLAG_NAME(PlayingPaused)>() == true,"PlayingPaused should be active"); + BOOST_CHECK_MESSAGE(p.is_flag_active<BOOST_MSM_EUML_FLAG_NAME(FirstSongPlaying)>() == true,"FirstSongPlaying should be active"); + + + p.process_event(next_song); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<Playing_type&>().current_state()[0] == 1,"Song2 should be active"); + BOOST_CHECK_MESSAGE( + p.get_state<Playing_type&>().get_state<BOOST_MSM_EUML_STATE_NAME(Song2)&>().get_attribute(entry_counter) == 1, + "Song2 entry not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<Playing_type&>().get_state<BOOST_MSM_EUML_STATE_NAME(Song1)&>().get_attribute(exit_counter) == 1, + "Song1 exit not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<Playing_type&>().get_attribute(start_next_song_counter) == 0, + "submachine action not called correctly"); + + p.process_event(next_song); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<Playing_type&>().current_state()[0] == 2,"Song3 should be active"); + BOOST_CHECK_MESSAGE( + p.get_state<Playing_type&>().get_state<BOOST_MSM_EUML_STATE_NAME(Song3)&>().get_attribute(entry_counter) == 1, + "Song3 entry not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<Playing_type&>().get_state<BOOST_MSM_EUML_STATE_NAME(Song2)&>().get_attribute(exit_counter) == 1, + "Song2 exit not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<Playing_type&>().get_attribute(start_next_song_counter) == 1, + "submachine action not called correctly"); + + p.process_event(previous_song); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<Playing_type&>().current_state()[0] == 1,"Song2 should be active"); + BOOST_CHECK_MESSAGE( + p.get_state<Playing_type&>().get_state<BOOST_MSM_EUML_STATE_NAME(Song2)&>().get_attribute(entry_counter) == 2, + "Song2 entry not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<Playing_type&>().get_state<BOOST_MSM_EUML_STATE_NAME(Song3)&>().get_attribute(exit_counter) == 1, + "Song3 exit not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<Playing_type&>().get_attribute(start_prev_song_guard_counter) == 1, + "submachine guard not called correctly"); + //flags + BOOST_CHECK_MESSAGE(p.is_flag_active<BOOST_MSM_EUML_FLAG_NAME(PlayingPaused)>() == true,"PlayingPaused should be active"); + BOOST_CHECK_MESSAGE(p.is_flag_active<BOOST_MSM_EUML_FLAG_NAME(FirstSongPlaying)>() == false,"FirstSongPlaying should not be active"); + + p.process_event(pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 1,"Paused should be active"); //Paused + BOOST_CHECK_MESSAGE(p.get_state<Playing_type&>().get_attribute(exit_counter) == 1,"Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Paused)&>().get_attribute(entry_counter) == 1, + "Paused entry not called correctly"); + //flags + BOOST_CHECK_MESSAGE(p.is_flag_active<BOOST_MSM_EUML_FLAG_NAME(PlayingPaused)>() == true,"PlayingPaused should be active"); + + // go back to Playing + p.process_event(end_pause); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Paused)&>().get_attribute(exit_counter) == 1, + "Paused exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<Playing_type&>().get_attribute(entry_counter) == 2,"Playing entry not called correctly"); + + p.process_event(pause); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 1,"Paused should be active"); //Paused + BOOST_CHECK_MESSAGE(p.get_state<Playing_type&>().get_attribute(exit_counter) == 2,"Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Paused)&>().get_attribute(entry_counter) == 2, + "Paused entry not called correctly"); + + p.process_event(stop); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Paused)&>().get_attribute(exit_counter) == 2, + "Paused exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(entry_counter) == 2, + "Stopped entry not called correctly"); + //flags + BOOST_CHECK_MESSAGE(p.is_flag_active<BOOST_MSM_EUML_FLAG_NAME(PlayingPaused)>() == false,"PlayingPaused should not be active"); + BOOST_CHECK_MESSAGE(p.is_flag_active<BOOST_MSM_EUML_FLAG_NAME(CDLoaded)>() == true,"CDLoaded should be active"); + //BOOST_CHECK_MESSAGE(p.is_flag_active<BOOST_MSM_EUML_FLAG_NAME(CDLoaded),player::Flag_AND>() == false,"CDLoaded with AND should not be active"); + + p.process_event(stop); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(exit_counter) == 2, + "Stopped exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(entry_counter) == 3, + "Stopped entry not called correctly"); + + //test interrupt + BOOST_CHECK_MESSAGE(p.current_state()[1] == 5,"AllOk should be active"); //AllOk + p.process_event(error_found); + BOOST_CHECK_MESSAGE(p.current_state()[1] == 6,"ErrorMode should be active"); //ErrorMode + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(AllOk)&>().get_attribute(exit_counter) == 1, + "AllOk exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(ErrorMode)&>().get_attribute(entry_counter) == 1, + "ErrorMode entry not called correctly"); + + // try generating more events + p.process_event(play); + BOOST_CHECK_MESSAGE(p.current_state()[1] == 6,"ErrorMode should be active"); //ErrorMode + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(AllOk)&>().get_attribute(exit_counter) == 1, + "AllOk exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(ErrorMode)&>().get_attribute(entry_counter) == 1, + "ErrorMode entry not called correctly"); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(exit_counter) == 2, + "Stopped exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(entry_counter) == 3, + "Stopped entry not called correctly"); + + p.process_event(end_error); + BOOST_CHECK_MESSAGE(p.current_state()[1] == 5,"AllOk should be active"); //AllOk + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(ErrorMode)&>().get_attribute(exit_counter) == 1, + "ErrorMode exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(AllOk)&>().get_attribute(entry_counter) == 2, + "AllOk entry not called correctly"); + + p.process_event(play); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(exit_counter) == 3, + "Stopped exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<Playing_type&>().get_attribute(entry_counter) == 3,"Playing entry not called correctly"); + + //test terminate + BOOST_CHECK_MESSAGE(p.current_state()[1] == 5,"AllOk should be active"); //AllOk + p.process_event(do_terminate); + BOOST_CHECK_MESSAGE(p.current_state()[1] == 7,"ErrorTerminate should be active"); //ErrorTerminate + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(AllOk)&>().get_attribute(exit_counter) == 2, + "AllOk exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(ErrorTerminate)&>().get_attribute(entry_counter) == 1, + "ErrorTerminate entry not called correctly"); + + // try generating more events + p.process_event(stop); + BOOST_CHECK_MESSAGE(p.current_state()[1] == 7,"ErrorTerminate should be active"); //ErrorTerminate + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(ErrorTerminate)&>().get_attribute(exit_counter) == 0, + "ErrorTerminate exit not called correctly"); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<Playing_type&>().get_attribute(exit_counter) == 2,"Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(entry_counter) == 3, + "Stopped entry not called correctly"); + + p.process_event(end_error()); + BOOST_CHECK_MESSAGE(p.current_state()[1] == 7,"ErrorTerminate should be active"); //ErrorTerminate + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<Playing_type&>().get_attribute(exit_counter) == 2,"Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(entry_counter) == 3, + "Stopped entry not called correctly"); + + p.process_event(stop()); + BOOST_CHECK_MESSAGE(p.current_state()[1] == 7,"ErrorTerminate should be active"); //ErrorTerminate + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<Playing_type&>().get_attribute(exit_counter) == 2,"Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(entry_counter) == 3, + "Stopped entry not called correctly"); + + } +} + diff --git a/libs/msm/test/Serialize.cpp b/libs/msm/test/Serialize.cpp new file mode 100644 index 0000000000..3a1a636ca6 --- /dev/null +++ b/libs/msm/test/Serialize.cpp @@ -0,0 +1,309 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> +#define BOOST_TEST_MODULE MyTest +#include <boost/test/unit_test.hpp> +// include headers that implement a archive in simple text format +#include <boost/archive/text_oarchive.hpp> +#include <boost/archive/text_iarchive.hpp> +#include <boost/serialization/tracking.hpp> + +#include <fstream> + +namespace msm = boost::msm; +namespace mpl = boost::mpl; + +namespace +{ + // events + struct play {}; + struct end_pause {}; + struct stop {}; + struct pause {}; + struct open_close {}; + + // A "complicated" event type that carries some data. + enum DiskTypeEnum + { + DISK_CD=0, + DISK_DVD=1 + }; + struct cd_detected + { + cd_detected(std::string name, DiskTypeEnum diskType) + : name(name), + disc_type(diskType) + {} + + std::string name; + DiskTypeEnum disc_type; + }; + + // front-end: define the FSM structure + struct player_ : public msm::front::state_machine_def<player_> + { + unsigned int start_playback_counter; + unsigned int can_close_drawer_counter; + int front_end_data; + + player_(): + start_playback_counter(0), + can_close_drawer_counter(0), + front_end_data(4) + {} + + //we want to serialize some data contained by the front-end + // to achieve this, ask for it + typedef int do_serialize; + // and provide a serialize + template<class Archive> + void serialize(Archive & ar, const unsigned int ) + { + ar & front_end_data; + } + + // The list of FSM states + struct Empty : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + int some_dummy_data; + // we want Empty to be serialized + typedef int do_serialize; + template<class Archive> + void serialize(Archive & ar, const unsigned int ) + { + ar & some_dummy_data; + } + + }; + struct Open : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + // sm_ptr still supported but deprecated as functors are a much better way to do the same thing + struct Stopped : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + struct Playing : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + // state not defining any entry or exit + struct Paused : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + // the initial state of the player SM. Must be defined + typedef Empty initial_state; + + // transition actions + void start_playback(play const&) {++start_playback_counter; } + void open_drawer(open_close const&) { } + void store_cd_info(cd_detected const&) { } + void stop_playback(stop const&) { } + void pause_playback(pause const&) { } + void resume_playback(end_pause const&) { } + void stop_and_open(open_close const&) { } + void stopped_again(stop const&){} + // guard conditions + bool good_disk_format(cd_detected const& evt) + { + // to test a guard condition, let's say we understand only CDs, not DVD + if (evt.disc_type != DISK_CD) + { + return false; + } + return true; + } + bool can_close_drawer(open_close const&) + { + ++can_close_drawer_counter; + return true; + } + + typedef player_ p; // makes transition table cleaner + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Stopped , play , Playing , &p::start_playback >, + a_row < Stopped , open_close , Open , &p::open_drawer >, + _row < Stopped , stop , Stopped >, + // +---------+-------------+---------+---------------------+----------------------+ + g_row < Open , open_close , Empty , &p::can_close_drawer >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Empty , open_close , Open , &p::open_drawer >, + row < Empty , cd_detected , Stopped , &p::store_cd_info ,&p::good_disk_format >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Playing , stop , Stopped , &p::stop_playback >, + a_row < Playing , pause , Paused , &p::pause_playback >, + a_row < Playing , open_close , Open , &p::stop_and_open >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Paused , end_pause , Playing , &p::resume_playback >, + a_row < Paused , stop , Stopped , &p::stop_playback >, + a_row < Paused , open_close , Open , &p::stop_and_open > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& , FSM&,int) + { + BOOST_FAIL("no_transition called!"); + } + // init counters + template <class Event,class FSM> + void on_entry(Event const&,FSM& fsm) + { + fsm.template get_state<player_::Stopped&>().entry_counter=0; + fsm.template get_state<player_::Stopped&>().exit_counter=0; + fsm.template get_state<player_::Open&>().entry_counter=0; + fsm.template get_state<player_::Open&>().exit_counter=0; + fsm.template get_state<player_::Empty&>().entry_counter=0; + fsm.template get_state<player_::Empty&>().exit_counter=0; + fsm.template get_state<player_::Empty&>().some_dummy_data=3; + fsm.template get_state<player_::Playing&>().entry_counter=0; + fsm.template get_state<player_::Playing&>().exit_counter=0; + fsm.template get_state<player_::Paused&>().entry_counter=0; + fsm.template get_state<player_::Paused&>().exit_counter=0; + } + + }; + // Pick a back-end + typedef msm::back::state_machine<player_> player; + +// static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" }; + + + BOOST_AUTO_TEST_CASE( my_test ) + { + player p; + + p.start(); + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().entry_counter == 1,"Empty entry not called correctly"); + + p.process_event(open_close()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 1,"Open should be active"); //Open + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().exit_counter == 1,"Empty exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().entry_counter == 1,"Open entry not called correctly"); + + // test the serialization + std::ofstream ofs("fsm.txt"); + // save fsm to archive (current state is Open) + { + boost::archive::text_oarchive oa(ofs); + // write class instance to archive + oa << p; + } + // reload fsm in state Open + player p2; + { + // create and open an archive for input + std::ifstream ifs("fsm.txt"); + boost::archive::text_iarchive ia(ifs); + // read class state from archive + ia >> p2; + } + // we now use p2 as it was loaded + // check that we kept Empty's data value + BOOST_CHECK_MESSAGE(p2.get_state<player_::Empty&>().some_dummy_data == 3,"Empty not deserialized correctly"); + BOOST_CHECK_MESSAGE(p2.front_end_data == 4,"Front-end not deserialized correctly"); + + p.process_event(open_close()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 2,"Empty should be active"); //Empty + BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().exit_counter == 1,"Open exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().entry_counter == 2,"Empty entry not called correctly"); + BOOST_CHECK_MESSAGE(p.can_close_drawer_counter == 1,"guard not called correctly"); + + p.process_event( + cd_detected("louie, louie",DISK_DVD)); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 2,"Empty should be active"); //Empty + BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().exit_counter == 1,"Open exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().entry_counter == 2,"Empty entry not called correctly"); + + p.process_event( + cd_detected("louie, louie",DISK_CD)); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().exit_counter == 2,"Empty exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 1,"Stopped entry not called correctly"); + + p.process_event(play()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().exit_counter == 1,"Stopped exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().entry_counter == 1,"Playing entry not called correctly"); + BOOST_CHECK_MESSAGE(p.start_playback_counter == 1,"action not called correctly"); + + p.process_event(pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Paused should be active"); //Paused + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().exit_counter == 1,"Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().entry_counter == 1,"Paused entry not called correctly"); + + // go back to Playing + p.process_event(end_pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().exit_counter == 1,"Paused exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().entry_counter == 2,"Playing entry not called correctly"); + + p.process_event(pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Paused should be active"); //Paused + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().exit_counter == 2,"Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().entry_counter == 2,"Paused entry not called correctly"); + + p.process_event(stop()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().exit_counter == 2,"Paused exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 2,"Stopped entry not called correctly"); + + p.process_event(stop()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().exit_counter == 2,"Stopped exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 3,"Stopped entry not called correctly"); + } +} +// eliminate object tracking (even if serialized through a pointer) +// at the risk of a programming error creating duplicate objects. +// this is to get rid of warning because p is not const +BOOST_CLASS_TRACKING(player, boost::serialization::track_never) + diff --git a/libs/msm/test/SerializeSimpleEuml.cpp b/libs/msm/test/SerializeSimpleEuml.cpp new file mode 100644 index 0000000000..d605f7bc82 --- /dev/null +++ b/libs/msm/test/SerializeSimpleEuml.cpp @@ -0,0 +1,214 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <iostream> +#include <boost/msm/back/state_machine.hpp> +#include <boost/msm/front/euml/euml.hpp> + +#define BOOST_TEST_MODULE MyTest +#include <boost/test/unit_test.hpp> +// include headers that implement a archive in simple text format +#include <boost/archive/text_oarchive.hpp> +#include <boost/archive/text_iarchive.hpp> +#include <boost/serialization/tracking.hpp> + +#include <fstream> + +using namespace std; +using namespace boost::msm::front::euml; +namespace msm = boost::msm; + +namespace +{ + // A "complicated" event type that carries some data. + enum DiskTypeEnum + { + DISK_CD=0, + DISK_DVD=1 + }; + // events + BOOST_MSM_EUML_EVENT(play) + BOOST_MSM_EUML_EVENT(end_pause) + BOOST_MSM_EUML_EVENT(stop) + BOOST_MSM_EUML_EVENT(pause) + BOOST_MSM_EUML_EVENT(open_close) + // A "complicated" event type that carries some data. + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(std::string,cd_name) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(DiskTypeEnum,cd_type) + BOOST_MSM_EUML_ATTRIBUTES((attributes_ << cd_name << cd_type ), cd_detected_attributes) + BOOST_MSM_EUML_EVENT_WITH_ATTRIBUTES(cd_detected,cd_detected_attributes) + + //states + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,entry_counter) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,exit_counter) + + BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Empty) + BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Open) + BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Stopped) + BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Playing) + BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Paused) + + //fsm + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,start_playback_counter) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,can_close_drawer_counter) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,test_fct_counter) + BOOST_MSM_EUML_ACTION(No_Transition) + { + template <class FSM,class Event> + void operator()(Event const&,FSM&,int) + { + BOOST_FAIL("no_transition called!"); + } + }; + BOOST_MSM_EUML_ACTION(good_disk_format) + { + template <class FSM,class EVT,class SourceState,class TargetState> + bool operator()(EVT const& evt,FSM&,SourceState& ,TargetState& ) + { + if (evt.get_attribute(cd_type)!=DISK_CD) + { + return false; + } + return true; + } + }; + BOOST_MSM_EUML_TRANSITION_TABLE(( + Playing == Stopped + play / (++fsm_(start_playback_counter),++fsm_(test_fct_counter) ), + Playing == Paused + end_pause , + // +------------------------------------------------------------------------------+ + Empty == Open + open_close / ++fsm_(can_close_drawer_counter), + // +------------------------------------------------------------------------------+ + Open == Empty + open_close , + Open == Paused + open_close , + Open == Stopped + open_close , + Open == Playing + open_close , + // +------------------------------------------------------------------------------+ + Paused == Playing + pause , + // +------------------------------------------------------------------------------+ + Stopped == Playing + stop , + Stopped == Paused + stop , + Stopped == Empty + cd_detected [good_disk_format || + (event_(cd_type)==Int_<DISK_CD>())] / process_(play) , + Stopped == Stopped + stop + // +------------------------------------------------------------------------------+ + ),transition_table) + + BOOST_MSM_EUML_DECLARE_STATE_MACHINE(( transition_table, //STT + init_ << Empty, // Init State + no_action, // Entry + no_action, // Exit + attributes_ << start_playback_counter + << can_close_drawer_counter << test_fct_counter, // Attributes + configure_ << no_configure_, // configuration + No_Transition // no_transition handler + ), + player_) //fsm name + + typedef msm::back::state_machine<player_> player; + +// static char const* const state_names[] = { "Stopped", "Paused", "Open", "Empty", "Playing" }; + + + BOOST_AUTO_TEST_CASE( my_test ) + { + player p2; + + p2.start(); + BOOST_CHECK_MESSAGE(p2.get_state<BOOST_MSM_EUML_STATE_NAME(Empty)&>().get_attribute(entry_counter) == 1, + "Empty entry not called correctly"); + + p2.process_event(open_close()); + BOOST_CHECK_MESSAGE(p2.current_state()[0] == 2,"Open should be active"); //Open + BOOST_CHECK_MESSAGE(p2.get_state<BOOST_MSM_EUML_STATE_NAME(Empty)&>().get_attribute(exit_counter) == 1, + "Empty exit not called correctly"); + BOOST_CHECK_MESSAGE(p2.get_state<BOOST_MSM_EUML_STATE_NAME(Open)&>().get_attribute(entry_counter) == 1, + "Open entry not called correctly"); + + // test the serialization + std::ofstream ofs("fsm.txt"); + // save fsm to archive (current state is Open) + { + boost::archive::text_oarchive oa(ofs); + // write class instance to archive + oa << p2; + } + // reload fsm in state Open + player p; + { + // create and open an archive for input + std::ifstream ifs("fsm.txt"); + boost::archive::text_iarchive ia(ifs); + // read class state from archive + ia >> p; + } + + p.process_event(open_close()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Empty should be active"); //Empty + + p.process_event( + cd_detected("louie, louie",DISK_DVD)); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Empty should be active"); //Empty + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Open)&>().get_attribute(exit_counter) == 1, + "Open exit not called correctly"); + + p.process_event( + cd_detected("louie, louie",DISK_CD)); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(entry_counter) == 1, + "Stopped entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(exit_counter) == 1, + "Stopped exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Playing)&>().get_attribute(entry_counter) == 1, + "Playing entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_attribute(start_playback_counter) == 1,"action not called correctly"); + BOOST_CHECK_MESSAGE(p.get_attribute(test_fct_counter) == 1,"action not called correctly"); + + p.process_event(pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 1,"Paused should be active"); //Paused + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Playing)&>().get_attribute(exit_counter) == 1, + "Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Paused)&>().get_attribute(entry_counter) == 1, + "Paused entry not called correctly"); + + // go back to Playing + p.process_event(end_pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Paused)&>().get_attribute(exit_counter) == 1, + "Paused exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Playing)&>().get_attribute(entry_counter) == 2, + "Playing entry not called correctly"); + + p.process_event(pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 1,"Paused should be active"); //Paused + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Playing)&>().get_attribute(exit_counter) == 2, + "Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Paused)&>().get_attribute(entry_counter) == 2, + "Paused entry not called correctly"); + + p.process_event(stop()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Paused)&>().get_attribute(exit_counter) == 2, + "Paused exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(entry_counter) == 2, + "Stopped entry not called correctly"); + + p.process_event(stop()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(exit_counter) == 2, + "Stopped exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(entry_counter) == 3, + "Stopped entry not called correctly"); + } +} + +// eliminate object tracking (even if serialized through a pointer) +// at the risk of a programming error creating duplicate objects. +// this is to get rid of warning because p is not const +BOOST_CLASS_TRACKING(player, boost::serialization::track_never) diff --git a/libs/msm/test/SerializeWithHistory.cpp b/libs/msm/test/SerializeWithHistory.cpp new file mode 100644 index 0000000000..743102ee9f --- /dev/null +++ b/libs/msm/test/SerializeWithHistory.cpp @@ -0,0 +1,365 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> +#define BOOST_TEST_MODULE MyTest +#include <boost/test/unit_test.hpp> +// include headers that implement a archive in simple text format +#include <boost/archive/text_oarchive.hpp> +#include <boost/archive/text_iarchive.hpp> +#include <boost/serialization/tracking.hpp> + +#include <fstream> + +namespace msm = boost::msm; +namespace mpl = boost::mpl; + +namespace +{ + // events + struct play {}; + struct end_pause {}; + struct stop {}; + struct pause {}; + struct open_close {}; + struct NextSong {}; + struct PreviousSong {}; + struct cd_detected + { + cd_detected(std::string name) + : name(name) + {} + std::string name; + }; + + // front-end: define the FSM structure + struct player_ : public msm::front::state_machine_def<player_> + { + unsigned int start_playback_counter; + unsigned int can_close_drawer_counter; + + player_(): + start_playback_counter(0), + can_close_drawer_counter(0) + {} + // The list of FSM states + struct Empty : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct Open : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + // sm_ptr still supported but deprecated as functors are a much better way to do the same thing + struct Stopped : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + struct Playing_ : public msm::front::state_machine_def<Playing_> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + unsigned int start_next_song_counter; + unsigned int start_prev_song_guard_counter; + + Playing_(): + start_next_song_counter(0), + start_prev_song_guard_counter(0) + {} + + // The list of FSM states + struct Song1 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct Song2 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct Song3 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + // the initial state. Must be defined + typedef Song1 initial_state; + // transition actions + void start_next_song(NextSong const&) {++start_next_song_counter; } + void start_prev_song(PreviousSong const&) { } + // guard conditions + bool start_prev_song_guard(PreviousSong const&) {++start_prev_song_guard_counter;return true; } + + typedef Playing_ pl; // makes transition table cleaner + // Transition table for Playing + struct transition_table : mpl::vector4< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + _row < Song1 , NextSong , Song2 >, + row < Song2 , PreviousSong, Song1 , &pl::start_prev_song,&pl::start_prev_song_guard>, + a_row < Song2 , NextSong , Song3 , &pl::start_next_song >, + g_row < Song3 , PreviousSong, Song2 ,&pl::start_prev_song_guard> + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const&, FSM&,int) + { + BOOST_FAIL("no_transition called!"); + } + }; + // back-end + typedef msm::back::state_machine<Playing_,msm::back::ShallowHistory<mpl::vector<end_pause> > > Playing; + + // state not defining any entry or exit + struct Paused : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + // the initial state of the player SM. Must be defined + typedef Empty initial_state; + + // transition actions + void start_playback(play const&) {++start_playback_counter; } + void open_drawer(open_close const&) { } + void store_cd_info(cd_detected const&) { } + void stop_playback(stop const&) { } + void pause_playback(pause const&) { } + void resume_playback(end_pause const&) { } + void stop_and_open(open_close const&) { } + void stopped_again(stop const&) {} + //guards + bool can_close_drawer(open_close const&) + { + ++can_close_drawer_counter; + return true; + } + + + typedef player_ p; // makes transition table cleaner + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Stopped , play , Playing , &p::start_playback >, + a_row < Stopped , open_close , Open , &p::open_drawer >, + _row < Stopped , stop , Stopped >, + // +---------+-------------+---------+---------------------+----------------------+ + g_row < Open , open_close , Empty , &p::can_close_drawer >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Empty , open_close , Open , &p::open_drawer >, + a_row < Empty , cd_detected , Stopped , &p::store_cd_info >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Playing , stop , Stopped , &p::stop_playback >, + a_row < Playing , pause , Paused , &p::pause_playback >, + a_row < Playing , open_close , Open , &p::stop_and_open >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Paused , end_pause , Playing , &p::resume_playback >, + a_row < Paused , stop , Stopped , &p::stop_playback >, + a_row < Paused , open_close , Open , &p::stop_and_open > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + BOOST_FAIL("no_transition called!"); + } + // init counters + template <class Event,class FSM> + void on_entry(Event const&,FSM& fsm) + { + fsm.template get_state<player_::Stopped&>().entry_counter=0; + fsm.template get_state<player_::Stopped&>().exit_counter=0; + fsm.template get_state<player_::Open&>().entry_counter=0; + fsm.template get_state<player_::Open&>().exit_counter=0; + fsm.template get_state<player_::Empty&>().entry_counter=0; + fsm.template get_state<player_::Empty&>().exit_counter=0; + fsm.template get_state<player_::Playing&>().entry_counter=0; + fsm.template get_state<player_::Playing&>().exit_counter=0; + fsm.template get_state<player_::Playing&>().template get_state<player_::Playing::Song1&>().entry_counter=0; + fsm.template get_state<player_::Playing&>().template get_state<player_::Playing::Song1&>().exit_counter=0; + fsm.template get_state<player_::Playing&>().template get_state<player_::Playing::Song2&>().entry_counter=0; + fsm.template get_state<player_::Playing&>().template get_state<player_::Playing::Song2&>().exit_counter=0; + fsm.template get_state<player_::Playing&>().template get_state<player_::Playing::Song3&>().entry_counter=0; + fsm.template get_state<player_::Playing&>().template get_state<player_::Playing::Song3&>().exit_counter=0; + fsm.template get_state<player_::Paused&>().entry_counter=0; + fsm.template get_state<player_::Paused&>().exit_counter=0; + } + + }; + // Pick a back-end + typedef msm::back::state_machine<player_> player; + +// static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" }; + + + BOOST_AUTO_TEST_CASE( my_test ) + { + player p; + + p.start(); + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().entry_counter == 1,"Empty entry not called correctly"); + + p.process_event(open_close()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 1,"Open should be active"); //Open + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().exit_counter == 1,"Empty exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().entry_counter == 1,"Open entry not called correctly"); + + p.process_event(open_close()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 2,"Empty should be active"); //Empty + BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().exit_counter == 1,"Open exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().entry_counter == 2,"Empty entry not called correctly"); + BOOST_CHECK_MESSAGE(p.can_close_drawer_counter == 1,"guard not called correctly"); + + p.process_event(cd_detected("louie, louie")); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().exit_counter == 2,"Empty exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 1,"Stopped entry not called correctly"); + + p.process_event(play()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().exit_counter == 1,"Stopped exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().entry_counter == 1,"Playing entry not called correctly"); + BOOST_CHECK_MESSAGE(p.start_playback_counter == 1,"action not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().current_state()[0] == 0,"Song1 should be active"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().get_state<player_::Playing::Song1&>().entry_counter == 1, + "Song1 entry not called correctly"); + + p.process_event(NextSong()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().current_state()[0] == 1,"Song2 should be active"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().get_state<player_::Playing::Song2&>().entry_counter == 1, + "Song2 entry not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().get_state<player_::Playing::Song1&>().exit_counter == 1, + "Song1 exit not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().start_next_song_counter == 0, + "submachine action not called correctly"); + + p.process_event(NextSong()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().current_state()[0] == 2,"Song3 should be active"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().get_state<player_::Playing::Song3&>().entry_counter == 1, + "Song3 entry not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().get_state<player_::Playing::Song2&>().exit_counter == 1, + "Song2 exit not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().start_next_song_counter == 1, + "submachine action not called correctly"); + + p.process_event(PreviousSong()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().current_state()[0] == 1,"Song2 should be active"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().get_state<player_::Playing::Song2&>().entry_counter == 2, + "Song2 entry not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().get_state<player_::Playing::Song3&>().exit_counter == 1, + "Song3 exit not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().start_prev_song_guard_counter == 1, + "submachine guard not called correctly"); + + p.process_event(pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Paused should be active"); //Paused + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().exit_counter == 1,"Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().entry_counter == 1,"Paused entry not called correctly"); + + std::ofstream ofs("fsm.txt"); + // save fsm to archive (current state is Pause, Playing is in Song2) + { + boost::archive::text_oarchive oa(ofs); + // write class instance to archive + oa << p; + } + // reload fsm in state Open + player p2; + { + // create and open an archive for input + std::ifstream ifs("fsm.txt"); + boost::archive::text_iarchive ia(ifs); + // read class state from archive + ia >> p2; + } + // go back to Playing + p2.process_event(end_pause()); + BOOST_CHECK_MESSAGE(p2.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p2.get_state<player_::Playing&>().current_state()[0] == 1,"Song2 should be active"); + + p2.process_event(pause()); + BOOST_CHECK_MESSAGE(p2.current_state()[0] == 4,"Paused should be active"); //Paused + + p2.process_event(stop()); + BOOST_CHECK_MESSAGE(p2.current_state()[0] == 0,"Stopped should be active"); //Stopped + + p2.process_event(stop()); + BOOST_CHECK_MESSAGE(p2.current_state()[0] == 0,"Stopped should be active"); //Stopped + + p2.process_event(play()); + BOOST_CHECK_MESSAGE(p2.get_state<player_::Playing&>().current_state()[0] == 0,"Song1 should be active"); + } +} +// eliminate object tracking (even if serialized through a pointer) +// at the risk of a programming error creating duplicate objects. +// this is to get rid of warning because p is not const +BOOST_CLASS_TRACKING(player, boost::serialization::track_never) + diff --git a/libs/msm/test/SimpleEuml.cpp b/libs/msm/test/SimpleEuml.cpp new file mode 100644 index 0000000000..07505a7800 --- /dev/null +++ b/libs/msm/test/SimpleEuml.cpp @@ -0,0 +1,195 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <iostream> +#include <boost/msm/back/state_machine.hpp> +#include <boost/msm/front/euml/euml.hpp> + +#define BOOST_TEST_MODULE MyTest +#include <boost/test/unit_test.hpp> + +using namespace std; +using namespace boost::msm::front::euml; +namespace msm = boost::msm; + +namespace +{ + // A "complicated" event type that carries some data. + enum DiskTypeEnum + { + DISK_CD=0, + DISK_DVD=1 + }; + // events + BOOST_MSM_EUML_EVENT(play) + BOOST_MSM_EUML_EVENT(end_pause) + BOOST_MSM_EUML_EVENT(stop) + BOOST_MSM_EUML_EVENT(pause) + BOOST_MSM_EUML_EVENT(open_close) + // A "complicated" event type that carries some data. + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(std::string,cd_name) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(DiskTypeEnum,cd_type) + BOOST_MSM_EUML_ATTRIBUTES((attributes_ << cd_name << cd_type ), cd_detected_attributes) + BOOST_MSM_EUML_EVENT_WITH_ATTRIBUTES(cd_detected,cd_detected_attributes) + + //states + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,entry_counter) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,exit_counter) + + BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Empty) + BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Open) + BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Stopped) + BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Playing) + BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Paused) + + //fsm + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,start_playback_counter) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,can_close_drawer_counter) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,test_fct_counter) + BOOST_MSM_EUML_ACTION(No_Transition) + { + template <class FSM,class Event> + void operator()(Event const&,FSM&,int) + { + BOOST_FAIL("no_transition called!"); + } + }; + BOOST_MSM_EUML_ACTION(good_disk_format) + { + template <class FSM,class EVT,class SourceState,class TargetState> + bool operator()(EVT const& evt,FSM&,SourceState& ,TargetState& ) + { + if (evt.get_attribute(cd_type)!=DISK_CD) + { + return false; + } + return true; + } + }; + BOOST_MSM_EUML_TRANSITION_TABLE(( + Playing == Stopped + play / (++fsm_(start_playback_counter),++fsm_(test_fct_counter) ), + Playing == Paused + end_pause , + // +------------------------------------------------------------------------------+ + Empty == Open + open_close / ++fsm_(can_close_drawer_counter), + // +------------------------------------------------------------------------------+ + Open == Empty + open_close , + Open == Paused + open_close , + Open == Stopped + open_close , + Open == Playing + open_close , + // +------------------------------------------------------------------------------+ + Paused == Playing + pause , + // +------------------------------------------------------------------------------+ + Stopped == Playing + stop , + Stopped == Paused + stop , + Stopped == Empty + cd_detected [good_disk_format || + (event_(cd_type)==Int_<DISK_CD>())] / process_(play) , + Stopped == Stopped + stop + // +------------------------------------------------------------------------------+ + ),transition_table) + + BOOST_MSM_EUML_DECLARE_STATE_MACHINE(( transition_table, //STT + init_ << Empty, // Init State + no_action, // Entry + no_action, // Exit + attributes_ << start_playback_counter + << can_close_drawer_counter << test_fct_counter, // Attributes + configure_ << no_configure_, // configuration + No_Transition // no_transition handler + ), + player_) //fsm name + + typedef msm::back::state_machine<player_> player; + +// static char const* const state_names[] = { "Stopped", "Paused", "Open", "Empty", "Playing" }; + + + BOOST_AUTO_TEST_CASE( my_test ) + { + player p; + + p.start(); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Empty)&>().get_attribute(entry_counter) == 1, + "Empty entry not called correctly"); + + p.process_event(open_close()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 2,"Open should be active"); //Open + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Empty)&>().get_attribute(exit_counter) == 1, + "Empty exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Open)&>().get_attribute(entry_counter) == 1, + "Open entry not called correctly"); + + p.process_event(open_close()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Empty should be active"); //Empty + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Open)&>().get_attribute(exit_counter) == 1, + "Open exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Empty)&>().get_attribute(entry_counter) == 2, + "Empty entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_attribute(can_close_drawer_counter) == 1,"guard not called correctly"); + + p.process_event( + cd_detected("louie, louie",DISK_DVD)); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Empty should be active"); //Empty + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Open)&>().get_attribute(exit_counter) == 1, + "Open exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Empty)&>().get_attribute(entry_counter) == 2, + "Empty entry not called correctly"); + + p.process_event( + cd_detected("louie, louie",DISK_CD)); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Empty)&>().get_attribute(exit_counter) == 2, + "Empty exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(entry_counter) == 1, + "Stopped entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(exit_counter) == 1, + "Stopped exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Playing)&>().get_attribute(entry_counter) == 1, + "Playing entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_attribute(start_playback_counter) == 1,"action not called correctly"); + BOOST_CHECK_MESSAGE(p.get_attribute(test_fct_counter) == 1,"action not called correctly"); + + p.process_event(pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 1,"Paused should be active"); //Paused + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Playing)&>().get_attribute(exit_counter) == 1, + "Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Paused)&>().get_attribute(entry_counter) == 1, + "Paused entry not called correctly"); + + // go back to Playing + p.process_event(end_pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Paused)&>().get_attribute(exit_counter) == 1, + "Paused exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Playing)&>().get_attribute(entry_counter) == 2, + "Playing entry not called correctly"); + + p.process_event(pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 1,"Paused should be active"); //Paused + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Playing)&>().get_attribute(exit_counter) == 2, + "Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Paused)&>().get_attribute(entry_counter) == 2, + "Paused entry not called correctly"); + + p.process_event(stop()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Paused)&>().get_attribute(exit_counter) == 2, + "Paused exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(entry_counter) == 2, + "Stopped entry not called correctly"); + + p.process_event(stop()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(exit_counter) == 2, + "Stopped exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(entry_counter) == 3, + "Stopped entry not called correctly"); + } +} + diff --git a/libs/msm/test/SimpleEuml2.cpp b/libs/msm/test/SimpleEuml2.cpp new file mode 100644 index 0000000000..487a37bed8 --- /dev/null +++ b/libs/msm/test/SimpleEuml2.cpp @@ -0,0 +1,195 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <iostream> +#include <boost/msm/back/state_machine.hpp> +#include <boost/msm/front/euml/euml.hpp> + +#define BOOST_TEST_MODULE MyTest +#include <boost/test/unit_test.hpp> + +using namespace std; +using namespace boost::msm::front::euml; +namespace msm = boost::msm; + +namespace +{ + // A "complicated" event type that carries some data. + enum DiskTypeEnum + { + DISK_CD=0, + DISK_DVD=1 + }; + // events + BOOST_MSM_EUML_EVENT(play) + BOOST_MSM_EUML_EVENT(end_pause) + BOOST_MSM_EUML_EVENT(stop) + BOOST_MSM_EUML_EVENT(pause) + BOOST_MSM_EUML_EVENT(open_close) + // A "complicated" event type that carries some data. + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(std::string,cd_name) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(DiskTypeEnum,cd_type) + BOOST_MSM_EUML_ATTRIBUTES((attributes_ << cd_name << cd_type ), cd_detected_attributes) + BOOST_MSM_EUML_EVENT_WITH_ATTRIBUTES(cd_detected,cd_detected_attributes) + + //states + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,entry_counter) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,exit_counter) + + BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Empty) + BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Open) + BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Stopped) + BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Playing) + BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Paused) + + //fsm + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,start_playback_counter) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,can_close_drawer_counter) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,test_fct_counter) + BOOST_MSM_EUML_ACTION(No_Transition) + { + template <class FSM,class Event> + void operator()(Event const&,FSM&,int) + { + BOOST_FAIL("no_transition called!"); + } + }; + BOOST_MSM_EUML_ACTION(good_disk_format) + { + template <class FSM,class EVT,class SourceState,class TargetState> + bool operator()(EVT const& evt,FSM&,SourceState& ,TargetState& ) + { + if (evt.get_attribute(cd_type)!=DISK_CD) + { + return false; + } + return true; + } + }; + BOOST_MSM_EUML_TRANSITION_TABLE(( + Stopped + play / (++fsm_(start_playback_counter),++fsm_(test_fct_counter)) == Playing, + Paused + end_pause == Playing, + // +------------------------------------------------------------------------------+ + Open + open_close / ++fsm_(can_close_drawer_counter) == Empty, + // +------------------------------------------------------------------------------+ + Empty + open_close == Open, + Paused + open_close == Open, + Stopped + open_close == Open, + Playing + open_close == Open, + // +------------------------------------------------------------------------------+ + Playing + pause == Paused, + // +------------------------------------------------------------------------------+ + Playing + stop == Stopped, + Paused + stop == Stopped, + Empty + cd_detected [good_disk_format || + (event_(cd_type)==Int_<DISK_CD>())] / process_(play) == Stopped, + Stopped + stop == Stopped + // +------------------------------------------------------------------------------+ + ),transition_table) + + BOOST_MSM_EUML_DECLARE_STATE_MACHINE(( transition_table, //STT + init_ << Empty, // Init State + no_action, // Entry + no_action, // Exit + attributes_ << start_playback_counter + << can_close_drawer_counter << test_fct_counter, // Attributes + configure_ << no_configure_, // configuration + No_Transition // no_transition handler + ), + player_) //fsm name + + typedef msm::back::state_machine<player_> player; + +// static char const* const state_names[] = { "Stopped", "Paused", "Open", "Empty", "Playing" }; + + + BOOST_AUTO_TEST_CASE( my_test ) + { + player p; + + p.start(); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Empty)&>().get_attribute(entry_counter) == 1, + "Empty entry not called correctly"); + + p.process_event(open_close()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 2,"Open should be active"); //Open + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Empty)&>().get_attribute(exit_counter) == 1, + "Empty exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Open)&>().get_attribute(entry_counter) == 1, + "Open entry not called correctly"); + + p.process_event(open_close()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Empty should be active"); //Empty + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Open)&>().get_attribute(exit_counter) == 1, + "Open exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Empty)&>().get_attribute(entry_counter) == 2, + "Empty entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_attribute(can_close_drawer_counter) == 1,"guard not called correctly"); + + p.process_event( + cd_detected("louie, louie",DISK_DVD)); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Empty should be active"); //Empty + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Open)&>().get_attribute(exit_counter) == 1, + "Open exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Empty)&>().get_attribute(entry_counter) == 2, + "Empty entry not called correctly"); + + p.process_event( + cd_detected("louie, louie",DISK_CD)); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Empty)&>().get_attribute(exit_counter) == 2, + "Empty exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(entry_counter) == 1, + "Stopped entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(exit_counter) == 1, + "Stopped exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Playing)&>().get_attribute(entry_counter) == 1, + "Playing entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_attribute(start_playback_counter) == 1,"action not called correctly"); + BOOST_CHECK_MESSAGE(p.get_attribute(test_fct_counter) == 1,"action not called correctly"); + + p.process_event(pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 1,"Paused should be active"); //Paused + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Playing)&>().get_attribute(exit_counter) == 1, + "Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Paused)&>().get_attribute(entry_counter) == 1, + "Paused entry not called correctly"); + + // go back to Playing + p.process_event(end_pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Paused)&>().get_attribute(exit_counter) == 1, + "Paused exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Playing)&>().get_attribute(entry_counter) == 2, + "Playing entry not called correctly"); + + p.process_event(pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 1,"Paused should be active"); //Paused + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Playing)&>().get_attribute(exit_counter) == 2, + "Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Paused)&>().get_attribute(entry_counter) == 2, + "Paused entry not called correctly"); + + p.process_event(stop()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Paused)&>().get_attribute(exit_counter) == 2, + "Paused exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(entry_counter) == 2, + "Stopped entry not called correctly"); + + p.process_event(stop()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(exit_counter) == 2, + "Stopped exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(entry_counter) == 3, + "Stopped entry not called correctly"); + } +} + diff --git a/libs/msm/test/SimpleInternal.cpp b/libs/msm/test/SimpleInternal.cpp new file mode 100644 index 0000000000..1a58aecb6d --- /dev/null +++ b/libs/msm/test/SimpleInternal.cpp @@ -0,0 +1,274 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> +#define BOOST_TEST_MODULE MyTest +#include <boost/test/unit_test.hpp> + +namespace msm = boost::msm; +namespace mpl = boost::mpl; + +namespace +{ + // events + struct play {}; + struct end_pause {}; + struct stop {}; + struct pause {}; + struct open_close {}; + struct internal_evt {}; + struct to_ignore {}; + + // A "complicated" event type that carries some data. + enum DiskTypeEnum + { + DISK_CD=0, + DISK_DVD=1 + }; + struct cd_detected + { + cd_detected(std::string name, DiskTypeEnum diskType) + : name(name), + disc_type(diskType) + {} + + std::string name; + DiskTypeEnum disc_type; + }; + + // front-end: define the FSM structure + struct player_ : public msm::front::state_machine_def<player_> + { + unsigned int start_playback_counter; + unsigned int can_close_drawer_counter; + unsigned int internal_action_counter; + unsigned int internal_guard_counter; + + player_(): + start_playback_counter(0), + can_close_drawer_counter(0), + internal_action_counter(0), + internal_guard_counter(0) + {} + + // The list of FSM states + struct Empty : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct Open : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + // sm_ptr still supported but deprecated as functors are a much better way to do the same thing + struct Stopped : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + struct Playing : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + // state not defining any entry or exit + struct Paused : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + // the initial state of the player SM. Must be defined + typedef Empty initial_state; + + // transition actions + void start_playback(play const&) {++start_playback_counter; } + void open_drawer(open_close const&) { } + void store_cd_info(cd_detected const&) { } + void stop_playback(stop const&) { } + void pause_playback(pause const&) { } + void resume_playback(end_pause const&) { } + void stop_and_open(open_close const&) { } + void stopped_again(stop const&){} + void internal_action(internal_evt const&){++internal_action_counter; } + bool internal_guard(cd_detected const&){++internal_guard_counter;return false;} + bool internal_guard2(internal_evt const&){++internal_guard_counter;return true;} + // guard conditions + bool good_disk_format(cd_detected const& evt) + { + // to test a guard condition, let's say we understand only CDs, not DVD + if (evt.disc_type != DISK_CD) + { + return false; + } + return true; + } + bool can_close_drawer(open_close const&) + { + ++can_close_drawer_counter; + return true; + } + + typedef player_ p; // makes transition table cleaner + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Stopped , play , Playing , &p::start_playback >, + a_row < Stopped , open_close , Open , &p::open_drawer >, + _row < Stopped , stop , Stopped >, + // +---------+-------------+---------+---------------------+----------------------+ + g_row < Open , open_close , Empty , &p::can_close_drawer >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Empty , open_close , Open , &p::open_drawer >, + row < Empty , cd_detected , Stopped , &p::store_cd_info ,&p::good_disk_format >, + irow < Empty , internal_evt, &p::internal_action ,&p::internal_guard2 >, + _irow < Empty , to_ignore >, + g_irow < Empty , cd_detected ,&p::internal_guard >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Playing , stop , Stopped , &p::stop_playback >, + a_row < Playing , pause , Paused , &p::pause_playback >, + a_row < Playing , open_close , Open , &p::stop_and_open >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Paused , end_pause , Playing , &p::resume_playback >, + a_row < Paused , stop , Stopped , &p::stop_playback >, + a_row < Paused , open_close , Open , &p::stop_and_open > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const&, FSM&,int) + { + BOOST_FAIL("no_transition called!"); + } + // init counters + template <class Event,class FSM> + void on_entry(Event const&,FSM& fsm) + { + fsm.template get_state<player_::Stopped&>().entry_counter=0; + fsm.template get_state<player_::Stopped&>().exit_counter=0; + fsm.template get_state<player_::Open&>().entry_counter=0; + fsm.template get_state<player_::Open&>().exit_counter=0; + fsm.template get_state<player_::Empty&>().entry_counter=0; + fsm.template get_state<player_::Empty&>().exit_counter=0; + fsm.template get_state<player_::Playing&>().entry_counter=0; + fsm.template get_state<player_::Playing&>().exit_counter=0; + fsm.template get_state<player_::Paused&>().entry_counter=0; + fsm.template get_state<player_::Paused&>().exit_counter=0; + } + + }; + // Pick a back-end + typedef msm::back::state_machine<player_> player; + +// static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" }; + + + BOOST_AUTO_TEST_CASE( my_test ) + { + player p; + + p.start(); + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().entry_counter == 1,"Empty entry not called correctly"); + // internal events + p.process_event(to_ignore()); + p.process_event(internal_evt()); + BOOST_CHECK_MESSAGE(p.internal_action_counter == 1,"Internal action not called correctly"); + BOOST_CHECK_MESSAGE(p.internal_guard_counter == 1,"Internal guard not called correctly"); + + p.process_event(open_close()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 1,"Open should be active"); //Open + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().exit_counter == 1,"Empty exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().entry_counter == 1,"Open entry not called correctly"); + + p.process_event(open_close()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 2,"Empty should be active"); //Empty + BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().exit_counter == 1,"Open exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().entry_counter == 2,"Empty entry not called correctly"); + BOOST_CHECK_MESSAGE(p.can_close_drawer_counter == 1,"guard not called correctly"); + + p.process_event( + cd_detected("louie, louie",DISK_DVD)); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 2,"Empty should be active"); //Empty + BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().exit_counter == 1,"Open exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().entry_counter == 2,"Empty entry not called correctly"); + BOOST_CHECK_MESSAGE(p.internal_guard_counter == 2,"Internal guard not called correctly"); + + p.process_event( + cd_detected("louie, louie",DISK_CD)); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().exit_counter == 2,"Empty exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 1,"Stopped entry not called correctly"); + BOOST_CHECK_MESSAGE(p.internal_guard_counter == 3,"Internal guard not called correctly"); + + p.process_event(play()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().exit_counter == 1,"Stopped exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().entry_counter == 1,"Playing entry not called correctly"); + BOOST_CHECK_MESSAGE(p.start_playback_counter == 1,"action not called correctly"); + + p.process_event(pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Paused should be active"); //Paused + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().exit_counter == 1,"Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().entry_counter == 1,"Paused entry not called correctly"); + + // go back to Playing + p.process_event(end_pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().exit_counter == 1,"Paused exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().entry_counter == 2,"Playing entry not called correctly"); + + p.process_event(pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Paused should be active"); //Paused + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().exit_counter == 2,"Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().entry_counter == 2,"Paused entry not called correctly"); + + p.process_event(stop()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().exit_counter == 2,"Paused exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 2,"Stopped entry not called correctly"); + + p.process_event(stop()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().exit_counter == 2,"Stopped exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 3,"Stopped entry not called correctly"); + } +} + diff --git a/libs/msm/test/SimpleInternalEuml.cpp b/libs/msm/test/SimpleInternalEuml.cpp new file mode 100644 index 0000000000..936f5a0cbe --- /dev/null +++ b/libs/msm/test/SimpleInternalEuml.cpp @@ -0,0 +1,253 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <iostream> +#include <boost/msm/back/state_machine.hpp> +#include <boost/msm/front/euml/euml.hpp> + +#define BOOST_TEST_MODULE MyTest +#include <boost/test/unit_test.hpp> + +using namespace std; +using namespace boost::msm::front::euml; +namespace msm = boost::msm; + +namespace +{ + // A "complicated" event type that carries some data. + enum DiskTypeEnum + { + DISK_CD=0, + DISK_DVD=1 + }; + // events + BOOST_MSM_EUML_EVENT(play) + BOOST_MSM_EUML_EVENT(end_pause) + BOOST_MSM_EUML_EVENT(stop) + BOOST_MSM_EUML_EVENT(pause) + BOOST_MSM_EUML_EVENT(open_close) + BOOST_MSM_EUML_EVENT(internal_evt) + BOOST_MSM_EUML_EVENT(to_ignore) + // A "complicated" event type that carries some data. + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(std::string,cd_name) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(DiskTypeEnum,cd_type) + BOOST_MSM_EUML_ATTRIBUTES((attributes_ << cd_name << cd_type ), cd_detected_attributes) + BOOST_MSM_EUML_EVENT_WITH_ATTRIBUTES(cd_detected,cd_detected_attributes) + + //states + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,entry_counter) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,exit_counter) + + BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Open) + BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Stopped) + BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Playing) + BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Paused) + + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,empty_internal_guard_counter) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,empty_internal_action_counter) + + BOOST_MSM_EUML_ACTION(internal_guard_fct) + { + template <class FSM,class EVT,class SourceState,class TargetState> + bool operator()(EVT const&, FSM& ,SourceState& src,TargetState& ) + { + ++src.get_attribute(empty_internal_guard_counter); + return false; + } + }; + BOOST_MSM_EUML_DECLARE_STATE((++state_(entry_counter),++state_(exit_counter), + attributes_ << entry_counter << exit_counter + << empty_internal_guard_counter << empty_internal_action_counter),Empty_def) + // derive to be able to add an internal transition table + struct Empty_impl : public Empty_def + { + Empty_impl(){} + BOOST_MSM_EUML_DECLARE_INTERNAL_TRANSITION_TABLE(( + internal_evt [internal_guard_fct] / ++source_(empty_internal_action_counter) + )) + }; + // declare an instance for the stt as we are manually declaring a state + Empty_impl const Empty; + + //fsm + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,start_playback_counter) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,can_close_drawer_counter) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,internal_action_counter) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,internal_guard_counter) + BOOST_MSM_EUML_ACTION(No_Transition) + { + template <class FSM,class Event> + void operator()(Event const&,FSM&,int) + { + BOOST_FAIL("no_transition called!"); + } + }; + BOOST_MSM_EUML_ACTION(good_disk_format) + { + template <class FSM,class EVT,class SourceState,class TargetState> + bool operator()(EVT const& evt,FSM&,SourceState& ,TargetState& ) + { + if (evt.get_attribute(cd_type)!=DISK_CD) + { + return false; + } + return true; + } + }; + BOOST_MSM_EUML_ACTION(internal_guard) + { + template <class FSM,class EVT,class SourceState,class TargetState> + bool operator()(EVT const& evt,FSM& fsm,SourceState& ,TargetState& ) + { + ++fsm.get_attribute(internal_guard_counter); + return false; + } + }; + BOOST_MSM_EUML_ACTION(internal_guard2) + { + template <class FSM,class EVT,class SourceState,class TargetState> + bool operator()(EVT const& evt,FSM& fsm,SourceState& ,TargetState& ) + { + ++fsm.get_attribute(internal_guard_counter); + return true; + } + }; + BOOST_MSM_EUML_TRANSITION_TABLE(( + Playing == Stopped + play / ++fsm_(start_playback_counter) , + Playing == Paused + end_pause , + // +------------------------------------------------------------------------------+ + Empty == Open + open_close / ++fsm_(can_close_drawer_counter), + Empty + to_ignore , + Empty + internal_evt [internal_guard2] / ++fsm_(internal_action_counter) , + Empty + cd_detected [internal_guard] , + // +------------------------------------------------------------------------------+ + Open == Empty + open_close , + Open == Paused + open_close , + Open == Stopped + open_close , + Open == Playing + open_close , + // +------------------------------------------------------------------------------+ + Paused == Playing + pause , + // +------------------------------------------------------------------------------+ + Stopped == Playing + stop , + Stopped == Paused + stop , + Stopped == Empty + cd_detected [good_disk_format] , + Stopped == Stopped + stop + // +------------------------------------------------------------------------------+ + ),transition_table) + + BOOST_MSM_EUML_DECLARE_STATE_MACHINE(( transition_table, //STT + init_ << Empty, // Init State + no_action, // Entry + no_action, // Exit + attributes_ << start_playback_counter << can_close_drawer_counter + << internal_action_counter << internal_guard_counter, // Attributes + configure_ << no_configure_, // configuration + No_Transition // no_transition handler + ), + player_) //fsm name + + typedef msm::back::state_machine<player_> player; + +// static char const* const state_names[] = { "Stopped", "Paused", "Open", "Empty", "Playing" }; + + + BOOST_AUTO_TEST_CASE( my_test ) + { + player p; + + p.start(); + BOOST_CHECK_MESSAGE(p.get_state<Empty_impl&>().get_attribute(entry_counter) == 1, + "Empty entry not called correctly"); + + // internal events + p.process_event(to_ignore); + p.process_event(internal_evt); + BOOST_CHECK_MESSAGE(p.get_attribute(internal_action_counter) == 1,"Internal action not called correctly"); + BOOST_CHECK_MESSAGE(p.get_attribute(internal_guard_counter) == 1,"Internal guard not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<Empty_impl&>().get_attribute(empty_internal_action_counter) == 0, + "Empty internal action not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<Empty_impl&>().get_attribute(empty_internal_guard_counter) == 1, + "Empty internal guard not called correctly"); + + p.process_event(open_close()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 2,"Open should be active"); //Open + BOOST_CHECK_MESSAGE(p.get_state<Empty_impl&>().get_attribute(exit_counter) == 1, + "Empty exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<Empty_impl&>().get_attribute(entry_counter) == 1, + "Open entry not called correctly"); + + p.process_event(open_close()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Empty should be active"); //Empty + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Open)&>().get_attribute(exit_counter) == 1, + "Open exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<Empty_impl&>().get_attribute(entry_counter) == 2, + "Empty entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_attribute(can_close_drawer_counter) == 1,"guard not called correctly"); + + p.process_event( + cd_detected("louie, louie",DISK_DVD)); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Empty should be active"); //Empty + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Open)&>().get_attribute(exit_counter) == 1, + "Open exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<Empty_impl&>().get_attribute(entry_counter) == 2, + "Empty entry not called correctly"); + + p.process_event( + cd_detected("louie, louie",DISK_CD)); + p.process_event(play); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<Empty_impl&>().get_attribute(exit_counter) == 2, + "Empty exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(entry_counter) == 1, + "Stopped entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(exit_counter) == 1, + "Stopped exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Playing)&>().get_attribute(entry_counter) == 1, + "Playing entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_attribute(start_playback_counter) == 1,"action not called correctly"); + + p.process_event(pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 1,"Paused should be active"); //Paused + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Playing)&>().get_attribute(exit_counter) == 1, + "Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Paused)&>().get_attribute(entry_counter) == 1, + "Paused entry not called correctly"); + + // go back to Playing + p.process_event(end_pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Paused)&>().get_attribute(exit_counter) == 1, + "Paused exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Playing)&>().get_attribute(entry_counter) == 2, + "Playing entry not called correctly"); + + p.process_event(pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 1,"Paused should be active"); //Paused + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Playing)&>().get_attribute(exit_counter) == 2, + "Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Paused)&>().get_attribute(entry_counter) == 2, + "Paused entry not called correctly"); + + p.process_event(stop()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Paused)&>().get_attribute(exit_counter) == 2, + "Paused exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(entry_counter) == 2, + "Stopped entry not called correctly"); + + p.process_event(stop()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(exit_counter) == 2, + "Stopped exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(entry_counter) == 3, + "Stopped entry not called correctly"); + } +} + diff --git a/libs/msm/test/SimpleInternalFunctors.cpp b/libs/msm/test/SimpleInternalFunctors.cpp new file mode 100644 index 0000000000..155d0eb9e0 --- /dev/null +++ b/libs/msm/test/SimpleInternalFunctors.cpp @@ -0,0 +1,331 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> +#include <boost/msm/front/functor_row.hpp> +#include <boost/msm/front/euml/common.hpp> + +#define BOOST_TEST_MODULE MyTest +#include <boost/test/unit_test.hpp> + +using namespace std; +namespace msm = boost::msm; +using namespace msm::front; +namespace mpl = boost::mpl; + +namespace +{ + // events + struct play {}; + struct end_pause {}; + struct stop {}; + struct pause {}; + struct open_close {}; + struct internal_evt {}; + struct to_ignore {}; + + // A "complicated" event type that carries some data. + enum DiskTypeEnum + { + DISK_CD=0, + DISK_DVD=1 + }; + struct cd_detected + { + cd_detected(std::string name, DiskTypeEnum diskType) + : name(name), + disc_type(diskType) + {} + + std::string name; + DiskTypeEnum disc_type; + }; + + // front-end: define the FSM structure + struct player_ : public msm::front::state_machine_def<player_> + { + unsigned int start_playback_counter; + unsigned int can_close_drawer_counter; + unsigned int internal_action_counter; + unsigned int internal_guard_counter; + + player_(): + start_playback_counter(0), + can_close_drawer_counter(0), + internal_action_counter(0), + internal_guard_counter(0) + {} + + // The list of FSM states + struct Empty : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + unsigned int empty_internal_guard_counter; + unsigned int empty_internal_action_counter; + struct internal_guard_fct + { + template <class EVT,class FSM,class SourceState,class TargetState> + bool operator()(EVT const& evt ,FSM&,SourceState& src,TargetState& ) + { + ++src.empty_internal_guard_counter; + return false; + } + }; + struct internal_action_fct + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& src,TargetState& ) + { + ++src.empty_internal_action_counter; + } + }; + // Transition table for Empty + struct internal_transition_table : mpl::vector< + // Start Event Next Action Guard + Internal < internal_evt , internal_action_fct ,internal_guard_fct > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + }; + struct Open : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + // sm_ptr still supported but deprecated as functors are a much better way to do the same thing + struct Stopped : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + struct Playing : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + // state not defining any entry or exit + struct Paused : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + // the initial state of the player SM. Must be defined + typedef Empty initial_state; + + // transition actions + void start_playback(play const&) {++start_playback_counter; } + void open_drawer(open_close const&) { } + void store_cd_info(cd_detected const&) { } + void stop_playback(stop const&) { } + void pause_playback(pause const&) { } + void resume_playback(end_pause const&) { } + void stop_and_open(open_close const&) { } + void stopped_again(stop const&){} + struct internal_action + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& evt ,FSM& fsm,SourceState& ,TargetState& ) + { + ++fsm.internal_action_counter; + } + }; + struct internal_guard + { + template <class EVT,class FSM,class SourceState,class TargetState> + bool operator()(EVT const& evt ,FSM& fsm,SourceState& ,TargetState& ) + { + ++fsm.internal_guard_counter; + return false; + } + }; + struct internal_guard2 + { + template <class EVT,class FSM,class SourceState,class TargetState> + bool operator()(EVT const& evt ,FSM& fsm,SourceState& ,TargetState& ) + { + ++fsm.internal_guard_counter; + return true; + } + }; + // guard conditions + bool good_disk_format(cd_detected const& evt) + { + // to test a guard condition, let's say we understand only CDs, not DVD + if (evt.disc_type != DISK_CD) + { + return false; + } + return true; + } + bool can_close_drawer(open_close const&) + { + ++can_close_drawer_counter; + return true; + } + + typedef player_ p; // makes transition table cleaner + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Stopped , play , Playing , &p::start_playback >, + a_row < Stopped , open_close , Open , &p::open_drawer >, + _row < Stopped , stop , Stopped >, + // +---------+-------------+---------+---------------------+----------------------+ + g_row < Open , open_close , Empty , &p::can_close_drawer >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Empty , open_close , Open , &p::open_drawer >, + row < Empty , cd_detected , Stopped , &p::store_cd_info ,&p::good_disk_format >, + Row < Empty , internal_evt, none , internal_action ,internal_guard2 >, + Row < Empty , to_ignore , none , none , none >, + Row < Empty , cd_detected , none , none ,internal_guard >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Playing , stop , Stopped , &p::stop_playback >, + a_row < Playing , pause , Paused , &p::pause_playback >, + a_row < Playing , open_close , Open , &p::stop_and_open >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Paused , end_pause , Playing , &p::resume_playback >, + a_row < Paused , stop , Stopped , &p::stop_playback >, + a_row < Paused , open_close , Open , &p::stop_and_open > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const&, FSM&,int) + { + BOOST_FAIL("no_transition called!"); + } + // init counters + template <class Event,class FSM> + void on_entry(Event const&,FSM& fsm) + { + fsm.template get_state<player_::Stopped&>().entry_counter=0; + fsm.template get_state<player_::Stopped&>().exit_counter=0; + fsm.template get_state<player_::Open&>().entry_counter=0; + fsm.template get_state<player_::Open&>().exit_counter=0; + fsm.template get_state<player_::Empty&>().entry_counter=0; + fsm.template get_state<player_::Empty&>().exit_counter=0; + fsm.template get_state<player_::Empty&>().empty_internal_guard_counter=0; + fsm.template get_state<player_::Empty&>().empty_internal_action_counter=0; + fsm.template get_state<player_::Playing&>().entry_counter=0; + fsm.template get_state<player_::Playing&>().exit_counter=0; + fsm.template get_state<player_::Paused&>().entry_counter=0; + fsm.template get_state<player_::Paused&>().exit_counter=0; + } + + }; + // Pick a back-end + typedef msm::back::state_machine<player_> player; + +// static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" }; + + + BOOST_AUTO_TEST_CASE( my_test ) + { + player p; + + p.start(); + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().entry_counter == 1,"Empty entry not called correctly"); + // internal events + p.process_event(to_ignore()); + p.process_event(internal_evt()); + BOOST_CHECK_MESSAGE(p.internal_action_counter == 1,"Internal action not called correctly"); + BOOST_CHECK_MESSAGE(p.internal_guard_counter == 1,"Internal guard not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().empty_internal_action_counter == 0,"Empty internal action not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().empty_internal_guard_counter == 1,"Empty internal guard not called correctly"); + + p.process_event(open_close()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 1,"Open should be active"); //Open + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().exit_counter == 1,"Empty exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().entry_counter == 1,"Open entry not called correctly"); + + p.process_event(open_close()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 2,"Empty should be active"); //Empty + BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().exit_counter == 1,"Open exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().entry_counter == 2,"Empty entry not called correctly"); + BOOST_CHECK_MESSAGE(p.can_close_drawer_counter == 1,"guard not called correctly"); + + p.process_event( + cd_detected("louie, louie",DISK_DVD)); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 2,"Empty should be active"); //Empty + BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().exit_counter == 1,"Open exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().entry_counter == 2,"Empty entry not called correctly"); + BOOST_CHECK_MESSAGE(p.internal_guard_counter == 2,"Internal guard not called correctly"); + + p.process_event( + cd_detected("louie, louie",DISK_CD)); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().exit_counter == 2,"Empty exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 1,"Stopped entry not called correctly"); + BOOST_CHECK_MESSAGE(p.internal_guard_counter == 3,"Internal guard not called correctly"); + + p.process_event(play()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().exit_counter == 1,"Stopped exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().entry_counter == 1,"Playing entry not called correctly"); + BOOST_CHECK_MESSAGE(p.start_playback_counter == 1,"action not called correctly"); + + p.process_event(pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Paused should be active"); //Paused + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().exit_counter == 1,"Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().entry_counter == 1,"Paused entry not called correctly"); + + // go back to Playing + p.process_event(end_pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().exit_counter == 1,"Paused exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().entry_counter == 2,"Playing entry not called correctly"); + + p.process_event(pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Paused should be active"); //Paused + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().exit_counter == 2,"Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().entry_counter == 2,"Paused entry not called correctly"); + + p.process_event(stop()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().exit_counter == 2,"Paused exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 2,"Stopped entry not called correctly"); + + p.process_event(stop()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().exit_counter == 2,"Stopped exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 3,"Stopped entry not called correctly"); + } +} + diff --git a/libs/msm/test/SimpleMachine.cpp b/libs/msm/test/SimpleMachine.cpp new file mode 100644 index 0000000000..964ac32346 --- /dev/null +++ b/libs/msm/test/SimpleMachine.cpp @@ -0,0 +1,255 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> +#define BOOST_TEST_MODULE MyTest +#include <boost/test/unit_test.hpp> + +namespace msm = boost::msm; +namespace mpl = boost::mpl; + +namespace +{ + // events + struct play {}; + struct end_pause {}; + struct stop {}; + struct pause {}; + struct open_close {}; + + // A "complicated" event type that carries some data. + enum DiskTypeEnum + { + DISK_CD=0, + DISK_DVD=1 + }; + struct cd_detected + { + cd_detected(std::string name, DiskTypeEnum diskType) + : name(name), + disc_type(diskType) + {} + + std::string name; + DiskTypeEnum disc_type; + }; + + // front-end: define the FSM structure + struct player_ : public msm::front::state_machine_def<player_> + { + unsigned int start_playback_counter; + unsigned int can_close_drawer_counter; + + player_(): + start_playback_counter(0), + can_close_drawer_counter(0) + {} + + // The list of FSM states + struct Empty : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct Open : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + // sm_ptr still supported but deprecated as functors are a much better way to do the same thing + struct Stopped : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + struct Playing : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + // state not defining any entry or exit + struct Paused : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + // the initial state of the player SM. Must be defined + typedef Empty initial_state; + + // transition actions + void start_playback(play const&) {++start_playback_counter; } + void open_drawer(open_close const&) { } + void store_cd_info(cd_detected const&) { } + void stop_playback(stop const&) { } + void pause_playback(pause const&) { } + void resume_playback(end_pause const&) { } + void stop_and_open(open_close const&) { } + void stopped_again(stop const&){} + // guard conditions + bool good_disk_format(cd_detected const& evt) + { + // to test a guard condition, let's say we understand only CDs, not DVD + if (evt.disc_type != DISK_CD) + { + return false; + } + return true; + } + bool can_close_drawer(open_close const&) + { + ++can_close_drawer_counter; + return true; + } + + typedef player_ p; // makes transition table cleaner + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Stopped , play , Playing , &p::start_playback >, + a_row < Stopped , open_close , Open , &p::open_drawer >, + _row < Stopped , stop , Stopped >, + // +---------+-------------+---------+---------------------+----------------------+ + g_row < Open , open_close , Empty , &p::can_close_drawer >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Empty , open_close , Open , &p::open_drawer >, + row < Empty , cd_detected , Stopped , &p::store_cd_info ,&p::good_disk_format >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Playing , stop , Stopped , &p::stop_playback >, + a_row < Playing , pause , Paused , &p::pause_playback >, + a_row < Playing , open_close , Open , &p::stop_and_open >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Paused , end_pause , Playing , &p::resume_playback >, + a_row < Paused , stop , Stopped , &p::stop_playback >, + a_row < Paused , open_close , Open , &p::stop_and_open > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& , FSM&,int) + { + BOOST_FAIL("no_transition called!"); + } + // init counters + template <class Event,class FSM> + void on_entry(Event const&,FSM& fsm) + { + fsm.template get_state<player_::Stopped&>().entry_counter=0; + fsm.template get_state<player_::Stopped&>().exit_counter=0; + fsm.template get_state<player_::Open&>().entry_counter=0; + fsm.template get_state<player_::Open&>().exit_counter=0; + fsm.template get_state<player_::Empty&>().entry_counter=0; + fsm.template get_state<player_::Empty&>().exit_counter=0; + fsm.template get_state<player_::Playing&>().entry_counter=0; + fsm.template get_state<player_::Playing&>().exit_counter=0; + fsm.template get_state<player_::Paused&>().entry_counter=0; + fsm.template get_state<player_::Paused&>().exit_counter=0; + } + + }; + // Pick a back-end + typedef msm::back::state_machine<player_> player; + +// static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" }; + + + BOOST_AUTO_TEST_CASE( my_test ) + { + player p; + + p.start(); + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().entry_counter == 1,"Empty entry not called correctly"); + + p.process_event(open_close()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 1,"Open should be active"); //Open + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().exit_counter == 1,"Empty exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().entry_counter == 1,"Open entry not called correctly"); + + p.process_event(open_close()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 2,"Empty should be active"); //Empty + BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().exit_counter == 1,"Open exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().entry_counter == 2,"Empty entry not called correctly"); + BOOST_CHECK_MESSAGE(p.can_close_drawer_counter == 1,"guard not called correctly"); + + p.process_event( + cd_detected("louie, louie",DISK_DVD)); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 2,"Empty should be active"); //Empty + BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().exit_counter == 1,"Open exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().entry_counter == 2,"Empty entry not called correctly"); + + p.process_event( + cd_detected("louie, louie",DISK_CD)); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().exit_counter == 2,"Empty exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 1,"Stopped entry not called correctly"); + + p.process_event(play()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().exit_counter == 1,"Stopped exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().entry_counter == 1,"Playing entry not called correctly"); + BOOST_CHECK_MESSAGE(p.start_playback_counter == 1,"action not called correctly"); + + p.process_event(pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Paused should be active"); //Paused + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().exit_counter == 1,"Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().entry_counter == 1,"Paused entry not called correctly"); + + // go back to Playing + p.process_event(end_pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().exit_counter == 1,"Paused exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().entry_counter == 2,"Playing entry not called correctly"); + + p.process_event(pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Paused should be active"); //Paused + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().exit_counter == 2,"Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().entry_counter == 2,"Paused entry not called correctly"); + + p.process_event(stop()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().exit_counter == 2,"Paused exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 2,"Stopped entry not called correctly"); + + p.process_event(stop()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().exit_counter == 2,"Stopped exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 3,"Stopped entry not called correctly"); + } +} + diff --git a/libs/msm/test/SimpleWithFunctors.cpp b/libs/msm/test/SimpleWithFunctors.cpp new file mode 100644 index 0000000000..872368e36a --- /dev/null +++ b/libs/msm/test/SimpleWithFunctors.cpp @@ -0,0 +1,359 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> +// functors +#include <boost/msm/front/functor_row.hpp> +#include <boost/msm/front/euml/common.hpp> +// for And_ operator +#include <boost/msm/front/euml/operator.hpp> + +#define BOOST_TEST_MODULE MyTest +#include <boost/test/unit_test.hpp> + +using namespace std; +namespace msm = boost::msm; +namespace mpl = boost::mpl; +using namespace msm::front; +// for And_ operator +using namespace msm::front::euml; + +namespace +{ + // events + struct play {}; + struct end_pause {}; + struct stop {}; + struct pause {}; + struct open_close {}; + + // A "complicated" event type that carries some data. + enum DiskTypeEnum + { + DISK_CD=0, + DISK_DVD=1 + }; + struct cd_detected + { + cd_detected(std::string name, DiskTypeEnum diskType) + : name(name), + disc_type(diskType) + {} + + std::string name; + DiskTypeEnum disc_type; + }; + + // front-end: define the FSM structure + struct player_ : public msm::front::state_machine_def<player_> + { + unsigned int start_playback_counter; + unsigned int can_close_drawer_counter; + unsigned int test_fct_counter; + + player_(): + start_playback_counter(0), + can_close_drawer_counter(0), + test_fct_counter(0) + {} + + // The list of FSM states + struct Empty : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct Open : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + // sm_ptr still supported but deprecated as functors are a much better way to do the same thing + struct Stopped : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + struct Playing : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + // state not defining any entry or exit + struct Paused : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + // the initial state of the player SM. Must be defined + typedef Empty initial_state; + + // transition actions + struct TestFct + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const&, FSM& fsm,SourceState& ,TargetState& ) + { + ++fsm.test_fct_counter; + } + }; + struct start_playback + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& fsm,SourceState& ,TargetState& ) + { + ++fsm.start_playback_counter; + } + }; + struct open_drawer + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + } + }; + struct close_drawer + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + } + }; + struct store_cd_info + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const&,FSM& fsm ,SourceState& ,TargetState& ) + { + fsm.process_event(play()); + } + }; + struct stop_playback + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + } + }; + struct pause_playback + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + } + }; + struct resume_playback + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + } + }; + struct stop_and_open + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + } + }; + struct stopped_again + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + } + }; + // guard conditions + struct DummyGuard + { + template <class EVT,class FSM,class SourceState,class TargetState> + bool operator()(EVT const& evt,FSM& fsm,SourceState& src,TargetState& tgt) + { + return true; + } + }; + struct good_disk_format + { + template <class EVT,class FSM,class SourceState,class TargetState> + bool operator()(EVT const& evt ,FSM&,SourceState& ,TargetState& ) + { + // to test a guard condition, let's say we understand only CDs, not DVD + if (evt.disc_type != DISK_CD) + { + return false; + } + return true; + } + }; + struct always_true + { + template <class EVT,class FSM,class SourceState,class TargetState> + bool operator()(EVT const& evt ,FSM&,SourceState& ,TargetState& ) + { + return true; + } + }; + struct can_close_drawer + { + template <class EVT,class FSM,class SourceState,class TargetState> + bool operator()(EVT const& evt ,FSM& fsm,SourceState& ,TargetState& ) + { + ++fsm.can_close_drawer_counter; + return true; + } + }; + + typedef player_ p; // makes transition table cleaner + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + Row < Stopped , play , Playing , ActionSequence_ + <mpl::vector< + TestFct,start_playback> > + , DummyGuard >, + Row < Stopped , open_close , Open , open_drawer , none >, + Row < Stopped , stop , Stopped , none , none >, + // +---------+-------------+---------+---------------------------+----------------------+ + Row < Open , open_close , Empty , close_drawer , can_close_drawer >, + // +---------+-------------+---------+---------------------------+----------------------+ + Row < Empty , open_close , Open , open_drawer , none >, + Row < Empty , cd_detected , Stopped , store_cd_info , And_<good_disk_format, + always_true> >, + // +---------+-------------+---------+---------------------------+----------------------+ + Row < Playing , stop , Stopped , stop_playback , none >, + Row < Playing , pause , Paused , pause_playback , none >, + Row < Playing , open_close , Open , stop_and_open , none >, + // +---------+-------------+---------+---------------------------+----------------------+ + Row < Paused , end_pause , Playing , resume_playback , none >, + Row < Paused , stop , Stopped , stop_playback , none >, + Row < Paused , open_close , Open , stop_and_open , none > + + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const&, FSM&,int) + { + BOOST_FAIL("no_transition called!"); + } + // init counters + template <class Event,class FSM> + void on_entry(Event const&,FSM& fsm) + { + fsm.template get_state<player_::Stopped&>().entry_counter=0; + fsm.template get_state<player_::Stopped&>().exit_counter=0; + fsm.template get_state<player_::Open&>().entry_counter=0; + fsm.template get_state<player_::Open&>().exit_counter=0; + fsm.template get_state<player_::Empty&>().entry_counter=0; + fsm.template get_state<player_::Empty&>().exit_counter=0; + fsm.template get_state<player_::Playing&>().entry_counter=0; + fsm.template get_state<player_::Playing&>().exit_counter=0; + fsm.template get_state<player_::Paused&>().entry_counter=0; + fsm.template get_state<player_::Paused&>().exit_counter=0; + } + + }; + // Pick a back-end + typedef msm::back::state_machine<player_> player; + +// static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" }; + + + BOOST_AUTO_TEST_CASE( my_test ) + { + player p; + + p.start(); + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().entry_counter == 1,"Empty entry not called correctly"); + + p.process_event(open_close()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 1,"Open should be active"); //Open + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().exit_counter == 1,"Empty exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().entry_counter == 1,"Open entry not called correctly"); + + p.process_event(open_close()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 2,"Empty should be active"); //Empty + BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().exit_counter == 1,"Open exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().entry_counter == 2,"Empty entry not called correctly"); + BOOST_CHECK_MESSAGE(p.can_close_drawer_counter == 1,"guard not called correctly"); + + p.process_event( + cd_detected("louie, louie",DISK_DVD)); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 2,"Empty should be active"); //Empty + BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().exit_counter == 1,"Open exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().entry_counter == 2,"Empty entry not called correctly"); + + p.process_event( + cd_detected("louie, louie",DISK_CD)); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().exit_counter == 2,"Empty exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 1,"Stopped entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().exit_counter == 1,"Stopped exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().entry_counter == 1,"Playing entry not called correctly"); + BOOST_CHECK_MESSAGE(p.start_playback_counter == 1,"action not called correctly"); + BOOST_CHECK_MESSAGE(p.test_fct_counter == 1,"action not called correctly"); + + p.process_event(pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Paused should be active"); //Paused + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().exit_counter == 1,"Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().entry_counter == 1,"Paused entry not called correctly"); + + // go back to Playing + p.process_event(end_pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().exit_counter == 1,"Paused exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().entry_counter == 2,"Playing entry not called correctly"); + + p.process_event(pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Paused should be active"); //Paused + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().exit_counter == 2,"Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().entry_counter == 2,"Paused entry not called correctly"); + + p.process_event(stop()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().exit_counter == 2,"Paused exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 2,"Stopped entry not called correctly"); + + p.process_event(stop()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().exit_counter == 2,"Stopped exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 3,"Stopped entry not called correctly"); + } +} + diff --git a/libs/msm/test/TestConstructor.cpp b/libs/msm/test/TestConstructor.cpp new file mode 100644 index 0000000000..f862f1efc4 --- /dev/null +++ b/libs/msm/test/TestConstructor.cpp @@ -0,0 +1,267 @@ +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> +#define BOOST_TEST_MODULE MyTest +#include <boost/test/unit_test.hpp> + +namespace msm = boost::msm; +namespace mpl = boost::mpl; + + +namespace +{ + struct SomeExternalContext + { + SomeExternalContext(int b):bla(b){} + int bla; + }; + // events + struct play {}; + struct end_pause {}; + struct stop {}; + struct pause {}; + struct open_close {}; + struct NextSong {}; + struct PreviousSong {}; + + // A "complicated" event type that carries some data. + enum DiskTypeEnum + { + DISK_CD=0, + DISK_DVD=1 + }; + struct cd_detected + { + cd_detected(std::string name, DiskTypeEnum diskType) + : name(name), + disc_type(diskType) + {} + + std::string name; + DiskTypeEnum disc_type; + }; + + // front-end: define the FSM structure + struct player_ : public msm::front::state_machine_def<player_> + { + player_(SomeExternalContext& context,int someint) + :context_(context) + { + BOOST_CHECK_MESSAGE(context_.bla == 3,"Wrong context value"); + BOOST_CHECK_MESSAGE(someint == 5,"Wrong int value"); + context.bla = 10; + } + + SomeExternalContext& context_; + + // The list of FSM states + struct Empty : public msm::front::state<> + { + int data_; + Empty():data_(0){} + Empty(int i):data_(i){} + // every (optional) entry/exit methods get the event passed. + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Empty" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Empty" << std::endl;} + }; + struct Open : public msm::front::state<> + { + int data_; + Open():data_(0){} + Open(int i):data_(i){} + + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) {std::cout << "entering: Open" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Open" << std::endl;} + }; + + // sm_ptr still supported but deprecated as functors are a much better way to do the same thing + struct Stopped : public msm::front::state<msm::front::default_base_state,msm::front::sm_ptr> + { + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) {std::cout << "entering: Stopped" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Stopped" << std::endl;} + void set_sm_ptr(player_* pl) + { + m_player=pl; + } + player_* m_player; + }; + + struct Playing_ : public msm::front::state_machine_def<Playing_> + { + // when playing, the CD is loaded and we are in either pause or playing (duh) + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Playing" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Playing" << std::endl;} + + // The list of FSM states + struct Song1 : public msm::front::state<> + { + int data_; + Song1():data_(0){} + Song1(int i):data_(i){} + + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: First song" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: First Song" << std::endl;} + + }; + struct Song2 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: Second song" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: Second Song" << std::endl;} + }; + struct Song3 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: Third song" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: Third Song" << std::endl;} + }; + // the initial state. Must be defined + typedef Song1 initial_state; + // transition actions + void start_next_song(NextSong const&) { std::cout << "Playing::start_next_song\n"; } + void start_prev_song(PreviousSong const&) { std::cout << "Playing::start_prev_song\n"; } + // guard conditions + + typedef Playing_ pl; // makes transition table cleaner + // Transition table for Playing + struct transition_table : mpl::vector4< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Song1 , NextSong , Song2 , &pl::start_next_song >, + a_row < Song2 , PreviousSong, Song1 , &pl::start_prev_song >, + a_row < Song2 , NextSong , Song3 , &pl::start_next_song >, + a_row < Song3 , PreviousSong, Song2 , &pl::start_prev_song > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + // back-end + typedef msm::back::state_machine<Playing_> Playing; + + // state not defining any entry or exit + struct Paused : public msm::front::state<> + { + }; + + // the initial state of the player SM. Must be defined + typedef Empty initial_state; + + // transition actions + void start_playback(play const&) { std::cout << "player::start_playback\n"; } + void open_drawer(open_close const&) { std::cout << "player::open_drawer\n"; } + void close_drawer(open_close const&) { std::cout << "player::close_drawer\n"; } + void store_cd_info(cd_detected const&) { std::cout << "player::store_cd_info\n"; } + void stop_playback(stop const&) { std::cout << "player::stop_playback\n"; } + void pause_playback(pause const&) { std::cout << "player::pause_playback\n"; } + void resume_playback(end_pause const&) { std::cout << "player::resume_playback\n"; } + void stop_and_open(open_close const&) { std::cout << "player::stop_and_open\n"; } + void stopped_again(stop const&) {std::cout << "player::stopped_again\n";} + // guard conditions + bool good_disk_format(cd_detected const& evt) + { + // to test a guard condition, let's say we understand only CDs, not DVD + if (evt.disc_type != DISK_CD) + { + std::cout << "wrong disk, sorry" << std::endl; + return false; + } + return true; + } + // used to show a transition conflict. This guard will simply deactivate one transition and thus + // solve the conflict + bool auto_start(cd_detected const&) + { + return false; + } + + typedef player_ p; // makes transition table cleaner + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Stopped , play , Playing , &p::start_playback >, + a_row < Stopped , open_close , Open , &p::open_drawer >, + _row < Stopped , stop , Stopped >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Open , open_close , Empty , &p::close_drawer >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Empty , open_close , Open , &p::open_drawer >, + row < Empty , cd_detected , Stopped , &p::store_cd_info ,&p::good_disk_format >, + row < Empty , cd_detected , Playing , &p::store_cd_info ,&p::auto_start >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Playing , stop , Stopped , &p::stop_playback >, + a_row < Playing , pause , Paused , &p::pause_playback >, + a_row < Playing , open_close , Open , &p::stop_and_open >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Paused , end_pause , Playing , &p::resume_playback >, + a_row < Paused , stop , Stopped , &p::stop_playback >, + a_row < Paused , open_close , Open , &p::stop_and_open > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + BOOST_FAIL("no_transition called!"); + } + }; + // Pick a back-end + typedef msm::back::state_machine<player_> player; + + + BOOST_AUTO_TEST_CASE( my_test ) + { + SomeExternalContext ctx(3); + player p1(boost::ref(ctx),5); + BOOST_CHECK_MESSAGE(p1.context_.bla == 10,"Wrong returned context value"); + + ctx.bla = 3; + player p2(msm::back::states_ << player_::Empty(1),boost::ref(ctx),5); + BOOST_CHECK_MESSAGE(p2.get_state<player_::Empty&>().data_ == 1,"Wrong Empty value"); + + p2.set_states(msm::back::states_ << player_::Empty(5)); + BOOST_CHECK_MESSAGE(p2.get_state<player_::Empty&>().data_ == 5,"Wrong Empty value"); + + p2.set_states(msm::back::states_ << player_::Empty(7) << player_::Open(2)); + BOOST_CHECK_MESSAGE(p2.get_state<player_::Empty&>().data_ == 7,"Wrong Empty value"); + BOOST_CHECK_MESSAGE(p2.get_state<player_::Open&>().data_ == 2,"Wrong Open value"); + + ctx.bla = 3; + player p(msm::back::states_ << player_::Empty(1) + << player_::Playing(msm::back::states_ << player_::Playing_::Song1(8)), + boost::ref(ctx),5); + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().get_state<player_::Playing_::Song1&>().data_ == 8,"Wrong Open value"); + } +} + |