Zth (libzth)
fsm14.h
Go to the documentation of this file.
1 #ifndef ZTH_FSM14_H
2 #define ZTH_FSM14_H
3 /*
4  * Zth (libzth), a cooperative userspace multitasking library.
5  * Copyright (C) 2019-2022 Jochem Rutgers
6  *
7  * This Source Code Form is subject to the terms of the Mozilla Public
8  * License, v. 2.0. If a copy of the MPL was not distributed with this
9  * file, You can obtain one at https://mozilla.org/MPL/2.0/.
10  */
11 
19 #if defined(__cplusplus) && __cplusplus >= 201402L
20 
21 # include <libzth/macros.h>
22 # include <libzth/allocator.h>
23 # include <libzth/sync.h>
24 # include <libzth/util.h>
25 
26 # include <atomic>
27 # include <bitset>
28 # include <functional>
29 # include <limits>
30 # include <stdexcept>
31 # include <type_traits>
32 # include <utility>
33 
34 namespace zth {
35 namespace fsm {
36 
37 
38 
40 // Misc utilities
41 //
42 
43 # ifdef DOXYGEN
44 
45 template <typename T>
47 
48 # else // !DOXYGEN
49 
50 template <typename R, typename C, typename A>
51 struct function_traits_detail {
52  using return_type = R;
53  using class_type = C;
54  using arg_type = A;
55 
56  static constexpr bool is_member = !std::is_same<C, void>::value;
57  static constexpr bool is_functor = false;
58 };
59 
60 // Assume it's a lambda, with captures (so it is a functor).
61 template <typename T>
62 struct function_traits : function_traits<decltype(&T::operator())> {
63  static constexpr bool is_functor = true;
64 };
65 
66 // R() normal function
67 template <typename R>
68 struct function_traits<R (&)()> : function_traits_detail<R, void, void> {};
69 
70 # if defined(__cpp_noexcept_function_type) && __cpp_noexcept_function_type >= 201510
71 template <typename R>
72 struct function_traits<R (&)() noexcept> : function_traits_detail<R, void, void> {};
73 # endif
74 
75 // R(A) normal function
76 template <typename R, typename A>
77 struct function_traits<R (&)(A)> : function_traits_detail<R, void, A> {};
78 
79 # if defined(__cpp_noexcept_function_type) && __cpp_noexcept_function_type >= 201510
80 template <typename R, typename A>
81 struct function_traits<R (&)(A) noexcept> : function_traits_detail<R, void, A> {};
82 # endif
83 
84 // R() normal function
85 template <typename R>
86 struct function_traits<R (*)()> : function_traits_detail<R, void, void> {};
87 
88 # if defined(__cpp_noexcept_function_type) && __cpp_noexcept_function_type >= 201510
89 template <typename R>
90 struct function_traits<R (*)() noexcept> : function_traits_detail<R, void, void> {};
91 # endif
92 
93 // R(A) normal function
94 template <typename R, typename A>
95 struct function_traits<R (*)(A)> : function_traits_detail<R, void, A> {};
96 
97 # if defined(__cpp_noexcept_function_type) && __cpp_noexcept_function_type >= 201510
98 template <typename R, typename A>
99 struct function_traits<R (*)(A) noexcept> : function_traits_detail<R, void, A> {};
100 # endif
101 
102 // R(C::*)() class member
103 template <typename R, typename C>
104 struct function_traits<R (C::*)()> : function_traits_detail<R, C, void> {};
105 
106 # if defined(__cpp_noexcept_function_type) && __cpp_noexcept_function_type >= 201510
107 template <typename R, typename C>
108 struct function_traits<R (C::*)() noexcept> : function_traits_detail<R, C, void> {};
109 # endif
110 
111 // R(C::*)(A) class member
112 template <typename R, typename C, typename A>
113 struct function_traits<R (C::*)(A)> : function_traits_detail<R, C, A> {};
114 
115 # if defined(__cpp_noexcept_function_type) && __cpp_noexcept_function_type >= 201510
116 template <typename R, typename C, typename A>
117 struct function_traits<R (C::*)(A) noexcept> : function_traits_detail<R, C, A> {};
118 # endif
119 
120 // R(C::*)() const class member
121 template <typename R, typename C>
122 struct function_traits<R (C::*)() const> : function_traits_detail<R, C, void> {};
123 
124 # if defined(__cpp_noexcept_function_type) && __cpp_noexcept_function_type >= 201510
125 template <typename R, typename C>
126 struct function_traits<R (C::*)() const noexcept> : function_traits_detail<R, C, void> {};
127 # endif
128 
129 // R(C::*)(A) const class member
130 template <typename R, typename C, typename A>
131 struct function_traits<R (C::*)(A) const> : function_traits_detail<R, C, A> {};
132 
133 # if defined(__cpp_noexcept_function_type) && __cpp_noexcept_function_type >= 201510
134 template <typename R, typename C, typename A>
135 struct function_traits<R (C::*)(A) const noexcept> : function_traits_detail<R, C, A> {};
136 # endif
137 
138 # endif // !DOXYGEN
139 
140 
142 // Symbol
143 //
144 
145 class BasicFsm;
146 class Fsm;
147 
152 struct invalid_fsm : public std::logic_error {
153  explicit invalid_fsm(char const* str)
154  : std::logic_error{str}
155  {}
156 };
157 
165 class Symbol {
167 public:
172  constexpr Symbol(char const* s = nullptr) noexcept
173  : m_symbol{s}
174  {}
175 
179  constexpr bool constexpr_eq(Symbol const& s) const noexcept
180  {
181  char const* a = m_symbol;
182  char const* b = s.m_symbol;
183 
184  // We cannot compare the pointers a == b directly in a
185  // constexpr context. This implementation does a string
186  // compare.
187 
188  if(!a ^ !b)
189  return false;
190  if(!a && !b)
191  return true;
192 
193  while(*a && *a == *b) {
194  a++;
195  b++;
196  }
197 
198  return *a == *b;
199  }
200 
201  bool operator==(Symbol const& s) const noexcept
202  {
203  return *this == s.m_symbol;
204  }
205 
206  bool operator==(char const* s) const noexcept
207  {
208  return m_symbol == s || constexpr_eq(s);
209  }
210 
211  template <typename S>
212  bool operator!=(S&& s) const noexcept
213  {
214  return !(*this == std::forward<S>(s));
215  }
216 
220  __attribute__((returns_nonnull)) constexpr char const* str() const noexcept
221  {
222  return m_symbol ? m_symbol : "-";
223  }
224 
229  constexpr char const* symbol() const noexcept
230  {
231  return m_symbol;
232  }
233 
237  __attribute__((returns_nonnull)) operator char const *() const noexcept
238  {
239  return str();
240  }
241 
245  constexpr bool valid() const noexcept
246  {
247  return m_symbol;
248  }
249 
253  operator bool() const noexcept
254  {
255  return valid();
256  }
257 
258 private:
260  char const* m_symbol{};
261 };
262 
267 constexpr Symbol operator""_S(char const* s, size_t UNUSED_PAR(len)) noexcept
268 {
269  return s;
270 }
271 
276 using State = Symbol;
277 
278 template <bool enable = Config::NamedFsm>
279 class Named {};
280 
281 template <>
282 class Named<false> {
283 protected:
284  constexpr explicit Named(char const* UNUSED_PAR(name) = nullptr) noexcept {}
285 
286  ~Named() = default;
287 
288 public:
289  cow_string name() const
290  {
291  return format("%p", this);
292  }
293 };
294 
295 template <>
296 class Named<true> : public Named<false> {
297 protected:
298  constexpr explicit Named(char const* name) noexcept
299  : m_name{name}
300  {}
301 
302  ~Named() = default;
303 
304 public:
305  cow_string name() const
306  {
307  if(m_name)
308  return m_name;
309 
310  return Named<false>::name();
311  }
312 
313 private:
314  char const* m_name;
315 };
316 
317 template <
318  typename T, typename R,
319  bool haveArg = !std::is_same<typename function_traits<T>::arg_type, void>::value,
320  bool isMember =
321  !haveArg && function_traits<T>::is_member && !function_traits<T>::is_functor
322  && std::is_base_of<BasicFsm, typename function_traits<T>::class_type>::value,
323  bool isOk =
324  std::is_convertible<typename function_traits<T>::return_type, R>::value
325  && (!haveArg
326  || std::is_convertible<BasicFsm&, typename function_traits<T>::arg_type>::value
327  || std::is_base_of<
328  BasicFsm,
329  std::remove_reference_t<typename function_traits<T>::arg_type>>::value)>
330 class Callback {};
331 
332 template <typename T, typename R>
333 class Callback<T, R, false, false, true> : public Named<> {
335 public:
336  template <typename T_>
337  constexpr Callback(T_&& c, char const* name = nullptr)
338  : Named{name}
339  , m_callback{std::forward<T_>(c)}
340  {}
341 
342  R call(BasicFsm& UNUSED_PAR(fsm)) const
343  {
344  return m_callback();
345  }
346 
347 private:
348  T m_callback;
349 };
350 
351 template <typename T, typename R>
352 class Callback<T, R, true, false, true> : public Named<> {
354 public:
355  template <typename T_>
356  constexpr Callback(T_&& c, char const* name = nullptr)
357  : Named{name}
358  , m_callback{std::forward<T_>(c)}
359  {}
360 
361  R call(BasicFsm& fsm) const
362  {
363  using A = typename function_traits<T>::arg_type;
364  check<A>(fsm);
365  return m_callback(static_cast<A>(fsm));
366  }
367 
368 private:
369  template <
370  typename A,
371  std::enable_if_t<
372  std::is_base_of<BasicFsm, std::remove_reference_t<A>>::value, int> = 0>
373  static void check(BasicFsm& UNUSED_PAR(fsm))
374  {
375 # ifdef __GXX_RTTI
376  zth_assert(dynamic_cast<std::remove_reference_t<A>*>(&fsm) != nullptr);
377 # endif
378  }
379 
380  template <
381  typename A,
382  std::enable_if_t<
383  !std::is_base_of<BasicFsm, std::remove_reference_t<A>>::value, int> = 0>
384  static void check(BasicFsm& fsm)
385  {}
386 
387 private:
388  T m_callback;
389 };
390 
391 template <typename T, typename R>
392 class Callback<T, R, false, true, true> : public Named<> {
394 public:
395  template <typename T_>
396  constexpr Callback(T_&& c, char const* name = nullptr)
397  : Named{name}
398  , m_callback{std::forward<T_>(c)}
399  {}
400 
401  R call(BasicFsm& fsm) const
402  {
403  using C = typename function_traits<T>::class_type;
404 # ifdef __GXX_RTTI
405  zth_assert(dynamic_cast<C*>(&fsm) != nullptr);
406 # endif
407  return ((static_cast<C&>(fsm)).*m_callback)();
408  }
409 
410 private:
411  T m_callback;
412 };
413 
416 public:
417  constexpr GuardPollInterval(bool enabled = false)
418  : TimeInterval(enabled ? TimeInterval::null() : TimeInterval::infinity())
419  {}
420 
421  constexpr GuardPollInterval(GuardPollInterval&&) noexcept = default;
422  GuardPollInterval& operator=(GuardPollInterval&&) noexcept = default;
423  constexpr GuardPollInterval(GuardPollInterval const&) noexcept = default;
424  GuardPollInterval& operator=(GuardPollInterval const&) noexcept = default;
425 
426  template <typename... A>
427  constexpr GuardPollInterval(A&&... a) noexcept
428  : TimeInterval(std::forward<A>(a)...)
429  {}
430 
431  constexpr operator bool() const noexcept
432  {
433  return hasPassed();
434  }
435 };
436 
437 class Guard {
438 protected:
439  constexpr Guard() = default;
440  ~Guard() = default;
441 
442  Guard(Guard&&) noexcept = default;
443  Guard& operator=(Guard&&) noexcept = default;
444 
445 public:
446  Guard(Guard const&) = delete;
447  void operator=(Guard const&) = delete;
448 
449  virtual GuardPollInterval enabled(BasicFsm& fsm) const = 0;
450  virtual cow_string name() const = 0;
451 
452  auto operator()(BasicFsm& fsm) const
453  {
454  return enabled(fsm);
455  }
456 };
457 
458 template <typename T>
459 class TypedGuard final
460  : public Guard
461  , protected Callback<T, GuardPollInterval> {
463 public:
465 
466  template <typename T_>
467  constexpr explicit TypedGuard(T_&& g, char const* name = nullptr)
468  : Callback_type{std::forward<T_>(g), name}
469  {}
470 
471  virtual GuardPollInterval enabled(BasicFsm& fsm) const final
472  {
473  return this->call(fsm);
474  }
475 
476  virtual cow_string name() const final
477  {
478  return Callback_type::name();
479  }
480 };
481 
482 class InputGuard final : public Guard {
484 public:
485  constexpr explicit InputGuard(Symbol&& input)
486  : m_input{std::move(input)}
487  {}
488 
489  virtual GuardPollInterval enabled(BasicFsm& fsm) const final;
490 
491  virtual cow_string name() const final
492  {
493  return m_input.str();
494  }
495 
496 private:
497  Symbol m_input;
498 };
499 
529 template <typename T>
530 constexpr auto guard(T&& g, char const* name = nullptr)
531 {
532  return TypedGuard<std::decay_t<T>>{std::forward<T>(g), name};
533 }
534 
546 constexpr inline auto guard(Symbol&& input) noexcept
547 {
548  return InputGuard{std::move(input)};
549 }
550 
551 inline bool always_guard() noexcept
552 {
553  return true;
554 }
555 
561 // This could be solved simply by using [](){ return true; } as function, but
562 // using lambda in constexpr is only allowed since C++17. So, use the separate
563 // function instead.
564 inline17 constexpr auto always = guard(always_guard, "always");
565 
566 inline bool never_guard() noexcept
567 {
568  return false;
569 }
570 
576 inline17 constexpr auto never = guard(never_guard, "never");
577 
578 class Action {
579 protected:
580  constexpr Action() = default;
581  ~Action() = default;
582 
583  Action(Action&&) noexcept = default;
584  Action& operator=(Action&&) noexcept = default;
585 
586 public:
587  Action(Action const&) = delete;
588  void operator=(Action const&) = delete;
589 
590  virtual void run(BasicFsm& UNUSED_PAR(fsm)) const {}
591 
592  virtual cow_string name() const
593  {
594  return format("%p\n", this);
595  }
596 
597  void operator()(BasicFsm& fsm) const
598  {
599  run(fsm);
600  }
601 };
602 
603 template <typename T>
604 class TypedAction final
605  : public Action
606  , protected Callback<T, void> {
608 public:
610 
611  template <typename T_>
612  constexpr explicit TypedAction(T_&& a, char const* name = nullptr)
613  : Callback_type{std::forward<T_>(a), name}
614  {}
615 
616  virtual void run(BasicFsm& fsm) const final
617  {
618  this->call(fsm);
619  }
620 
621  virtual cow_string name() const final
622  {
623  return Callback_type::name();
624  }
625 };
626 
648 template <typename T>
649 constexpr auto action(T&& a, char const* name = nullptr)
650 {
651  return TypedAction<std::decay_t<T>>{std::forward<T>(a), name};
652 }
653 
654 inline void nothing_action() {}
655 
661 inline17 constexpr auto nothing = action(nothing_action, "nothing");
662 
664  : public Guard
665  , protected Action {
666 protected:
667  constexpr GuardedActionBase() = default;
668 
669 public:
670  virtual GuardPollInterval tryRun(BasicFsm& fsm) const
671  {
672  if(auto e = !enabled(fsm))
673  return e;
674 
675  run(fsm);
676  return true;
677  }
678 
680  {
681  return tryRun(fsm);
682  }
683 };
684 
685 class GuardedAction final : public GuardedActionBase {
687 public:
688  constexpr GuardedAction(Guard const& guard, Action const& action) noexcept
689  : m_guard{guard}
690  , m_action{action}
691  {}
692 
693  constexpr GuardedAction(Symbol&& input, Action const& action) noexcept
694  : m_guard{never}
695  , m_input{std::move(input)}
696  , m_action{action}
697  {}
698 
699  constexpr GuardedAction(Guard const& guard) noexcept
701  {}
702 
703  constexpr GuardedAction(Symbol&& input) noexcept
704  : GuardedAction{std::move(input), nothing}
705  {}
706 
707  constexpr GuardedAction(Action const& action) noexcept
709  {}
710 
711  constexpr GuardedAction() noexcept
713  {}
714 
715  constexpr bool isInput() const noexcept
716  {
717  return m_input.valid();
718  }
719 
720  constexpr bool hasGuard() const noexcept
721  {
722  return !isInput();
723  }
724 
725  constexpr Symbol const& input() const noexcept
726  {
727  return m_input;
728  }
729 
730  virtual GuardPollInterval enabled(BasicFsm& fsm) const final;
731 
732  constexpr auto const& guard() const noexcept
733  {
734  zth_assert(hasGuard());
735  return m_guard;
736  }
737 
738  constexpr auto const& action() const noexcept
739  {
740  return m_action;
741  }
742 
743  virtual void run(BasicFsm& fsm) const final
744  {
745  m_action(fsm);
746  }
747 
748  virtual cow_string name() const final
749  {
750  return isInput() ? format("input %s / %s", input().str(), action().name().c_str())
751  : format(
752  "guard %s / %s", guard().name().c_str(),
753  action().name().c_str());
754  }
755 
756 private:
757  Guard const& m_guard;
758  Symbol m_input;
759  Action const& m_action;
760 };
761 
762 constexpr inline auto operator/(Guard const& g, Action const& a) noexcept
763 {
764  return GuardedAction{g, a};
765 }
766 
767 constexpr inline auto operator+(Symbol&& input) noexcept
768 {
769  return GuardedAction{std::move(input)};
770 }
771 
781 constexpr inline auto input(Symbol&& symbol) noexcept
782 {
783  return GuardedAction{std::move(symbol)};
784 }
785 
786 constexpr inline auto operator+(GuardedAction&& g) noexcept
787 {
788  return std::move(g);
789 }
790 
791 // + "a" / b is first compiled into GuardedAction{ "a" } / b.
792 // This operator/ should fix that by using "a" as input guard for b.
793 constexpr inline auto operator/(GuardedAction&& ga, Action const& a)
794 {
795  if(!(ga.isInput() && &ga.action() == &nothing))
796  zth_throw(invalid_fsm("Ambiguous /"));
797 
798  return GuardedAction{Symbol{ga.input()}, a};
799 }
800 
801 class Transition;
802 
803 class TransitionStart final : public GuardedActionBase {
805 public:
806  constexpr TransitionStart(State&& state) noexcept
807  : m_state{std::move(state)}
808  , m_guardedAction{never / nothing}
809  {}
810 
811  constexpr TransitionStart(Guard const& guard) noexcept
812  : m_state{}
813  , m_guardedAction{guard / nothing}
814  {}
815 
816  constexpr TransitionStart(Action const& action) noexcept
817  : m_state{}
818  , m_guardedAction{always / action}
819  {}
820 
821  constexpr TransitionStart(GuardedAction&& ga) noexcept
822  : m_state{}
823  , m_guardedAction{std::move(ga)}
824  {}
825 
826  constexpr TransitionStart(State&& state, GuardedAction&& guardedAction) noexcept
827  : m_state{std::move(state)}
828  , m_guardedAction{std::move(guardedAction)}
829  {}
830 
831  constexpr auto const& state() const noexcept
832  {
833  return m_state;
834  }
835 
836  constexpr bool isInput() const noexcept
837  {
838  return m_guardedAction.isInput();
839  }
840 
841  constexpr bool hasGuard() const noexcept
842  {
843  return m_guardedAction.hasGuard();
844  }
845 
846  constexpr Symbol const& input() const noexcept
847  {
848  return m_guardedAction.input();
849  }
850 
851  constexpr auto const& guard() const noexcept
852  {
853  return m_guardedAction.guard();
854  }
855 
856  constexpr auto const& action() const noexcept
857  {
858  return m_guardedAction.action();
859  }
860 
861  virtual GuardPollInterval enabled(BasicFsm& fsm) const final
862  {
863  return m_guardedAction.enabled(fsm);
864  }
865 
866  virtual GuardPollInterval tryRun(BasicFsm& fsm) const final
867  {
868  return m_guardedAction.tryRun(fsm);
869  }
870 
871  virtual cow_string name() const override
872  {
873  return format("%s + %s", state().str(), m_guardedAction.name().c_str());
874  }
875 
876 private:
877  State m_state;
878  GuardedAction m_guardedAction;
879 };
880 
881 constexpr inline auto operator+(State&& state, GuardedAction&& action) noexcept
882 {
883  return TransitionStart{std::move(state), std::move(action)};
884 }
885 
886 constexpr inline auto operator+(State&& state, Guard const& guard) noexcept
887 {
888  return TransitionStart{std::move(state), GuardedAction{guard, nothing}};
889 }
890 
891 constexpr inline auto operator+(State&& state, Symbol&& input) noexcept
892 {
893  return TransitionStart{std::move(state), GuardedAction{std::move(input)}};
894 }
895 
896 constexpr inline auto operator+(char const* state, Symbol&& input) noexcept
897 {
898  return TransitionStart{state, GuardedAction{std::move(input)}};
899 }
900 
901 constexpr inline auto operator+(State&& state, char const* input) noexcept
902 {
903  return TransitionStart{std::move(state), GuardedAction{input}};
904 }
905 
906 constexpr inline auto const& operator+(Guard const& guard) noexcept
907 {
908  return guard;
909 }
910 
911 constexpr inline auto operator/(State&& state, Action const& action) noexcept
912 {
913  return TransitionStart{std::move(state), GuardedAction{always, action}};
914 }
915 
916 // "a" + "b" / c is first compiled into "a" + TransitionStart{ "b" + always / c }.
917 // This operator+ should fix that by converting "b" from State to Input guard.
918 constexpr inline auto operator+(State&& state, TransitionStart&& t)
919 {
920  if(!(t.hasGuard() && &t.guard() == &always))
921  zth_throw(invalid_fsm{"Ambiguous +"});
922 
923  return TransitionStart{std::move(state), GuardedAction{Symbol{t.state()}, t.action()}};
924 }
925 
926 class Transition final : public GuardedActionBase {
928 public:
929  template <typename F>
930  constexpr Transition(F&& from) noexcept
931  : m_from{std::forward<F>(from)}
932  , m_to{}
933  {}
934 
935  template <typename F, typename T>
936  constexpr Transition(F&& from, T&& to) noexcept
937  : m_from{std::forward<F>(from)}
938  , m_to{std::forward<T>(to)}
939  {}
940 
941  virtual GuardPollInterval enabled(BasicFsm& fsm) const final
942  {
943  return m_from.enabled(fsm);
944  }
945 
946  virtual GuardPollInterval tryRun(BasicFsm& fsm) const final
947  {
948  return m_from.tryRun(fsm);
949  }
950 
951  constexpr auto const& from() const noexcept
952  {
953  return m_from.state();
954  }
955 
956  constexpr bool isInput() const noexcept
957  {
958  return m_from.isInput();
959  }
960 
961  constexpr bool hasGuard() const noexcept
962  {
963  return m_from.hasGuard();
964  }
965 
966  constexpr Symbol const& input() const noexcept
967  {
968  return m_from.input();
969  }
970 
971  constexpr auto const& guard() const noexcept
972  {
973  return m_from.guard();
974  }
975 
976  constexpr auto const& action() const noexcept
977  {
978  return m_from.action();
979  }
980 
981  constexpr auto const& to() const noexcept
982  {
983  return m_to;
984  }
985 
986  virtual cow_string name() const final
987  {
988  return format("%s >>= %s", m_from.name().c_str(), m_to.str());
989  }
990 
991 private:
992  TransitionStart m_from;
993  State m_to;
994 };
995 
996 constexpr inline Transition operator>>=(TransitionStart&& from, State&& to) noexcept
997 {
998  return Transition{std::move(from), std::move(to)};
999 }
1000 
1001 constexpr inline Transition operator>>=(State&& from, State&& to) noexcept
1002 {
1003  return Transition{std::move(from) + always, std::move(to)};
1004 }
1005 
1006 constexpr inline Transition operator>>=(char const* from, State&& to) noexcept
1007 {
1008  return Transition{State{from} + always, std::move(to)};
1009 }
1010 
1011 constexpr inline Transition operator>>=(Guard const& from, State&& to) noexcept
1012 {
1013  return Transition{State{} + from, std::move(to)};
1014 }
1015 
1016 constexpr inline Transition operator>>=(Action const& from, State&& to) noexcept
1017 {
1018  return Transition{State{} / from, std::move(to)};
1019 }
1020 
1021 constexpr inline Transition operator>>=(GuardedAction&& from, State&& to) noexcept
1022 {
1023  return Transition{State{} + std::move(from), std::move(to)};
1024 }
1025 
1027 public:
1028  using index_type = size_t;
1029 
1030  virtual size_t size() const noexcept = 0;
1031 
1032  virtual bool hasGuard(index_type i) const noexcept = 0;
1033 
1034  virtual bool hasInput(index_type i) const noexcept
1035  {
1036  return !hasGuard(i);
1037  }
1038 
1039  virtual Guard const& guard(index_type i) const noexcept = 0;
1040  virtual GuardPollInterval enabled(index_type i, BasicFsm& fsm) const = 0;
1041  virtual Symbol input(index_type i) const noexcept = 0;
1042  virtual Action const& action(index_type i) const noexcept = 0;
1043  virtual index_type to(index_type i) const noexcept = 0;
1044  virtual State const& state(index_type i) const noexcept = 0;
1045 
1046  Fsm spawn() const;
1047 
1048  template <typename F, std::enable_if_t<std::is_base_of<BasicFsm, F>::value, int> = 0>
1049  F& init(F& fsm) const noexcept
1050  {
1051  fsm.init(*this);
1052  return fsm;
1053  }
1054 
1055  void dump(FILE* f = stdout) const
1056  {
1057  size_t size_ = size();
1058  for(index_type i = 0; i < size_; i++) {
1059  fprintf(f, "%3zu: %-16s + %s %-18s / %-18s >>= %3zu\n", i, state(i).str(),
1060  hasGuard(i) ? "guard" : "input",
1061  hasGuard(i) ? guard(i).name().c_str() : input(i).str(),
1062  action(i).name().c_str(), to(i));
1063  }
1064  }
1065 
1066  void uml(FILE* f = stdout) const
1067  {
1068  fprintf(f, "@startuml\n");
1069 
1070  size_t size_ = size();
1071  index_type statei = 0;
1072  size_t t = 0;
1073  for(index_type i = 0; i < size_; i++) {
1074  if(state(i).valid()) {
1075  statei = i;
1076  fprintf(f, "state \"%s\" as s%zu\n", state(statei).str(), statei);
1077  t = 0;
1078  }
1079 
1080  string edge;
1081 
1082  if(statei == 0)
1083  edge += "[*]";
1084  else
1085  edge += format("s%zu", statei);
1086 
1087  if(to(i))
1088  edge += format(" --> s%zu", to(i));
1089 
1090  edge += format(" : (%zu)", t);
1091 
1092  if(hasGuard(i)) {
1093  if(&guard(i) == &never)
1094  continue;
1095 
1096  if(&guard(i) != &always)
1097  edge += format(" [%s]", guard(i).name().c_str());
1098  } else {
1099  edge += format(" %s", input(i).str());
1100  }
1101 
1102  if(&action(i) != &nothing) {
1103  edge += format(" / %s", action(i).name().c_str());
1104  }
1105 
1106  fprintf(f, "%s\n", edge.c_str());
1107  t++;
1108  }
1109 
1110  fprintf(f, "@enduml\n");
1111  }
1112 };
1113 
1114 template <size_t Size>
1115 class Transitions final : public TransitionsBase {
1117 protected:
1118  using Index = typename smallest_uint<Size>::type;
1119 
1120  enum class Flag : uint8_t {
1121  input = 0x01,
1122  };
1123 
1125  constexpr CompiledTransition() noexcept
1126  : guard{}
1127  , action{}
1128  , from{}
1129  , to{}
1130  , flags{}
1131  {}
1132 
1133  // This is either a Guard const* or char const*, depending on
1134  // m_flags & Flag::input. A union would be better, but you
1135  // cannot change the active union field in a contexpr before
1136  // C++20.
1137  void const* guard;
1138  Action const* action;
1141  uint8_t flags;
1142  };
1143 
1144  constexpr Transitions() = default;
1145 
1146 public:
1147  static constexpr Transitions compile(std::initializer_list<Transition> l)
1148  {
1149  zth_assert(l.size() == Size);
1150 
1151  Transitions f;
1152 
1153  if(Size == 0) {
1154  f.m_transitions[0].guard = &never;
1155  f.m_transitions[0].action = &nothing;
1156  } else {
1157  f.m_transitions[0].guard = &always;
1158  f.m_transitions[0].action = &nothing;
1159  f.m_transitions[0].to = 1;
1160 
1161  if(!l.begin()->from().valid())
1162  zth_throw(invalid_fsm{"First state must be valid"});
1163  }
1164 
1165  index_type i = 1;
1166  State prev;
1167 
1168  for(auto const& t : l) {
1169  if(!prev.constexpr_eq(t.from()) && t.from().valid()) {
1170  prev = f.m_transitions[i].from = t.from();
1171  if(find(t.from(), l) != (size_t)i)
1173  "State transitions are not contiguous"});
1174  }
1175  if(t.hasGuard()) {
1176  f.m_transitions[i].guard = &t.guard();
1177  } else {
1178  f.m_transitions[i].guard = t.input().symbol();
1179  f.m_transitions[i].flags = static_cast<uint8_t>(Flag::input);
1180  }
1181  f.m_transitions[i].action = &t.action();
1182  f.m_transitions[i].to = static_cast<Index>(find(t.to(), l));
1183  i++;
1184  }
1185 
1186  return f;
1187  }
1188 
1189  virtual size_t size() const noexcept final
1190  {
1191  return Size + 1u;
1192  }
1193 
1194  virtual bool isInput(index_type i) const noexcept final
1195  {
1196  return (m_transitions[i].flags & static_cast<uint8_t>(Flag::input)) != 0;
1197  }
1198 
1199  virtual bool hasGuard(index_type i) const noexcept final
1200  {
1201  return !isInput(i);
1202  }
1203 
1204  virtual Guard const& guard(index_type i) const noexcept final
1205  {
1206  zth_assert(i < size());
1207  zth_assert(hasGuard(i));
1208  return *static_cast<Guard const*>(m_transitions[i].guard);
1209  }
1210 
1211  virtual Symbol input(index_type i) const noexcept final
1212  {
1213  zth_assert(i < size());
1214  zth_assert(!hasGuard(i));
1215  return static_cast<char const*>(m_transitions[i].guard);
1216  }
1217 
1218  virtual GuardPollInterval enabled(index_type i, BasicFsm& fsm) const final;
1219 
1220  virtual Action const& action(index_type i) const noexcept final
1221  {
1222  zth_assert(i < size());
1223  return *m_transitions[i].action;
1224  }
1225 
1226  virtual index_type to(index_type i) const noexcept final
1227  {
1228  zth_assert(i < size());
1229  return static_cast<index_type>(m_transitions[i].to);
1230  }
1231 
1232  virtual State const& state(index_type i) const noexcept final
1233  {
1234  zth_assert(i < size());
1235  return m_transitions[i].from;
1236  }
1237 
1238 private:
1239  static constexpr size_t find(State const& state, std::initializer_list<Transition> l)
1240  {
1241  if(!state.valid())
1242  return 0;
1243 
1244  size_t i = 0;
1245  for(auto const& t : l) {
1246  if(t.from().constexpr_eq(state))
1247  return i + 1;
1248  i++;
1249  }
1250 
1251 # if GCC_VERSION < 90000
1252  // This following if-statement is not required,
1253  // but triggers https://gcc.gnu.org/bugzilla/show_bug.cgi?id=67371 otherwise.
1254  if(i >= l.size())
1255 # endif
1256  zth_throw(invalid_fsm{"Target state not found"});
1257 
1258  // Unreachable.
1259  return Size + 1u;
1260  }
1261 
1262 private:
1263  static_assert(std::numeric_limits<Index>::max() > Size, "");
1264  CompiledTransition m_transitions[Size + 1u];
1265 };
1266 
1272 template <typename... T>
1273 constexpr auto compile(T&&... t)
1274 {
1275  return Transitions<sizeof...(T)>::compile({Transition{std::forward<T>(t)}...});
1276 }
1277 
1285 class BasicFsm : public UniqueID<BasicFsm> {
1287 public:
1289 
1294  enum class Flag : size_t {
1295  entry,
1296  selfloop,
1297  blocked,
1298  transition,
1299  pushed,
1300  popped,
1301  stop,
1302  input,
1303  flags,
1304  };
1305 
1309  BasicFsm(cow_string const& name = "FSM")
1310  : UniqueID{name}
1311  {
1312  zth_assert(std::atomic_is_lock_free(&m_state));
1313  }
1314 
1319  : BasicFsm{}
1320  {
1321  *this = std::move(f);
1322  }
1323 
1327  BasicFsm& operator=(BasicFsm&& f) noexcept
1328  {
1329  m_fsm = f.m_fsm;
1330  m_flags = f.m_flags;
1331  m_prev = f.m_prev;
1332  m_transition = f.m_transition;
1333  state_(f.state_());
1334  m_stack = std::move(f.m_stack);
1335  m_inputs = std::move(f.m_inputs);
1336 
1337  f.m_fsm = nullptr;
1338  f.state_(0);
1339 
1340  UniqueID::operator=(std::move(f));
1341  return *this;
1342  }
1343 
1347  virtual ~BasicFsm() = default;
1348 
1354  State const& state() const noexcept
1355  {
1356  zth_assert(valid());
1357  return m_fsm->state(state_());
1358  }
1359 
1363  bool valid() const noexcept
1364  {
1365  return m_fsm;
1366  }
1367 
1372  void reserveStack(size_t capacity)
1373  {
1374  if(m_stack.capacity() > capacity * 2U)
1375  m_stack.shrink_to_fit();
1376 
1377  m_stack.reserve(capacity);
1378  }
1379 
1383  virtual void reset() noexcept
1384  {
1385  zth_dbg(fsm, "[%s] Reset", id_str());
1386  state_(0);
1387  m_prev = m_transition = 0;
1388  m_flags.reset();
1389  m_stack.clear();
1390  }
1391 
1395  State const& prev() const noexcept
1396  {
1397  zth_assert(valid());
1398  return m_fsm->state(m_prev);
1399  }
1400 
1415  {
1416  zth_assert(valid());
1417 
1418  auto i = state_();
1419  auto size = m_fsm->size();
1420 
1421  // Find next enabled guard.
1422  GuardPollInterval again{false};
1424  while(!(p = m_fsm->enabled(i, *this))) {
1425  // Save the shortest poll interval.
1426  if(p < again)
1427  again = p;
1428 
1429  if(++i == size || m_fsm->state(i).valid()) {
1430  // No enabled guards.
1432  zth_dbg(fsm, "[%s] Blocked for %s", id_str(), again.str().c_str());
1433  return again;
1434  }
1435  }
1436 
1437  // Got it. Take transition.
1438  m_transition = i;
1439  auto to = m_fsm->to(i);
1440 
1442 
1443  if(!to) {
1444  // No transition, only do the action.
1445  if(m_fsm->hasInput(i))
1446  zth_dbg(fsm, "[%s] Have input %s, no transition", id_str(),
1447  m_fsm->input(i).str());
1448  else
1449  zth_dbg(fsm, "[%s] Guard %s enabled, no transition", id_str(),
1450  m_fsm->guard(i).name().c_str());
1452  setFlag(Flag::input, m_fsm->hasInput(i));
1453  enter();
1454  } else {
1455  if(m_fsm->hasInput(i))
1456  zth_dbg(fsm, "[%s] Have input %s, transition %s -> %s", id_str(),
1457  m_fsm->input(i).str(), m_fsm->state(state_()).str(),
1458  m_fsm->state(to).str());
1459  else
1460  zth_dbg(fsm, "[%s] Guard %s enabled, transition %s -> %s", id_str(),
1461  m_fsm->guard(i).name().c_str(),
1462  m_fsm->state(state_()).str(), m_fsm->state(to).str());
1463 
1464  setFlag(Flag::selfloop, to == state_());
1466  if(state_())
1467  leave();
1468 
1469  m_prev = state_();
1470  state_(to);
1471 
1474  setFlag(Flag::input, m_fsm->hasInput(i));
1475  enter();
1476 
1478  }
1479 
1480  return true;
1481  }
1482 
1492  void push()
1493  {
1494  zth_assert(valid());
1495 
1496  m_stack.push_back(m_prev);
1498  zth_dbg(fsm, "[%s] Push %s", id_str(), m_fsm->state(state_()).str());
1499  }
1500 
1513  virtual void pop()
1514  {
1515  zth_assert(valid());
1516  zth_assert(!m_stack.empty());
1517 
1518  index_type to = m_stack.back();
1519  zth_assert(to != 0);
1520 
1521  zth_dbg(fsm, "[%s] Pop %s -> %s", id_str(), m_fsm->state(state_()).str(),
1522  m_fsm->state(to).str());
1523 
1525  setFlag(Flag::selfloop, state_() == to);
1526 
1527  if(!flag(Flag::transition)) {
1528  // leave() was not called by step().
1530  leave();
1531  }
1532 
1533  m_prev = state_();
1534  state_(to);
1535  m_stack.pop_back();
1536  m_transition = 0;
1537 
1541  enter();
1542 
1544  }
1545 
1551  bool popped() const noexcept
1552  {
1553  return flag(Flag::popped);
1554  }
1555 
1559  bool flag(Flag f) const noexcept
1560  {
1561  return m_flags.test(flagIndex(f));
1562  }
1563 
1569  bool entry() noexcept
1570  {
1571  if(!flag(Flag::entry))
1572  return false;
1573 
1575  return true;
1576  }
1577 
1584  Symbol input() const noexcept
1585  {
1586  if(!flag(Flag::input))
1587  return Symbol{};
1588 
1589  zth_assert(valid());
1590  zth_assert(m_fsm->hasInput(m_transition));
1591 
1592  return m_fsm->input(m_transition);
1593  }
1594 
1598  virtual void input(Symbol i)
1599  {
1600  if(!i.valid())
1601  return;
1602 
1603  m_inputs.emplace_back(std::move(i));
1604  }
1605 
1611  bool clearInput(Symbol i) noexcept
1612  {
1613  if(!i.valid())
1614  return false;
1615 
1616  for(size_t j = 0; j < m_inputs.size(); j++)
1617  if(m_inputs[j] == i) {
1618  m_inputs[j] = std::move(m_inputs.back());
1619  m_inputs.pop_back();
1620  return true;
1621  }
1622 
1623  return false;
1624  }
1625 
1633  void clearInput() noexcept
1634  {
1635  clearInput(input());
1636  }
1637 
1641  void clearInputs() noexcept
1642  {
1643  m_inputs.clear();
1644  }
1645 
1651  void reserveInputs(size_t capacity)
1652  {
1653  if(m_inputs.capacity() > capacity * 2U)
1654  m_inputs.shrink_to_fit();
1655 
1656  m_inputs.reserve(capacity);
1657  }
1658 
1663  bool hasInput(Symbol i) const noexcept
1664  {
1665  if(!i.valid())
1666  return false;
1667 
1668  for(size_t j = 0; j < m_inputs.size(); j++)
1669  if(m_inputs[j] == i)
1670  return true;
1671 
1672  return false;
1673  }
1674 
1675 protected:
1679  bool setFlag(Flag f, bool value = true) noexcept
1680  {
1681  m_flags.set(flagIndex(f), value);
1682  return value;
1683  }
1684 
1690  void clearFlag(Flag f) noexcept
1691  {
1692  setFlag(f, false);
1693  }
1694 
1698  virtual void leave()
1699  {
1700  zth_dbg(fsm, "[%s] Leave %s", id_str(), state().str());
1701  }
1702 
1710  virtual void enter()
1711  {
1712  if(m_transition) {
1713  zth_dbg(fsm, "[%s] Enter %s%s; run action %s", id_str(), state().str(),
1714  flag(Flag::selfloop) ? " (loop)" : "",
1715  m_fsm->action(m_transition).name().c_str());
1716 
1717  m_fsm->action(m_transition).run(*this);
1718  } else {
1719  zth_dbg(fsm, "[%s] Enter %s (dummy transition)", id_str(), state().str());
1720  }
1721  }
1722 
1723 private:
1727  static constexpr size_t flagIndex(Flag f) noexcept
1728  {
1729  return static_cast<size_t>(f);
1730  }
1731 
1735  void init(TransitionsBase const& c) noexcept
1736  {
1737  if(c.size() == 0)
1738  // Invalid.
1739  return;
1740 
1741  m_fsm = &c;
1742  reset();
1743  }
1744 
1745  friend TransitionsBase;
1746 
1750  index_type state_() const noexcept
1751  {
1752  return m_state.load(std::memory_order_relaxed);
1753  }
1754 
1758  void state_(index_type s) noexcept
1759  {
1760  return m_state.store(s, std::memory_order_relaxed);
1761  }
1762 
1763 private:
1765  TransitionsBase const* m_fsm{};
1767  std::bitset<static_cast<size_t>(Flag::flags)> m_flags;
1769  index_type m_prev{};
1771  index_type m_transition{};
1773  std::atomic<index_type> m_state{};
1777  vector_type<Symbol>::type m_inputs;
1778 };
1779 
1788 class Fsm : public BasicFsm {
1790 public:
1791  using base = BasicFsm;
1792 
1796  Fsm(cow_string const& name = "FSM")
1797  : base{name}
1798  {}
1799 
1803  Fsm(Fsm&& f)
1804  : Fsm{}
1805  {
1806  *this = std::move(f);
1807  }
1808 
1812  Fsm& operator=(Fsm&& f) noexcept
1813  {
1814  base::operator=(std::move(f));
1815  return *this;
1816  }
1817 
1821  virtual ~Fsm() override = default;
1822 
1823  virtual void reset() noexcept override
1824  {
1825  base::reset();
1826  m_t = Timestamp::now();
1827  trigger();
1828  }
1829 
1838  {
1839  zth_dbg(fsm, "[%s] Run for %s", id_str(), (until - Timestamp::now()).str().c_str());
1841 
1842  while(true) {
1843  GuardPollInterval p = step();
1844 
1845  if(flag(Flag::stop))
1846  // Return now, but we could continue anyway.
1847  return p;
1848 
1849  auto now = Timestamp::now();
1850  if(now > until)
1851  return p;
1852 
1853  if(p)
1854  continue;
1855 
1856  auto p_end = now + p;
1857  m_trigger.wait(std::min(p_end, until), now);
1858  }
1859  }
1860 
1868  GuardPollInterval run(bool returnWhenBlocked = false)
1869  {
1870  zth_dbg(fsm, "[%s] Run%s", id_str(), returnWhenBlocked ? " until blocked" : "");
1872 
1873  while(true) {
1874  GuardPollInterval p = step();
1875 
1876  if(flag(Flag::stop))
1877  // Return now, but we could continue anyway.
1878  return p;
1879 
1880  if(p)
1881  continue;
1882 
1883  if(returnWhenBlocked)
1884  return p;
1885 
1886  m_trigger.wait(p);
1887  }
1888  }
1889 
1897  void trigger() noexcept
1898  {
1899  m_trigger.signal(false);
1900  }
1901 
1908  void stop() noexcept
1909  {
1910  zth_dbg(fsm, "[%s] Stop requested", id_str());
1912  }
1913 
1920  virtual void input(Symbol i) override
1921  {
1922  if(!i.valid())
1923  return;
1924 
1925  base::input(i);
1926  trigger();
1927  }
1928 
1935  Timestamp const& t() const noexcept
1936  {
1937  return m_t;
1938  }
1939 
1945  {
1946  return t().passed();
1947  }
1948 
1952  template <time_t s>
1954  {
1955  return TimeInterval{s} - fsm.dt();
1956  }
1957 
1961  template <uint64_t ms>
1963  {
1964  return TimeInterval{
1965  (time_t)(ms / 1'000ULL), (long)(ms * 1'000'000ULL) % 1'000'000'000L}
1966  - fsm.dt();
1967  }
1968 
1972  template <uint64_t us>
1974  {
1975  return TimeInterval{
1976  (time_t)(us / 1'000'000ULL), (long)(us * 1'000ULL) % 1'000'000'000L}
1977  - fsm.dt();
1978  }
1979 
1980 protected:
1988  virtual void enter() override
1989  {
1990  if(flag(Flag::transition))
1991  m_t = Timestamp::now();
1992 
1993  base::enter();
1994  }
1995 
1996 private:
1998  Signal m_trigger;
2000  Timestamp m_t;
2001 };
2002 
2008 inline17 constexpr auto entry = guard(&BasicFsm::entry, "entry");
2009 
2016 inline17 constexpr auto push = action(&BasicFsm::push, "push");
2017 
2023 inline17 constexpr auto pop = action(&BasicFsm::pop, "pop");
2024 
2030 inline17 constexpr auto popped = guard(&BasicFsm::popped, "popped");
2031 
2037 inline17 constexpr auto stop = action(&Fsm::stop, "stop");
2038 
2050 inline17 constexpr auto consume =
2051  action<void (BasicFsm::*)() noexcept>(&BasicFsm::clearInput, "consume");
2052 
2059 template <time_t s>
2060 inline17 constexpr auto timeout_s = guard(&Fsm::timeoutGuard_s<s>, "timeout");
2061 
2068 template <uint64_t ms>
2069 inline17 constexpr auto timeout_ms = guard(&Fsm::timeoutGuard_ms<ms>, "timeout");
2070 
2077 template <uint64_t us>
2078 inline17 constexpr auto timeout_us = guard(&Fsm::timeoutGuard_us<us>, "timeout");
2079 
2081 {
2082  return fsm.hasInput(m_input);
2083 }
2084 
2086 {
2087  if(isInput())
2088  return fsm.hasInput(input());
2089  else
2090  return m_guard.enabled(fsm);
2091 }
2092 
2093 template <size_t Size>
2095 {
2096  if(isInput(i))
2097  return fsm.hasInput(input(i));
2098  else
2099  return guard(i).enabled(fsm);
2100 }
2101 
2103 {
2104  Fsm fsm;
2105  fsm.init(*this);
2106  return fsm;
2107 }
2108 
2109 } // namespace fsm
2110 } // namespace zth
2111 #endif // C++14
2112 #endif // ZTH_FSM14_H
Fiber-aware signal.
Definition: sync.h:456
void signal(bool queue=true, bool queueEveryTime=false) noexcept
Definition: sync.h:509
void wait()
Definition: sync.h:473
Convenient wrapper around struct timespec that contains a time interval.
Definition: time.h:52
constexpr bool hasPassed() const noexcept
Definition: time.h:239
constexpr static TimeInterval infinity() noexcept
Definition: time.h:61
constexpr static TimeInterval null() noexcept
Definition: time.h:66
Convenient wrapper around struct timespec that contains an absolute timestamp.
Definition: time.h:527
static Timestamp now()
Definition: time.h:554
TimeInterval passed() const
Definition: time.h:620
friend cow_string str(UniqueIDBase const &)
Keeps track of a process-wide unique ID within the type T.
Definition: util.h:657
virtual char const * id_str() const override
Definition: util.h:751
string const & name() const noexcept
Definition: util.h:725
UniqueID & operator=(UniqueID const &)=delete
Copy-on-write string.
Definition: util.h:344
char const * c_str() const
Definition: util.h:416
constexpr Action()=default
~Action()=default
void operator()(BasicFsm &fsm) const
Definition: fsm14.h:597
virtual void run(BasicFsm &fsm) const
Definition: fsm14.h:590
Action(Action &&) noexcept=default
virtual cow_string name() const
Definition: fsm14.h:592
Basic FSM base class.
Definition: fsm14.h:1285
void push()
Push the current state onto the state stack.
Definition: fsm14.h:1492
TransitionsBase::index_type index_type
Definition: fsm14.h:1288
Flag
Flags that indicate properties of the current state.
Definition: fsm14.h:1294
@ entry
Just entered the current state.
@ transition
A transition is taken.
@ flags
Not a flag, just a count of the other flags.
@ blocked
The FSM is blocked, as no guards are enabled.
@ input
The current state was entered via an input symbol.
@ pushed
The current state was entered using push().
@ popped
The current state was entered using pop().
@ stop
stop() was requested. run() will terminate.
@ selfloop
Took transition to the same state.
virtual void leave()
Called when the current state is about to be left.
Definition: fsm14.h:1698
void reserveInputs(size_t capacity)
Reserve memory for the given amount of input symbols.
Definition: fsm14.h:1651
virtual ~BasicFsm()=default
Dtor.
Symbol input() const noexcept
Return the input symbol that triggered the transition to the current state.
Definition: fsm14.h:1584
bool setFlag(Flag f, bool value=true) noexcept
Set or clear the given flag.
Definition: fsm14.h:1679
virtual void enter()
Called when the current state was just entered.
Definition: fsm14.h:1710
BasicFsm & operator=(BasicFsm &&f) noexcept
Move assignment.
Definition: fsm14.h:1327
BasicFsm(BasicFsm &&f)
Move ctor.
Definition: fsm14.h:1318
bool flag(Flag f) const noexcept
Check if the given flag is set.
Definition: fsm14.h:1559
void clearInput() noexcept
Clear the input symbol that triggered the current state.
Definition: fsm14.h:1633
bool valid() const noexcept
Checks if the FSM was initialized.
Definition: fsm14.h:1363
virtual void pop()
Pop the previous state from the state stack.
Definition: fsm14.h:1513
void clearInputs() noexcept
Clear all inputs.
Definition: fsm14.h:1641
State const & prev() const noexcept
Return the previous state.
Definition: fsm14.h:1395
bool popped() const noexcept
Check if the current state was reached via pop().
Definition: fsm14.h:1551
void clearFlag(Flag f) noexcept
Set the given flag.
Definition: fsm14.h:1690
bool clearInput(Symbol i) noexcept
Remove the given input symbol.
Definition: fsm14.h:1611
bool entry() noexcept
Check if the state was just entered.
Definition: fsm14.h:1569
BasicFsm(cow_string const &name="FSM")
Ctor.
Definition: fsm14.h:1309
State const & state() const noexcept
Return the current state.
Definition: fsm14.h:1354
void reserveStack(size_t capacity)
Reserve memory to push() a amount of states.
Definition: fsm14.h:1372
GuardPollInterval step()
Try to take one step in the state machine.
Definition: fsm14.h:1414
virtual void input(Symbol i)
Register the given input symbol.
Definition: fsm14.h:1598
virtual void reset() noexcept
Reset the state machine.
Definition: fsm14.h:1383
bool hasInput(Symbol i) const noexcept
Checks if the given input symbol was registered before.
Definition: fsm14.h:1663
constexpr Callback(T_ &&c, char const *name=nullptr)
Definition: fsm14.h:337
constexpr Callback(T_ &&c, char const *name=nullptr)
Definition: fsm14.h:396
constexpr Callback(T_ &&c, char const *name=nullptr)
Definition: fsm14.h:356
FSM base class.
Definition: fsm14.h:1788
Timestamp const & t() const noexcept
Returns the time stamp when the current state was entered.
Definition: fsm14.h:1935
virtual void enter() override
Called when the current state was just entered.
Definition: fsm14.h:1988
virtual void input(Symbol i) override
Register the given input symbol.
Definition: fsm14.h:1920
GuardPollInterval run(Timestamp const &until)
Run the state machine until the given time stamp.
Definition: fsm14.h:1837
static GuardPollInterval timeoutGuard_us(Fsm &fsm)
Callback function for the zth::fsm::timeout_us guard.
Definition: fsm14.h:1973
void stop() noexcept
Interrupt a running FSM.
Definition: fsm14.h:1908
void trigger() noexcept
Trigger a currently running FSM to reevaluate the guards immediately.
Definition: fsm14.h:1897
GuardPollInterval run(bool returnWhenBlocked=false)
Run the state machine.
Definition: fsm14.h:1868
static GuardPollInterval timeoutGuard_ms(Fsm &fsm)
Callback function for the zth::fsm::timeout_ms guard.
Definition: fsm14.h:1962
TimeInterval dt() const
Returns the time since the current state was entered.
Definition: fsm14.h:1944
virtual ~Fsm() override=default
Dtor.
virtual void reset() noexcept override
Reset the state machine.
Definition: fsm14.h:1823
Fsm(Fsm &&f)
Move ctor.
Definition: fsm14.h:1803
static GuardPollInterval timeoutGuard_s(Fsm &fsm)
Callback function for the zth::fsm::timeout_s guard.
Definition: fsm14.h:1953
Fsm(cow_string const &name="FSM")
Ctor.
Definition: fsm14.h:1796
Fsm & operator=(Fsm &&f) noexcept
Move assignment.
Definition: fsm14.h:1812
Guard(Guard &&) noexcept=default
~Guard()=default
constexpr Guard()=default
virtual GuardPollInterval enabled(BasicFsm &fsm) const =0
virtual cow_string name() const =0
constexpr GuardPollInterval(bool enabled=false)
Definition: fsm14.h:417
constexpr GuardPollInterval(GuardPollInterval &&) noexcept=default
constexpr GuardedActionBase()=default
GuardPollInterval operator()(BasicFsm &fsm) const
Definition: fsm14.h:679
virtual GuardPollInterval tryRun(BasicFsm &fsm) const
Definition: fsm14.h:670
constexpr bool hasGuard() const noexcept
Definition: fsm14.h:720
virtual cow_string name() const final
Definition: fsm14.h:748
constexpr Symbol const & input() const noexcept
Definition: fsm14.h:725
constexpr GuardedAction(Guard const &guard, Action const &action) noexcept
Definition: fsm14.h:688
constexpr GuardedAction(Action const &action) noexcept
Definition: fsm14.h:707
constexpr auto const & guard() const noexcept
Definition: fsm14.h:732
constexpr GuardedAction(Symbol &&input) noexcept
Definition: fsm14.h:703
constexpr GuardedAction() noexcept
Definition: fsm14.h:711
virtual GuardPollInterval enabled(BasicFsm &fsm) const final
Definition: fsm14.h:2085
constexpr bool isInput() const noexcept
Definition: fsm14.h:715
constexpr GuardedAction(Symbol &&input, Action const &action) noexcept
Definition: fsm14.h:693
constexpr auto const & action() const noexcept
Definition: fsm14.h:738
virtual void run(BasicFsm &fsm) const final
Definition: fsm14.h:743
constexpr GuardedAction(Guard const &guard) noexcept
Definition: fsm14.h:699
constexpr InputGuard(Symbol &&input)
Definition: fsm14.h:485
virtual GuardPollInterval enabled(BasicFsm &fsm) const final
Definition: fsm14.h:2080
virtual cow_string name() const final
Definition: fsm14.h:491
constexpr Named(char const *name=nullptr) noexcept
Definition: fsm14.h:284
cow_string name() const
Definition: fsm14.h:289
cow_string name() const
Definition: fsm14.h:305
constexpr Named(char const *name) noexcept
Definition: fsm14.h:298
A input/state symbol.
Definition: fsm14.h:165
bool operator!=(S &&s) const noexcept
Definition: fsm14.h:212
bool operator==(Symbol const &s) const noexcept
Definition: fsm14.h:201
constexpr Symbol(char const *s=nullptr) noexcept
Ctor.
Definition: fsm14.h:172
constexpr bool valid() const noexcept
Check if the symbol is valid.
Definition: fsm14.h:245
constexpr char const * str() const noexcept
Return a string representation of this symbol.
Definition: fsm14.h:220
bool operator==(char const *s) const noexcept
Definition: fsm14.h:206
constexpr bool constexpr_eq(Symbol const &s) const noexcept
Like == operator, but constexpr.
Definition: fsm14.h:179
constexpr char const * symbol() const noexcept
Return the symbol string.
Definition: fsm14.h:229
virtual GuardPollInterval enabled(BasicFsm &fsm) const final
Definition: fsm14.h:941
constexpr Symbol const & input() const noexcept
Definition: fsm14.h:966
virtual cow_string name() const final
Definition: fsm14.h:986
constexpr auto const & guard() const noexcept
Definition: fsm14.h:971
constexpr auto const & to() const noexcept
Definition: fsm14.h:981
constexpr auto const & action() const noexcept
Definition: fsm14.h:976
constexpr bool isInput() const noexcept
Definition: fsm14.h:956
virtual GuardPollInterval tryRun(BasicFsm &fsm) const final
Definition: fsm14.h:946
constexpr bool hasGuard() const noexcept
Definition: fsm14.h:961
constexpr Transition(F &&from, T &&to) noexcept
Definition: fsm14.h:936
constexpr auto const & from() const noexcept
Definition: fsm14.h:951
constexpr Transition(F &&from) noexcept
Definition: fsm14.h:930
constexpr TransitionStart(GuardedAction &&ga) noexcept
Definition: fsm14.h:821
constexpr TransitionStart(State &&state, GuardedAction &&guardedAction) noexcept
Definition: fsm14.h:826
constexpr auto const & guard() const noexcept
Definition: fsm14.h:851
virtual GuardPollInterval tryRun(BasicFsm &fsm) const final
Definition: fsm14.h:866
constexpr auto const & action() const noexcept
Definition: fsm14.h:856
constexpr auto const & state() const noexcept
Definition: fsm14.h:831
constexpr bool hasGuard() const noexcept
Definition: fsm14.h:841
constexpr bool isInput() const noexcept
Definition: fsm14.h:836
constexpr Symbol const & input() const noexcept
Definition: fsm14.h:846
constexpr TransitionStart(State &&state) noexcept
Definition: fsm14.h:806
constexpr TransitionStart(Guard const &guard) noexcept
Definition: fsm14.h:811
constexpr TransitionStart(Action const &action) noexcept
Definition: fsm14.h:816
virtual cow_string name() const override
Definition: fsm14.h:871
virtual GuardPollInterval enabled(BasicFsm &fsm) const final
Definition: fsm14.h:861
virtual State const & state(index_type i) const noexcept=0
virtual bool hasInput(index_type i) const noexcept
Definition: fsm14.h:1034
virtual Guard const & guard(index_type i) const noexcept=0
virtual GuardPollInterval enabled(index_type i, BasicFsm &fsm) const =0
virtual size_t size() const noexcept=0
F & init(F &fsm) const noexcept
Definition: fsm14.h:1049
void uml(FILE *f=stdout) const
Definition: fsm14.h:1066
virtual Action const & action(index_type i) const noexcept=0
virtual index_type to(index_type i) const noexcept=0
virtual bool hasGuard(index_type i) const noexcept=0
virtual Symbol input(index_type i) const noexcept=0
void dump(FILE *f=stdout) const
Definition: fsm14.h:1055
virtual index_type to(index_type i) const noexcept final
Definition: fsm14.h:1226
virtual size_t size() const noexcept final
Definition: fsm14.h:1189
virtual Action const & action(index_type i) const noexcept final
Definition: fsm14.h:1220
constexpr Transitions()=default
static constexpr Transitions compile(std::initializer_list< Transition > l)
Definition: fsm14.h:1147
virtual Symbol input(index_type i) const noexcept final
Definition: fsm14.h:1211
virtual State const & state(index_type i) const noexcept final
Definition: fsm14.h:1232
typename smallest_uint< Size >::type Index
Definition: fsm14.h:1118
virtual GuardPollInterval enabled(index_type i, BasicFsm &fsm) const final
Definition: fsm14.h:2094
virtual bool isInput(index_type i) const noexcept final
Definition: fsm14.h:1194
virtual Guard const & guard(index_type i) const noexcept final
Definition: fsm14.h:1204
virtual bool hasGuard(index_type i) const noexcept final
Definition: fsm14.h:1199
constexpr TypedAction(T_ &&a, char const *name=nullptr)
Definition: fsm14.h:612
virtual void run(BasicFsm &fsm) const final
Definition: fsm14.h:616
virtual cow_string name() const final
Definition: fsm14.h:621
virtual cow_string name() const final
Definition: fsm14.h:476
virtual GuardPollInterval enabled(BasicFsm &fsm) const final
Definition: fsm14.h:471
constexpr TypedGuard(T_ &&g, char const *name=nullptr)
Definition: fsm14.h:467
constexpr auto consume
Action consume the current input symbol.
Definition: fsm14.h:2050
constexpr auto never
Trivial guard that is never enabled.
Definition: fsm14.h:576
constexpr auto guard(T &&g, char const *name=nullptr)
Create a guard from a function.
Definition: fsm14.h:530
constexpr auto entry
Guard that is only enabled upon entry of a state.
Definition: fsm14.h:2008
constexpr auto timeout_us
A guard that is enabled after a us microseconds after entering the current state.
Definition: fsm14.h:2078
constexpr auto nothing
Trivial action that does nothing.
Definition: fsm14.h:661
constexpr auto action(T &&a, char const *name=nullptr)
Create an action from a function.
Definition: fsm14.h:649
constexpr auto timeout_s
A guard that is enabled after a s seconds after entering the current state.
Definition: fsm14.h:2060
constexpr auto pop
Action to pop the current state from the stack.
Definition: fsm14.h:2023
constexpr auto push
Action to push the new state onto the stack.
Definition: fsm14.h:2016
constexpr auto stop
Action to return from Fsm::run().
Definition: fsm14.h:2037
constexpr auto input(Symbol &&symbol) noexcept
Create a guard from an input symbol.
Definition: fsm14.h:781
constexpr auto popped
Guard to indicate that the current state was reached via pop.
Definition: fsm14.h:2030
constexpr auto timeout_ms
A guard that is enabled after a ms milliseconds after entering the current state.
Definition: fsm14.h:2069
constexpr auto always
Trivial guard that is always enabled.
Definition: fsm14.h:564
constexpr auto compile(T &&... t)
Compile a transition description.
Definition: fsm14.h:1273
#define zth_dbg(group, fmt, a...)
Debug printf()-like function.
Definition: util.h:210
#define ZTH_CLASS_NEW_DELETE(T)
Define new/delete operators for a class, which are allocator-aware.
Definition: allocator.h:114
#define zth_throw(e)
Definition: macros.h:222
#define UNUSED_PAR(name)
Definition: macros.h:79
constexpr auto operator/(Guard const &g, Action const &a) noexcept
Definition: fsm14.h:762
void nothing_action()
Definition: fsm14.h:654
constexpr auto operator+(Symbol &&input) noexcept
Definition: fsm14.h:767
bool never_guard() noexcept
Definition: fsm14.h:566
constexpr Transition operator>>=(TransitionStart &&from, State &&to) noexcept
Definition: fsm14.h:996
bool always_guard() noexcept
Definition: fsm14.h:551
Definition: allocator.h:23
cow_string str(T value)
Returns an zth::string representation of the given value.
Definition: util.h:517
string format(char const *fmt,...)
Format like sprintf(), but save the result in an zth::string.
Definition: util.h:503
constexpr CompiledTransition() noexcept
Definition: fsm14.h:1125
Exception thrown when the FSM description is incorrect.
Definition: fsm14.h:152
invalid_fsm(char const *str)
Definition: fsm14.h:153
std::vector< index_type, typename Config::Allocator< index_type >::type > type
Definition: allocator.h:134
#define zth_assert(expr)
assert(), but better integrated in Zth.
Definition: util.h:236