Skip to content

Instantly share code, notes, and snippets.

@rpoisel
Created May 13, 2023 12:43
Show Gist options
  • Save rpoisel/bada82555a1b08c98f41f6e72616e50a to your computer and use it in GitHub Desktop.
Save rpoisel/bada82555a1b08c98f41f6e72616e50a to your computer and use it in GitHub Desktop.
Realtime FSM implemented in modern C++
#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