tin  1.5.9
tsm.h
Go to the documentation of this file.
1 // Copyright (c) 2026 Tinverse LLC. All rights reserved.
2 // SPDX-License-Identifier: LicenseRef-Tinverse-Commercial
3 
12 
13 #pragma once
14 #if defined(TSM_ENABLE_REFLECTION) && TSM_ENABLE_REFLECTION
15 #if __has_include(<meta>)
16 #include <meta>
17 #else
18 #error \
19  "TSM_ENABLE_REFLECTION requires a compiler and standard library with <meta>"
20 #endif
21 #endif
22 
23 #include <functional>
24 #include <array>
25 #include <concepts>
26 #include <cstddef>
27 #include <cstdint>
28 #include <new>
29 #include <tuple>
30 #include <type_traits>
31 #include <utility>
32 
33 #include "tsm/platform/profile.h"
34 #include "tsm/policies.h"
36 #include "tsm/ticks.h"
37 #include "tsm/transition.h"
38 #include "tsm/type_list.h"
39 
40 namespace tsm {
41 namespace detail {
42 
44 
47 template<typename From,
48  typename Event,
49  typename TransitionList,
50  size_t Index = 0,
51  typename = void>
52 struct transition_map_helper;
53 
58 template<typename From, typename Event, typename... TransitionEntries, size_t Index>
59 struct transition_map_helper<
60  From,
61  Event,
62  type_list<TransitionEntries...>,
63  Index,
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>;
68 
69  using type = typename std::conditional_t<
70  match,
71  CurrentTransition,
72  typename transition_map_helper<From,
73  Event,
74  type_list<TransitionEntries...>,
75  Index + 1>::type>;
76 };
77 
78 template<typename From, typename Event, typename... TransitionEntries, size_t Index>
79 struct transition_map_helper<
80  From,
81  Event,
82  type_list<TransitionEntries...>,
83  Index,
84  std::enable_if_t<!(Index < sizeof...(TransitionEntries))>> {
85  using type = void;
86 };
87 
94 template<typename From, typename Event, typename TransitionList>
95 struct matching_transition {
96  using type = typename transition_map_helper<From,
97  Event,
98  as_type_list_t<TransitionList>>::type;
99 };
100 
102 template<typename From, typename Event, typename TransitionList>
104  typename matching_transition<From, Event, TransitionList>::type;
105 
107 template<typename State, typename Event, typename TransitionList>
108 inline constexpr bool has_valid_transition_v =
109  !std::is_same_v<typename matching_transition<State, Event, TransitionList>::type,
110  void>;
111 
112 template<typename State, typename Event, typename TransitionList>
114  has_valid_transition_v<State, Event, TransitionList>;
115 
116 template<typename T>
118  &T::exit;
119 };
120 
121 template<typename T>
123  &T::entry;
124 };
125 
126 template<typename T>
128  &T::guard;
129 };
130 
131 template<typename T>
133  &T::action;
134 };
135 
136 template<typename T>
138  typename T::hierarchy;
139 };
140 
141 template<typename T, typename = void>
142 struct context_type_of {
143  using type = T;
144 };
145 
146 template<typename T>
147 struct context_type_of<T, std::void_t<typename T::context_type>> {
148  using type = typename T::context_type;
149 };
150 
151 template<typename T>
152 using context_type_of_t = typename context_type_of<T>::type;
153 
154 template<typename T>
156  typename T::deferred;
157 };
158 
159 template<typename T>
160 struct deferred_of {
161  using type = type_list<>;
162 };
163 
164 template<typename T>
165  requires has_deferred_type_c<T>
166 struct deferred_of<T> {
167  using type = as_type_list_t<typename T::deferred>;
168 };
169 
170 template<typename T>
171 using deferred_of_t = typename deferred_of<T>::type;
172 
173 template<typename Deferred>
174 struct is_defer_rule : std::bool_constant<defer_like<Deferred>> {};
175 
176 template<typename DeferredList>
177 struct deferred_event_list;
178 
179 template<typename... Deferred>
180 struct deferred_event_list<type_list<Deferred...>> {
181  using type = typename unique_tuple<type_list<typename Deferred::event...>,
182  type_list<>>::type;
183 };
184 
185 template<typename DeferredList>
187  typename deferred_event_list<DeferredList>::type;
188 
189 template<typename Event, std::size_t Capacity>
190 struct deferred_event_queue {
191  [[nodiscard]] std::uint16_t count() const { return count_; }
192 
193  bool push(Event const& event) {
194  if (count_ >= Capacity) {
195  return false;
196  }
197  storage_[(head_ + count_) % Capacity] = event;
198  ++count_;
199  return true;
200  }
201 
202  bool pop(Event& event) {
203  if (count_ == 0U) {
204  return false;
205  }
206  event = std::move(storage_[head_]);
207  head_ = (head_ + 1U) % Capacity;
208  --count_;
209  return true;
210  }
211 
212  std::array<Event, Capacity> storage_{};
213  std::uint16_t head_{};
214  std::uint16_t count_{};
215 };
216 
217 template<typename Events, std::size_t Capacity>
218 struct deferred_queue_storage;
219 
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>...>;
223 };
224 
225 template<typename Events, std::size_t Capacity>
227  typename deferred_queue_storage<Events, Capacity>::type;
228 
229 template<typename State, typename Event, typename DeferredList>
230 struct defers_event : std::false_type {};
231 
232 template<typename State, typename Event, typename... Deferred>
233 struct defers_event<State, Event, type_list<Deferred...>>
234  : std::bool_constant<(
235  false || ... ||
236  (std::is_same_v<typename Deferred::state, State> &&
237  std::is_same_v<typename Deferred::event, Event>))> {};
238 
239 template<typename T>
240 struct has_transitions : std::bool_constant<has_transitions_c<T>> {};
241 
242 template<typename T>
243 struct is_unwrapped_state_machine
244  : std::bool_constant<has_transitions_c<T> && !requires { T::is_hsm; }> {};
245 
246 template<typename T>
247 inline constexpr bool has_transitions_v = has_transitions_c<T>;
248 
249 template<typename State, typename Event, typename Context>
250 concept state_handles_event = requires(State& state, Context& ctx, Event& event) {
251  { state.handle(ctx, event) } -> std::convertible_to<bool>;
252 };
253 
254 template<typename T>
255 concept hsm_like = requires {
256  T::is_hsm;
257 };
258 
259 template<typename T>
260 struct is_hsm_trait : std::bool_constant<hsm_like<T>> {};
261 
262 template<typename T>
263 inline constexpr bool is_hsm_trait_v = hsm_like<T>;
264 
266 template<typename T>
267 inline constexpr bool is_state_trait_v = has_transitions_c<T> && !hsm_like<T>;
268 
269 template<typename T>
271  T::is_clocked_hsm;
272 };
273 
274 template<typename T>
275 struct is_clocked_hsm : std::bool_constant<clocked_hsm_like<T>> {};
276 
277 template<typename T>
278 inline constexpr bool is_clocked_hsm_v = clocked_hsm_like<T>;
279 
280 template<typename...>
281 inline constexpr bool dependent_false_v = false;
282 
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>;
287  };
288 
289 template<typename Callable, typename Context>
291  requires(Callable&& callable, Context& ctx) {
292  { std::invoke(std::forward<Callable>(callable), ctx) } -> std::same_as<bool>;
293  };
294 
295 template<typename Callable, typename Event>
297  requires(Callable&& callable, Event& event) {
298  { std::invoke(std::forward<Callable>(callable), event) } -> std::same_as<bool>;
299  };
300 
301 template<typename Callable>
303  requires(Callable&& callable) {
304  { std::invoke(std::forward<Callable>(callable)) } -> std::same_as<bool>;
305  };
306 
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>;
313 
314 template<typename Callable, typename Context, typename Event>
315 bool invoke_guard(Callable&& callable, Context& ctx, Event& e) {
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));
324  } else {
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()");
327  }
328 }
329 
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>;
334  };
335 
336 template<typename Callable, typename Context>
338  requires(Callable&& callable, Context& ctx) {
339  { std::invoke(std::forward<Callable>(callable), ctx) } -> std::same_as<void>;
340  };
341 
342 template<typename Callable, typename Event>
344  requires(Callable&& callable, Event& event) {
345  { std::invoke(std::forward<Callable>(callable), event) } -> std::same_as<void>;
346  };
347 
348 template<typename Callable>
350  requires(Callable&& callable) {
351  { std::invoke(std::forward<Callable>(callable)) } -> std::same_as<void>;
352  };
353 
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>;
360 
361 template<typename Callable, typename Context, typename Event>
362 void invoke_action(Callable&& callable, Context& ctx, Event& e) {
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));
371  } else {
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()");
374  }
375 }
376 
377 } // namespace detail
378 } // namespace tsm
379 
380 #include "tsm/core_algorithms.h"
381 
382 namespace tsm {
383 namespace detail {
384 
385 template<typename T>
386 struct state_slot {
387  T value{};
388 };
389 
390 template<typename List>
391 struct state_storage;
392 
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;
398  }
399 
400  template<typename State>
401  State const& get() const noexcept {
402  return state_slot<State>::value;
403  }
404 };
405 
406 template<typename Hierarchy, typename AuthoredTransitionList, typename = void>
407 struct hierarchy_transition_list {
408  using type = AuthoredTransitionList;
409 };
410 
411 template<typename Hierarchy, typename AuthoredTransitionList>
412 struct hierarchy_transition_list<
413  Hierarchy,
414  AuthoredTransitionList,
415  std::void_t<typename Hierarchy::transitions>> {
416  using type = typename Hierarchy::transitions;
417 };
418 
419 template<typename Context, bool HasExplicitHierarchy>
420 struct context_hierarchy_type {
422 };
423 
424 template<typename Context>
425 struct context_hierarchy_type<Context, true> {
426  using type = typename Context::hierarchy;
427 };
428 
429 template<typename Context,
430  typename AuthoredTransitionList,
431  typename CoreTable,
432  bool HasHierarchy>
433 struct hsm_state_model {
434  using hierarchy = void;
435  using transitions = AuthoredTransitionList;
436  using states = typename CoreTable::states;
437 };
438 
439 template<typename Context, typename AuthoredTransitionList, typename CoreTable>
440 struct hsm_state_model<Context, AuthoredTransitionList, CoreTable, true> {
441  using hierarchy =
442  typename context_hierarchy_type<Context, has_hierarchy_c<Context>>::type;
443  using transitions =
444  typename hierarchy_transition_list<hierarchy, AuthoredTransitionList>::type;
445  using states = typename hierarchy::states;
446 };
447 
448 template<typename Definition>
451 
452 template<typename Definition>
455 
463 
483 template<typename T,
484  typename DefinitionEntries = transitions_of_t<T>,
485  typename Configuration = tsm::policy::Configuration<>>
486 struct hsm {
487  static constexpr bool is_hsm = true;
488  using type = hsm<T, DefinitionEntries, Configuration>;
489  using HsmType = type; // alias for policy classes
490  using definition = T;
491  using context_type = context_type_of_t<T>;
492  using configuration = Configuration;
493  using semantics_policy = typename configuration::semantics;
494  using runtime_policy = typename configuration::runtime;
495  using logging_policy = typename configuration::logging;
496  using sequence_policy = typename semantics_policy::precompute_sequences;
497  using authored_definition_list = as_type_list_t<DefinitionEntries>;
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");
504  using deferred_list = deferred_of_t<T>;
505  static_assert(
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;
520  using state_model =
521  hsm_state_model<T,
522  authored_transition_list,
523  authored_core_table,
524  has_hierarchy>;
525  using hierarchy_type = typename state_model::hierarchy;
526  using transition_list = typename state_model::transitions;
527  using transitions = transition_list;
529  using initial_state = typename front_type_t<transition_list>::from;
530  using state_list = typename state_model::states;
531  using runtime_state_set = tsm::core::type_set<state_list>;
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>;
540  using deferred_event_set = tsm::core::type_set<deferred_events>;
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 =
547 
548  hsm()
549  requires std::default_initializable<context_type>
550  : context_{},
551  active_state_(state_index<initial_state>()) {
552  reset_active_path();
553  }
554 
555  explicit hsm(context_type const& context)
556  requires std::copy_constructible<context_type>
557  : context_(context),
558  active_state_(state_index<initial_state>()) {
559  reset_active_path();
560  }
561 
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>()) {
566  reset_active_path();
567  }
568 
569  [[nodiscard]] context_type& context() noexcept { return context_; }
570 
571  [[nodiscard]] context_type const& context() const noexcept { return context_; }
572 
573  [[nodiscard]] context_type& ctx() noexcept { return context_; }
574 
575  [[nodiscard]] context_type const& ctx() const noexcept { return context_; }
576 
577  template<typename State>
578  static consteval std::uint16_t state_index() {
579  return runtime_state_set::template index<State>();
580  }
581 
582  [[nodiscard]] constexpr std::uint16_t active_state_id() const noexcept {
583  return active_state_;
584  }
585 
586  [[nodiscard]] constexpr auto const& active_path() const noexcept {
587  return active_path_;
588  }
589 
590  template<typename State>
591  [[nodiscard]] bool active() const noexcept {
592  return active_state_ == state_index<State>();
593  }
594 
595  template<typename Parent>
596  [[nodiscard]] bool active_final() const noexcept {
597  return active_final_id<Parent, 0U>();
598  }
599 
600  template<typename State>
601  State& state() noexcept {
602  return states_.template get<State>();
603  }
604 
605  template<typename State>
606  State const& state() const noexcept {
607  return states_.template get<State>();
608  }
609 
610  template<typename Event>
611  void enter(Event& e) {
612  active_state_ = state_index<initial_state>();
613  reset_active_path();
614  this->entry(e, &state<initial_state>());
615  }
616 
617  template<typename Event>
618  void leave(Event& e) {
619  dispatch_exit<0>(e);
620  }
621 
628  template<typename Event>
629  bool handle(Event&& e) {
630  log_debug(tsm::log::Kind::Event, event_record<std::decay_t<Event>>());
631  bool handled = false;
632  if constexpr (has_hierarchy) {
633  handled = dispatch_hierarchy_path(e);
634  } else {
635  handled = dispatch_handle<0>(e);
636  }
637  bool deferred_now = false;
638  if (!handled) {
639  if (!recalling_deferred_ &&
640  should_defer_event<std::decay_t<Event>>()) {
641  handled = defer_event(e);
642  deferred_now = handled;
643  } else {
644  log_warn(tsm::log::Kind::Unhandled,
645  event_record<std::decay_t<Event>>());
646  }
647  }
648  if (handled && !deferred_now && !recalling_deferred_) {
649  recall_deferred_events();
650  }
651  return handled;
652  }
653 
654  template<typename Event, typename State>
655  void entry(Event&& e, State* state) noexcept {
656  log_debug(tsm::log::Kind::Entry,
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),
660  State*,
661  context_type&,
662  decltype(e)>) {
663  state->entry(context_, e);
664  } else if constexpr (std::is_invocable_v<decltype(&State::entry),
665  State*,
666  context_type&>) {
667  state->entry(context_);
668  } else if constexpr (std::is_invocable_v<decltype(&State::entry),
669  State*>) {
670  state->entry();
671  } else {
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()");
674  }
675  }
676  if constexpr (is_hsm_trait_v<State>) {
677  state->enter(e);
678  }
679  }
680 
681  template<typename Event, typename State>
682  void exit(Event&& e, State* state) noexcept {
683  log_debug(tsm::log::Kind::Exit,
684  state_record<State, std::decay_t<Event>>());
685  if constexpr (is_hsm_trait_v<State>) {
686  state->leave(e);
687  }
688  if constexpr (state_has_exit<State>) {
689  if constexpr (std::is_invocable_v<decltype(&State::exit),
690  State*,
691  context_type&,
692  decltype(e)>) {
693  state->exit(context_, e);
694  } else if constexpr (std::is_invocable_v<decltype(&State::exit),
695  State*,
696  context_type&>) {
697  state->exit(context_);
698  } else if constexpr (std::is_invocable_v<decltype(&State::exit),
699  State*>) {
700  state->exit();
701  } else {
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()");
704  }
705  }
706  }
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),
718  State*,
719  context_type&,
720  decltype(e)>) {
721  return state->guard(context_, e);
722  } else if constexpr (std::is_invocable_v<decltype(&State::guard),
723  State*,
724  context_type&>) {
725  return state->guard(context_);
726  } else if constexpr (std::is_invocable_v<decltype(&State::guard),
727  State*>) {
728  return state->guard();
729  } else {
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()");
732  }
733  } else {
734  if constexpr (transition_has_guard<Tn>) {
735  return invoke_guard(Tn::guard, context_, e);
736  } else {
737  return true;
738  }
739  }
740  }
741 
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),
751  State*,
752  context_type&,
753  decltype(e)>) {
754  state->action(context_, e);
755  } else if constexpr (std::is_invocable_v<decltype(&State::action),
756  State*,
757  context_type&>) {
758  state->action(context_);
759  } else if constexpr (std::is_invocable_v<decltype(&State::action),
760  State*>) {
761  state->action();
762  } else {
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()");
765  }
766  } else {
767  if constexpr (transition_has_action<Tn>) {
768  log_debug(tsm::log::Kind::Action,
769  transition_record<Tn, Event>());
770  invoke_action(Tn::action, context_, e);
771  }
772  }
773  }
774 
775  template<typename transition,
776  typename Event,
777  typename State = typename transition::from>
778  requires (!state_handles_event<State, Event, context_type>)
779  bool
780  handle_transition(State* current, Event& e) {
784  if (!this->check_guard<transition>(e, current)) {
786  transition_record<transition, Event>());
787  return false;
788  }
789 
790  return execute_transition<transition>(current, e);
791  }
792 
793  template<typename transition,
794  typename Event,
795  typename State = typename transition::from>
796  requires state_handles_event<State, Event, context_type>
797  bool
798  handle_transition(State* current, Event& e) {
799 
803  if (!current->handle(context_, e)) {
805  transition_record<transition, Event>());
806  return false;
807  }
808 
809  return execute_transition<transition>(current, e);
810  }
811 
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);
819  return true;
820  } else
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>(
833  active_state_,
834  selected_source,
835  destination);
836  return apply_transition_sequence<transition>(
837  sequence, destination, current, e);
838  } else
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>(
848  active_state_,
849  selected_source,
850  destination);
851  return apply_transition_sequence<transition>(
852  sequence, destination, current, e);
853  } else {
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);
861  } else
862  if constexpr (can_precompute_active_leaf_sequences<transition>()) {
863  auto const& sequence =
864  precomputed_sequence_for_active_leaf<transition>(
865  active_state_);
866  return apply_transition_sequence<transition>(
867  sequence, sequence.destination, current, e);
868  } else {
869  const auto sequence =
870  tsm::core::compute_transition_sequence<hierarchy_type>(
871  active_state_,
872  destination);
873  return apply_transition_sequence<transition>(
874  sequence, destination, current, e);
875  }
876  }
877  } else {
881  transition_record<transition, Event>());
882  this->exit(e, current);
883 
884  this->perform_action<transition>(e, current);
885 
886  using to = typename transition::to;
887  active_state_ = state_index<to>();
888  reset_active_path();
889 
890  this->entry(e, &state<to>());
891  resolve_decision_pseudostate<to>();
892  return true;
893  }
894  }
895 
896  template<typename transition, typename Sequence, typename Event, typename State>
897  bool apply_transition_sequence(Sequence const& sequence,
898  std::uint16_t destination,
899  State* current,
900  Event& e) {
901  if (sequence.lca == tsm::core::npos) {
902  log_error(tsm::log::Kind::Transition,
903  transition_record<transition, Event>());
904  return false;
905  }
906 
908  transition_record<transition, Event>());
909 
913  for (std::size_t i = 0; i < sequence.exits.size; ++i) {
914  dispatch_exit_id<0>(sequence.exits[i], e);
915  }
916 
917  this->perform_action<transition>(e, current);
918 
919  active_state_ =
920  sequence.final_path.size == 0U
921  ? destination
922  : sequence.final_path[sequence.final_path.size - 1U];
923  active_path_ = sequence.final_path;
924 
925  for (std::size_t i = 0; i < sequence.entries.size; ++i) {
926  dispatch_entry_id<0>(sequence.entries[i], e);
927  }
928  resolve_decision_pseudostate_id(destination);
929  return true;
930  }
931 
932  template<typename Transition, typename ActiveState>
933  static consteval auto precomputed_sequence_from_active_leaf() {
934  // Parent-sourced transitions are authored at a composite state, but the
935  // active source at runtime is one of that parent's descendant leaves.
936  // This builds one candidate sequence for each possible 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>();
941 
942  if constexpr (hierarchy_type::least_common_ancestor(active_leaf,
943  selected_source) ==
944  selected_source) {
945  return tsm::core::compute_transition_sequence<hierarchy_type>(
946  active_leaf,
947  destination);
948  } else {
950  invalid.destination = destination;
951  return invalid;
952  }
953  }
954 
955  template<typename Transition, typename States>
956  struct precomputed_sequence_table;
957 
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>()...
964  };
965  };
966 
967  template<typename Transition>
968  static consteval bool can_precompute_sequence() {
969  // Leaf-sourced external transitions have a single static source and a
970  // single concrete destination, so the full exit/entry sequence can be a
971  // compile-time constant.
972  if constexpr (!has_hierarchy || !sequence_policy::enabled) {
973  return false;
974  } else if constexpr (history_state_like<typename Transition::to> ||
975  deep_history_state_like<typename Transition::to>) {
976  return false;
977  } else {
978  constexpr auto source =
979  state_index<typename Transition::from>();
980  return hierarchy_type::initial_child_index(source) ==
982  }
983  }
984 
985  template<typename Transition>
986  static consteval bool can_precompute_active_leaf_sequences() {
987  // Composite-sourced external transitions can still be precomputed, but
988  // the selected sequence depends on the currently active descendant.
989  if constexpr (!has_hierarchy || !sequence_policy::enabled) {
990  return false;
991  } else if constexpr (transition_traits<Transition>::kind !=
992  transition_kind::external) {
993  return false;
994  } else if constexpr (history_state_like<typename Transition::to> ||
995  deep_history_state_like<typename Transition::to>) {
996  return false;
997  } else {
998  constexpr auto source =
999  state_index<typename Transition::from>();
1000  return hierarchy_type::initial_child_index(source) !=
1002  }
1003  }
1004 
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>(
1010  source,
1011  destination);
1012  }
1013 
1014  template<typename Transition>
1015  static constexpr auto const& precomputed_sequence_for_active_leaf(
1016  std::uint16_t active_leaf) {
1017  using table =
1018  precomputed_sequence_table<Transition, state_list>;
1019  return table::value[active_leaf];
1020  }
1021 
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);
1031  if (remembered != tsm::core::npos) {
1032  return deep_history_leaf(remembered);
1033  }
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);
1040  if (remembered != tsm::core::npos) {
1041  return remembered;
1042  }
1043  const auto initial = hierarchy_type::initial_child_index(parent);
1044  return initial == tsm::core::npos ? parent : initial;
1045  } else {
1046  return state_index<Destination>();
1047  }
1048  }
1049 
1050  [[nodiscard]] std::uint16_t deep_history_leaf(std::uint16_t state_id) const {
1051  if constexpr (has_hierarchy) {
1052  while (true) {
1053  const auto remembered = history_.get(state_id);
1054  if (remembered != tsm::core::npos) {
1055  state_id = remembered;
1056  continue;
1057  }
1058  const auto initial = hierarchy_type::initial_child_index(state_id);
1059  if (initial == tsm::core::npos) {
1060  return state_id;
1061  }
1062  state_id = initial;
1063  }
1064  } else {
1065  return state_id;
1066  }
1067  }
1068 
1069  template<std::size_t Index, typename Event>
1070  bool dispatch_handle(Event& e) {
1071  if constexpr (Index >= state_count) {
1072  return false;
1073  } else {
1074  switch (active_state_) {
1075  case Index:
1076  return handle_active_state<type_at_t<Index, state_list>>(e);
1077  default:
1078  return dispatch_handle<Index + 1U>(e);
1079  }
1080  }
1081  }
1082 
1083  template<typename State, typename Event>
1084  bool handle_active_state(Event& e) {
1085  return handle_source_state<State>(e);
1086  }
1087 
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);
1099  }
1100  }
1101  if (!handled) {
1102  if constexpr (transition_available<State,
1103  std::decay_t<Event>,
1104  transition_list>) {
1105  handled =
1106  handle_matching_transitions<State,
1107  Event,
1108  first_transition_index>(
1109  current,
1110  e);
1111  }
1112  }
1113  return handled;
1114  }
1115 
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) {
1124  return false;
1125  } else {
1126  using transition = type_at_t<Index, transition_list>;
1127  if constexpr (transition_matches<transition,
1128  State,
1129  std::decay_t<Event>>) {
1130  if (handle_transition<transition>(current, e)) {
1131  return true;
1132  }
1133  }
1134  return handle_matching_transitions<State, Event, Index + 1U>(
1135  current,
1136  e);
1137  }
1138  }
1139 
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)) {
1148  return true;
1149  }
1150  }
1151  return false;
1152  }
1153 
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) {
1161  return false;
1162  } else {
1163  if (state_id == Index) {
1164  return handle_source_state<type_at_t<Index, state_list>>(e);
1165  }
1166  return dispatch_source_id<Index + 1U>(state_id, e);
1167  }
1168  }
1169 
1170  template<typename Parent, std::size_t Index>
1171  [[nodiscard]] bool active_final_id() const noexcept {
1172  if constexpr (Index >= state_count) {
1173  return false;
1174  } else {
1175  using State = type_at_t<Index, state_list>;
1176  if constexpr (final_state_like<State>) {
1177  if constexpr (std::is_same_v<typename State::final_parent,
1178  Parent>) {
1179  return active_state_ == Index;
1180  } else {
1181  return active_final_id<Parent, Index + 1U>();
1182  }
1183  } else {
1184  return active_final_id<Parent, Index + 1U>();
1185  }
1186  }
1187  }
1188 
1189  template<std::size_t Index, typename Event>
1190  void dispatch_exit(Event& e) {
1191  if constexpr (Index < state_count) {
1192  switch (active_state_) {
1193  case Index:
1194  exit(e, &state<type_at_t<Index, state_list>>());
1195  break;
1196  default:
1197  dispatch_exit<Index + 1U>(e);
1198  break;
1199  }
1200  }
1201  }
1202 
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);
1211  exit(e, &state<type_at_t<Index, state_list>>());
1212  } else {
1213  dispatch_exit_id<Index + 1U>(state_id, e);
1214  }
1215  }
1216  }
1217 
1218  void remember_history(std::uint16_t state_id) {
1222  if constexpr (has_hierarchy) {
1223  const auto parent = hierarchy_type::parent_index(state_id);
1224  if (parent != tsm::core::npos) {
1225  history_.set(parent, state_id);
1226  }
1227  }
1228  }
1229 
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) {
1237  entry(e, &state<type_at_t<Index, state_list>>());
1238  } else {
1239  dispatch_entry_id<Index + 1U>(state_id, e);
1240  }
1241  }
1242  }
1243 
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);
1252  }
1253  }
1254 
1255  void resolve_decision_pseudostate_id(std::uint16_t state_id) {
1256  dispatch_decision_pseudostate_id<0U>(state_id);
1257  }
1258 
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) {
1263  using State = type_at_t<Index, state_list>;
1264  resolve_decision_pseudostate<State>();
1265  } else {
1266  dispatch_decision_pseudostate_id<Index + 1U>(state_id);
1267  }
1268  }
1269  }
1270 
1271  template<typename State>
1272  void current_state() {
1273  active_state_ = state_index<State>();
1274  reset_active_path();
1275  }
1276 
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();
1281  } else {
1282  return 0U;
1283  }
1284  }
1285 
1286  void reset_active_path() {
1287  if constexpr (has_hierarchy) {
1288  active_path_ = hierarchy_type::path_from_root(active_state_);
1289  } else {
1290  active_path_ = {};
1291  active_path_.push_back(active_state_);
1292  }
1293  }
1294 
1295  template<typename Event>
1296  static consteval std::uint16_t event_index() {
1297  return core_table::template event_index<Event>();
1298  }
1299 
1300  template<typename Event>
1301  static consteval std::uint16_t deferred_event_index() {
1302  return deferred_event_set::template index<Event>();
1303  }
1304 
1305  template<typename Event>
1306  static consteval tsm::log::Record event_record() {
1307  return { tsm::tick_duration{},
1310  active_state_placeholder(),
1311  event_index<Event>(),
1313  tsm::core::npos };
1314  }
1315 
1316  template<typename State, typename Event>
1317  static consteval tsm::log::Record state_record() {
1318  return { tsm::tick_duration{},
1321  state_index<State>(),
1322  event_index<Event>(),
1324  tsm::core::npos };
1325  }
1326 
1327  template<typename Transition, typename Event>
1328  static consteval tsm::log::Record transition_record() {
1329  return { tsm::tick_duration{},
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>() };
1336  }
1337 
1338  static consteval std::uint16_t active_state_placeholder() {
1339  return tsm::core::npos;
1340  }
1341 
1342  constexpr void log_debug(tsm::log::Kind kind,
1343  tsm::log::Record const& record) {
1344  if constexpr (logging_policy::enabled) {
1345  auto event = record;
1346  event.kind = kind;
1347  logger_.debug(kind, event);
1348  }
1349  }
1350 
1351  constexpr void log_info(tsm::log::Kind kind,
1352  tsm::log::Record const& record) {
1353  if constexpr (logging_policy::enabled) {
1354  auto event = record;
1355  event.kind = kind;
1356  logger_.info(kind, event);
1357  }
1358  }
1359 
1360  constexpr void log_warn(tsm::log::Kind kind,
1361  tsm::log::Record const& record) {
1362  if constexpr (logging_policy::enabled) {
1363  auto event = record;
1364  event.kind = kind;
1365  logger_.warn(kind, event);
1366  }
1367  }
1368 
1369  constexpr void log_error(tsm::log::Kind kind,
1370  tsm::log::Record const& record) {
1371  if constexpr (logging_policy::enabled) {
1372  auto event = record;
1373  event.kind = kind;
1374  logger_.error(kind, event);
1375  }
1376  }
1377 
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])) {
1388  return true;
1389  }
1390  }
1391  return false;
1392  } else {
1393  return dispatch_defer_source_id<Event, 0U>(active_state_);
1394  }
1395  } else {
1396  return false;
1397  }
1398  }
1399 
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) {
1403  return false;
1404  } else {
1405  if (state_id == Index) {
1406  using State = type_at_t<Index, state_list>;
1407  return defers_event<State, Event, deferred_list>::value;
1408  }
1409  return dispatch_defer_source_id<Event, Index + 1U>(state_id);
1410  }
1411  }
1412 
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);
1417  } else {
1418  return false;
1419  }
1420  }
1421 
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>();
1429  }
1430  }
1431 
1432  template<std::size_t Index>
1433  void recall_deferred_event_id() {
1434  if constexpr (Index < deferred_event_count) {
1435  using Event = type_at_t<Index, deferred_events>;
1436  Event event{};
1437  while (deferred_queue<Event>().pop(event)) {
1438  (void)handle(event);
1439  }
1440  recall_deferred_event_id<Index + 1U>();
1441  }
1442  }
1443 
1444  template<typename Event>
1445  [[nodiscard]] auto& deferred_queue() {
1446  return std::get<deferred_event_queue<Event, deferred_capacity>>(
1447  deferred_queues_);
1448  }
1449 
1450  template<typename Event>
1451  [[nodiscard]] auto const& deferred_queue() const {
1452  return std::get<deferred_event_queue<Event, deferred_capacity>>(
1453  deferred_queues_);
1454  }
1455 
1456  context_type context_;
1457  States states_{};
1458  std::uint16_t active_state_{};
1459  tsm::core::static_path<state_count> active_path_{};
1460  history_storage history_{};
1461  deferred_queues deferred_queues_{};
1462  bool recalling_deferred_{};
1463  [[no_unique_address]] logging_policy logger_{};
1464 
1465  struct deferred_recall_scope {
1466  explicit deferred_recall_scope(bool& flag) noexcept : flag_(flag) {
1467  flag_ = true;
1468  }
1469 
1470  deferred_recall_scope(deferred_recall_scope const&) = delete;
1471  deferred_recall_scope& operator=(deferred_recall_scope const&) = delete;
1472 
1473  ~deferred_recall_scope() noexcept { flag_ = false; }
1474 
1475  private:
1476  bool& flag_;
1477  };
1478 };
1479 
1480 template<typename T, typename = void>
1481 struct make_hsm;
1482 
1483 template<typename T>
1484 struct make_hsm<T, std::enable_if_t<!is_state_trait_v<T>>> {
1485  using type = T;
1486 };
1487 
1488 template<typename T>
1489 using make_hsm_t = typename make_hsm<T>::type;
1490 
1500 template<typename T, bool IsTransition = transition_like<T>>
1501 struct wrap_transition {
1502  using type = T;
1503 };
1504 
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;
1510 
1511  using wrap_from =
1512  std::conditional_t<is_state_trait_v<from>, make_hsm_t<from>, from>;
1513  using wrap_to =
1514  std::conditional_t<is_state_trait_v<to>, make_hsm_t<to>, to>;
1515 
1516  using type =
1517  typename transition_traits<T>::template with_endpoints<wrap_from,
1518  event,
1519  wrap_to>;
1520 };
1521 
1522 template<typename T>
1523 using wrap_transition_t = typename wrap_transition<T>::type;
1524 
1525 template<typename... TransitionEntries>
1526 struct wrap_transitions;
1527 
1528 template<typename... TransitionEntries>
1529 struct wrap_transitions<type_list<TransitionEntries...>> {
1530  using type = type_list<wrap_transition_t<TransitionEntries>...>;
1531 };
1532 
1533 template<typename T>
1534 using wrap_transitions_t = typename wrap_transitions<as_type_list_t<T>>::type;
1535 
1536 template<typename T>
1537 struct synchronization_entries_of {
1542  using explicit_rules = typename synchronization_of<T>::type;
1543  using authored_rules =
1546 };
1547 
1548 template<typename T>
1549 using synchronization_entries_of_t = typename synchronization_entries_of<T>::type;
1550 
1553 template<typename T>
1554 struct make_hsm<T, std::enable_if_t<is_state_trait_v<T>>> {
1557 
1558  using type = hsm<T, transitions>;
1559 };
1560 
1561 template<typename Join, typename Rules>
1562 struct join_to_rule_for;
1563 
1564 template<typename Join>
1565 struct join_to_rule_for<Join, type_list<>> {
1566  using type = void;
1567 };
1568 
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 {};
1573 
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>> {};
1577 
1578  using candidate =
1579  std::conditional_t<matching_join_to<First>::value, First, void>;
1580  using type =
1581  std::conditional_t<std::is_void_v<candidate>,
1582  typename join_to_rule_for<Join,
1583  type_list<Rest...>>::type,
1584  candidate>;
1585 };
1586 
1587 template<typename Join, typename Rules>
1588 using join_to_rule_for_t = typename join_to_rule_for<Join, Rules>::type;
1589 
1590 template<typename Join, typename Rules>
1591 struct join_to_rule_count;
1592 
1593 template<typename Join>
1594 struct join_to_rule_count<Join, type_list<>> : std::integral_constant<std::size_t, 0U> {};
1595 
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 {};
1600 
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>> {};
1604 
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;
1608 };
1609 
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");
1634 
1635  template<typename RegionDefinition>
1636  using region_hsm = hsm<RegionDefinition>;
1637 
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");
1643  }
1644 
1645  struct dispatch_result {
1649  std::uint64_t handled_mask{};
1650  std::uint64_t completion_handled_mask{};
1651  bool completed{};
1652  bool completion_dispatched{};
1653  bool synchronization_handled{};
1654 
1655  [[nodiscard]] constexpr bool handled() const {
1656  return handled_mask != 0U || synchronization_handled;
1657  }
1658 
1659  [[nodiscard]] constexpr bool completion_handled() const {
1660  return completion_handled_mask != 0U;
1661  }
1662 
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;
1667  }
1668 
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;
1673  }
1674 
1675  template<typename RegionDefinition>
1676  constexpr void mark_handled() {
1677  constexpr auto index = region_index<RegionDefinition>();
1678  handled_mask |= bit(index);
1679  }
1680 
1681  template<typename RegionDefinition>
1682  constexpr void mark_completion_handled() {
1683  constexpr auto index = region_index<RegionDefinition>();
1684  completion_handled_mask |= bit(index);
1685  }
1686  };
1687 
1688  template<typename RegionDefinition>
1689  [[nodiscard]] region_hsm<RegionDefinition>& region() {
1690  return std::get<region_hsm<RegionDefinition>>(regions_);
1691  }
1692 
1693  template<typename RegionDefinition>
1694  [[nodiscard]] region_hsm<RegionDefinition> const& region() const {
1695  return std::get<region_hsm<RegionDefinition>>(regions_);
1696  }
1697 
1698  template<typename Event>
1699  void enter(Event&& event) {
1700  auto& e = event;
1701  std::apply([&e](auto&... region_hsm) { (region_hsm.enter(e), ...); },
1702  regions_);
1703  }
1704 
1705  template<typename Event>
1706  void leave(Event&& event) {
1707  auto& e = event;
1708  std::apply([&e](auto&... region_hsm) { (region_hsm.leave(e), ...); },
1709  regions_);
1710  }
1711 
1712  template<typename Event>
1713  void entry(Event&& event) {
1714  enter(std::forward<Event>(event));
1715  }
1716 
1717  template<typename Event>
1718  void exit(Event&& event) {
1719  leave(std::forward<Event>(event));
1720  }
1721 
1722  template<typename Event>
1723  bool handle(Event&& event) {
1724  return dispatch(std::forward<Event>(event)).handled();
1725  }
1726 
1727  template<typename Event>
1728  dispatch_result dispatch(Event&& event) {
1734  auto& e = 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);
1740  return result;
1741  }
1742 
1743  template<typename State>
1744  [[nodiscard]] bool active() const {
1745  bool result = false;
1746  std::apply(
1747  [&result](auto const&... region_hsm) {
1748  ((result = region_active<State>(region_hsm) || result), ...);
1749  },
1750  regions_);
1751  return result;
1752  }
1753 
1754  template<typename ForkRule>
1755  void fork() {
1756  activate_fork_targets<ForkRule>();
1757  }
1758 
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{});
1764  }
1765 
1766  template<typename JoinFromRule>
1767  [[nodiscard]] bool join_enabled() const {
1768  static_assert(
1769  join_from_rule_like<JoinFromRule>,
1770  "tsm: join_enabled requires tsm::join_from<Join, Sources...>");
1771  return sources_active(typename JoinFromRule::sources{});
1772  }
1773 
1774  template<typename JoinFromRule, typename JoinToRule>
1775  bool join() {
1776  static_assert(
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>()) {
1785  return false;
1786  }
1787  activate_target<typename JoinToRule::target>();
1788  return true;
1789  }
1790 
1791  template<typename RegionDefinition>
1792  [[nodiscard]] bool region_complete() const {
1793  return region<RegionDefinition>()
1794  .template active_final<RegionDefinition>();
1795  }
1796 
1797  [[nodiscard]] bool complete() const {
1798  if constexpr (sizeof...(RegionDefinitions) == 0U) {
1799  return true;
1800  } else {
1801  return (region_complete<RegionDefinitions>() && ...);
1802  }
1803  }
1804 
1805  template<typename Event = completion_event>
1806  dispatch_result dispatch_completion_if_complete() {
1807  if (!complete()) {
1808  return {};
1809  }
1810  Event event{};
1811  dispatch_result result{};
1812  dispatch_completion_regions<0U>(result, event);
1813  result.completed = complete();
1814  result.completion_dispatched = true;
1815  completion_dispatched_ = true;
1816  return result;
1817  }
1818 
1819  template<typename State>
1820  static consteval bool state_in_regions() {
1821  return (region_hsm<RegionDefinitions>::runtime_state_set::template contains<State>() ||
1822  ...);
1823  }
1824 
1825  template<typename... States>
1826  static consteval bool states_in_regions(type_list<States...>) {
1827  return (state_in_regions<States>() && ...);
1828  }
1829 
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>();
1840  } else {
1841  return false;
1842  }
1843  }
1844 
1845  template<typename... Rules>
1846  static consteval bool validate_synchronization_rules(type_list<Rules...>) {
1847  return (valid_synchronization_rule<Rules>() && ...);
1848  }
1849 
1850  static consteval bool validate_synchronization_rules() {
1851  return validate_synchronization_rules(synchronization_rules{});
1852  }
1853 
1854  template<typename RegionDefinition>
1855  static consteval std::uint16_t region_index() {
1856  return tsm::core::type_set<regions>::template index<RegionDefinition>();
1857  }
1858 
1859  static constexpr std::uint64_t bit(std::uint16_t index) {
1860  return std::uint64_t{ 1U } << index;
1861  }
1862 
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>();
1872  }
1873  dispatch_regions<Index + 1U>(result, event);
1874  }
1875  }
1876 
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;
1887  }
1888  }
1889 
1890  template<std::size_t Index>
1891  bool process_fork_rules() {
1895  if constexpr (Index >= synchronization_rule_count) {
1896  return false;
1897  } else {
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;
1906  handled = true;
1907  }
1908  } else {
1909  synchronization_fork_mask_ &= ~mask;
1910  }
1911  }
1912  return process_fork_rules<Index + 1U>() || handled;
1913  }
1914  }
1915 
1916  template<std::size_t Index>
1917  bool process_join_rules() {
1921  if constexpr (Index >= synchronization_rule_count) {
1922  return false;
1923  } else {
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);
1928  using JoinTo =
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;
1936  }
1937  } else {
1938  synchronization_join_mask_ &= ~mask;
1939  }
1940  }
1941  return process_join_rules<Index + 1U>() || handled;
1942  }
1943  }
1944 
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;
1958  }
1959  }
1960  }
1961 
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>();
1968  }
1969  dispatch_completion_regions<Index + 1U>(result, event);
1970  }
1971  }
1972 
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>();
1977  } else {
1978  return false;
1979  }
1980  }
1981 
1982  template<typename... Targets>
1983  void activate_targets(type_list<Targets...>) {
1984  (activate_target<Targets>(), ...);
1985  }
1986 
1987  template<typename Target>
1988  void activate_target() {
1989  std::apply(
1990  [](auto&... region_hsm) {
1991  (activate_region_target<Target>(region_hsm), ...);
1992  },
1993  regions_);
1994  }
1995 
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>();
2001  }
2002  }
2003 
2004  template<typename... Sources>
2005  [[nodiscard]] bool sources_active(type_list<Sources...>) const {
2006  return (active<Sources>() && ...);
2007  }
2008 
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_{};
2013 };
2014 
2016 template<typename HsmType, typename Events>
2017 class erased_event;
2018 
2019 template<typename HsmType, typename = void>
2020 struct get_events_from_hsm;
2021 
2022 template<typename Transition>
2023 struct get_events_from_transition;
2024 
2025 template<typename TransitionList>
2026 struct aggregate_events;
2027 
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{}...));
2038 };
2039 
2040 template<>
2041 struct aggregate_events<type_list<>> {
2042  using type = std::tuple<>;
2043 };
2044 
2046 template<typename Transition>
2047 struct get_events_from_transition {
2048  using Event = std::tuple<typename Transition::event>;
2049  using FromEvents =
2050  typename get_events_from_hsm<typename Transition::from>::type;
2051  using ToEvents =
2052  typename get_events_from_hsm<typename Transition::to>::type;
2053  using type = decltype(std::tuple_cat(Event{}, FromEvents{}, ToEvents{}));
2054 };
2055 
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;
2066 };
2067 
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<>;
2073 };
2074 
2075 template<typename HsmType>
2078 
2092 template<typename HsmType, typename Events>
2093 class erased_event {
2094  using event_list = as_type_list_t<Events>;
2095 
2096  public:
2097  static constexpr std::size_t storage_size = max_sizeof(event_list{});
2098  static constexpr std::size_t storage_alignment = max_alignof(event_list{});
2099 
2100  erased_event() = default;
2101 
2102  template<typename Event>
2103  requires (contains_type<std::decay_t<Event>>(event_list{}))
2104  explicit erased_event(Event&& event) {
2105  emplace<std::decay_t<Event>>(std::forward<Event>(event));
2106  }
2107 
2108  erased_event(erased_event const& other) { copy_from(other); }
2109 
2110  erased_event(erased_event&& other) noexcept { move_from(other); }
2111 
2112  // cppcheck-suppress operatorEqVarError
2113  erased_event& operator=(erased_event const& other) {
2114  if (this != &other) {
2115  reset();
2116  copy_from(other);
2117  }
2118  return *this;
2119  }
2120 
2121  // cppcheck-suppress operatorEqVarError
2122  erased_event& operator=(erased_event&& other) noexcept {
2123  if (this != &other) {
2124  reset();
2125  move_from(other);
2126  }
2127  return *this;
2128  }
2129 
2130  ~erased_event() { reset(); }
2131 
2132  [[nodiscard]] bool empty() const { return dispatch_ == nullptr; }
2133 
2134  bool dispatch(HsmType& hsm) {
2135  if (dispatch_ == nullptr) {
2136  return false;
2137  }
2138  return dispatch_(hsm, data());
2139  }
2140 
2141  private:
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*);
2146 
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);
2151 
2152  new (data()) Event(std::forward<Args>(args)...);
2153  dispatch_ = [](HsmType& hsm, void* storage) -> bool {
2154  return hsm.handle(*static_cast<Event*>(storage));
2155  };
2156  copy_ = [](void* destination, void const* source) {
2157  new (destination) Event(*static_cast<Event const*>(source));
2158  };
2159  move_ = [](void* destination, void* source) {
2160  new (destination) Event(std::move(*static_cast<Event*>(source)));
2161  };
2162  destroy_ = [](void* storage) {
2163  static_cast<Event*>(storage)->~Event();
2164  };
2165  }
2166 
2167  void copy_from(erased_event const& other) {
2168  if (other.copy_ == nullptr) {
2169  return;
2170  }
2171  other.copy_(data(), other.data());
2172  dispatch_ = other.dispatch_;
2173  copy_ = other.copy_;
2174  move_ = other.move_;
2175  destroy_ = other.destroy_;
2176  }
2177 
2178  void move_from(erased_event& other) {
2179  if (other.move_ == nullptr) {
2180  return;
2181  }
2182  other.move_(data(), other.data());
2183  dispatch_ = other.dispatch_;
2184  copy_ = other.copy_;
2185  move_ = other.move_;
2186  destroy_ = other.destroy_;
2187  other.reset();
2188  }
2189 
2190  void reset() {
2193  if (destroy_ != nullptr) {
2194  destroy_(data());
2195  }
2196  dispatch_ = nullptr;
2197  copy_ = nullptr;
2198  move_ = nullptr;
2199  destroy_ = nullptr;
2200  }
2201 
2202  void* data() { return storage_; }
2203  void const* data() const { return storage_; }
2204 
2205  alignas(storage_alignment) unsigned char storage_[storage_size]{};
2206  dispatch_fn dispatch_{};
2207  copy_fn copy_{};
2208  move_fn move_{};
2209  destroy_fn destroy_{};
2210 };
2211 
2219 template<typename HsmType, std::size_t Capacity>
2220 class tick_scheduler {
2221  public:
2222  using events = get_events_t<HsmType>;
2223  using event_type = erased_event<HsmType, events>;
2224 
2225  static_assert(Capacity > 0U,
2226  "tsm: tick_scheduler requires at least one timer slot");
2227 
2228  template<typename Event>
2229  requires (contains_type<std::decay_t<Event>>(as_type_list_t<events>{}))
2230  [[nodiscard]] bool after_ticks(Event&& event, tsm::tick_rep ticks) {
2231  return schedule(std::forward<Event>(event), ticks, 0U, false);
2232  }
2233 
2234  template<typename Event>
2235  requires (contains_type<std::decay_t<Event>>(as_type_list_t<events>{}))
2236  [[nodiscard]] bool after_ticks(Event&& event, tsm::tick_count ticks) {
2237  return after_ticks(std::forward<Event>(event), ticks.count());
2238  }
2239 
2240  template<typename Event>
2241  requires (contains_type<std::decay_t<Event>>(as_type_list_t<events>{}))
2242  [[nodiscard]] bool every_ticks(Event&& event, tsm::tick_rep period) {
2243  return period != 0U &&
2244  schedule(std::forward<Event>(event), period, period, true);
2245  }
2246 
2247  template<typename Event>
2248  requires (contains_type<std::decay_t<Event>>(as_type_list_t<events>{}))
2249  [[nodiscard]] bool every_ticks(Event&& event, tsm::tick_count period) {
2250  return every_ticks(std::forward<Event>(event), period.count());
2251  }
2252 
2253  [[nodiscard]] std::size_t pending() const {
2254  std::size_t count{};
2255  for (auto const& timer : timers_) {
2256  if (timer.active) {
2257  // cppcheck-suppress useStlAlgorithm
2258  ++count;
2259  }
2260  }
2261  return count;
2262  }
2263 
2264  [[nodiscard]] bool empty() const { return pending() == 0U; }
2265 
2266  std::size_t tick(HsmType& hsm, tsm::tick_rep elapsed_ticks = 1U) {
2267  std::size_t dispatched{};
2268  tsm::tick_rep remaining_elapsed = elapsed_ticks;
2269 
2273  while (remaining_elapsed > 0U) {
2274  const tsm::tick_rep step = next_step(remaining_elapsed);
2275  advance(step);
2276  remaining_elapsed -= step;
2277  dispatched += dispatch_due(hsm);
2278  }
2279  return dispatched;
2280  }
2281 
2282  std::size_t tick(HsmType& hsm, tsm::tick_count elapsed_ticks) {
2283  return tick(hsm, elapsed_ticks.count());
2284  }
2285 
2286  void clear() {
2287  for (auto& timer : timers_) {
2288  // cppcheck-suppress useStlAlgorithm
2289  timer = timer_slot{};
2290  }
2291  }
2292 
2293  private:
2294  struct timer_slot {
2295  event_type event{};
2296  tsm::tick_rep remaining{};
2297  tsm::tick_rep period{};
2298  bool periodic{};
2299  bool active{};
2300  };
2301 
2302  template<typename Event>
2303  [[nodiscard]] bool schedule(Event&& event,
2305  tsm::tick_rep period,
2306  bool periodic) {
2307  for (auto& timer : timers_) {
2308  // cppcheck-suppress useStlAlgorithm
2309  if (!timer.active) {
2310  timer.event = event_type(std::forward<Event>(event));
2311  timer.remaining = ticks;
2312  timer.period = period;
2313  timer.periodic = periodic;
2314  timer.active = true;
2315  return true;
2316  }
2317  }
2318  return false;
2319  }
2320 
2321  [[nodiscard]] tsm::tick_rep next_step(tsm::tick_rep elapsed_limit) const {
2322  tsm::tick_rep step = elapsed_limit;
2323  for (auto const& timer : timers_) {
2324  if (!timer.active) {
2325  continue;
2326  }
2327  if (timer.remaining == 0U) {
2328  return 1U;
2329  }
2330  if (timer.remaining < step) {
2331  step = timer.remaining;
2332  }
2333  }
2334  return step;
2335  }
2336 
2337  void advance(tsm::tick_rep ticks) {
2338  for (auto& timer : timers_) {
2339  if (!timer.active) {
2340  continue;
2341  }
2342  if (timer.remaining > ticks) {
2343  timer.remaining -= ticks;
2344  } else {
2345  timer.remaining = 0U;
2346  }
2347  }
2348  }
2349 
2350  std::size_t dispatch_due(HsmType& hsm) {
2351  std::size_t dispatched{};
2352  for (auto& timer : timers_) {
2353  if (!timer.active) {
2354  continue;
2355  }
2356  if (timer.remaining == 0U) {
2357  auto event = timer.periodic ? timer.event : std::move(timer.event);
2358  (void)event.dispatch(hsm);
2359  ++dispatched;
2360  if (timer.periodic) {
2361  timer.remaining = timer.period;
2362  } else {
2363  timer = timer_slot{};
2364  }
2365  }
2366  }
2367  return dispatched;
2368  }
2369 
2370  std::array<timer_slot, Capacity> timers_{};
2371 };
2372 
2373 template<typename HsmType, std::size_t Capacity>
2374 using TickScheduler = tick_scheduler<HsmType, Capacity>;
2375 
2376 } // namespace detail
2377 
2378 #if defined(__linux__) && !defined(__QNXNTO__) && !defined(__ZEPHYR__) && \
2379  !defined(TSM_SUPPRESS_AUTO_PLATFORM_ADAPTERS)
2380 } // namespace tsm
2381 #include "tsm/linux.h"
2382 namespace tsm {
2383 #endif
2384 
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;
2393 using detail::edge;
2394 using detail::Event;
2395 using detail::External;
2396 using detail::final_state;
2397 using detail::fork;
2398 using detail::Fork;
2399 using detail::fork_to;
2400 using detail::guard;
2401 using detail::history_state;
2402 using detail::hsm;
2403 using detail::Internal;
2404 using detail::internal;
2405 using detail::join;
2406 using detail::Join;
2407 using detail::join_from;
2408 using detail::join_to;
2409 using detail::JoinTo;
2410 using detail::junction;
2411 using detail::local;
2412 using detail::make_hsm_t;
2413 using detail::on;
2414 using detail::OrthogonalExecutionPolicy;
2415 using detail::region;
2416 using detail::T;
2417 using detail::tick_scheduler;
2418 using detail::TickScheduler;
2420 using detail::type_list;
2421 using detail::with_action;
2422 using detail::with_guard;
2424 
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>
2432 using task_executor = detail::task_executor<Tasks...>;
2433 #endif
2434 
2435 template<typename... Deferred>
2436 using deferred_events = detail::type_list<Deferred...>;
2437 
2438 template<typename... Rules>
2439 using synchronization = detail::type_list<Rules...>;
2440 
2441 template<typename... TransitionEntries>
2442 using transition_table = detail::type_list<TransitionEntries...>;
2443 
2444 template<typename... TransitionEntries>
2445 using Ts = transition_table<TransitionEntries...>;
2446 
2447 template<detail::transition_like... TransitionEntries>
2448 consteval auto transitions(TransitionEntries...) {
2449  return Ts<TransitionEntries...>{};
2450 }
2451 
2452 #define TSM_TRANSITION(Name, From, Event, To) \
2453  struct Name { \
2454  using from = From; \
2455  using event = Event; \
2456  using to = To; \
2457  }
2458 
2459 #define TSM_TRANSITION_ACTION(Name, From, Event, To, Action) \
2460  struct Name { \
2461  using from = From; \
2462  using event = Event; \
2463  using to = To; \
2464  static constexpr auto action = Action; \
2465  }
2466 
2467 #define TSM_TRANSITION_GUARD(Name, From, Event, To, Guard) \
2468  struct Name { \
2469  using from = From; \
2470  using event = Event; \
2471  using to = To; \
2472  static constexpr auto guard = Guard; \
2473  }
2474 
2475 #define TSM_TRANSITION_GUARD_ACTION(Name, From, Event, To, Guard, Action) \
2476  struct Name { \
2477  using from = From; \
2478  using event = Event; \
2479  using to = To; \
2480  static constexpr auto guard = Guard; \
2481  static constexpr auto action = Action; \
2482  }
2483 
2484 #define TSM_TRANSITIONS(...) using transitions = ::tsm::Ts<__VA_ARGS__>
2485 
2486 } // namespace tsm
2487 
2488 #if defined(__FREE_RTOS__)
2489 #include "tsm/freertos.h"
2490 #endif
2491 #include "tsm/runtime.h"
2492 #if defined(__QNXNTO__)
2493 #include "tsm/qnx.h"
2494 #endif
2495 
2496 #if defined(TSM_ENABLE_REFLECTION) && TSM_ENABLE_REFLECTION
2497 #include "tsm/reflection.h"
2498 #endif
Definition: bare_metal.h:53
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
Definition: logging.h:42
Kind kind
Definition: logging.h:44
Definition: policies.h:109
Definition: policy.h:89
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.