14 #if defined(TSM_ENABLE_REFLECTION) && TSM_ENABLE_REFLECTION
15 #if __has_include(<meta>)
19 "TSM_ENABLE_REFLECTION requires a compiler and standard library with <meta>"
30 #include <type_traits>
47 template<
typename From,
49 typename TransitionList,
52 struct transition_map_helper;
58 template<
typename From,
typename Event,
typename... TransitionEntries,
size_t Index>
59 struct transition_map_helper<
62 type_list<TransitionEntries...>,
64 std::enable_if_t<(Index < sizeof...(TransitionEntries))>> {
65 using CurrentTransition =
66 std::tuple_element_t<Index, std::tuple<TransitionEntries...>>;
67 static constexpr
bool match = transition_matches<CurrentTransition, From, Event>;
69 using type =
typename std::conditional_t<
72 typename transition_map_helper<From,
74 type_list<TransitionEntries...>,
78 template<
typename From,
typename Event,
typename... TransitionEntries,
size_t Index>
79 struct transition_map_helper<
82 type_list<TransitionEntries...>,
84 std::enable_if_t<!(Index < sizeof...(TransitionEntries))>> {
94 template<
typename From,
typename Event,
typename TransitionList>
95 struct matching_transition {
96 using type =
typename transition_map_helper<From,
98 as_type_list_t<TransitionList>>::type;
102 template<
typename From,
typename Event,
typename TransitionList>
104 typename matching_transition<From, Event, TransitionList>::type;
107 template<
typename State,
typename Event,
typename TransitionList>
109 !std::is_same_v<typename matching_transition<State, Event, TransitionList>::type,
112 template<
typename State,
typename Event,
typename TransitionList>
114 has_valid_transition_v<State, Event, TransitionList>;
138 typename T::hierarchy;
141 template<
typename T,
typename =
void>
142 struct context_type_of {
147 struct context_type_of<T, std::void_t<typename T::context_type>> {
148 using type =
typename T::context_type;
156 typename T::deferred;
161 using type = type_list<>;
166 struct deferred_of<T> {
167 using type = as_type_list_t<typename T::deferred>;
173 template<
typename Deferred>
174 struct is_defer_rule : std::bool_constant<defer_like<Deferred>> {};
176 template<
typename DeferredList>
177 struct deferred_event_list;
179 template<
typename... Deferred>
180 struct deferred_event_list<type_list<Deferred...>> {
181 using type =
typename unique_tuple<type_list<
typename Deferred::event...>,
185 template<
typename DeferredList>
187 typename deferred_event_list<DeferredList>::type;
189 template<
typename Event, std::
size_t Capacity>
190 struct deferred_event_queue {
191 [[nodiscard]] std::uint16_t count()
const {
return count_; }
193 bool push(Event
const& event) {
194 if (count_ >= Capacity) {
197 storage_[(head_ + count_) % Capacity] = event;
202 bool pop(Event& event) {
206 event = std::move(storage_[head_]);
207 head_ = (head_ + 1U) % Capacity;
212 std::array<Event, Capacity> storage_{};
213 std::uint16_t head_{};
214 std::uint16_t count_{};
217 template<
typename Events, std::
size_t Capacity>
218 struct deferred_queue_storage;
220 template<
typename... Events, std::size_t Capacity>
221 struct deferred_queue_storage<type_list<Events...>, Capacity> {
222 using type = std::tuple<deferred_event_queue<Events, Capacity>...>;
225 template<
typename Events, std::
size_t Capacity>
227 typename deferred_queue_storage<Events, Capacity>::type;
229 template<
typename State,
typename Event,
typename DeferredList>
230 struct defers_event : std::false_type {};
232 template<
typename State,
typename Event,
typename... Deferred>
233 struct defers_event<State, Event, type_list<Deferred...>>
234 : std::bool_constant<(
236 (std::is_same_v<typename Deferred::state, State> &&
237 std::is_same_v<typename Deferred::event, Event>))> {};
240 struct has_transitions : std::bool_constant<has_transitions_c<T>> {};
243 struct is_unwrapped_state_machine
244 : std::bool_constant<has_transitions_c<T> && !requires { T::is_hsm; }> {};
249 template<
typename State,
typename Event,
typename Context>
251 { state.handle(ctx, event) } -> std::convertible_to<bool>;
260 struct is_hsm_trait : std::bool_constant<hsm_like<T>> {};
275 struct is_clocked_hsm : std::bool_constant<clocked_hsm_like<T>> {};
280 template<
typename...>
283 template<
typename Callable,
typename Context,
typename Event>
285 requires(Callable&& callable, Context& ctx, Event& event) {
286 { std::invoke(std::forward<Callable>(callable), ctx, event) } -> std::same_as<bool>;
289 template<
typename Callable,
typename Context>
291 requires(Callable&& callable, Context& ctx) {
292 { std::invoke(std::forward<Callable>(callable), ctx) } -> std::same_as<bool>;
295 template<
typename Callable,
typename Event>
297 requires(Callable&& callable, Event& event) {
298 { std::invoke(std::forward<Callable>(callable), event) } -> std::same_as<bool>;
301 template<
typename Callable>
304 { std::invoke(std::forward<Callable>(callable)) } -> std::same_as<bool>;
307 template<
typename Callable,
typename Context,
typename Event>
309 guard_with_context_event<Callable, Context, Event> ||
310 guard_with_context<Callable, Context> ||
311 guard_with_event<Callable, Event> ||
312 guard_without_args<Callable>;
314 template<
typename Callable,
typename Context,
typename Event>
316 if constexpr (guard_with_context_event<Callable, Context, Event>) {
317 return std::invoke(std::forward<Callable>(callable), ctx, e);
318 }
else if constexpr (guard_with_context<Callable, Context>) {
319 return std::invoke(std::forward<Callable>(callable), ctx);
320 }
else if constexpr (guard_with_event<Callable, Event>) {
321 return std::invoke(std::forward<Callable>(callable), e);
322 }
else if constexpr (guard_without_args<Callable>) {
323 return std::invoke(std::forward<Callable>(callable));
325 static_assert(dependent_false_v<Callable, Context, Event>,
326 "tsm: transition guard must have one of these exact signatures: bool(Context&, Event&), bool(Context&, Event const&), bool(Context&), bool(Event&), bool(Event const&), or bool()");
330 template<
typename Callable,
typename Context,
typename Event>
332 requires(Callable&& callable, Context& ctx, Event& event) {
333 { std::invoke(std::forward<Callable>(callable), ctx, event) } -> std::same_as<void>;
336 template<
typename Callable,
typename Context>
338 requires(Callable&& callable, Context& ctx) {
339 { std::invoke(std::forward<Callable>(callable), ctx) } -> std::same_as<void>;
342 template<
typename Callable,
typename Event>
344 requires(Callable&& callable, Event& event) {
345 { std::invoke(std::forward<Callable>(callable), event) } -> std::same_as<void>;
348 template<
typename Callable>
351 { std::invoke(std::forward<Callable>(callable)) } -> std::same_as<void>;
354 template<
typename Callable,
typename Context,
typename Event>
356 action_with_context_event<Callable, Context, Event> ||
357 action_with_context<Callable, Context> ||
358 action_with_event<Callable, Event> ||
359 action_without_args<Callable>;
361 template<
typename Callable,
typename Context,
typename Event>
363 if constexpr (action_with_context_event<Callable, Context, Event>) {
364 std::invoke(std::forward<Callable>(callable), ctx, e);
365 }
else if constexpr (action_with_context<Callable, Context>) {
366 std::invoke(std::forward<Callable>(callable), ctx);
367 }
else if constexpr (action_with_event<Callable, Event>) {
368 std::invoke(std::forward<Callable>(callable), e);
369 }
else if constexpr (action_without_args<Callable>) {
370 std::invoke(std::forward<Callable>(callable));
372 static_assert(dependent_false_v<Callable, Context, Event>,
373 "tsm: transition action must have one of these exact signatures: void(Context&, Event&), void(Context&, Event const&), void(Context&), void(Event&), void(Event const&), or void()");
390 template<
typename List>
391 struct state_storage;
393 template<
typename... States>
394 struct state_storage<type_list<States...>> : state_slot<States>... {
395 template<
typename State>
396 State& get() noexcept {
397 return state_slot<State>::value;
400 template<
typename State>
401 State
const& get() const noexcept {
402 return state_slot<State>::value;
406 template<
typename Hierarchy,
typename AuthoredTransitionList,
typename =
void>
407 struct hierarchy_transition_list {
408 using type = AuthoredTransitionList;
411 template<
typename Hierarchy,
typename AuthoredTransitionList>
412 struct hierarchy_transition_list<
414 AuthoredTransitionList,
415 std::void_t<typename Hierarchy::transitions>> {
419 template<
typename Context,
bool HasExplicitHierarchy>
420 struct context_hierarchy_type {
424 template<
typename Context>
425 struct context_hierarchy_type<Context, true> {
426 using type =
typename Context::hierarchy;
429 template<
typename Context,
430 typename AuthoredTransitionList,
433 struct hsm_state_model {
434 using hierarchy = void;
436 using states =
typename CoreTable::states;
439 template<
typename Context,
typename AuthoredTransitionList,
typename CoreTable>
440 struct hsm_state_model<Context, AuthoredTransitionList, CoreTable, true> {
442 typename context_hierarchy_type<Context, has_hierarchy_c<Context>>::type;
444 typename hierarchy_transition_list<hierarchy, AuthoredTransitionList>::type;
445 using states =
typename hierarchy::states;
448 template<
typename Definition>
452 template<
typename Definition>
487 static constexpr
bool is_hsm =
true;
488 using type = hsm<T, DefinitionEntries, Configuration>;
489 using HsmType = type;
490 using definition = T;
492 using configuration = Configuration;
493 using semantics_policy =
typename configuration::semantics;
495 using logging_policy =
typename configuration::logging;
496 using sequence_policy =
typename semantics_policy::precompute_sequences;
498 using authored_transition_list =
500 using authored_synchronization_list =
502 static_assert(!empty_type_list_v<authored_transition_list>,
503 "tsm: hsm requires at least one event transition");
506 type_list_traits::all_satisfy<is_defer_rule, deferred_list>::value,
507 "tsm: deferred must be tsm::type_list<tsm::defer<State, Event>...>");
508 using authored_core_table =
510 static_assert(authored_core_table::valid_triggers,
511 "tsm: duplicate (from, event) transitions are only allowed "
512 "when all duplicates are guarded, or for choice/junction "
513 "automatic transitions with at most one unguarded fallback");
514 static constexpr
bool has_composite_states =
515 type_list_traits::any_satisfy<
516 is_unwrapped_state_machine,
517 typename authored_core_table::states>::value;
518 static constexpr
bool has_hierarchy =
519 has_hierarchy_c<T> || has_composite_states;
522 authored_transition_list,
525 using hierarchy_type =
typename state_model::hierarchy;
530 using state_list =
typename state_model::states;
532 using States = state_storage<state_list>;
533 static constexpr std::size_t state_count = runtime_state_set::size;
534 static constexpr std::size_t transition_count =
535 core_table::transition_count;
536 static constexpr std::size_t first_transition_index = 0U;
537 using history_storage =
538 typename semantics_policy::history::template storage<state_count>;
541 static constexpr std::size_t deferred_event_count =
542 deferred_event_set::size;
543 static constexpr std::size_t deferred_capacity =
544 semantics_policy::deferred::capacity;
545 using deferred_queues =
549 requires std::default_initializable<context_type>
551 active_state_(state_index<initial_state>()) {
555 explicit hsm(context_type
const& context)
556 requires std::copy_constructible<context_type>
558 active_state_(state_index<initial_state>()) {
562 explicit hsm(context_type&& context)
563 requires std::move_constructible<context_type>
564 : context_(std::move(context)),
565 active_state_(state_index<initial_state>()) {
569 [[nodiscard]] context_type& context() noexcept {
return context_; }
571 [[nodiscard]] context_type
const& context()
const noexcept {
return context_; }
573 [[nodiscard]] context_type& ctx() noexcept {
return context_; }
575 [[nodiscard]] context_type
const& ctx()
const noexcept {
return context_; }
577 template<
typename State>
578 static consteval std::uint16_t state_index() {
579 return runtime_state_set::template index<State>();
582 [[nodiscard]] constexpr std::uint16_t active_state_id()
const noexcept {
583 return active_state_;
586 [[nodiscard]] constexpr
auto const& active_path()
const noexcept {
590 template<
typename State>
591 [[nodiscard]]
bool active()
const noexcept {
592 return active_state_ == state_index<State>();
595 template<
typename Parent>
596 [[nodiscard]]
bool active_final()
const noexcept {
597 return active_final_id<Parent, 0U>();
600 template<
typename State>
601 State& state() noexcept {
602 return states_.template get<State>();
605 template<
typename State>
606 State
const& state()
const noexcept {
607 return states_.template get<State>();
610 template<
typename Event>
611 void enter(Event& e) {
612 active_state_ = state_index<initial_state>();
614 this->entry(e, &state<initial_state>());
617 template<
typename Event>
618 void leave(Event& e) {
628 template<
typename Event>
629 bool handle(Event&& e) {
631 bool handled =
false;
632 if constexpr (has_hierarchy) {
633 handled = dispatch_hierarchy_path(e);
635 handled = dispatch_handle<0>(e);
637 bool deferred_now =
false;
639 if (!recalling_deferred_ &&
640 should_defer_event<std::decay_t<Event>>()) {
641 handled = defer_event(e);
642 deferred_now = handled;
645 event_record<std::decay_t<Event>>());
648 if (handled && !deferred_now && !recalling_deferred_) {
649 recall_deferred_events();
654 template<
typename Event,
typename State>
655 void entry(Event&& e, State* state) noexcept {
657 state_record<State, std::decay_t<Event>>());
658 if constexpr (state_has_entry<State>) {
659 if constexpr (std::is_invocable_v<decltype(&State::entry),
663 state->entry(context_, e);
664 }
else if constexpr (std::is_invocable_v<decltype(&State::entry),
667 state->entry(context_);
668 }
else if constexpr (std::is_invocable_v<decltype(&State::entry),
672 static_assert(dependent_false_v<State, Event>,
673 "tsm: state entry must have one of these exact signatures: void(Context&, Event&), void(Context&, Event const&), void(Context&), void(Event&), void(Event const&), or void()");
676 if constexpr (is_hsm_trait_v<State>) {
681 template<
typename Event,
typename State>
682 void exit(Event&& e, State* state) noexcept {
684 state_record<State, std::decay_t<Event>>());
685 if constexpr (is_hsm_trait_v<State>) {
688 if constexpr (state_has_exit<State>) {
689 if constexpr (std::is_invocable_v<decltype(&State::exit),
693 state->exit(context_, e);
694 }
else if constexpr (std::is_invocable_v<decltype(&State::exit),
697 state->exit(context_);
698 }
else if constexpr (std::is_invocable_v<decltype(&State::exit),
702 static_assert(dependent_false_v<State, Event>,
703 "tsm: state exit must have one of these exact signatures: void(Context&, Event&), void(Context&, Event const&), void(Context&), void(Event&), void(Event const&), or void()");
714 template<
typename Tn,
typename Event,
typename State>
715 bool check_guard(Event& e, State* state) {
716 if constexpr (state_has_guard<State>) {
717 if constexpr (std::is_invocable_v<decltype(&State::guard),
721 return state->guard(context_, e);
722 }
else if constexpr (std::is_invocable_v<decltype(&State::guard),
725 return state->guard(context_);
726 }
else if constexpr (std::is_invocable_v<decltype(&State::guard),
728 return state->guard();
730 static_assert(dependent_false_v<Tn, Event, State>,
731 "tsm: state guard must have one of these exact signatures: bool(Context&, Event&), bool(Context&, Event const&), bool(Context&), bool(Event&), bool(Event const&), or bool()");
734 if constexpr (transition_has_guard<Tn>) {
747 template<
typename Tn,
typename Event,
typename State>
748 void perform_action(Event& e, State* state) {
749 if constexpr (state_has_action<State>) {
750 if constexpr (std::is_invocable_v<decltype(&State::action),
754 state->action(context_, e);
755 }
else if constexpr (std::is_invocable_v<decltype(&State::action),
758 state->action(context_);
759 }
else if constexpr (std::is_invocable_v<decltype(&State::action),
763 static_assert(dependent_false_v<Tn, Event, State>,
764 "tsm: state action must have one of these exact signatures: void(Context&, Event&), void(Context&, Event const&), void(Context&), void(Event&), void(Event const&), or void()");
767 if constexpr (transition_has_action<Tn>) {
769 transition_record<Tn, Event>());
775 template<
typename transition,
777 typename State =
typename transition::from>
778 requires (!state_handles_event<State, Event, context_type>)
780 handle_transition(State* current, Event& e) {
784 if (!this->check_guard<transition>(e, current)) {
786 transition_record<transition, Event>());
790 return execute_transition<transition>(current, e);
793 template<
typename transition,
795 typename State =
typename transition::from>
796 requires state_handles_event<State, Event, context_type>
798 handle_transition(State* current, Event& e) {
803 if (!current->handle(context_, e)) {
805 transition_record<transition, Event>());
809 return execute_transition<transition>(current, e);
812 template<
typename transition,
typename Event,
typename State>
813 bool execute_transition(State* current, Event& e) {
816 if constexpr (transition_traits<transition>::kind ==
817 transition_kind::internal) {
818 this->perform_action<transition>(e, current);
821 if constexpr (has_hierarchy) {
822 using to =
typename transition::to;
823 const auto destination = resolve_destination<to>();
824 if constexpr (transition_traits<transition>::kind ==
825 transition_kind::reentering_external) {
829 constexpr
auto selected_source =
830 state_index<typename transition::from>();
831 const auto sequence =
832 tsm::core::compute_reentering_transition_sequence<hierarchy_type>(
836 return apply_transition_sequence<transition>(
837 sequence, destination, current, e);
839 if constexpr (transition_traits<transition>::kind ==
840 transition_kind::local) {
844 constexpr
auto selected_source =
845 state_index<typename transition::from>();
846 const auto sequence =
847 tsm::core::compute_local_transition_sequence<hierarchy_type>(
851 return apply_transition_sequence<transition>(
852 sequence, destination, current, e);
856 if constexpr (can_precompute_sequence<transition>()) {
857 constexpr
auto sequence =
858 precomputed_sequence<transition>();
859 return apply_transition_sequence<transition>(
860 sequence, sequence.destination, current, e);
862 if constexpr (can_precompute_active_leaf_sequences<transition>()) {
863 auto const& sequence =
864 precomputed_sequence_for_active_leaf<transition>(
866 return apply_transition_sequence<transition>(
867 sequence, sequence.destination, current, e);
869 const auto sequence =
870 tsm::core::compute_transition_sequence<hierarchy_type>(
873 return apply_transition_sequence<transition>(
874 sequence, destination, current, e);
881 transition_record<transition, Event>());
882 this->exit(e, current);
884 this->perform_action<transition>(e, current);
886 using to =
typename transition::to;
887 active_state_ = state_index<to>();
890 this->entry(e, &state<to>());
891 resolve_decision_pseudostate<to>();
896 template<
typename transition,
typename Sequence,
typename Event,
typename State>
897 bool apply_transition_sequence(Sequence
const& sequence,
898 std::uint16_t destination,
903 transition_record<transition, Event>());
908 transition_record<transition, Event>());
913 for (std::size_t i = 0; i < sequence.exits.size; ++i) {
914 dispatch_exit_id<0>(sequence.exits[i], e);
917 this->perform_action<transition>(e, current);
920 sequence.final_path.size == 0U
922 : sequence.final_path[sequence.final_path.size - 1U];
923 active_path_ = sequence.final_path;
925 for (std::size_t i = 0; i < sequence.entries.size; ++i) {
926 dispatch_entry_id<0>(sequence.entries[i], e);
928 resolve_decision_pseudostate_id(destination);
932 template<
typename Transition,
typename ActiveState>
933 static consteval
auto precomputed_sequence_from_active_leaf() {
937 constexpr
auto active_leaf = state_index<ActiveState>();
938 constexpr
auto selected_source =
939 state_index<typename Transition::from>();
940 constexpr
auto destination = state_index<typename Transition::to>();
942 if constexpr (hierarchy_type::least_common_ancestor(active_leaf,
945 return tsm::core::compute_transition_sequence<hierarchy_type>(
955 template<
typename Transition,
typename States>
956 struct precomputed_sequence_table;
958 template<
typename Transition,
typename... States>
959 struct precomputed_sequence_table<Transition, type_list<States...>> {
960 using sequence_type =
962 static constexpr std::array<sequence_type,
sizeof...(States)> value{
963 precomputed_sequence_from_active_leaf<Transition, States>()...
967 template<
typename Transition>
968 static consteval
bool can_precompute_sequence() {
972 if constexpr (!has_hierarchy || !sequence_policy::enabled) {
974 }
else if constexpr (history_state_like<typename Transition::to> ||
975 deep_history_state_like<typename Transition::to>) {
978 constexpr
auto source =
979 state_index<typename Transition::from>();
980 return hierarchy_type::initial_child_index(source) ==
985 template<
typename Transition>
986 static consteval
bool can_precompute_active_leaf_sequences() {
989 if constexpr (!has_hierarchy || !sequence_policy::enabled) {
991 }
else if constexpr (transition_traits<Transition>::kind !=
992 transition_kind::external) {
994 }
else if constexpr (history_state_like<typename Transition::to> ||
995 deep_history_state_like<typename Transition::to>) {
998 constexpr
auto source =
999 state_index<typename Transition::from>();
1000 return hierarchy_type::initial_child_index(source) !=
1005 template<
typename Transition>
1006 static consteval
auto precomputed_sequence() {
1007 constexpr
auto source = state_index<typename Transition::from>();
1008 constexpr
auto destination = state_index<typename Transition::to>();
1009 return tsm::core::compute_transition_sequence<hierarchy_type>(
1014 template<
typename Transition>
1015 static constexpr
auto const& precomputed_sequence_for_active_leaf(
1016 std::uint16_t active_leaf) {
1018 precomputed_sequence_table<Transition, state_list>;
1019 return table::value[active_leaf];
1022 template<
typename Destination>
1023 [[nodiscard]] std::uint16_t resolve_destination()
const {
1027 if constexpr (deep_history_state_like<Destination>) {
1028 constexpr
auto parent =
1029 state_index<typename Destination::history_parent>();
1030 const auto remembered = history_.get(parent);
1032 return deep_history_leaf(remembered);
1034 const auto initial = hierarchy_type::initial_child_index(parent);
1035 return initial ==
tsm::core::npos ? parent : deep_history_leaf(initial);
1036 }
else if constexpr (history_state_like<Destination>) {
1037 constexpr
auto parent =
1038 state_index<typename Destination::history_parent>();
1039 const auto remembered = history_.get(parent);
1043 const auto initial = hierarchy_type::initial_child_index(parent);
1046 return state_index<Destination>();
1050 [[nodiscard]] std::uint16_t deep_history_leaf(std::uint16_t state_id)
const {
1051 if constexpr (has_hierarchy) {
1053 const auto remembered = history_.get(state_id);
1055 state_id = remembered;
1058 const auto initial = hierarchy_type::initial_child_index(state_id);
1069 template<std::
size_t Index,
typename Event>
1070 bool dispatch_handle(Event& e) {
1071 if constexpr (Index >= state_count) {
1074 switch (active_state_) {
1076 return handle_active_state<type_at_t<Index, state_list>>(e);
1078 return dispatch_handle<Index + 1U>(e);
1083 template<
typename State,
typename Event>
1084 bool handle_active_state(Event& e) {
1085 return handle_source_state<State>(e);
1088 template<
typename State,
typename Event>
1089 bool handle_source_state(Event& e) {
1094 auto* current = &state<State>();
1095 bool handled =
false;
1096 if constexpr (is_hsm_trait_v<State>) {
1097 if (active_state_ == state_index<State>()) {
1098 handled = current->handle(e);
1103 std::decay_t<Event>,
1106 handle_matching_transitions<State,
1108 first_transition_index>(
1116 template<
typename State,
typename Event, std::
size_t Index>
1117 bool handle_matching_transitions(State* current, Event& e) {
1123 if constexpr (Index >= transition_count) {
1129 std::decay_t<Event>>) {
1130 if (handle_transition<transition>(current, e)) {
1134 return handle_matching_transitions<State, Event, Index + 1U>(
1140 template<
typename Event>
1141 bool dispatch_hierarchy_path(Event& e) {
1146 for (std::size_t i = active_path_.size; i > 0U; --i) {
1147 if (dispatch_source_id<0>(active_path_[i - 1U], e)) {
1154 template<std::
size_t Index,
typename Event>
1155 bool dispatch_source_id(std::uint16_t state_id, Event& e) {
1160 if constexpr (Index >= state_count) {
1163 if (state_id == Index) {
1164 return handle_source_state<type_at_t<Index, state_list>>(e);
1166 return dispatch_source_id<Index + 1U>(state_id, e);
1170 template<
typename Parent, std::
size_t Index>
1171 [[nodiscard]]
bool active_final_id()
const noexcept {
1172 if constexpr (Index >= state_count) {
1176 if constexpr (final_state_like<State>) {
1177 if constexpr (std::is_same_v<
typename State::final_parent,
1179 return active_state_ == Index;
1181 return active_final_id<Parent, Index + 1U>();
1184 return active_final_id<Parent, Index + 1U>();
1189 template<std::
size_t Index,
typename Event>
1190 void dispatch_exit(Event& e) {
1191 if constexpr (Index < state_count) {
1192 switch (active_state_) {
1197 dispatch_exit<Index + 1U>(e);
1203 template<std::
size_t Index,
typename Event>
1204 void dispatch_exit_id(std::uint16_t state_id, Event& e) {
1208 if constexpr (Index < state_count) {
1209 if (state_id == Index) {
1210 remember_history(state_id);
1213 dispatch_exit_id<Index + 1U>(state_id, e);
1218 void remember_history(std::uint16_t state_id) {
1222 if constexpr (has_hierarchy) {
1223 const auto parent = hierarchy_type::parent_index(state_id);
1225 history_.set(parent, state_id);
1230 template<std::
size_t Index,
typename Event>
1231 void dispatch_entry_id(std::uint16_t state_id, Event& e) {
1235 if constexpr (Index < state_count) {
1236 if (state_id == Index) {
1239 dispatch_entry_id<Index + 1U>(state_id, e);
1244 template<
typename State>
1245 void resolve_decision_pseudostate() {
1249 if constexpr (decision_pseudostate_like<State>) {
1250 automatic_event
event{};
1251 (void)this->handle(event);
1255 void resolve_decision_pseudostate_id(std::uint16_t state_id) {
1256 dispatch_decision_pseudostate_id<0U>(state_id);
1259 template<std::
size_t Index>
1260 void dispatch_decision_pseudostate_id(std::uint16_t state_id) {
1261 if constexpr (Index < state_count) {
1262 if (state_id == Index) {
1264 resolve_decision_pseudostate<State>();
1266 dispatch_decision_pseudostate_id<Index + 1U>(state_id);
1271 template<
typename State>
1272 void current_state() {
1273 active_state_ = state_index<State>();
1274 reset_active_path();
1277 template<
typename Event>
1278 [[nodiscard]] std::uint16_t deferred_count()
const {
1279 if constexpr (deferred_event_set::template contains<Event>()) {
1280 return deferred_queue<Event>().count();
1286 void reset_active_path() {
1287 if constexpr (has_hierarchy) {
1288 active_path_ = hierarchy_type::path_from_root(active_state_);
1291 active_path_.push_back(active_state_);
1295 template<
typename Event>
1296 static consteval std::uint16_t event_index() {
1297 return core_table::template event_index<Event>();
1300 template<
typename Event>
1301 static consteval std::uint16_t deferred_event_index() {
1302 return deferred_event_set::template index<Event>();
1305 template<
typename Event>
1310 active_state_placeholder(),
1311 event_index<Event>(),
1316 template<
typename State,
typename Event>
1321 state_index<State>(),
1322 event_index<Event>(),
1327 template<
typename Transition,
typename Event>
1332 state_index<typename Transition::from>(),
1333 event_index<std::decay_t<Event>>(),
1334 state_index<typename Transition::to>(),
1335 core_table::template transition_type_index<Transition>() };
1338 static consteval std::uint16_t active_state_placeholder() {
1344 if constexpr (logging_policy::enabled) {
1345 auto event = record;
1347 logger_.debug(kind, event);
1353 if constexpr (logging_policy::enabled) {
1354 auto event = record;
1356 logger_.info(kind, event);
1362 if constexpr (logging_policy::enabled) {
1363 auto event = record;
1365 logger_.warn(kind, event);
1371 if constexpr (logging_policy::enabled) {
1372 auto event = record;
1374 logger_.error(kind, event);
1378 template<
typename Event>
1379 bool should_defer_event()
const {
1383 if constexpr (deferred_event_set::template contains<Event>()) {
1384 if constexpr (has_hierarchy) {
1385 for (std::size_t i = active_path_.size; i > 0U; --i) {
1386 if (dispatch_defer_source_id<Event, 0U>(
1387 active_path_[i - 1U])) {
1393 return dispatch_defer_source_id<Event, 0U>(active_state_);
1400 template<
typename Event, std::
size_t Index>
1401 bool dispatch_defer_source_id(std::uint16_t state_id)
const {
1402 if constexpr (Index >= state_count) {
1405 if (state_id == Index) {
1407 return defers_event<State, Event, deferred_list>::value;
1409 return dispatch_defer_source_id<Event, Index + 1U>(state_id);
1413 template<
typename Event>
1414 bool defer_event(Event
const& event) {
1415 if constexpr (deferred_event_set::template contains<Event>()) {
1416 return deferred_queue<Event>().push(event);
1422 void recall_deferred_events() {
1426 if constexpr (deferred_event_count > 0U) {
1427 deferred_recall_scope recall{recalling_deferred_};
1428 recall_deferred_event_id<0U>();
1432 template<std::
size_t Index>
1433 void recall_deferred_event_id() {
1434 if constexpr (Index < deferred_event_count) {
1437 while (deferred_queue<Event>().pop(event)) {
1438 (void)handle(event);
1440 recall_deferred_event_id<Index + 1U>();
1444 template<
typename Event>
1445 [[nodiscard]]
auto& deferred_queue() {
1446 return std::get<deferred_event_queue<Event, deferred_capacity>>(
1450 template<
typename Event>
1451 [[nodiscard]]
auto const& deferred_queue()
const {
1452 return std::get<deferred_event_queue<Event, deferred_capacity>>(
1456 context_type context_;
1458 std::uint16_t active_state_{};
1460 history_storage history_{};
1461 deferred_queues deferred_queues_{};
1462 bool recalling_deferred_{};
1463 [[no_unique_address]] logging_policy logger_{};
1465 struct deferred_recall_scope {
1466 explicit deferred_recall_scope(
bool& flag) noexcept : flag_(flag) {
1470 deferred_recall_scope(deferred_recall_scope
const&) =
delete;
1471 deferred_recall_scope& operator=(deferred_recall_scope
const&) =
delete;
1473 ~deferred_recall_scope() noexcept { flag_ =
false; }
1480 template<
typename T,
typename =
void>
1483 template<
typename T>
1484 struct make_hsm<T, std::enable_if_t<!is_state_trait_v<T>>> {
1488 template<
typename T>
1500 template<
typename T,
bool IsTransition = transition_like<T>>
1501 struct wrap_transition {
1505 template<
typename T>
1506 struct wrap_transition<T, true> {
1507 using from =
typename T::from;
1508 using event =
typename T::event;
1509 using to =
typename T::to;
1512 std::conditional_t<is_state_trait_v<from>, make_hsm_t<from>, from>;
1514 std::conditional_t<is_state_trait_v<to>, make_hsm_t<to>, to>;
1517 typename transition_traits<T>::template with_endpoints<wrap_from,
1522 template<
typename T>
1525 template<
typename... TransitionEntries>
1526 struct wrap_transitions;
1528 template<
typename... TransitionEntries>
1529 struct wrap_transitions<type_list<TransitionEntries...>> {
1530 using type = type_list<wrap_transition_t<TransitionEntries>...>;
1533 template<
typename T>
1536 template<
typename T>
1537 struct synchronization_entries_of {
1542 using explicit_rules =
typename synchronization_of<T>::type;
1543 using authored_rules =
1548 template<
typename T>
1553 template<
typename T>
1554 struct make_hsm<T, std::enable_if_t<is_state_trait_v<T>>> {
1558 using type = hsm<T, transitions>;
1561 template<
typename Join,
typename Rules>
1562 struct join_to_rule_for;
1564 template<
typename Join>
1565 struct join_to_rule_for<
Join, type_list<>> {
1569 template<
typename Join,
typename First,
typename... Rest>
1570 struct join_to_rule_for<
Join, type_list<First, Rest...>> {
1571 template<
typename Rule>
1572 struct matching_join_to : std::false_type {};
1574 template<join_to_rule_like Rule>
1575 struct matching_join_to<Rule>
1576 : std::bool_constant<std::is_same_v<typename Rule::join, Join>> {};
1579 std::conditional_t<matching_join_to<First>::value, First,
void>;
1581 std::conditional_t<std::is_void_v<candidate>,
1582 typename join_to_rule_for<
Join,
1583 type_list<Rest...>>::type,
1587 template<
typename Join,
typename Rules>
1590 template<
typename Join,
typename Rules>
1591 struct join_to_rule_count;
1593 template<
typename Join>
1594 struct join_to_rule_count<
Join, type_list<>> : std::integral_constant<std::size_t, 0U> {};
1596 template<
typename Join,
typename First,
typename... Rest>
1597 struct join_to_rule_count<
Join, type_list<First, Rest...>> {
1598 template<
typename Rule>
1599 struct matching_join_to : std::false_type {};
1601 template<join_to_rule_like Rule>
1602 struct matching_join_to<Rule>
1603 : std::bool_constant<std::is_same_v<typename Rule::join, Join>> {};
1605 static constexpr std::size_t value =
1606 (matching_join_to<First>::value ? 1U : 0U) +
1607 join_to_rule_count<
Join, type_list<Rest...>>::value;
1617 template<
typename... RegionDefinitions>
1618 struct OrthogonalExecutionPolicy {
1619 static constexpr
bool is_hsm =
true;
1620 using type = OrthogonalExecutionPolicy<RegionDefinitions...>;
1621 using regions = type_list<RegionDefinitions...>;
1622 using synchronization_rules =
1623 concat_type_lists_t<synchronization_entries_of_t<RegionDefinitions>...>;
1624 using completion = completion_event;
1625 static constexpr std::size_t region_count =
sizeof...(RegionDefinitions);
1626 static constexpr std::size_t synchronization_rule_count =
1628 static_assert(synchronization_rule_count <= 64U,
1629 "tsm: OrthogonalExecutionPolicy synchronization tracking "
1630 "supports up to 64 synchronization rules");
1631 static_assert(region_count <= 64U,
1632 "tsm: OrthogonalExecutionPolicy dispatch result stores "
1633 "handled regions in a 64-bit mask");
1635 template<
typename RegionDefinition>
1636 using region_hsm = hsm<RegionDefinition>;
1638 OrthogonalExecutionPolicy() {
1639 static_assert(validate_synchronization_rules(),
1640 "tsm: synchronization rules must reference states owned "
1641 "by one orthogonal region, and each join_from must have "
1642 "exactly one matching join_to");
1645 struct dispatch_result {
1649 std::uint64_t handled_mask{};
1650 std::uint64_t completion_handled_mask{};
1652 bool completion_dispatched{};
1653 bool synchronization_handled{};
1655 [[nodiscard]] constexpr
bool handled()
const {
1656 return handled_mask != 0U || synchronization_handled;
1659 [[nodiscard]] constexpr
bool completion_handled()
const {
1660 return completion_handled_mask != 0U;
1663 template<
typename RegionDefinition>
1664 [[nodiscard]] constexpr
bool region_handled()
const {
1665 constexpr
auto index = region_index<RegionDefinition>();
1666 return (handled_mask & bit(index)) != 0U;
1669 template<
typename RegionDefinition>
1670 [[nodiscard]] constexpr
bool completion_region_handled()
const {
1671 constexpr
auto index = region_index<RegionDefinition>();
1672 return (completion_handled_mask & bit(index)) != 0U;
1675 template<
typename RegionDefinition>
1676 constexpr
void mark_handled() {
1677 constexpr
auto index = region_index<RegionDefinition>();
1678 handled_mask |= bit(index);
1681 template<
typename RegionDefinition>
1682 constexpr
void mark_completion_handled() {
1683 constexpr
auto index = region_index<RegionDefinition>();
1684 completion_handled_mask |= bit(index);
1688 template<
typename RegionDefinition>
1689 [[nodiscard]] region_hsm<RegionDefinition>& region() {
1690 return std::get<region_hsm<RegionDefinition>>(regions_);
1693 template<
typename RegionDefinition>
1694 [[nodiscard]] region_hsm<RegionDefinition>
const& region()
const {
1695 return std::get<region_hsm<RegionDefinition>>(regions_);
1698 template<
typename Event>
1699 void enter(Event&& event) {
1701 std::apply([&e](
auto&... region_hsm) { (region_hsm.enter(e), ...); },
1705 template<
typename Event>
1706 void leave(Event&& event) {
1708 std::apply([&e](
auto&... region_hsm) { (region_hsm.leave(e), ...); },
1712 template<
typename Event>
1713 void entry(Event&& event) {
1714 enter(std::forward<Event>(event));
1717 template<
typename Event>
1718 void exit(Event&& event) {
1719 leave(std::forward<Event>(event));
1722 template<
typename Event>
1723 bool handle(Event&& event) {
1724 return dispatch(std::forward<Event>(event)).handled();
1727 template<
typename Event>
1728 dispatch_result dispatch(Event&& event) {
1735 dispatch_result result{};
1736 dispatch_regions<0U>(result, e);
1737 process_synchronization(result);
1738 result.completed = complete();
1739 maybe_dispatch_completion<std::decay_t<Event>>(result);
1743 template<
typename State>
1744 [[nodiscard]]
bool active()
const {
1745 bool result =
false;
1747 [&result](
auto const&... region_hsm) {
1748 ((result = region_active<State>(region_hsm) || result), ...);
1754 template<
typename ForkRule>
1756 activate_fork_targets<ForkRule>();
1759 template<
typename ForkRule>
1760 void activate_fork_targets() {
1761 static_assert(fork_rule_like<ForkRule>,
1762 "tsm: fork requires tsm::fork_to<Fork, Targets...>");
1763 activate_targets(
typename ForkRule::targets{});
1766 template<
typename JoinFromRule>
1767 [[nodiscard]]
bool join_enabled()
const {
1769 join_from_rule_like<JoinFromRule>,
1770 "tsm: join_enabled requires tsm::join_from<Join, Sources...>");
1771 return sources_active(
typename JoinFromRule::sources{});
1774 template<
typename JoinFromRule,
typename JoinToRule>
1777 join_from_rule_like<JoinFromRule>,
1778 "tsm: join requires tsm::join_from<Join, Sources...>");
1779 static_assert(join_to_rule_like<JoinToRule>,
1780 "tsm: join requires tsm::join_to<Join, Target>");
1781 static_assert(std::is_same_v<
typename JoinFromRule::join,
1782 typename JoinToRule::join>,
1783 "tsm: join_from and join_to must name the same join");
1784 if (!join_enabled<JoinFromRule>()) {
1787 activate_target<typename JoinToRule::target>();
1791 template<
typename RegionDefinition>
1792 [[nodiscard]]
bool region_complete()
const {
1793 return region<RegionDefinition>()
1794 .template active_final<RegionDefinition>();
1797 [[nodiscard]]
bool complete()
const {
1798 if constexpr (
sizeof...(RegionDefinitions) == 0U) {
1801 return (region_complete<RegionDefinitions>() && ...);
1805 template<
typename Event = completion_event>
1806 dispatch_result dispatch_completion_if_complete() {
1811 dispatch_result result{};
1812 dispatch_completion_regions<0U>(result, event);
1813 result.completed = complete();
1814 result.completion_dispatched =
true;
1815 completion_dispatched_ =
true;
1819 template<
typename State>
1820 static consteval
bool state_in_regions() {
1821 return (region_hsm<RegionDefinitions>::runtime_state_set::template contains<State>() ||
1825 template<
typename... States>
1826 static consteval
bool states_in_regions(type_list<States...>) {
1827 return (state_in_regions<States>() && ...);
1830 template<
typename Rule>
1831 static consteval
bool valid_synchronization_rule() {
1832 if constexpr (fork_rule_like<Rule>) {
1833 return states_in_regions(
typename Rule::targets{});
1834 }
else if constexpr (join_from_rule_like<Rule>) {
1835 return states_in_regions(
typename Rule::sources{}) &&
1836 join_to_rule_count<
typename Rule::join,
1837 synchronization_rules>::value == 1U;
1838 }
else if constexpr (join_to_rule_like<Rule>) {
1839 return state_in_regions<typename Rule::target>();
1845 template<
typename... Rules>
1846 static consteval
bool validate_synchronization_rules(type_list<Rules...>) {
1847 return (valid_synchronization_rule<Rules>() && ...);
1850 static consteval
bool validate_synchronization_rules() {
1851 return validate_synchronization_rules(synchronization_rules{});
1854 template<
typename RegionDefinition>
1855 static consteval std::uint16_t region_index() {
1859 static constexpr std::uint64_t bit(std::uint16_t index) {
1860 return std::uint64_t{ 1U } << index;
1863 template<std::
size_t Index,
typename Event>
1864 void dispatch_regions(dispatch_result& result, Event& event) {
1868 if constexpr (Index < region_count) {
1869 using RegionDefinition = type_at_t<Index, regions>;
1870 if (region<RegionDefinition>().handle(event)) {
1871 result.template mark_handled<RegionDefinition>();
1873 dispatch_regions<Index + 1U>(result, event);
1877 void process_synchronization(dispatch_result& result) {
1882 if constexpr (!empty_type_list_v<synchronization_rules>) {
1883 result.synchronization_handled =
1884 process_fork_rules<0U>() || result.synchronization_handled;
1885 result.synchronization_handled =
1886 process_join_rules<0U>() || result.synchronization_handled;
1890 template<std::
size_t Index>
1891 bool process_fork_rules() {
1895 if constexpr (Index >= synchronization_rule_count) {
1898 using Rule = type_at_t<Index, synchronization_rules>;
1899 bool handled =
false;
1900 if constexpr (fork_rule_like<Rule>) {
1901 constexpr
auto mask = bit(Index);
1902 if (active<typename Rule::fork>()) {
1903 if ((synchronization_fork_mask_ & mask) == 0U) {
1904 activate_fork_targets<Rule>();
1905 synchronization_fork_mask_ |= mask;
1909 synchronization_fork_mask_ &= ~mask;
1912 return process_fork_rules<Index + 1U>() || handled;
1916 template<std::
size_t Index>
1917 bool process_join_rules() {
1921 if constexpr (Index >= synchronization_rule_count) {
1924 using Rule = type_at_t<Index, synchronization_rules>;
1925 bool handled =
false;
1926 if constexpr (join_from_rule_like<Rule>) {
1927 constexpr
auto mask = bit(Index);
1929 join_to_rule_for_t<typename Rule::join, synchronization_rules>;
1930 static_assert(!std::is_void_v<JoinTo>,
1931 "tsm: join_from requires a matching join_to");
1932 if (join_enabled<Rule>()) {
1933 if ((synchronization_join_mask_ & mask) == 0U) {
1934 handled = join<Rule, JoinTo>();
1935 synchronization_join_mask_ |= mask;
1938 synchronization_join_mask_ &= ~mask;
1941 return process_join_rules<Index + 1U>() || handled;
1945 template<
typename Event>
1946 void maybe_dispatch_completion(dispatch_result& result) {
1950 if constexpr (!std::is_same_v<Event, completion_event>) {
1951 if (result.completed && !completion_dispatched_) {
1952 completion_event
event{};
1953 dispatch_completion_regions<0U>(result, event);
1954 result.completion_dispatched =
true;
1955 completion_dispatched_ =
true;
1956 }
else if (!result.completed) {
1957 completion_dispatched_ =
false;
1962 template<std::
size_t Index,
typename Event>
1963 void dispatch_completion_regions(dispatch_result& result, Event& event) {
1964 if constexpr (Index < region_count) {
1965 using RegionDefinition = type_at_t<Index, regions>;
1966 if (region<RegionDefinition>().handle(event)) {
1967 result.template mark_completion_handled<RegionDefinition>();
1969 dispatch_completion_regions<Index + 1U>(result, event);
1973 template<
typename State,
typename RegionHsm>
1974 static bool region_active(RegionHsm
const& region_hsm) {
1975 if constexpr (RegionHsm::runtime_state_set::template contains<State>()) {
1976 return region_hsm.template active<State>();
1982 template<
typename... Targets>
1983 void activate_targets(type_list<Targets...>) {
1984 (activate_target<Targets>(), ...);
1987 template<
typename Target>
1988 void activate_target() {
1990 [](
auto&... region_hsm) {
1991 (activate_region_target<Target>(region_hsm), ...);
1996 template<
typename Target,
typename RegionHsm>
1997 static void activate_region_target(RegionHsm& region_hsm) {
1998 if constexpr (RegionHsm::runtime_state_set::template contains<Target>()) {
1999 region_hsm.template current_state<Target>();
2000 region_hsm.template resolve_decision_pseudostate<Target>();
2004 template<
typename... Sources>
2005 [[nodiscard]]
bool sources_active(type_list<Sources...>)
const {
2006 return (active<Sources>() && ...);
2009 std::tuple<region_hsm<RegionDefinitions>...> regions_{};
2010 std::uint64_t synchronization_fork_mask_{};
2011 std::uint64_t synchronization_join_mask_{};
2012 bool completion_dispatched_{};
2016 template<
typename HsmType,
typename Events>
2019 template<
typename HsmType,
typename =
void>
2020 struct get_events_from_hsm;
2022 template<
typename Transition>
2023 struct get_events_from_transition;
2025 template<
typename TransitionList>
2026 struct aggregate_events;
2034 template<
typename... TransitionEntries>
2035 struct aggregate_events<type_list<TransitionEntries...>> {
2036 using type = decltype(std::tuple_cat(
2037 typename get_events_from_transition<TransitionEntries>::type{}...));
2041 struct aggregate_events<type_list<>> {
2042 using type = std::tuple<>;
2046 template<
typename Transition>
2047 struct get_events_from_transition {
2048 using Event = std::tuple<typename Transition::event>;
2050 typename get_events_from_hsm<typename Transition::from>::type;
2052 typename get_events_from_hsm<typename Transition::to>::type;
2053 using type = decltype(std::tuple_cat(Event{}, FromEvents{}, ToEvents{}));
2061 template<
typename HsmType>
2062 struct get_events_from_hsm<HsmType,
2063 std::enable_if_t<has_transitions_v<HsmType>>> {
2064 using transition_list = as_type_list_t<typename HsmType::transitions>;
2065 using type =
typename aggregate_events<transition_list>::type;
2069 template<
typename HsmType>
2070 struct get_events_from_hsm<HsmType,
2071 std::enable_if_t<!has_transitions_v<HsmType>>> {
2072 using type = std::tuple<>;
2075 template<
typename HsmType>
2092 template<
typename HsmType,
typename Events>
2093 class erased_event {
2097 static constexpr std::size_t storage_size =
max_sizeof(event_list{});
2098 static constexpr std::size_t storage_alignment =
max_alignof(event_list{});
2100 erased_event() =
default;
2102 template<
typename Event>
2104 explicit erased_event(Event&& event) {
2105 emplace<std::decay_t<Event>>(std::forward<Event>(event));
2108 erased_event(erased_event
const& other) { copy_from(other); }
2110 erased_event(erased_event&& other) noexcept { move_from(other); }
2113 erased_event& operator=(erased_event
const& other) {
2114 if (
this != &other) {
2122 erased_event& operator=(erased_event&& other) noexcept {
2123 if (
this != &other) {
2130 ~erased_event() { reset(); }
2132 [[nodiscard]]
bool empty()
const {
return dispatch_ ==
nullptr; }
2134 bool dispatch(HsmType& hsm) {
2135 if (dispatch_ ==
nullptr) {
2138 return dispatch_(hsm, data());
2142 using dispatch_fn = bool (*)(HsmType&,
void*);
2143 using copy_fn = void (*)(
void*,
void const*);
2144 using move_fn = void (*)(
void*,
void*);
2145 using destroy_fn = void (*)(
void*);
2147 template<
typename Event,
typename... Args>
2148 void emplace(Args&&... args) {
2149 static_assert(
sizeof(Event) <= storage_size);
2150 static_assert(
alignof(Event) <= storage_alignment);
2152 new (data()) Event(std::forward<Args>(args)...);
2153 dispatch_ = [](HsmType& hsm,
void* storage) ->
bool {
2154 return hsm.handle(*
static_cast<Event*
>(storage));
2156 copy_ = [](
void* destination,
void const* source) {
2157 new (destination) Event(*
static_cast<Event const*
>(source));
2159 move_ = [](
void* destination,
void* source) {
2160 new (destination) Event(std::move(*
static_cast<Event*
>(source)));
2162 destroy_ = [](
void* storage) {
2163 static_cast<Event*
>(storage)->~Event();
2167 void copy_from(erased_event
const& other) {
2168 if (other.copy_ ==
nullptr) {
2171 other.copy_(data(), other.data());
2172 dispatch_ = other.dispatch_;
2173 copy_ = other.copy_;
2174 move_ = other.move_;
2175 destroy_ = other.destroy_;
2178 void move_from(erased_event& other) {
2179 if (other.move_ ==
nullptr) {
2182 other.move_(data(), other.data());
2183 dispatch_ = other.dispatch_;
2184 copy_ = other.copy_;
2185 move_ = other.move_;
2186 destroy_ = other.destroy_;
2193 if (destroy_ !=
nullptr) {
2196 dispatch_ =
nullptr;
2202 void* data() {
return storage_; }
2203 void const* data()
const {
return storage_; }
2205 alignas(storage_alignment)
unsigned char storage_[storage_size]{};
2206 dispatch_fn dispatch_{};
2209 destroy_fn destroy_{};
2219 template<
typename HsmType, std::
size_t Capacity>
2220 class tick_scheduler {
2222 using events = get_events_t<HsmType>;
2223 using event_type = erased_event<HsmType, events>;
2225 static_assert(Capacity > 0U,
2226 "tsm: tick_scheduler requires at least one timer slot");
2228 template<
typename Event>
2231 return schedule(std::forward<Event>(event),
ticks, 0U,
false);
2234 template<
typename Event>
2240 template<
typename Event>
2243 return period != 0U &&
2244 schedule(std::forward<Event>(event), period, period,
true);
2247 template<
typename Event>
2253 [[nodiscard]] std::size_t pending()
const {
2254 std::size_t count{};
2255 for (
auto const& timer : timers_) {
2264 [[nodiscard]]
bool empty()
const {
return pending() == 0U; }
2266 std::size_t tick(HsmType& hsm,
tsm::tick_rep elapsed_ticks = 1U) {
2267 std::size_t dispatched{};
2273 while (remaining_elapsed > 0U) {
2276 remaining_elapsed -= step;
2277 dispatched += dispatch_due(hsm);
2283 return tick(hsm, elapsed_ticks.
count());
2287 for (
auto& timer : timers_) {
2289 timer = timer_slot{};
2302 template<
typename Event>
2303 [[nodiscard]]
bool schedule(Event&& event,
2307 for (
auto& timer : timers_) {
2309 if (!timer.active) {
2310 timer.event = event_type(std::forward<Event>(event));
2311 timer.remaining =
ticks;
2313 timer.periodic = periodic;
2314 timer.active =
true;
2323 for (
auto const& timer : timers_) {
2324 if (!timer.active) {
2327 if (timer.remaining == 0U) {
2330 if (timer.remaining < step) {
2331 step = timer.remaining;
2338 for (
auto& timer : timers_) {
2339 if (!timer.active) {
2342 if (timer.remaining >
ticks) {
2343 timer.remaining -=
ticks;
2345 timer.remaining = 0U;
2350 std::size_t dispatch_due(HsmType& hsm) {
2351 std::size_t dispatched{};
2352 for (
auto& timer : timers_) {
2353 if (!timer.active) {
2356 if (timer.remaining == 0U) {
2357 auto event = timer.periodic ? timer.event : std::move(timer.event);
2358 (void)event.dispatch(hsm);
2360 if (timer.periodic) {
2361 timer.remaining = timer.period;
2363 timer = timer_slot{};
2370 std::array<timer_slot, Capacity> timers_{};
2373 template<
typename HsmType, std::
size_t Capacity>
2378 #if defined(__linux__) && !defined(__QNXNTO__) && !defined(__ZEPHYR__) && \
2379 !defined(TSM_SUPPRESS_AUTO_PLATFORM_ADAPTERS)
2385 using detail::action;
2386 using detail::automatic_event;
2387 using detail::choice;
2388 using detail::ClockedTransition;
2389 using detail::ClockTickEvent;
2390 using detail::completion_event;
2391 using detail::deep_history_state;
2392 using detail::defer;
2394 using detail::Event;
2395 using detail::External;
2396 using detail::final_state;
2399 using detail::fork_to;
2400 using detail::guard;
2401 using detail::history_state;
2403 using detail::Internal;
2404 using detail::internal;
2407 using detail::join_from;
2408 using detail::join_to;
2410 using detail::junction;
2411 using detail::local;
2414 using detail::OrthogonalExecutionPolicy;
2415 using detail::region;
2417 using detail::tick_scheduler;
2420 using detail::type_list;
2425 #if defined(__linux__) && !defined(__QNXNTO__) && !defined(__ZEPHYR__) && \
2426 !defined(TSM_SUPPRESS_AUTO_PLATFORM_ADAPTERS)
2427 template<
typename... Tasks>
2429 template<
typename... Tasks>
2431 template<
typename... Tasks>
2435 template<
typename... Deferred>
2438 template<
typename... Rules>
2441 template<
typename... TransitionEntries>
2444 template<
typename... TransitionEntries>
2449 return Ts<TransitionEntries...>{};
2452 #define TSM_TRANSITION(Name, From, Event, To) \
2454 using from = From; \
2455 using event = Event; \
2459 #define TSM_TRANSITION_ACTION(Name, From, Event, To, Action) \
2461 using from = From; \
2462 using event = Event; \
2464 static constexpr auto action = Action; \
2467 #define TSM_TRANSITION_GUARD(Name, From, Event, To, Guard) \
2469 using from = From; \
2470 using event = Event; \
2472 static constexpr auto guard = Guard; \
2475 #define TSM_TRANSITION_GUARD_ACTION(Name, From, Event, To, Guard, Action) \
2477 using from = From; \
2478 using event = Event; \
2480 static constexpr auto guard = Guard; \
2481 static constexpr auto action = Action; \
2484 #define TSM_TRANSITIONS(...) using transitions = ::tsm::Ts<__VA_ARGS__>
2488 #if defined(__FREE_RTOS__)
2492 #if defined(__QNXNTO__)
2496 #if defined(TSM_ENABLE_REFLECTION) && TSM_ENABLE_REFLECTION
constexpr hierarchy algorithms used by the HSM runtime.
FreeRTOS queue storage and task executor adapters.
Linux host adapters for timing and optional realtime scheduling.
consteval bool contains_type()
Definition: core_algorithms.h:137
typename infer_hierarchy< Context, Root >::type infer_hierarchy_t
Definition: core_algorithms.h:447
constexpr std::uint16_t npos
Definition: core_algorithms.h:31
concept transition_matches
Definition: transition.h:448
typename deferred_of< T >::type deferred_of_t
Definition: tsm.h:171
constexpr bool is_clocked_hsm_v
Definition: tsm.h:278
unique_tuple_t< typename get_events_from_hsm< HsmType >::type > get_events_t
Definition: tsm.h:2077
constexpr bool is_hsm_trait_v
Definition: tsm.h:263
constexpr bool is_state_trait_v
Define a helper to check for 'from' and 'to' types in transitions,.
Definition: tsm.h:267
concept hsm_like
Definition: tsm.h:255
void invoke_action(Callable &&callable, Context &ctx, Event &e)
Definition: tsm.h:362
fork_to< ForkMarker, Targets... > Fork
Definition: transition.h:122
realtime_thread_executor(Tasks &...) -> realtime_thread_executor< Tasks... >
transition_kind
Definition: transition.h:195
filter_type_list_t< is_synchronization_rule, as_type_list_t< Definition > > synchronization_entries_t
Definition: tsm.h:454
consteval std::size_t max_alignof(type_list< T, Ts... >)
Definition: type_list.h:146
typename join_to_rule_for< Join, Rules >::type join_to_rule_for_t
Definition: tsm.h:1588
concept state_has_guard
Definition: tsm.h:127
constexpr bool has_transitions_v
Definition: tsm.h:247
typename make_hsm< T >::type make_hsm_t
Definition: tsm.h:1489
concept guard_with_context
Definition: tsm.h:290
thread_executor(Tasks &...) -> thread_executor< Tasks... >
bool invoke_guard(Callable &&callable, Context &ctx, Event &e)
Definition: tsm.h:315
typename deferred_queue_storage< Events, Capacity >::type deferred_queue_storage_t
Definition: tsm.h:227
requires(!has_transition_type_c< T > &&has_transition_member_c< T >) struct transitions_of< T >
Definition: transition.h:479
typename synchronization_entries_of< T >::type synchronization_entries_of_t
Definition: tsm.h:1549
typename concat_type_lists< Lists... >::type concat_type_lists_t
Definition: type_list.h:202
concept has_deferred_type_c
Definition: tsm.h:155
concept action_with_context
Definition: tsm.h:337
concept state_has_exit
Definition: tsm.h:117
concept has_hierarchy_c
Definition: tsm.h:137
concept clocked_hsm_like
Definition: tsm.h:270
typename as_type_list< T >::type as_type_list_t
Definition: type_list.h:175
typename deferred_event_list< DeferredList >::type deferred_event_list_t
Definition: tsm.h:187
concept action_without_args
Definition: tsm.h:349
typename context_type_of< T >::type context_type_of_t
Definition: tsm.h:152
concept state_has_entry
Definition: tsm.h:122
concept transition_guard
Definition: tsm.h:308
thread_executor< Tasks... > task_executor
Definition: linux.h:611
consteval std::size_t max_sizeof(type_list< T, Ts... >)
Definition: type_list.h:134
concept guard_without_args
Definition: tsm.h:302
typename filter_type_list< Predicate, List >::type filter_type_list_t
Definition: type_list.h:222
constexpr bool dependent_false_v
Definition: tsm.h:281
typename front_type< T >::type front_type_t
Definition: type_list.h:72
constexpr bool has_valid_transition_v
variable template to check for the existence of a valid transition
Definition: tsm.h:108
transition_with_guard< transition_kind::external, From, Event, To, Guard > with_guard
Definition: transition.h:263
join_to< JoinMarker, Target > JoinTo
Definition: transition.h:140
concept transition_like
Definition: transition.h:417
transition_with_action< transition_kind::external, From, Event, To, Action > with_action
Definition: transition.h:259
typename wrap_transition< T >::type wrap_transition_t
Definition: tsm.h:1523
filter_type_list_t< is_transition, as_type_list_t< Definition > > transition_entries_t
Definition: tsm.h:450
concept guard_with_event
Definition: tsm.h:296
typename type_at< Index, List >::type type_at_t
Definition: type_list.h:100
typename matching_transition< From, Event, TransitionList >::type find_transition_t
find transition
Definition: tsm.h:104
concept state_has_action
Definition: tsm.h:132
typename wrap_transitions< as_type_list_t< T > >::type wrap_transitions_t
Definition: tsm.h:1534
typename transitions_of< T >::type transitions_of_t
Definition: transition.h:486
concept transition_action
Definition: tsm.h:355
concept transition_available
Definition: tsm.h:113
concept action_with_event
Definition: tsm.h:343
typename unique_tuple< Ts >::type unique_tuple_t
Definition: type_list.h:398
join_from< JoinMarker, Sources... > Join
Definition: transition.h:137
concept guard_with_context_event
Definition: tsm.h:284
concept action_with_context_event
Definition: tsm.h:331
transition_with_guard_action< transition_kind::external, From, Event, To, Guard, Action > with_guard_action
Definition: transition.h:272
tick_scheduler< HsmType, Capacity > TickScheduler
Definition: tsm.h:2374
concept state_handles_event
Definition: tsm.h:250
Kind
Definition: logging.h:25
Definition: bare_metal.h:20
detail::type_list< TransitionEntries... > transition_table
Definition: tsm.h:2442
constexpr tick_count ticks(tick_rep value) noexcept
Definition: ticks.h:85
consteval auto transitions(TransitionEntries...)
Definition: tsm.h:2448
transition_table< TransitionEntries... > Ts
Definition: tsm.h:2445
sleep_ticks_awaitable after_ticks(tsm::tick_rep ticks) noexcept
Definition: coroutine.h:638
periodic_ticks every_ticks
Definition: coroutine.h:682
detail::realtime_thread_executor< Tasks... > realtime_thread_executor
Definition: tsm.h:2430
detail::type_list< Rules... > synchronization
Definition: tsm.h:2439
detail::type_list< Deferred... > deferred_events
Definition: tsm.h:2436
std::chrono::duration< tick_rep, tick_period > tick_duration
Chrono duration type used for semantic scheduler ticks.
Definition: ticks.h:50
TSM_TICK_REP tick_rep
Definition: ticks.h:35
detail::thread_executor< Tasks... > thread_executor
Definition: tsm.h:2428
Semantic configuration tags for deterministic HSM behavior.
Target profile contracts for production and embedded builds.
QNX Neutrino 7.x executor and tick adapters.
Experimental C++ reflection metadata extraction.
Fixed-capacity ring queue storage for runtime events.
Definition: core_algorithms.h:50
Definition: core_algorithms.h:744
std::uint16_t destination
Definition: core_algorithms.h:745
Definition: core_algorithms.h:181
Definition: core_algorithms.h:153
Kind kind
Definition: logging.h:44
Definition: policies.h:109
Strong value type for semantic scheduler ticks.
Definition: ticks.h:54
constexpr tick_rep count() const noexcept
Definition: ticks.h:73
tick_period period
Definition: ticks.h:56
Target-neutral tick value type.
Authoring vocabulary for transitions, pseudostates, and synchronization.
Convenience include for runtime queue, executor, and topology APIs.
Small compile-time list and set utilities for C++ type tokens.