mountcontrol/cxx/mcc_fsm.h
Timur A. Fatkhullin 7e987536b8 ...
2025-05-30 00:36:20 +03:00

352 lines
12 KiB
C++

#pragma once
/* MOUNT CONTROL COMPONENTS LIBRARY */
/* FINITE-STATE MACHINE IMPLEMENTATION */
#include <functional>
#include <string_view>
#include <typeindex>
#include <unordered_map>
#include <variant>
#include "mcc_fsm_utils.h"
namespace mcc::fsm
{
class MccFsmAbstractState;
namespace traits
{
template <typename T>
concept fsm_event_c =
std::is_default_constructible_v<T> && std::is_move_constructible_v<T> && std::movable<T> && requires {
{ T::ID } -> std::same_as<const std::string_view>; // static constant member of event identificator
};
template <typename T>
concept fsm_event_state_pair_c = requires {
[]<traits::fsm_event_c EvT, std::derived_from<MccFsmAbstractState> StT>(std::type_identity<std::pair<EvT, StT>>) {
}(std::type_identity<T>());
};
template <typename T>
concept fsm_event_state_pair_tuple_c =
requires { []<fsm_event_state_pair_c... PTs>(std::type_identity<std::tuple<PTs...>>) {}(std::type_identity<T>()); };
} // namespace traits
/*
A base FSM state class with implementation of "event -- new state" transition table
*/
class MccFsmAbstractState
{
static constexpr std::string_view ID = "MCC-FSM-STATE";
protected:
MccFsmAbstractState() = default;
// helpers
template <typename T, typename TpT>
struct in_tuple;
template <typename T, typename... Ts>
struct in_tuple<T, std::tuple<Ts...>> : std::disjunction<std::is_same<T, Ts>...> {
};
template <typename T, typename... Ts>
static constexpr bool in_tpl_v = in_tuple<T, Ts...>::value;
// template <typename T, typename... PTs>
// struct in_pair : std::disjunction<std::is_same<T, typename PTs::first_type>...> {
// };
template <typename T, typename... PTs>
static constexpr bool in_pair_v = std::disjunction<std::is_same<T, typename PTs::first_type>...>::value;
// static constexpr bool in_pair_v = in_pair<T, PTs...>::value;
/* transition table definition */
public:
template <traits::fsm_event_state_pair_c... PTs>
struct trans_table_t;
template <traits::fsm_event_state_pair_c PT>
struct trans_table_t<PT> {
using events_t = std::tuple<typename PT::first_type>;
using states_t = std::tuple<typename PT::first_second>;
using unique_states_t = states_t;
using evst_pairs_t = std::tuple<PT>;
static constexpr bool unique = true;
template <traits::fsm_event_c EvT>
using find_state_by_event_t =
std::conditional_t<std::same_as<EvT, typename PT::first_type>, typename PT::first_second, std::nullptr_t>;
};
template <traits::fsm_event_state_pair_c PT, traits::fsm_event_state_pair_c... PTs>
struct trans_table_t<PT, PTs...> {
private:
using ev_t = typename PT::first_type;
using st_t = typename PT::second_type;
// static constexpr bool non_unique = in_pair_v<ev_t, PTs...>;
static constexpr bool non_unique = (std::same_as<ev_t, typename PTs::second_type> || ...);
public:
using events_t =
std::conditional_t<non_unique,
typename trans_table_t<PTs...>::events_t,
decltype(std::tuple_cat(std::declval<std::tuple<ev_t>>(),
std::declval<typename trans_table_t<PTs...>::events_t>()))>;
using states_t =
std::conditional_t<non_unique,
typename trans_table_t<PTs...>::states_t,
decltype(std::tuple_cat(std::declval<std::tuple<st_t>>(),
std::declval<typename trans_table_t<PTs...>::states_t>()))>;
using unique_states_t = std::conditional_t<
non_unique,
typename trans_table_t<PTs...>::unique_states_t,
std::conditional_t<(std::same_as<st_t, typename PTs::second_type> || ...),
typename trans_table_t<PTs...>::unique_states_t,
decltype(std::tuple_cat(
std::declval<std::tuple<st_t>>(),
std::declval<typename trans_table_t<PTs...>::unique_states_t>()))>>;
using evst_pairs_t =
std::conditional_t<non_unique,
typename trans_table_t<PTs...>::evst_pairs_t,
decltype(std::tuple_cat(std::declval<std::tuple<PT>>(),
std::declval<typename trans_table_t<PTs...>::evst_pairs_t>()))>;
private:
template <traits::fsm_event_c EvT>
struct find_state_by_event {
template <size_t I = 0, typename T = void>
struct helper {
static constexpr size_t idx =
std::same_as<EvT, std::tuple_element_t<I, events_t>> ? I : helper<I + 1>::idx;
};
// to stop unwind the recursive 'helper' template
// in std::conditional_t trait
template <typename T>
struct helper<std::tuple_size_v<events_t> - 1, T> {
static constexpr size_t idx = std::tuple_size_v<events_t> - 1;
};
using res_t = std::
conditional_t<in_tpl_v<EvT, events_t>, std::tuple_element_t<helper<0>::idx, states_t>, std::nullptr_t>;
};
public:
template <traits::fsm_event_c EvT>
using find_state_by_event_t = find_state_by_event<EvT>::res_t;
}; // end of 'trans_table_t<PT, PTs...>'
}; // end of 'MccFsmAbstractState'
namespace traits
{
template <typename T>
concept fsm_trans_table_c = requires {
[]<fsm_event_state_pair_c... PTs>(std::type_identity<mcc::fsm::MccFsmAbstractState::trans_table_t<PTs...>>) {
}(std::type_identity<T>());
};
template <typename T>
concept fsm_state_c =
std::is_default_constructible_v<T> && std::is_move_constructible_v<T> && std::movable<T> &&
std::derived_from<T, MccFsmAbstractState> && requires {
// state class must define a type 'transition_t' which must be a full specialization of
// MccFsmAbstractState::trans_table_t class
[]<fsm_trans_table_c TabT>(std::type_identity<TabT>) {}(std::type_identity<typename T::transition_t>());
};
} // namespace traits
template <traits::fsm_state_c InitStateT>
class MccFiniteStateMachine
{
public:
template <traits::fsm_state_c ST>
MccFiniteStateMachine(ST initial_state) : _currentStateID(ST::ID), _currentStateIDX(typeid(ST))
{
// setStateFunc<ST::transition_t::evst_pairs_t>();
initFromInitial<ST::transition_t::evst_pairs_t>(typeid(ST));
}
template <traits::fsm_event_c EvT>
void dispatchEvent(EvT event)
{
enter_state_func_umap_t<EvT>[this][_currentStateIDX](std::move(event));
// state_func_umap_t<EvT>[this][_currentStateIDX](std::move(event));
}
protected:
template <traits::fsm_event_c EvT>
using state_func_t = std::unordered_map<std::type_index, std::function<void(EvT)>>;
template <traits::fsm_event_c EvT>
inline static std::unordered_map<const MccFiniteStateMachine*, state_func_t<EvT>> state_func_umap_t{};
template <traits::fsm_event_c EvT>
inline static std::unordered_map<const MccFiniteStateMachine*, state_func_t<EvT>> enter_state_func_umap_t{};
template <traits::fsm_event_c EvT>
inline static std::unordered_map<const MccFiniteStateMachine*, state_func_t<EvT>> exit_state_func_umap_t{};
std::string_view _currentStateID;
std::type_index _currentStateIDX;
std::function<void()> _exitCurrentStateFunc;
template <traits::fsm_event_state_pair_c PT, traits::fsm_event_state_pair_c... PTs>
consteval static void initFromInitial(std::type_index state_idx)
{
initFromInitial<PT>(state_idx);
if constexpr (sizeof...(PTs)) {
initFromInitial<PTs...>(state_idx);
}
}
template <traits::fsm_event_state_pair_c PT>
consteval static void initFromInitial(std::type_index state_idx)
{
using ev_t = typename PT::first_type;
using st_t = typename PT::second_type;
enter_state_func_umap_t<ev_t>[][state_idx] = [](ev_t event) {
st_t state;
initFromInitial<st_t::transition_t::evst_pairs_t>(st_t::ID);
if constexpr (requires(st_t inst) {
{ inst.enter(std::declval<ev_t>()) };
}) {
state.enter(std::move(event));
} else {
state.enter();
}
};
}
// template <traits::fsm_event_state_pair_c PT, traits::fsm_event_state_pair_c... PTs>
// void setStateFunc()
// {
// setStateFunc<PT>();
// if constexpr (sizeof...(PTs)) {
// setStateFunc<PTs...>();
// }
// }
// template <traits::fsm_event_state_pair_c PT>
// void setStateFunc()
// {
// using ev_t = typename PT::first_type;
// using st_t = typename PT::second_type;
// state_func_umap_t<ev_t>[this][_currentStateIDX] = [](ev_t event) {
// st_t state;
// if constexpr (requires(st_t inst) {
// { inst.enter(std::declval<ev_t>()) };
// }) {
// state.enter(std::move(event));
// } else {
// state.enter();
// }
// };
// }
// template<>
// merge N std::tuple types with filtering dublicates
// (NOTE: the first std::tuple type must contain unique types!!!)
template <typename... TplTs>
struct merge_tuples_t;
template <>
struct merge_tuples_t<> {
using res_t = std::tuple<>;
};
template <typename TplT>
struct merge_tuples_t<TplT> {
using res_t = TplT;
};
template <typename TplT1, typename TplT2>
struct merge_tuples_t<TplT1, TplT2> {
using res_t = TplT1;
};
template <typename... T1, typename T2, typename... T2s>
struct merge_tuples_t<std::tuple<T1...>, std::tuple<T2, T2s...>>
: std::conditional_t<(std::same_as<T1, T2> || ...),
merge_tuples_t<std::tuple<T1...>, std::tuple<T2s...>>,
merge_tuples_t<std::tuple<T1..., T2>, std::tuple<T2s...>>> {
};
template <typename TplT1, typename TplT2, typename TplT3, typename... TplTs>
struct merge_tuples_t<TplT1, TplT2, TplT3, TplTs...>
: merge_tuples_t<typename merge_tuples_t<TplT1, TplT2>::res_t, TplT3, TplTs...> {
};
template <typename... TplTs>
using merge_tuples_res_t = typename merge_tuples_t<TplTs...>::res_t;
template <typename TplT>
struct variant_maker_t;
template <typename... Ts>
struct variant_maker_t<std::tuple<Ts...>> {
// using variant_t = std::variant<Ts*...>;
using variant_t = std::variant<std::reference_wrapper<Ts>...>;
};
template <typename... TplTs>
struct collect_states_t;
// template <typename TplT>
// struct collect_states_t<TplT> {
// using states_t = TplT;
// };
template <traits::fsm_state_c... StTs, typename... TplTs>
struct collect_states_t<std::tuple<StTs...>, TplTs...> {
using states_t =
merge_tuples_res_t<std::tuple<StTs...>,
typename collect_states_t<typename StTs::transition_t::unique_states_t...>::states_t,
TplTs...>;
};
using states_t = typename collect_states_t<InitStateT>::states_t;
states_t _states;
typename variant_maker_t<states_t>::variant_t _currentStateVariant;
};
} // namespace mcc::fsm