9#include <unordered_map>
16#ifdef FSMGINE_MULTI_THREADED
26template<
typename TEvent>
29template<
typename TEvent>
39 : std::runtime_error(
"FSM state not found: " + state) {}
48 : std::runtime_error(
"FSM has not been initialized with a state") {}
58 : std::invalid_argument(message) {}
93template<
typename TEvent = std::monostate>
98 using Predicate = std::function<bool(
const TEvent&)>;
102 using Action = std::function<void(
const TEvent&)>;
107 std::vector<Action> on_enter_actions;
108 std::vector<Action> on_exit_actions;
109 std::vector<Transition<TEvent>> transitions;
111 StateData() =
default;
112 StateData(
const StateData&) =
delete;
113 StateData& operator=(
const StateData&) =
delete;
114 StateData(StateData&&) =
default;
115 StateData& operator=(StateData&&) =
default;
124 FSM& operator=(
const FSM&) =
delete;
129#ifdef FSMGINE_MULTI_THREADED
130 std::unique_lock<std::mutex> lock(other.mutex_);
132 states_ = std::move(other.states_);
133 current_state_ = other.current_state_;
134 has_initial_state_ = other.has_initial_state_;
141 if (
this != &other) {
142#ifdef FSMGINE_MULTI_THREADED
143 std::unique_lock<std::mutex> lock(mutex_);
144 std::unique_lock<std::mutex> other_lock(other.mutex_);
146 states_ = std::move(other.states_);
147 current_state_ = other.current_state_;
148 has_initial_state_ = other.has_initial_state_;
192 static_assert(std::is_same_v<TEvent, std::monostate>,
"process() can only be used with event-less FSMs (FSM<> or FSM<std::monostate>).");
193 return process(std::monostate{});
205 void addOnEnterAction(std::string_view state,
Action action);
208 void addOnExitAction(std::string_view state,
Action action);
210 std::unordered_map<std::string_view, StateData> states_;
211 std::string_view current_state_;
212 bool has_initial_state_ =
false;
214#ifdef FSMGINE_MULTI_THREADED
215 mutable std::mutex mutex_;
219 StateData& getOrCreateState(std::string_view state);
220 void executeOnExitActions(std::string_view state,
const TEvent& event)
const;
221 void executeOnEnterActions(std::string_view state,
const TEvent& event)
const;
226template<
typename TEvent>
231template<
typename TEvent>
233#ifdef FSMGINE_MULTI_THREADED
234 std::unique_lock<std::mutex> lock(mutex_);
239 auto interned_state = interner.intern(state);
242 auto it = states_.find(interned_state);
243 if (it == states_.end()) {
245 std::string error_msg;
246 error_msg.reserve(50 + state.size());
247 error_msg.append(
"Cannot set initial state to undefined state: ");
248 error_msg.append(state);
252 current_state_ = interned_state;
253 has_initial_state_ =
true;
256 static const TEvent dummy_event{};
257 executeOnEnterActions(current_state_, dummy_event);
260template<
typename TEvent>
262#ifdef FSMGINE_MULTI_THREADED
263 std::unique_lock<std::mutex> lock(mutex_);
268 auto interned_state = interner.intern(state);
271 auto it = states_.find(interned_state);
272 if (it == states_.end()) {
274 std::string error_msg;
275 error_msg.reserve(50 + state.size());
276 error_msg.append(
"Cannot set current state to undefined state: ");
277 error_msg.append(state);
282 static const TEvent dummy_event{};
283 if (has_initial_state_ && current_state_ != interned_state) {
284 executeOnExitActions(current_state_, dummy_event);
287 current_state_ = interned_state;
288 has_initial_state_ =
true;
290 executeOnEnterActions(current_state_, dummy_event);
293template<
typename TEvent>
295#ifdef FSMGINE_MULTI_THREADED
296 std::unique_lock<std::mutex> lock(mutex_);
299 if (!has_initial_state_) {
303 auto it = states_.find(current_state_);
304 if (it == states_.end()) {
308 const auto& state_data = it->second;
310 for (
const auto& transition : state_data.transitions) {
311 if (transition.predicatesPass(event)) {
312 auto target_state = transition.getTargetState();
314 if (target_state.empty()) {
319 auto target_it = states_.find(target_state);
320 if (target_it == states_.end()) {
324 transition.executeActions(event);
326 if (current_state_ != target_state) {
327 executeOnExitActions(current_state_, event);
328 current_state_ = target_state;
329 executeOnEnterActions(current_state_, event);
339template<
typename TEvent>
341#ifdef FSMGINE_MULTI_THREADED
342 std::unique_lock<std::mutex> lock(mutex_);
345 if (!has_initial_state_) {
349 return current_state_;
352template<
typename TEvent>
354#ifdef FSMGINE_MULTI_THREADED
355 std::unique_lock<std::mutex> lock(mutex_);
360 auto interned_from_state = interner.intern(from_state);
361 auto& state_data = getOrCreateState(interned_from_state);
364 if (!target_state.empty()) {
365 auto interned_target_state = interner.intern(target_state);
366 getOrCreateState(interned_target_state);
369 state_data.transitions.push_back(std::move(transition));
372template<
typename TEvent>
373void FSM<TEvent>::addOnEnterAction(std::string_view state, Action action) {
374#ifdef FSMGINE_MULTI_THREADED
375 std::unique_lock<std::mutex> lock(mutex_);
380 auto interned_state = interner.intern(state);
381 auto& state_data = getOrCreateState(interned_state);
384 state_data.on_enter_actions.push_back(std::move(action));
388template<
typename TEvent>
389void FSM<TEvent>::addOnExitAction(std::string_view state, Action action) {
390#ifdef FSMGINE_MULTI_THREADED
391 std::unique_lock<std::mutex> lock(mutex_);
396 auto interned_state = interner.intern(state);
397 auto& state_data = getOrCreateState(interned_state);
400 state_data.on_exit_actions.push_back(std::move(action));
404template<
typename TEvent>
405typename FSM<TEvent>::StateData& FSM<TEvent>::getOrCreateState(std::string_view state) {
406 auto it = states_.find(state);
407 if (it == states_.end()) {
408 auto [inserted_it, success] = states_.emplace(state, StateData{});
409 return inserted_it->second;
414template<
typename TEvent>
415void FSM<TEvent>::executeOnExitActions(std::string_view state,
const TEvent& event)
const {
416 auto it = states_.find(state);
417 if (it != states_.end()) {
418 for (
const auto& action : it->second.on_exit_actions) {
424template<
typename TEvent>
425void FSM<TEvent>::executeOnEnterActions(std::string_view state,
const TEvent& event)
const {
426 auto it = states_.find(state);
427 if (it != states_.end()) {
428 for (
const auto& action : it->second.on_enter_actions) {
String interning utility for memory-efficient state name storage.
State transition representation with guards and actions.
Main builder class for constructing FSMs with a fluent interface.
Definition FSMBuilder.hpp:112
Exception thrown for invalid state operations.
Definition FSM.hpp:53
FSMInvalidStateError(const std::string &message)
Constructs an exception for invalid state operations.
Definition FSM.hpp:57
Exception thrown when FSM operations are attempted before initialization.
Definition FSM.hpp:44
FSMNotInitializedError()
Constructs an exception for uninitialized FSM.
Definition FSM.hpp:47
Exception thrown when attempting to access a state that doesn't exist.
Definition FSM.hpp:34
FSMStateNotFoundError(const std::string &state)
Constructs an exception for a missing state.
Definition FSM.hpp:38
A high-performance finite state machine implementation.
Definition FSM.hpp:94
bool process(const TEvent &event)
Processes an event and potentially transitions to a new state.
Definition FSM.hpp:294
FSM()=default
Default constructor.
FSM & operator=(FSM &&other) noexcept
Move assignment operator.
Definition FSM.hpp:140
FSMBuilder< TEvent > get_builder()
Creates a builder for fluent FSM construction.
Definition FSM.hpp:227
std::string_view getCurrentState() const
Gets the name of the current state.
Definition FSM.hpp:340
std::function< void(const TEvent &)> Action
Type alias for transition actions.
Definition FSM.hpp:102
FSM(FSM &&other) noexcept
Move constructor.
Definition FSM.hpp:128
bool process()
Processes a transition for event-less FSMs.
Definition FSM.hpp:191
std::function< bool(const TEvent &)> Predicate
Type alias for transition predicates.
Definition FSM.hpp:98
void setCurrentState(std::string_view state)
Changes the current state of the FSM.
Definition FSM.hpp:261
void setInitialState(std::string_view state)
Sets the initial state of the FSM.
Definition FSM.hpp:232
static StringInterner & instance()
Gets the singleton instance of StringInterner.
Builder for creating transitions with a fluent interface.
Definition FSMBuilder.hpp:37
Represents a transition between states in a finite state machine.
Definition Transition.hpp:41
std::string_view getTargetState() const
Gets the target state for this transition.
Definition Transition.hpp:144
Main namespace for the FSMgine library.
Definition FSM.hpp:23