The hana state machine (hsm) is a finite state machine library based on the boost hana meta programming library. It follows the principles of the boost msm and boost sml libraries, but tries to reduce own complex meta programming code to a minimum.
The following table compares features among popular c++ state machine libraries. A click on a particular feature check mark will forward to the feature documentation.
Feature | Hsm | Sml | Msm | Statechart |
---|---|---|---|---|
External transition | ✓ | ✓ | ✓ | ✓ |
Anonymous transition (Completion) | ✓ | ✓ | ✓ | ✗ |
Internal transition | ✓ | ✓ | ✓ | ✓ |
Direct transition | ✓ | ✗ | ✓ | ✗ |
Guards / actions | ✓ | ✓ | ✓ | ✓ |
Entry / exit actions | ✓ | ✓ | ✓ | ✓ |
Orthogonal regions | ✓ | ✓ | ✓ | ✓ |
Hierachies / sub state machines | ✓ | ✓ | ✓ | ✓ |
Event defering | ✓ | ✓ | ✓ | ✓ |
Transition logging | ✓ | ✓ | ✓ | ? |
Initial pseudo state | ✓ | ✓ | ✓ | ✓ |
History pseudo state | ✓ | ✓ | ✓ | ✓ |
eUml postfix frontend | ✓ | ✓ | ✓ | ✗ |
eUml prefix frontend | ✓ | ✓ | ✓ | ✗ |
Entry / exit pseudo state | ✓ | ✗ | ✓ | ✗ |
State data members | ✓ | ✓ | ✓ | ✓ |
Unexpected event / no transition handler | ✓ | ✗ | ✓ | ✗ |
Dependency injection | ✓ | ✓ | ✗ | ✗ |
Single amalgamation header | ✓ | ✓ | ✗ | ✗ |
Custom target state construction | ✓ | ✗ | ✗ | ✗ |
Chain actions | ✓ | ✓ | ✓ | ? |
Example (Run)
#include "hsm/hsm.h"
#include <iostream>
#include <cassert>
// States
struct Locked {
};
struct Unlocked {
};
// Events
struct Push {
};
struct Coin {
};
// Guards
const auto noError = [](auto /*event*/, auto /*source*/, auto /*target*/) { return true; };
// Actions
constexpr auto beep
= [](auto /*event*/, auto /*source*/, auto /*target*/) { std::cout << "beep!" << std::endl; };
constexpr auto blink = [](auto /*event*/, auto /*source*/, auto /*target*/) {
std::cout << "blink, blink, blink!" << std::endl;
};
struct Turnstile {
static constexpr auto make_transition_table()
{
// clang-format off
return hsm::transition_table(
// Source + Event [Guard] / Action = Target
// +-------------------+-----------------+---------+--------+----------------------+
* hsm::state<Locked> + hsm::event<Push> / beep = hsm::state<Locked> ,
hsm::state<Locked> + hsm::event<Coin> [noError] / blink = hsm::state<Unlocked>,
// +--------------------+---------------------+---------+--------+------------------------+
hsm::state<Unlocked> + hsm::event<Push> [noError] = hsm::state<Locked> ,
hsm::state<Unlocked> + hsm::event<Coin> / blink = hsm::state<Unlocked>
// +--------------------+---------------------+---------+--------+------------------------+
);
// clang-format on
}
};
auto main() -> int
{
hsm::sm<Turnstile> turnstileSm;
// The turnstile is initially locked
assert(turnstileSm.is(hsm::state<Locked>));
// Inserting a coin unlocks it
turnstileSm.process_event(Coin {});
assert(turnstileSm.is(hsm::state<Unlocked>));
// Entering the turnstile will lock it again
turnstileSm.process_event(Push {});
assert(turnstileSm.is(hsm::state<Locked>));
return 0;
}
- Follow the link to the compiler explorer: https://godbolt.org/z/r9sTrMfqE
The benchmark result are taken from the state machine benchmark repository.
Benchmark | Hsm | Sml | Msm | Statechart |
---|---|---|---|---|
Simple state machine | 99 ms | 17 ms | 18 ms | 443 ms |
Complex state machine | 818 ms | 978 ms | 881 ms | 1374 ms |
Benchmark | Hsm | Sml | Msm | Statechart |
---|---|---|---|---|
Simple state machine | 6.41 s | 0.62 s | 5.17 s | 1.52 s |
Complex state machine | 41.99 s | 3.01 s | 25.54 s | 4.27 s |
- Boost 1.72
- C++17
- >= g++-8
- >= clang-8
- Cmake 3.14
- Gtest
- Download amalgamation header and put it into your project src folder
- Include amalgamation header:
#include "path/to/amalgamation/header/hsm.h"
To use this library from a CMake project, you can locate it directly with find_package() and use the namespaced imported target from the generated package configuration:
# CMakeLists.txt
find_package(hsm 1.3.5 REQUIRED)
...
add_library(foo ...)
...
target_link_libraries(foo PRIVATE hsm::hsm)
cmake -S . -B build
cmake --install build/ --prefix /tmp/
mkdir -p build/dependencies/conan
conan install . -if build/dependencies/conan -s compiler.libcxx=libstdc++11 --build missing
cmake -S . -B build
cmake --install build/ --prefix /tmp/ -D "CMAKE_MODULE_PATH=${PWD}/build/dependencies/conan"
conan remote add conan-erikzenker https://api.bintray.com/conan/erikzenker/conan-erikzenker
conan install hsm/1.0@erikzenker/testing --build missing
pacaur -S hsm-git
cmake -S test -B build
cmake --build build/test
cd build/test
ctest --output-on-failure
- erikzenker(at)hotmail.com