|
|
|||
File indexing completed on 2025-11-09 09:23:42
0001 // This file is part of the ACTS project. 0002 // 0003 // Copyright (C) 2016 CERN for the benefit of the ACTS project 0004 // 0005 // This Source Code Form is subject to the terms of the Mozilla Public 0006 // License, v. 2.0. If a copy of the MPL was not distributed with this 0007 // file, You can obtain one at https://mozilla.org/MPL/2.0/. 0008 0009 #pragma once 0010 0011 #include <optional> 0012 #include <string_view> 0013 #include <variant> 0014 0015 namespace Acts { 0016 0017 /// Implementation of a finite state machine engine 0018 /// 0019 /// Allows setting up a system of states and transitions between them. States 0020 /// are definedd as empty structs (footprint: 1 byte). Transitions call 0021 /// functions using overload resolution. This works by subclassing this class, 0022 /// providing the deriving type as the first template argument (CRTP) and 0023 /// providing methods like 0024 /// 0025 /// ```cpp 0026 /// event_return on_event(const S&, const E&); 0027 /// ``` 0028 /// 0029 /// The arguments are the state `S` and the triggered event `E`. Their values 0030 /// can be discarded (you can attach values to events of course, if you like) 0031 /// The return type of these functions is effectively `std::optional<State>`, so 0032 /// you can either return `std::nullopt` to remain in the same state, or an 0033 /// instance of another state. That state will then become active. 0034 /// 0035 /// You can also define a method template, which will serve as a catch-all 0036 /// handler (due to the fact that it will match any state/event combination): 0037 /// 0038 /// ```cpp 0039 /// template <typename State, typename Event> 0040 /// event_return on_event(const State&, const Event&) const { 0041 /// return Terminated{}; 0042 /// } 0043 /// ``` 0044 /// 0045 /// If for a given state and event no suitable overload of `on_event` (and you 0046 /// also haven't defined a catch-all as described above), a transition to 0047 /// `Terminated` will be triggered. This is essentially equivalent to the method 0048 /// template above. 0049 /// 0050 /// If this triggers, it will switch to the `Terminated` state (which is always 0051 /// included in the FSM). 0052 /// 0053 /// Additionally, the FSM will attempt to call functions like 0054 /// ```cpp 0055 /// void on_enter(const State&); 0056 /// void on_exit(const State&); 0057 /// ``` 0058 /// when entering/exiting a state. This can be used to 0059 /// perform actions regardless of the source or destination state in a 0060 /// transition to a given state. This is also fired in case a transition to 0061 /// `Terminated` occurs. 0062 /// 0063 /// The base class also calls 0064 /// ```cpp 0065 /// void on_process(const Event&); 0066 /// void on_process(const State&, const Event&); 0067 /// void on_process(const State1& const Event&, const State2&); 0068 /// ``` 0069 /// during event processing, and allow for things like event and 0070 /// transition logging. 0071 /// 0072 /// The `on_event`, `on_enter`, `on_exit` and `on_process` methods need to be 0073 /// implemented exhaustively, i.e. for all state/event combinations. This might 0074 /// require you to add catch-all no-op functions like 0075 /// ```cpp 0076 /// template <typename...Args> 0077 /// event_return on_event(Args&&...args) {} // noop 0078 /// ``` 0079 /// and so on. 0080 /// 0081 /// The public interface for the user of the FSM are the 0082 /// ```cpp 0083 /// template <typename... Args> 0084 /// void setState(StateVariant state, Args&&... args); 0085 /// 0086 /// template <typename Event, typename... Args> 0087 /// void dispatch(Event&& event, Args&&... args) { 0088 /// ``` 0089 /// 0090 /// `setState` triggers a transition to a given state, `dispatch` triggers 0091 /// processing on an event from the given state. Both will call the appropriate 0092 /// `on_exit` and `on_enter` overloads. Both also accept an arbitrary number of 0093 /// additional arguments that are passed to the `on_event`, `on_exit` and 0094 /// `on_enter` overloads. 0095 /// 0096 /// @tparam Derived Class deriving from the FSM 0097 /// @tparam States Argument pack with the state types that the FSM can be 0098 /// handled. 0099 template <typename Derived, typename... States> 0100 class FiniteStateMachine { 0101 public: 0102 /// Contractual termination state. Is transitioned to if State+Event do not 0103 /// have a transition defined. 0104 struct Terminated { 0105 /// Name of this state (useful for logging) 0106 constexpr static std::string_view name = "Terminated"; 0107 }; 0108 0109 /// Variant type allowing tagged type erased storage of the current state of 0110 /// the FSM. 0111 using StateVariant = std::variant<Terminated, States...>; 0112 0113 protected: 0114 /// Type alias for finite state machine base class 0115 using fsm_base = FiniteStateMachine<Derived, States...>; 0116 0117 /// Type alias for event return type (optional state variant) 0118 using event_return = std::optional<StateVariant>; 0119 0120 public: 0121 /// Default constructor. The default state is taken to be the first in the 0122 /// `States` template arguments 0123 FiniteStateMachine() 0124 : m_state(typename std::tuple_element_t<0, std::tuple<States...>>{}) {} 0125 0126 /// Constructor from an explicit state. The FSM is initialized to this state. 0127 /// @param state Initial state for the FSM. 0128 explicit FiniteStateMachine(StateVariant state) : m_state(std::move(state)) {} 0129 0130 /// Get the current state of the FSM (as a variant). 0131 /// @return StateVariant The current state of the FSM. 0132 const StateVariant& getState() const noexcept { return m_state; } 0133 0134 public: 0135 /// Sets the state to a given one. Triggers `on_exit` and `on_enter` for the 0136 /// given states. 0137 /// @tparam State Type of the target state 0138 /// @tparam Args Additional arguments passed through callback overloads. 0139 /// @param state Instance of the target state 0140 /// @param args The additional arguments 0141 template <typename State, typename... Args> 0142 void setState(State state, Args&&... args) { 0143 Derived& child = static_cast<Derived&>(*this); 0144 0145 // call on exit function 0146 std::visit([&](auto& s) { child.on_exit(s, std::forward<Args>(args)...); }, 0147 m_state); 0148 0149 m_state = std::move(state); 0150 0151 // call on enter function, the type is known from the template argument. 0152 child.on_enter(std::get<State>(m_state), std::forward<Args>(args)...); 0153 } 0154 0155 /// Returns whether the FSM is in the specified state 0156 /// @tparam State type to check against 0157 /// @return Whether the FSM is in the given state. 0158 template <typename S> 0159 bool is(const S& /*state*/) const noexcept { 0160 return is<S>(); 0161 } 0162 0163 /// Returns whether the FSM is in the specified state. Alternative version 0164 /// directly taking only the template argument. 0165 /// @tparam State type to check against 0166 /// @return Whether the FSM is in the given state. 0167 template <typename S> 0168 bool is() const noexcept { 0169 if (std::get_if<S>(&m_state)) { 0170 return true; 0171 } 0172 return false; 0173 } 0174 0175 /// Returns whether the FSM is in the terminated state. 0176 /// @return Whether the FSM is in the terminated state. 0177 bool terminated() const noexcept { return is<Terminated>(); } 0178 0179 protected: 0180 /// Handles processing of an event. 0181 /// @note This should only be called from inside the class Deriving from FSM. 0182 /// @tparam Event Type of the event being processed 0183 /// @tparam Args Arguments being passed to the overload handlers. 0184 /// @param event Instance of the event 0185 /// @param args Additional arguments 0186 /// @return Variant state type, signifying if a transition is supposed to 0187 /// happen. 0188 template <typename Event, typename... Args> 0189 event_return process_event(Event&& event, Args&&... args) { 0190 Derived& child = static_cast<Derived&>(*this); 0191 0192 child.on_process(event); 0193 0194 auto new_state = std::visit( 0195 [&](auto& s) -> std::optional<StateVariant> { 0196 auto s2 = child.on_event(s, std::forward<Event>(event), 0197 std::forward<Args>(args)...); 0198 0199 if (s2) { 0200 std::visit([&](auto& s2_) { child.on_process(s, event, s2_); }, 0201 *s2); 0202 } else { 0203 child.on_process(s, event); 0204 } 0205 return s2; 0206 }, 0207 m_state); 0208 return new_state; 0209 } 0210 0211 public: 0212 /// Public interface to handle an event. Will call the appropriate event 0213 /// handlers and perform any required transitions. 0214 /// @tparam Event Type of the event being triggered 0215 /// @tparam Args Additional arguments being passed to overload handlers. 0216 /// @param event Instance of the event being triggere 0217 /// @param args Additional arguments 0218 template <typename Event, typename... Args> 0219 void dispatch(Event&& event, Args&&... args) { 0220 auto new_state = process_event(std::forward<Event>(event), args...); 0221 if (new_state) { 0222 std::visit( 0223 [&](auto& s) { setState(std::move(s), std::forward<Args>(args)...); }, 0224 *new_state); 0225 } 0226 } 0227 0228 private: 0229 StateVariant m_state; 0230 }; 0231 0232 } // namespace Acts
| [ Source navigation ] | [ Diff markup ] | [ Identifier search ] | [ general search ] |
|
This page was automatically generated by the 2.3.7 LXR engine. The LXR team |
|