Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 104 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -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{});
}
```
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand All @@ -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>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@ namespace mp11 = boost::mp11;
namespace
{

// Events.
// Events
struct InterruptEvent {};
struct SmInternalEvent {};

// Actions.
// Actions
struct Interrupt
{
template <typename Fsm>
Expand All @@ -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_>
{
Expand Down
2 changes: 1 addition & 1 deletion doc/modules/ROOT/attachments/backmp11/Logger.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ class Logger : public back::default_observer
}
};

// Events.
// Events
struct TransitionEvent {};
struct InternalTransitionEvent {};
struct SmInternalTransitionEvent {};
Expand Down
76 changes: 76 additions & 0 deletions doc/modules/ROOT/attachments/backmp11/MinimalExample.cpp
Original file line number Diff line number Diff line change
@@ -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{});
}
12 changes: 6 additions & 6 deletions doc/modules/ROOT/attachments/backmp11/StateMachineInterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ namespace mp11 = boost::mp11;
namespace
{

// State machine interface.
// State machine interface
class state_machine
{
public:
Expand All @@ -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
Expand Down Expand Up @@ -97,10 +97,10 @@ class state_machine_impl
}
};

// Events.
// Events
struct Greet {};

// Actions.
// Actions
struct PrintMessage
{
template <typename Fsm, typename Source, typename Target>
Expand All @@ -115,7 +115,7 @@ struct PrintMessage
[[maybe_unused]] static inline bool talk{true};
};

// States.
// States
template <typename Derived>
struct StateBase : front::state<>
{
Expand All @@ -130,7 +130,7 @@ struct MyState : StateBase<MyState> {};

struct MyOtherState : StateBase<MyOtherState> {};

// State machines.
// State machines

struct MyStateMachine_ : front::state_machine_def<MyStateMachine_>
{
Expand Down
8 changes: 4 additions & 4 deletions doc/modules/ROOT/attachments/backmp11/Visitor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,15 @@ namespace mp11 = boost::mp11;
namespace
{

// Events.
// Events
struct Play
{
std::string_view song;
};

struct Stop {};

// States.
// States
struct Idle : front::state<> {};

template <const char* Name>
Expand All @@ -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
{
Expand All @@ -64,7 +64,7 @@ struct IsSong
}
};

// State machine.
// State machine
struct Playing_ : front::state_machine_def<Playing_>
{
template <typename Fsm>
Expand Down
4 changes: 2 additions & 2 deletions doc/modules/ROOT/pages/backmp11-back-end.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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)`
Expand Down Expand Up @@ -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
Expand Down
Loading
Loading