Created
May 13, 2023 12:43
-
-
Save rpoisel/bada82555a1b08c98f41f6e72616e50a to your computer and use it in GitHub Desktop.
Realtime FSM implemented in modern C++
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <csignal> | |
#include <chrono> | |
#include <iostream> | |
#include <optional> | |
#include <thread> | |
#include <variant> | |
template <class Context, class... States> class FSM { | |
public: | |
virtual ~FSM() = default; | |
using StateVariant = std::variant<States...>; | |
using OptionalStateVariant = std::optional<StateVariant>; | |
FSM(StateVariant &&initialState, Context &&context) | |
: curState{std::move(initialState)}, context_{std::move(context)} {} | |
void update() { | |
std::visit([this](auto &state) { state.update(context_); }, curState); | |
auto newState = std::visit( | |
[this](auto &state) -> OptionalStateVariant { | |
return transition(state, context_); | |
}, | |
curState); | |
if (newState) { | |
curState = std::move(newState.value()); | |
} | |
} | |
Context &context() { return context_; } | |
private: | |
StateVariant curState; | |
Context context_; | |
}; | |
static inline std::size_t rand_number(std::size_t min, std::size_t max) { | |
return min + std::rand() / ((RAND_MAX + 1u) / max); | |
} | |
struct StateA; | |
struct StateB; | |
struct StateC; | |
struct ABCContext { | |
std::uint8_t cnt_sigint = 0; | |
}; | |
using StateVariant = std::variant<StateA, StateB, StateC>; | |
using OptionalStateVariant = std::optional<StateVariant>; | |
struct StateA { | |
StateA(std::size_t c) : cycles{c}, cycle{0} {} | |
void update(ABCContext &context) { | |
std::cout << "StateA: " << ++cycle << " of " << cycles << std::endl; | |
} | |
std::size_t cycles; | |
std::size_t cycle; | |
}; | |
struct StateB { | |
void update(ABCContext &context) { std::cout << "StateB" << std::endl; } | |
}; | |
struct StateC { | |
void update(ABCContext &context) { | |
std::cout << "StateC (final state)" << std::endl; | |
} | |
}; | |
OptionalStateVariant transition(StateA &stateA, ABCContext &context) { | |
if (context.cnt_sigint > 0) { | |
return StateC{}; | |
} else if (stateA.cycle == stateA.cycles) { | |
return StateB(); | |
} | |
return std::nullopt; | |
} | |
OptionalStateVariant transition(StateB &stateB, ABCContext &context) { | |
if (context.cnt_sigint > 0) { | |
return StateC{}; | |
} | |
return StateA(rand_number(1, 6)); | |
} | |
OptionalStateVariant transition(StateC &stateC, ABCContext &context) { | |
return std::nullopt; | |
} | |
static FSM<ABCContext, StateA, StateB, StateC> fsm{StateB{}, | |
std::move(ABCContext{})}; | |
auto main() noexcept -> int { | |
using namespace std::chrono_literals; | |
std::srand(std::time(nullptr)); | |
std::signal(SIGINT, [](int signal) -> void { ++fsm.context().cnt_sigint; }); | |
while (fsm.context().cnt_sigint < 2) { | |
fsm.update(); | |
std::this_thread::sleep_for(500ms); | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment