...
This commit is contained in:
parent
47b2dbe7a1
commit
26130e5c65
254
cxx/mcc_fsm.h
254
cxx/mcc_fsm.h
@ -5,7 +5,10 @@
|
||||
/* FINITE-STATE MACHINE IMPLEMENTATION */
|
||||
|
||||
|
||||
#include <functional>
|
||||
#include <string_view>
|
||||
#include <typeindex>
|
||||
#include <unordered_map>
|
||||
#include "mcc_fsm_utils.h"
|
||||
|
||||
|
||||
@ -13,6 +16,8 @@ namespace mcc::fsm
|
||||
{
|
||||
|
||||
|
||||
class MccFsmAbstractState;
|
||||
|
||||
namespace traits
|
||||
{
|
||||
|
||||
@ -23,13 +28,254 @@ concept fsm_event_c =
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
concept fsm_state_c =
|
||||
std::is_default_constructible_v<T> && std::is_move_constructible_v<T> && std::movable<T> && requires {
|
||||
[]<mcc::traits::pair_holder_c PHT>(std::type_identity<PHT>) {}(T::transition_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>());
|
||||
};
|
||||
|
||||
{ T::ID } -> std::same_as<const std::string_view>; // static constant member of state identificator
|
||||
|
||||
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
|
||||
|
||||
|
||||
|
||||
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 <typename StateTplT, typename>
|
||||
struct merge_tuples_t;
|
||||
};
|
||||
|
||||
} // namespace mcc::fsm
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user