Back to home page

sPhenix code displayed by LXR

 
 

    


File indexing completed on 2025-08-06 08:10:17

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