Skip to content

jfayot/dynamic-state-machine

Repository files navigation

Dynamic StateMachine

A Dynamic StateMachine where states and transitions are created runtime.

Project status

GitHub Release Project Status: Active – The project has reached a stable, usable state and is being actively developed. License: MIT

Build status

Windows Build Status Linux Build Status MacOS Build Status

Quality

Tests Status codecov CodeQL Coverity scan Codacy Badge

Features

  • Flat-style and enclosed-style setup
  • Unlimited number of states and transitions
  • Hierarchical states
  • Entry and exit actions
  • Internal and external transitions
  • Transition actions
  • Transition guard conditions
  • State history (deep and shallow)
  • Event processing
  • Event deferring
  • Event posting
  • Orthogonal regions
  • Optional shared storage
  • Visitable
  • Observable (check current active states)
  • Exception handling
  • Platform independent, C++17
  • Header only
  • No external dependencies except STL
  • Moderate use of templates

Build and install

cmake -B build -DCMAKE_INSTALL_PREFIX=/path/to/install/directory
cmake --build build --target install

Usage

Either build and install dsm package and then use cmake's findPackage to include it in your project:

cmake_minimum_required(VERSION 3.16)

project(your_project
  LANGUAGES CXX
)

add_executable(your_target ${CMAKE_CURRENT_LIST_DIR}/main.cpp)

findPackage(dsm VERSION 0.1.1 REQUIRED)

target_link_libraries(your_target PRIVATE dsm::dsm)

Or include it directly in you project using CPM:

cmake_minimum_required(VERSION 3.16)

project(your_project
  LANGUAGES CXX
)

# download CPM.cmake
file(
  DOWNLOAD
  https://github.com/cpm-cmake/CPM.cmake/releases/download/v0.40.2/CPM.cmake
  ${CMAKE_CURRENT_BINARY_DIR}/cmake/CPM.cmake
  EXPECTED_HASH SHA256=c8cdc32c03816538ce22781ed72964dc864b2a34a310d3b7104812a5ca2d835d
)

include(${CMAKE_CURRENT_BINARY_DIR}/cmake/CPM.cmake)

CPMAddPackage("gh:jfayot/dynamic-state-machine@0.1.1")

add_executable(your_target ${CMAKE_CURRENT_LIST_DIR}/main.cpp)

target_link_libraries(your_target PRIVATE dsm::dsm)

Basic example

Considering the following minimal state machine, it can be coded as follows:

basic-uml

Flat-style StateMachine setup

minimal_flat.cpp

#include "dsm/dsm.hpp"
#include <cassert>

using namespace dsm;

struct e1 : Event<e1> {};

struct minimal : StateMachine<minimal> {};
struct s0 : State<s0, minimal> {};
struct s1 : State<s1, minimal> {};

int main()
{
    minimal sm;

    sm.addState<s0, Entry>();
    sm.addState<s1>();
    sm.addTransition<s0, e1, s1>();

    sm.start();
    assert(sm.checkStates<s0>());
    std::cout << sm << std::endl;
    sm.processEvent(e1{});
    assert(sm.checkStates<s1>());
    std::cout << sm << std::endl;
}

Enclosed-style StateMachine setup

minimal_enclosed.cpp

#include "dsm/dsm.hpp"
#include <cassert>

using namespace dsm;

struct minimal;
struct e1 : Event<e1> {};
struct s1 : State<s1, minimal> {};
struct s0 : State<s0, minimal> {
    TTransitions getTransitions() override {
        return { createTransition<e1, s1>() };
    }
};
struct minimal : StateMachine<minimal> {
    TStates getStates() override {
        return { createState<s0, Entry>(), createState<s1>() };
    }
};

int main()
{
    minimal sm;

    sm.setup();

    sm.start();
    assert(sm.checkStates<s0>());
    std::cout << sm << std::endl;
    sm.processEvent(e1{});
    assert(sm.checkStates<s1>());
    std::cout << sm << std::endl;
}

Other examples

Title UML Source file
entry/exit actions entry_exit_actions entry_exit_actions.cpp
transition action transition_action transition_action.cpp
transition guard transition_guard transition_guard.cpp
self transition self_transition self_transition.cpp
composite state composite_state composite_state.cpp
shallow history shallow_history shallow_history.cpp
deep history deep_history deep_history.cpp
orthogonal regions orthogonal_regions orthogonal_regions.cpp