mountcontrol/cxx/mcc_fsm.h

379 lines
13 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
requires std::same_as<const std::string_view, decltype(T::ID)>;
};
template <typename T>
concept fsm_event_state_pair_c = requires {
[]<traits::fsm_event_c EvT, typename StT>(std::type_identity<std::pair<EvT, StT>>) {
// []<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::second_type>;
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::second_type, 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...>'
using transition_t = trans_table_t<>;
}; // 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>());
// requires true;
};
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));
// }
MccFiniteStateMachine() : _currentStateID(InitStateT::ID), _currentStateVariant(&std::get<InitStateT>(_states)) {}
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{InitStateT::ID};
std::type_index _currentStateIDX{typeid(InitStateT)};
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();
// }
// };
// }
// 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<std::tuple<>> {
// 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*...>;
};
// template <typename InTplT, typename OutTplT = std::tuple<>>
// struct deduce_states_t;
// template <traits::fsm_state_c... StTs, typename OutTplT>
// struct deduce_states_t<std::tuple<StTs...>, OutTplT> {
// using curr_collection_t =
// merge_tuples_res_t<OutTplT, std::tuple<StTs...>, typename StTs::transition_t::unique_states_t...>;
// using states_t = std::conditional_t<
// std::tuple_size_v<OutTplT> == std::tuple_size_v<curr_collection_t>,
// curr_collection_t,
// merge_tuples_res_t<curr_collection_t,
// typename deduce_states_t<merge_tuples_res_t<
// typename StTs::transition_t::unique_states_t...>>::curr_collection_t>>;
// };
template <bool stop, typename ResTplT, typename... InTplTs>
struct deduce_states_t;
template <typename ResTplT, typename... InTplTs>
struct deduce_states_t<true, ResTplT, InTplTs...> {
using states_t = ResTplT;
};
template <typename ResTplT, traits::fsm_state_c... StTs, typename... InTplTs>
struct deduce_states_t<false, ResTplT, std::tuple<StTs...>, InTplTs...> {
using curr_collection_t =
merge_tuples_res_t<ResTplT, std::tuple<StTs...>, typename StTs::transition_t::unique_states_t...>;
using states_t = typename deduce_states_t<std::tuple_size_v<ResTplT> == std::tuple_size_v<curr_collection_t>,
curr_collection_t,
typename StTs::transition_t::unique_states_t...>::states_t;
};
public:
// using states_t = typename deduce_states_t<std::tuple<InitStateT>>::states_t;
using states_t = typename deduce_states_t<false, std::tuple<>, std::tuple<InitStateT>>::states_t;
protected:
states_t _states;
typename variant_maker_t<states_t>::variant_t _currentStateVariant;
};
} // namespace mcc::fsm