mirror of
https://github.com/boostorg/msm.git
synced 2026-07-21 13:23:45 +00:00
doc: refinements
This commit is contained in:
committed by
Christian Granzin
parent
863e2cf900
commit
baaf752ff4
@@ -1,3 +1,104 @@
|
||||
# msm
|
||||
Boost.org msm module
|
||||
[Please look at the generated documentation](https://www.boost.org/doc/libs/master/doc/antora/msm/index.html)
|
||||
# Meta State Machine (MSM)
|
||||
|
||||
MSM is a Boost library that uses C++ metaprogramming for creating high-performance UML2 finite state machines.
|
||||
It separates state machine modeling (front-end) from execution (back-end), allowing each to be chosen independently.
|
||||
The classic `back` back-end supports C++03, the `backmp11` back-end requires C++17.
|
||||
|
||||
The full documentation is available on Boost ([latest release](https://www.boost.org/doc/libs/latest/doc/antora/msm/index.html), [develop](https://www.boost.org/doc/libs/develop/doc/antora/msm/index.html)).
|
||||
|
||||
# Features
|
||||
|
||||
Front-end:
|
||||
|
||||
- State transitions (external, internal, completion) with actions and guards
|
||||
- State entry & exit actions
|
||||
- Deferred events — via state property and transition action
|
||||
- Kleene (any) events — match any event in a transition
|
||||
- Event hierarchies — use inheritance to match events in a transition
|
||||
- Hierarchical state machines (composite states)
|
||||
- Orthogonal regions
|
||||
- History
|
||||
- Pseudostates — entry, exit, fork, terminate
|
||||
- State flags
|
||||
|
||||
Back-end (`backmp11`):
|
||||
|
||||
- Shared context in state machine hierarchies — data and root machine reference accessible across all submachines
|
||||
- Observer — hook into state machine activities for logging and monitoring
|
||||
- State visitor — traverse through active or all states
|
||||
- Serialization — built-in support for Boost.Serialization, Boost.JSON, and nlohmann/json
|
||||
- Compile-time trade-offs — choose between optimizing for runtime speed or compile time
|
||||
- Heapless execution — configurable to avoid dynamic memory allocations
|
||||
- Virtual dispatch support — encapsulate implementation details and swap implementations at runtime
|
||||
|
||||
# Usage
|
||||
|
||||
This example models a connection to a host. It uses the functor front-end and the `backmp11` back-end. More examples are available in the documentation.
|
||||
|
||||
```cpp
|
||||
#include <iostream>
|
||||
|
||||
#include <boost/msm/backmp11/state_machine.hpp>
|
||||
#include <boost/msm/front/functor_row.hpp>
|
||||
#include <boost/msm/front/state_machine_def.hpp>
|
||||
|
||||
namespace mp11 = boost::mp11;
|
||||
namespace back = boost::msm::backmp11;
|
||||
namespace front = boost::msm::front;
|
||||
|
||||
// Events
|
||||
struct Connect
|
||||
{
|
||||
std::string host;
|
||||
};
|
||||
struct Disconnect {};
|
||||
|
||||
// Guards
|
||||
struct IsValidHost
|
||||
{
|
||||
template <typename Fsm>
|
||||
bool operator()(const Connect& event, Fsm&)
|
||||
{
|
||||
return !event.host.empty();
|
||||
}
|
||||
};
|
||||
|
||||
// Actions
|
||||
struct LogConnection
|
||||
{
|
||||
template <typename Fsm>
|
||||
void operator()(const Connect& event, Fsm& fsm)
|
||||
{
|
||||
std::cout << "Connected to " << event.host
|
||||
<< " (connection #" << ++fsm.connection_count << ")"
|
||||
<< std::endl;
|
||||
}
|
||||
};
|
||||
|
||||
// States
|
||||
struct Disconnected : front::state<> {};
|
||||
struct Connected : front::state<> {};
|
||||
|
||||
// State machine
|
||||
struct Connection_ : front::state_machine_def<Connection_>
|
||||
{
|
||||
using initial_state = Disconnected;
|
||||
using transition_table = mp11::mp_list<
|
||||
// Source Event Target Action Guard
|
||||
front::Row<Disconnected, Connect, Connected , LogConnection, IsValidHost>,
|
||||
front::Row<Connected , Disconnect, Disconnected>
|
||||
>;
|
||||
|
||||
size_t connection_count = 0;
|
||||
};
|
||||
using Connection = back::state_machine<Connection_>;
|
||||
|
||||
int main()
|
||||
{
|
||||
Connection sm;
|
||||
sm.start();
|
||||
sm.process_event(Connect{""}); // rejected by IsValidHost
|
||||
sm.process_event(Connect{"localhost"}); // prints "Connected to localhost (connection #1)"
|
||||
sm.process_event(Disconnect{});
|
||||
}
|
||||
```
|
||||
|
||||
@@ -41,14 +41,14 @@ struct MyConfig : back::state_machine_config
|
||||
/*InlineCapacity=*/max_event_size>;
|
||||
};
|
||||
|
||||
// Events.
|
||||
// Events
|
||||
struct Greet
|
||||
{
|
||||
// A heapless event must be copy constructible and nothrow move constructible.
|
||||
std::array<char, max_event_size> message{};
|
||||
};
|
||||
|
||||
// Actions.
|
||||
// Actions
|
||||
struct PrintMessage
|
||||
{
|
||||
template <typename Fsm>
|
||||
@@ -58,10 +58,10 @@ struct PrintMessage
|
||||
}
|
||||
};
|
||||
|
||||
// States.
|
||||
// States
|
||||
struct MyState : front::state<> {};
|
||||
|
||||
// State machine.
|
||||
// State machine
|
||||
struct MyStateMachine_ : front::state_machine_def<MyStateMachine_>
|
||||
{
|
||||
template <typename Fsm>
|
||||
|
||||
@@ -24,11 +24,11 @@ namespace mp11 = boost::mp11;
|
||||
namespace
|
||||
{
|
||||
|
||||
// Events.
|
||||
// Events
|
||||
struct InterruptEvent {};
|
||||
struct SmInternalEvent {};
|
||||
|
||||
// Actions.
|
||||
// Actions
|
||||
struct Interrupt
|
||||
{
|
||||
template <typename Fsm>
|
||||
@@ -52,12 +52,12 @@ struct PrintMessage
|
||||
[[maybe_unused]] static inline bool talk{true};
|
||||
};
|
||||
|
||||
// States.
|
||||
// States
|
||||
struct MyState : front::state<> {};
|
||||
|
||||
struct MyOtherState : front::state<> {};
|
||||
|
||||
// State machine.
|
||||
// State machine
|
||||
struct InterruptibleStateMachine_
|
||||
: front::state_machine_def<InterruptibleStateMachine_>
|
||||
{
|
||||
|
||||
@@ -144,7 +144,7 @@ class Logger : public back::default_observer
|
||||
}
|
||||
};
|
||||
|
||||
// Events.
|
||||
// Events
|
||||
struct TransitionEvent {};
|
||||
struct InternalTransitionEvent {};
|
||||
struct SmInternalTransitionEvent {};
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
// Copyright 2026 Christian Granzin
|
||||
// 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/backmp11/state_machine.hpp>
|
||||
#include <boost/msm/front/functor_row.hpp>
|
||||
#include <boost/msm/front/state_machine_def.hpp>
|
||||
|
||||
namespace mp11 = boost::mp11;
|
||||
namespace back = boost::msm::backmp11;
|
||||
namespace front = boost::msm::front;
|
||||
|
||||
// Events
|
||||
struct Connect
|
||||
{
|
||||
std::string host;
|
||||
};
|
||||
struct Disconnect {};
|
||||
|
||||
// Guards
|
||||
struct IsValidHost
|
||||
{
|
||||
template <typename Fsm>
|
||||
bool operator()(const Connect& event, Fsm&)
|
||||
{
|
||||
return !event.host.empty();
|
||||
}
|
||||
};
|
||||
|
||||
// Actions
|
||||
struct LogConnection
|
||||
{
|
||||
template <typename Fsm>
|
||||
void operator()(const Connect& event, Fsm& fsm)
|
||||
{
|
||||
std::cout << "Connected to " << event.host
|
||||
<< " (connection #" << ++fsm.connection_count << ")"
|
||||
<< std::endl;
|
||||
}
|
||||
};
|
||||
|
||||
// States
|
||||
struct Disconnected : front::state<> {};
|
||||
struct Connected : front::state<> {};
|
||||
|
||||
// State machine
|
||||
struct Connection_ : front::state_machine_def<Connection_>
|
||||
{
|
||||
using initial_state = Disconnected;
|
||||
using transition_table = mp11::mp_list<
|
||||
// Source Event Target Action Guard
|
||||
front::Row<Disconnected, Connect, Connected , LogConnection, IsValidHost>,
|
||||
front::Row<Connected , Disconnect, Disconnected>
|
||||
>;
|
||||
|
||||
size_t connection_count = 0;
|
||||
};
|
||||
using Connection = back::state_machine<Connection_>;
|
||||
|
||||
[[maybe_unused]] void minimal_example()
|
||||
{
|
||||
Connection sm;
|
||||
sm.start();
|
||||
sm.process_event(Connect{""}); // rejected by IsValidHost
|
||||
sm.process_event(Connect{"localhost"}); // prints "Connected to localhost (connection #1)"
|
||||
sm.process_event(Disconnect{});
|
||||
}
|
||||
@@ -27,7 +27,7 @@ namespace mp11 = boost::mp11;
|
||||
namespace
|
||||
{
|
||||
|
||||
// State machine interface.
|
||||
// State machine interface
|
||||
class state_machine
|
||||
{
|
||||
public:
|
||||
@@ -49,7 +49,7 @@ class state_machine
|
||||
const back::any_event& event) = 0;
|
||||
};
|
||||
|
||||
// Implementation of the interface.
|
||||
// Implementation of the interface
|
||||
struct MyConfig : back::state_machine_config
|
||||
{
|
||||
// Use favor compile time, we need type erasure on events
|
||||
@@ -97,10 +97,10 @@ class state_machine_impl
|
||||
}
|
||||
};
|
||||
|
||||
// Events.
|
||||
// Events
|
||||
struct Greet {};
|
||||
|
||||
// Actions.
|
||||
// Actions
|
||||
struct PrintMessage
|
||||
{
|
||||
template <typename Fsm, typename Source, typename Target>
|
||||
@@ -115,7 +115,7 @@ struct PrintMessage
|
||||
[[maybe_unused]] static inline bool talk{true};
|
||||
};
|
||||
|
||||
// States.
|
||||
// States
|
||||
template <typename Derived>
|
||||
struct StateBase : front::state<>
|
||||
{
|
||||
@@ -130,7 +130,7 @@ struct MyState : StateBase<MyState> {};
|
||||
|
||||
struct MyOtherState : StateBase<MyOtherState> {};
|
||||
|
||||
// State machines.
|
||||
// State machines
|
||||
|
||||
struct MyStateMachine_ : front::state_machine_def<MyStateMachine_>
|
||||
{
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace mp11 = boost::mp11;
|
||||
namespace
|
||||
{
|
||||
|
||||
// Events.
|
||||
// Events
|
||||
struct Play
|
||||
{
|
||||
std::string_view song;
|
||||
@@ -33,7 +33,7 @@ struct Play
|
||||
|
||||
struct Stop {};
|
||||
|
||||
// States.
|
||||
// States
|
||||
struct Idle : front::state<> {};
|
||||
|
||||
template <const char* Name>
|
||||
@@ -53,7 +53,7 @@ constexpr const char hey_jude[] = "Hey Jude";
|
||||
constexpr const char all_you_need_is_love[] = "All You Need Is Love";
|
||||
constexpr const char paint_it_black[] = "Paint It Black";
|
||||
|
||||
// Guards.
|
||||
// Guards
|
||||
template <const char* Name>
|
||||
struct IsSong
|
||||
{
|
||||
@@ -64,7 +64,7 @@ struct IsSong
|
||||
}
|
||||
};
|
||||
|
||||
// State machine.
|
||||
// State machine
|
||||
struct Playing_ : front::state_machine_def<Playing_>
|
||||
{
|
||||
template <typename Fsm>
|
||||
|
||||
@@ -188,7 +188,7 @@ To avoid a heap allocation per slot, the back-end uses a small-object optimizati
|
||||
The container must not invalidate existing iterators when `push_back` or `push_front` is called. The required interface is:
|
||||
|
||||
- `push_back(const T&)` — for event enqueueing and deferral
|
||||
- `push_front(const T&)` — for completion transitions
|
||||
- `push_front(const T&)` — for completion transitions and state machine termination
|
||||
- `begin()`
|
||||
- `end()`
|
||||
- `erase(iterator)`
|
||||
@@ -565,7 +565,7 @@ Futhermore, the back-end forwards Kleene events without converting them to `std:
|
||||
|
||||
Not supported, because the interrupt state evaluation requires a visitor to determine whether an interrupt state is active. This significantly increases the state machine's compilation time and runtime as soon as an interrupt state is defined. If needed, the interrupt state feature can be resembled with a state machine extension (see xref:backmp11-back-end/examples.adoc#_back_adapter[`back` adapter example]).
|
||||
|
||||
In `backmp11`, you can extend the state machine API to implement your own interrupt mechanism and tailor it to your needs. See the xref:backmp11-back-end/examples.adoc#_interruptible_state_machine[`interruptible state machine example`], which allows interruptions from any state and enqueues events for later processing instead of discarding them.
|
||||
Alternatively, you can extend the state machine API to implement your own interrupt mechanism and tailor it to your needs. See the xref:backmp11-back-end/examples.adoc#_interruptible_state_machine[`interruptible state machine example`], which allows interruptions from any state and enqueues events for later processing instead of discarding them.
|
||||
|
||||
|
||||
==== Initialization of states in the constructor and the `set_states(...)` method
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
[[founding-idea]]
|
||||
|
||||
= Founding idea
|
||||
|
||||
Let's start with an example taken from the C++ Template Metaprogramming
|
||||
book:
|
||||
|
||||
[source,cpp]
|
||||
----
|
||||
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;
|
||||
}
|
||||
};
|
||||
----
|
||||
|
||||
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.
|
||||
@@ -3,7 +3,7 @@
|
||||
= `front` tutorial
|
||||
|
||||
You will find four front-ends, which are as many state machine description languages, with many more
|
||||
possible. For potential language writers, this document contains a
|
||||
possible. For potential language writers, the documentation contains a
|
||||
xref:internals.adoc#internals-front-back-interface[description of the interface between front-end and back-end].
|
||||
|
||||
The first front-end is an adaptation of the example provided in the
|
||||
|
||||
@@ -6,61 +6,64 @@ 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.
|
||||
|
||||
* When do I need a state machine?
|
||||
+
|
||||
== When do I need a state machine?
|
||||
|
||||
More often than 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:
|
||||
+
|
||||
`void incoming_data(data)`
|
||||
+
|
||||
`{`
|
||||
+
|
||||
`if (data = packet_3 && flag1 = work_done && flag2 > step3)...`
|
||||
+
|
||||
`}`
|
||||
+
|
||||
|
||||
[source,cpp]
|
||||
----
|
||||
void incoming_data(data)
|
||||
{
|
||||
if (data = packet_3 && flag1 = work_done && flag2 > step3) ...
|
||||
}
|
||||
----
|
||||
|
||||
This starts to look like event processing (contained inside data) if
|
||||
some stage of the object life has been achieved (but is ugly).
|
||||
+
|
||||
|
||||
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.
|
||||
+
|
||||
|
||||
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.
|
||||
* Another state machine library? What for?
|
||||
+
|
||||
|
||||
== Another state machine library? What for?
|
||||
|
||||
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:
|
||||
** speed: "state machines are slow" is usually the first criticism you
|
||||
|
||||
* 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.
|
||||
** ease of use: good argument. If you used another library, you are
|
||||
|
||||
* ease of use: good argument. If you used another library, you are
|
||||
probably right. Many state machine definitions will look similar to:
|
||||
+
|
||||
`state s1 = new State; // a state`
|
||||
+
|
||||
`state s2 = new State; // another state`
|
||||
+
|
||||
`event e = new Event; // event`
|
||||
+
|
||||
`s1->addTransition(e,s2); // transition s1 -> s2`
|
||||
+
|
||||
|
||||
[source,cpp]
|
||||
----
|
||||
state s1 = new State; // a state
|
||||
state s2 = new State; // another state
|
||||
event e = new Event; // event
|
||||
s1->addTransition(e, s2); // transition s1 -> s2
|
||||
----
|
||||
|
||||
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
|
||||
@@ -68,27 +71,82 @@ 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.
|
||||
** expressiveness: MSM offers several front-ends and constantly tries to
|
||||
|
||||
* 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:
|
||||
+
|
||||
`state1 = state2 + event [condition] / action`
|
||||
a transition with eUML (one of MSM's front-ends) as `state1 = state2 + event [condition] / action`.
|
||||
+
|
||||
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.
|
||||
** model-driven-development: a common difficulty of a model-driven
|
||||
|
||||
* 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.
|
||||
** features: most developers use only 20% of the richly defined UML
|
||||
|
||||
* 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.
|
||||
+
|
||||
Let us not wait any longer, I hope you will enjoy MSM and have fun with
|
||||
it!
|
||||
|
||||
[[founding-idea]]
|
||||
|
||||
== Founding idea
|
||||
|
||||
Let's start with an example taken from the C++ Template Metaprogramming
|
||||
book:
|
||||
|
||||
[source,cpp]
|
||||
----
|
||||
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;
|
||||
}
|
||||
};
|
||||
----
|
||||
|
||||
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.
|
||||
|
||||
Let us not wait any longer, I hope you will enjoy MSM and have fun with it!
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
* xref:index.adoc[Table of Contents]
|
||||
** xref:preface.adoc[Preface]
|
||||
** xref:founding-idea.adoc[Founding idea]
|
||||
** xref:uml-short-guide.adoc[UML short guide]
|
||||
** xref:front-tutorial.adoc[`front` tutorial]
|
||||
*** xref:front-tutorial/basic-front-end.adoc[Basic front-end]
|
||||
|
||||
@@ -22,6 +22,8 @@
|
||||
|
||||
#include "Utils.hpp"
|
||||
|
||||
#include "attachments/backmp11/MinimalExample.cpp"
|
||||
|
||||
namespace msm = boost::msm;
|
||||
namespace mp11 = boost::mp11;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user