Zth (libzth)
Loading...
Searching...
No Matches
fsm14.h
Go to the documentation of this file.
1#ifndef ZTH_FSM14_H
2#define ZTH_FSM14_H
3/*
4 * SPDX-FileCopyrightText: 2019-2026 Jochem Rutgers
5 *
6 * SPDX-License-Identifier: MPL-2.0
7 */
8
16#include <libzth/macros.h>
17
18#if defined(__cplusplus) && __cplusplus >= 201402L
19
20# include <libzth/allocator.h>
21# include <libzth/sync.h>
22# include <libzth/util.h>
23
24# include <atomic>
25# include <bitset>
26# include <functional>
27# include <limits>
28# include <stdexcept>
29# include <type_traits>
30# include <utility>
31
32namespace zth {
33namespace fsm {
34
35
36
38// Misc utilities
39//
40
41# ifdef DOXYGEN
42
43template <typename T>
45
46# else // !DOXYGEN
47
48template <typename R, typename C, typename A>
49struct function_traits_detail {
50 using return_type = R;
51 using class_type = C;
52 using arg_type = A;
53
54 static constexpr bool is_member = !std::is_same<C, void>::value;
55 static constexpr bool is_functor = false;
56};
57
58// Assume it's a lambda, with captures (so it is a functor).
59template <typename T>
60struct function_traits : function_traits<decltype(&T::operator())> {
61 // cppcheck-suppress duplInheritedMember
62 static constexpr bool is_functor = true;
63};
64
65// R() normal function
66template <typename R>
67struct function_traits<R (&)()> : function_traits_detail<R, void, void> {};
68
69# if defined(__cpp_noexcept_function_type) && __cpp_noexcept_function_type >= 201510
70template <typename R>
71struct function_traits<R (&)() noexcept> : function_traits_detail<R, void, void> {};
72# endif
73
74// R(A) normal function
75template <typename R, typename A>
76struct function_traits<R (&)(A)> : function_traits_detail<R, void, A> {};
77
78# if defined(__cpp_noexcept_function_type) && __cpp_noexcept_function_type >= 201510
79template <typename R, typename A>
80struct function_traits<R (&)(A) noexcept> : function_traits_detail<R, void, A> {};
81# endif
82
83// R() normal function
84template <typename R>
85struct function_traits<R (*)()> : function_traits_detail<R, void, void> {};
86
87# if defined(__cpp_noexcept_function_type) && __cpp_noexcept_function_type >= 201510
88template <typename R>
89struct function_traits<R (*)() noexcept> : function_traits_detail<R, void, void> {};
90# endif
91
92// R(A) normal function
93template <typename R, typename A>
94struct function_traits<R (*)(A)> : function_traits_detail<R, void, A> {};
95
96# if defined(__cpp_noexcept_function_type) && __cpp_noexcept_function_type >= 201510
97template <typename R, typename A>
98struct function_traits<R (*)(A) noexcept> : function_traits_detail<R, void, A> {};
99# endif
100
101// R(C::*)() class member
102template <typename R, typename C>
103struct function_traits<R (C::*)()> : function_traits_detail<R, C, void> {};
104
105# if defined(__cpp_noexcept_function_type) && __cpp_noexcept_function_type >= 201510
106template <typename R, typename C>
107struct function_traits<R (C::*)() noexcept> : function_traits_detail<R, C, void> {};
108# endif
109
110// R(C::*)(A) class member
111template <typename R, typename C, typename A>
112struct function_traits<R (C::*)(A)> : function_traits_detail<R, C, A> {};
113
114# if defined(__cpp_noexcept_function_type) && __cpp_noexcept_function_type >= 201510
115template <typename R, typename C, typename A>
116struct function_traits<R (C::*)(A) noexcept> : function_traits_detail<R, C, A> {};
117# endif
118
119// R(C::*)() const class member
120template <typename R, typename C>
121struct function_traits<R (C::*)() const> : function_traits_detail<R, C, void> {};
122
123# if defined(__cpp_noexcept_function_type) && __cpp_noexcept_function_type >= 201510
124template <typename R, typename C>
125struct function_traits<R (C::*)() const noexcept> : function_traits_detail<R, C, void> {};
126# endif
127
128// R(C::*)(A) const class member
129template <typename R, typename C, typename A>
130struct function_traits<R (C::*)(A) const> : function_traits_detail<R, C, A> {};
131
132# if defined(__cpp_noexcept_function_type) && __cpp_noexcept_function_type >= 201510
133template <typename R, typename C, typename A>
134struct function_traits<R (C::*)(A) const noexcept> : function_traits_detail<R, C, A> {};
135# endif
136
137# endif // !DOXYGEN
138
139
141// Symbol
142//
143
144class BasicFsm;
145class Fsm;
146
151struct invalid_fsm : public std::logic_error {
152 explicit invalid_fsm(char const* str)
153 : std::logic_error{str}
154 {}
155};
156
164class Symbol {
166public:
171 // cppcheck-suppress noExplicitConstructor
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
258private:
260 char const* m_symbol{};
261};
262
267constexpr Symbol operator""_S(char const* s, size_t UNUSED_PAR(len)) noexcept
268{
269 return s;
270}
271
276using State = Symbol;
277
278template <bool enable = Config::NamedFsm>
279class Named {};
280
281template <>
282class Named<false> {
283protected:
284 constexpr explicit Named(char const* UNUSED_PAR(name) = nullptr) noexcept {}
285
286 ~Named() = default;
287
288public:
290 {
291 return format("%p", this);
292 }
293};
294
295template <>
296class Named<true> : public Named<false> {
297protected:
298 constexpr explicit Named(char const* name) noexcept
299 : m_name{name}
300 {}
301
302 ~Named() = default;
303
304public:
305 // cppcheck-suppress duplInheritedMember
307 {
308 if(m_name)
309 return m_name;
310
311 return Named<false>::name();
312 }
313
314private:
315 char const* m_name;
316};
317
318template <
319 typename T, typename R,
320 bool haveArg = !std::is_same<typename function_traits<T>::arg_type, void>::value,
321 bool isMember =
322 !haveArg && function_traits<T>::is_member && !function_traits<T>::is_functor
323 && std::is_base_of<BasicFsm, typename function_traits<T>::class_type>::value,
324 bool isOk =
325 std::is_convertible<typename function_traits<T>::return_type, R>::value
326 && (!haveArg
327 || std::is_convertible<BasicFsm&, typename function_traits<T>::arg_type>::value
328 || std::is_base_of<
329 BasicFsm,
330 std::remove_reference_t<typename function_traits<T>::arg_type>>::value)>
331class Callback {};
332
333template <typename T, typename R>
334class Callback<T, R, false, false, true> : public Named<> {
336public:
337 template <typename T_>
338 // cppcheck-suppress noExplicitConstructor
339 constexpr Callback(T_&& c, char const* name = nullptr)
340 : Named{name}
341 , m_callback{std::forward<T_>(c)}
342 {}
343
344 // cppcheck-suppress constParameterReference
345 R call(BasicFsm& UNUSED_PAR(fsm)) const
346 {
347 return m_callback();
348 }
349
350private:
351 T m_callback;
352};
353
354template <typename T, typename R>
355class Callback<T, R, true, false, true> : public Named<> {
357public:
358 template <typename T_>
359 // cppcheck-suppress noExplicitConstructor
360 constexpr Callback(T_&& c, char const* name = nullptr)
361 : Named{name}
362 , m_callback{std::forward<T_>(c)}
363 {}
364
365 R call(BasicFsm& fsm) const
366 {
367 using A = typename function_traits<T>::arg_type;
368 check<A>(fsm);
369 return m_callback(static_cast<A>(fsm));
370 }
371
372private:
373 template <
374 typename A,
375 std::enable_if_t<
376 std::is_base_of<BasicFsm, std::remove_reference_t<A>>::value, int> = 0>
377 static void check(BasicFsm& UNUSED_PAR(fsm))
378 {
379# ifdef __GXX_RTTI
380 zth_assert(dynamic_cast<std::remove_reference_t<A>*>(&fsm) != nullptr);
381# endif
382 }
383
384 template <
385 typename A,
386 std::enable_if_t<
387 !std::is_base_of<BasicFsm, std::remove_reference_t<A>>::value, int> = 0>
388 static void check(BasicFsm& fsm)
389 {}
390
391private:
392 T m_callback;
393};
394
395template <typename T, typename R>
396class Callback<T, R, false, true, true> : public Named<> {
398public:
399 template <typename T_>
400 // cppcheck-suppress noExplicitConstructor
401 constexpr Callback(T_&& c, char const* name = nullptr)
402 : Named{name}
403 , m_callback{std::forward<T_>(c)}
404 {}
405
406 // cppcheck-suppress constParameterReference
407 R call(BasicFsm& fsm) const
408 {
409 using C = typename function_traits<T>::class_type;
410# ifdef __GXX_RTTI
411 zth_assert(dynamic_cast<C*>(&fsm) != nullptr);
412# endif
413 return ((static_cast<C&>(fsm)).*m_callback)();
414 }
415
416private:
417 T m_callback;
418};
419
422public:
423 // cppcheck-suppress noExplicitConstructor
424 constexpr GuardPollInterval(bool enabled = false)
426 {}
427
428 constexpr GuardPollInterval(GuardPollInterval&&) noexcept = default;
429 GuardPollInterval& operator=(GuardPollInterval&&) noexcept = default;
430 constexpr GuardPollInterval(GuardPollInterval const&) noexcept = default;
431 GuardPollInterval& operator=(GuardPollInterval const&) noexcept = default;
432
433 template <typename... A>
434 // cppcheck-suppress noExplicitConstructor
435 constexpr GuardPollInterval(A&&... a) noexcept
436 : TimeInterval(std::forward<A>(a)...)
437 {}
438
439 constexpr operator bool() const noexcept
440 {
441 return hasPassed();
442 }
443};
444
445class Guard {
446protected:
447 constexpr Guard() = default;
448 ~Guard() = default;
449
450 Guard(Guard&&) noexcept = default;
451 Guard& operator=(Guard&&) noexcept = default;
452
453public:
454 Guard(Guard const&) = delete;
455 void operator=(Guard const&) = delete;
456
457 virtual GuardPollInterval enabled(BasicFsm& fsm) const = 0;
458 virtual cow_string name() const = 0;
459
460 auto operator()(BasicFsm& fsm) const
461 {
462 return enabled(fsm);
463 }
464};
465
466template <typename T>
467class TypedGuard final : public Guard, protected Callback<T, GuardPollInterval> {
469public:
471
472 template <typename T_>
473 constexpr explicit TypedGuard(T_&& g, char const* name = nullptr)
474 : Callback_type{std::forward<T_>(g), name}
475 {}
476
477 virtual GuardPollInterval enabled(BasicFsm& fsm) const final
478 {
479 return this->call(fsm);
480 }
481
482 virtual cow_string name() const final
483 {
484 return Callback_type::name();
485 }
486};
487
488class InputGuard final : public Guard {
490public:
491 constexpr explicit InputGuard(Symbol&& input)
492 : m_input{std::move(input)}
493 {}
494
495 virtual GuardPollInterval enabled(BasicFsm& fsm) const final;
496
497 virtual cow_string name() const final
498 {
499 return m_input.str();
500 }
501
502private:
503 Symbol m_input;
504};
505
535template <typename T>
536constexpr auto guard(T&& g, char const* name = nullptr)
537{
538 return TypedGuard<std::decay_t<T>>{std::forward<T>(g), name};
539}
540
552constexpr inline auto guard(Symbol&& input) noexcept
553{
554 return InputGuard{std::move(input)};
555}
556
557inline bool always_guard() noexcept
558{
559 return true;
560}
561
567// This could be solved simply by using [](){ return true; } as function, but
568// using lambda in constexpr is only allowed since C++17. So, use the separate
569// function instead.
570inline17 constexpr auto always = guard(always_guard, "always");
571
572inline bool never_guard() noexcept
573{
574 return false;
575}
576
582inline17 constexpr auto never = guard(never_guard, "never");
583
584class Action {
585protected:
586 constexpr Action() = default;
587 ~Action() = default;
588
589 Action(Action&&) noexcept = default;
590 Action& operator=(Action&&) noexcept = default;
591
592public:
593 Action(Action const&) = delete;
594 void operator=(Action const&) = delete;
595
596 virtual void run(BasicFsm& UNUSED_PAR(fsm)) const {}
597
598 virtual cow_string name() const
599 {
600 return format("%p\n", this);
601 }
602
603 void operator()(BasicFsm& fsm) const
604 {
605 run(fsm);
606 }
607};
608
609template <typename T>
610class TypedAction final : public Action, protected Callback<T, void> {
612public:
614
615 template <typename T_>
616 constexpr explicit TypedAction(T_&& a, char const* name = nullptr)
617 : Callback_type{std::forward<T_>(a), name}
618 {}
619
620 virtual void run(BasicFsm& fsm) const final
621 {
622 this->call(fsm);
623 }
624
625 virtual cow_string name() const final
626 {
627 return Callback_type::name();
628 }
629};
630
652template <typename T>
653constexpr auto action(T&& a, char const* name = nullptr)
654{
655 return TypedAction<std::decay_t<T>>{std::forward<T>(a), name};
656}
657
658inline void nothing_action() {}
659
665inline17 constexpr auto nothing = action(nothing_action, "nothing");
666
667class GuardedActionBase : public Guard, protected Action {
668protected:
669 constexpr GuardedActionBase() = default;
670
671public:
673 {
674 if(auto e = !enabled(fsm))
675 return e;
676
677 run(fsm);
678 return true;
679 }
680
681 // cppcheck-suppress duplInheritedMember
683 {
684 return tryRun(fsm);
685 }
686};
687
688class GuardedAction final : public GuardedActionBase {
690public:
691 constexpr GuardedAction(Guard const& guard, Action const& action) noexcept
692 : m_guard{guard}
693 , m_action{action}
694 {}
695
696 constexpr GuardedAction(Symbol&& input, Action const& action) noexcept
697 : m_guard{never}
698 , m_input{std::move(input)}
699 , m_action{action}
700 {}
701
702 // cppcheck-suppress noExplicitConstructor
703 constexpr GuardedAction(Guard const& guard) noexcept
705 {}
706
707 // cppcheck-suppress noExplicitConstructor
708 constexpr GuardedAction(Symbol&& input) noexcept
709 : GuardedAction{std::move(input), nothing}
710 {}
711
712 // cppcheck-suppress noExplicitConstructor
713 constexpr GuardedAction(Action const& action) noexcept
715 {}
716
717 constexpr GuardedAction() noexcept
719 {}
720
721 constexpr bool isInput() const noexcept
722 {
723 return m_input.valid();
724 }
725
726 constexpr bool hasGuard() const noexcept
727 {
728 return !isInput();
729 }
730
731 constexpr Symbol const& input() const noexcept
732 {
733 return m_input;
734 }
735
736 virtual GuardPollInterval enabled(BasicFsm& fsm) const final;
737
738 constexpr auto const& guard() const noexcept
739 {
741 return m_guard;
742 }
743
744 constexpr auto const& action() const noexcept
745 {
746 return m_action;
747 }
748
749 virtual void run(BasicFsm& fsm) const final
750 {
751 m_action(fsm);
752 }
753
754 virtual cow_string name() const final
755 {
756 return isInput() ? format("input %s / %s", input().str(), action().name().c_str())
757 : format("guard %s / %s", guard().name().c_str(),
758 action().name().c_str());
759 }
760
761private:
762 Guard const& m_guard;
763 Symbol m_input;
764 Action const& m_action;
765};
766
767constexpr inline auto operator/(Guard const& g, Action const& a) noexcept
768{
769 return GuardedAction{g, a};
770}
771
772constexpr inline auto operator+(Symbol&& input) noexcept
773{
774 return GuardedAction{std::move(input)};
775}
776
786constexpr inline auto input(Symbol&& symbol) noexcept
787{
788 return GuardedAction{std::move(symbol)};
789}
790
791constexpr inline auto operator+(GuardedAction&& g) noexcept
792{
793 return std::move(g);
794}
795
796// + "a" / b is first compiled into GuardedAction{ "a" } / b.
797// This operator/ should fix that by using "a" as input guard for b.
798constexpr inline auto operator/(GuardedAction&& ga, Action const& a)
799{
800 if(!(ga.isInput() && &ga.action() == &nothing))
801 zth_throw(invalid_fsm("Ambiguous /"));
802
803 return GuardedAction{Symbol{ga.input()}, a};
804}
805
806class Transition;
807
808class TransitionStart final : public GuardedActionBase {
810public:
811 // cppcheck-suppress noExplicitConstructor
812 constexpr TransitionStart(State&& state) noexcept
813 : m_state{std::move(state)}
814 , m_guardedAction{never / nothing}
815 {}
816
817 // cppcheck-suppress noExplicitConstructor
818 constexpr TransitionStart(Guard const& guard) noexcept
819 : m_state{}
820 , m_guardedAction{guard / nothing}
821 {}
822
823 // cppcheck-suppress noExplicitConstructor
824 constexpr TransitionStart(Action const& action) noexcept
825 : m_state{}
826 , m_guardedAction{always / action}
827 {}
828
829 // cppcheck-suppress noExplicitConstructor
830 constexpr TransitionStart(GuardedAction&& ga) noexcept
831 : m_state{}
832 , m_guardedAction{std::move(ga)}
833 {}
834
835 constexpr TransitionStart(State&& state, GuardedAction&& guardedAction) noexcept
836 : m_state{std::move(state)}
837 , m_guardedAction{std::move(guardedAction)}
838 {}
839
840 constexpr auto const& state() const noexcept
841 {
842 return m_state;
843 }
844
845 constexpr bool isInput() const noexcept
846 {
847 return m_guardedAction.isInput();
848 }
849
850 constexpr bool hasGuard() const noexcept
851 {
852 return m_guardedAction.hasGuard();
853 }
854
855 constexpr Symbol const& input() const noexcept
856 {
857 return m_guardedAction.input();
858 }
859
860 constexpr auto const& guard() const noexcept
861 {
862 return m_guardedAction.guard();
863 }
864
865 constexpr auto const& action() const noexcept
866 {
867 return m_guardedAction.action();
868 }
869
870 virtual GuardPollInterval enabled(BasicFsm& fsm) const final
871 {
872 return m_guardedAction.enabled(fsm);
873 }
874
875 virtual GuardPollInterval tryRun(BasicFsm& fsm) const final
876 {
877 return m_guardedAction.tryRun(fsm);
878 }
879
880 virtual cow_string name() const override
881 {
882 return format("%s + %s", state().str(), m_guardedAction.name().c_str());
883 }
884
885private:
886 State m_state;
887 GuardedAction m_guardedAction;
888};
889
890constexpr inline auto operator+(State&& state, GuardedAction&& action) noexcept
891{
892 return TransitionStart{std::move(state), std::move(action)};
893}
894
895constexpr inline auto operator+(State&& state, Guard const& guard) noexcept
896{
897 return TransitionStart{std::move(state), GuardedAction{guard, nothing}};
898}
899
900constexpr inline auto operator+(State&& state, Symbol&& input) noexcept
901{
902 return TransitionStart{std::move(state), GuardedAction{std::move(input)}};
903}
904
905constexpr inline auto operator+(char const* state, Symbol&& input) noexcept
906{
907 return TransitionStart{state, GuardedAction{std::move(input)}};
908}
909
910constexpr inline auto operator+(State&& state, char const* input) noexcept
911{
912 return TransitionStart{std::move(state), GuardedAction{input}};
913}
914
915constexpr inline auto const& operator+(Guard const& guard) noexcept
916{
917 return guard;
918}
919
920constexpr inline auto operator/(State&& state, Action const& action) noexcept
921{
922 return TransitionStart{std::move(state), GuardedAction{always, action}};
923}
924
925// "a" + "b" / c is first compiled into "a" + TransitionStart{ "b" + always / c }.
926// This operator+ should fix that by converting "b" from State to Input guard.
927constexpr inline auto operator+(State&& state, TransitionStart&& t)
928{
929 if(!(t.hasGuard() && &t.guard() == &always))
930 zth_throw(invalid_fsm{"Ambiguous +"});
931
932 return TransitionStart{std::move(state), GuardedAction{Symbol{t.state()}, t.action()}};
933}
934
935class Transition final : public GuardedActionBase {
937public:
938 template <typename F>
939 // cppcheck-suppress noExplicitConstructor
940 constexpr Transition(F&& from) noexcept
941 : m_from{std::forward<F>(from)}
942 , m_to{}
943 {}
944
945 template <typename F, typename T>
946 constexpr Transition(F&& from, T&& to) noexcept
947 : m_from{std::forward<F>(from)}
948 , m_to{std::forward<T>(to)}
949 {}
950
951 virtual GuardPollInterval enabled(BasicFsm& fsm) const final
952 {
953 return m_from.enabled(fsm);
954 }
955
956 virtual GuardPollInterval tryRun(BasicFsm& fsm) const final
957 {
958 return m_from.tryRun(fsm);
959 }
960
961 constexpr auto const& from() const noexcept
962 {
963 return m_from.state();
964 }
965
966 constexpr bool isInput() const noexcept
967 {
968 return m_from.isInput();
969 }
970
971 constexpr bool hasGuard() const noexcept
972 {
973 return m_from.hasGuard();
974 }
975
976 constexpr Symbol const& input() const noexcept
977 {
978 return m_from.input();
979 }
980
981 constexpr auto const& guard() const noexcept
982 {
983 return m_from.guard();
984 }
985
986 constexpr auto const& action() const noexcept
987 {
988 return m_from.action();
989 }
990
991 constexpr auto const& to() const noexcept
992 {
993 return m_to;
994 }
995
996 virtual cow_string name() const final
997 {
998 return format("%s >>= %s", m_from.name().c_str(), m_to.str());
999 }
1000
1001private:
1002 TransitionStart m_from;
1003 State m_to;
1004};
1005
1006constexpr inline Transition operator>>=(TransitionStart&& from, State&& to) noexcept
1007{
1008 return Transition{std::move(from), std::move(to)};
1009}
1010
1011constexpr inline Transition operator>>=(State&& from, State&& to) noexcept
1012{
1013 return Transition{std::move(from) + always, std::move(to)};
1014}
1015
1016constexpr inline Transition operator>>=(char const* from, State&& to) noexcept
1017{
1018 return Transition{State{from} + always, std::move(to)};
1019}
1020
1021constexpr inline Transition operator>>=(Guard const& from, State&& to) noexcept
1022{
1023 return Transition{State{} + from, std::move(to)};
1024}
1025
1026constexpr inline Transition operator>>=(Action const& from, State&& to) noexcept
1027{
1028 return Transition{State{} / from, std::move(to)};
1029}
1030
1031constexpr inline Transition operator>>=(GuardedAction&& from, State&& to) noexcept
1032{
1033 return Transition{State{} + std::move(from), std::move(to)};
1034}
1035
1037public:
1038 using index_type = size_t;
1039
1040 virtual size_t size() const noexcept = 0;
1041
1042 virtual bool hasGuard(index_type i) const noexcept = 0;
1043
1044 virtual bool hasInput(index_type i) const noexcept
1045 {
1046 return !hasGuard(i);
1047 }
1048
1049 virtual Guard const& guard(index_type i) const noexcept = 0;
1050 virtual GuardPollInterval enabled(index_type i, BasicFsm& fsm) const = 0;
1051 virtual Symbol input(index_type i) const noexcept = 0;
1052 virtual Action const& action(index_type i) const noexcept = 0;
1053 virtual index_type to(index_type i) const noexcept = 0;
1054 virtual State const& state(index_type i) const noexcept = 0;
1055
1056 Fsm spawn() const;
1057
1058 template <typename F, std::enable_if_t<std::is_base_of<BasicFsm, F>::value, int> = 0>
1059 F& init(F& fsm) const noexcept
1060 {
1061 fsm.init(*this);
1062 return fsm;
1063 }
1064
1065 void dump(FILE* f = stdout) const
1066 {
1067 size_t size_ = size();
1068 for(index_type i = 0; i < size_; i++) {
1069 fprintf(f, "%3zu: %-16s + %s %-18s / %-18s >>= %3zu\n", i, state(i).str(),
1070 hasGuard(i) ? "guard" : "input",
1071 hasGuard(i) ? guard(i).name().c_str() : input(i).str(),
1072 action(i).name().c_str(), to(i));
1073 }
1074 }
1075
1076 void uml(FILE* f = stdout) const
1077 {
1078 fprintf(f, "@startuml\n");
1079
1080 size_t size_ = size();
1081 index_type statei = 0;
1082 size_t t = 0;
1083 for(index_type i = 0; i < size_; i++) {
1084 if(state(i).valid()) {
1085 statei = i;
1086 fprintf(f, "state \"%s\" as s%zu\n", state(statei).str(), statei);
1087 t = 0;
1088 }
1089
1090 string edge;
1091
1092 if(statei == 0)
1093 edge += "[*]";
1094 else
1095 edge += format("s%zu", statei);
1096
1097 if(to(i))
1098 edge += format(" --> s%zu", to(i));
1099
1100 edge += format(" : (%zu)", t);
1101
1102 if(hasGuard(i)) {
1103 if(&guard(i) == &never)
1104 continue;
1105
1106 if(&guard(i) != &always)
1107 edge += format(" [%s]", guard(i).name().c_str());
1108 } else {
1109 edge += format(" %s", input(i).str());
1110 }
1111
1112 if(&action(i) != &nothing) {
1113 edge += format(" / %s", action(i).name().c_str());
1114 }
1115
1116 fprintf(f, "%s\n", edge.c_str());
1117 t++;
1118 }
1119
1120 fprintf(f, "@enduml\n");
1121 }
1122};
1123
1124template <size_t Size>
1125class Transitions final : public TransitionsBase {
1127protected:
1128 using Index = typename smallest_uint<Size>::type;
1129
1130 enum class Flag : uint8_t {
1131 input = 0x01,
1132 };
1133
1135 constexpr CompiledTransition() noexcept
1136 : guard{}
1137 , action{}
1138 , from{}
1139 , to{}
1140 , flags{}
1141 {}
1142
1143 // This is either a Guard const* or char const*, depending on
1144 // m_flags & Flag::input. A union would be better, but you
1145 // cannot change the active union field in a contexpr before
1146 // C++20.
1147 void const* guard;
1151 uint8_t flags;
1152 };
1153
1154 constexpr Transitions() = default;
1155
1156public:
1157 static constexpr Transitions compile(std::initializer_list<Transition> l)
1158 {
1159 zth_assert(l.size() == Size);
1160
1161 Transitions f;
1162
1163 if(Size == 0) {
1164 f.m_transitions[0].guard = &never;
1165 f.m_transitions[0].action = &nothing;
1166 } else {
1167 f.m_transitions[0].guard = &always;
1168 f.m_transitions[0].action = &nothing;
1169 f.m_transitions[0].to = 1;
1170
1171 if(!l.begin()->from().valid())
1172 zth_throw(invalid_fsm{"First state must be valid"});
1173 }
1174
1175 index_type i = 1;
1176 State prev;
1177
1178 for(auto const& t : l) {
1179 if(!prev.constexpr_eq(t.from()) && t.from().valid()) {
1180 prev = f.m_transitions[i].from = t.from();
1181 if(find(t.from(), l) != (size_t)i)
1183 "State transitions are not contiguous"});
1184 }
1185 if(t.hasGuard()) {
1186 f.m_transitions[i].guard = &t.guard();
1187 } else {
1188 f.m_transitions[i].guard = t.input().symbol();
1189 f.m_transitions[i].flags = static_cast<uint8_t>(Flag::input);
1190 }
1191 f.m_transitions[i].action = &t.action();
1192 f.m_transitions[i].to = static_cast<Index>(find(t.to(), l));
1193 i++;
1194 }
1195
1196 return f;
1197 }
1198
1199 virtual size_t size() const noexcept final
1200 {
1201 return Size + 1U;
1202 }
1203
1204 virtual bool isInput(index_type i) const noexcept final
1205 {
1206 return (m_transitions[i].flags & static_cast<uint8_t>(Flag::input)) != 0;
1207 }
1208
1209 virtual bool hasGuard(index_type i) const noexcept final
1210 {
1211 return !isInput(i);
1212 }
1213
1214 virtual Guard const& guard(index_type i) const noexcept final
1215 {
1216 zth_assert(i < size());
1217 zth_assert(hasGuard(i));
1218 return *static_cast<Guard const*>(m_transitions[i].guard);
1219 }
1220
1221 virtual Symbol input(index_type i) const noexcept final
1222 {
1223 zth_assert(i < size());
1224 zth_assert(!hasGuard(i));
1225 return static_cast<char const*>(m_transitions[i].guard);
1226 }
1227
1228 virtual GuardPollInterval enabled(index_type i, BasicFsm& fsm) const final;
1229
1230 virtual Action const& action(index_type i) const noexcept final
1231 {
1232 zth_assert(i < size());
1233 return *m_transitions[i].action;
1234 }
1235
1236 virtual index_type to(index_type i) const noexcept final
1237 {
1238 zth_assert(i < size());
1239 return static_cast<index_type>(m_transitions[i].to);
1240 }
1241
1242 virtual State const& state(index_type i) const noexcept final
1243 {
1244 zth_assert(i < size());
1245 return m_transitions[i].from;
1246 }
1247
1248private:
1249 static constexpr size_t find(State const& state, std::initializer_list<Transition> l)
1250 {
1251 if(!state.valid())
1252 return 0;
1253
1254 size_t i = 0;
1255 for(auto const& t : l) {
1256 if(t.from().constexpr_eq(state))
1257 return i + 1;
1258 i++;
1259 }
1260
1261# if GCC_VERSION < 90000
1262 // This following if-statement is not required,
1263 // but triggers https://gcc.gnu.org/bugzilla/show_bug.cgi?id=67371 otherwise.
1264 if(i >= l.size())
1265# endif
1266 zth_throw(invalid_fsm{"Target state not found"});
1267
1268 // Unreachable.
1269 return Size + 1U;
1270 }
1271
1272private:
1273 static_assert(std::numeric_limits<Index>::max() > Size, "");
1274 CompiledTransition m_transitions[Size + 1U];
1275};
1276
1282template <typename... T>
1283constexpr auto compile(T&&... t)
1284{
1285 return Transitions<sizeof...(T)>::compile({Transition{std::forward<T>(t)}...});
1286}
1287
1295class BasicFsm : public UniqueID<BasicFsm> {
1297public:
1299
1304 enum class Flag : size_t {
1305 entry,
1306 selfloop,
1307 blocked,
1308 transition,
1309 pushed,
1310 popped,
1311 stop,
1312 input,
1313 flags,
1314 };
1315
1319 explicit BasicFsm(cow_string const& name = "FSM")
1320 : UniqueID{name}
1321 {
1322 zth_assert(std::atomic_is_lock_free(&m_state));
1323 }
1324
1329 : BasicFsm{}
1330 {
1331 *this = std::move(f);
1332 }
1333
1338 {
1339 m_fsm = f.m_fsm;
1340 m_flags = f.m_flags;
1341 m_prev = f.m_prev;
1342 m_transition = f.m_transition;
1343 state_(f.state_());
1344 m_stack = std::move(f.m_stack);
1345 m_inputs = std::move(f.m_inputs);
1346
1347 f.m_fsm = nullptr;
1348 f.state_(0);
1349
1350 UniqueID::operator=(std::move(f));
1351 return *this;
1352 }
1353
1357 virtual ~BasicFsm() override = default;
1358
1364 State const& state() const noexcept
1365 {
1366 zth_assert(valid());
1367 return m_fsm->state(state_());
1368 }
1369
1373 bool valid() const noexcept
1374 {
1375 return m_fsm;
1376 }
1377
1382 void reserveStack(size_t capacity)
1383 {
1384 if(m_stack.capacity() > capacity * 2U)
1385 m_stack.shrink_to_fit();
1386
1387 m_stack.reserve(capacity);
1388 }
1389
1393 virtual void reset() noexcept
1394 {
1395 zth_dbg(fsm, "[%s] Reset", id_str());
1396 state_(0);
1397 m_prev = m_transition = 0;
1398 m_flags.reset();
1399 m_stack.clear();
1400 }
1401
1405 State const& prev() const noexcept
1406 {
1407 zth_assert(valid());
1408 return m_fsm->state(m_prev);
1409 }
1410
1425 {
1426 zth_assert(valid());
1427
1428 auto i = state_();
1429 auto size = m_fsm->size();
1430
1431 // Find next enabled guard.
1432 GuardPollInterval again{false};
1434 while(!(p = m_fsm->enabled(i, *this))) {
1435 // Save the shortest poll interval.
1436 if(p < again)
1437 again = p;
1438
1439 if(++i == size || m_fsm->state(i).valid()) {
1440 // No enabled guards.
1442 zth_dbg(fsm, "[%s] Blocked for %s", id_str(), again.str().c_str());
1443 return again;
1444 }
1445 }
1446
1447 // Got it. Take transition.
1448 m_transition = i;
1449 auto to = m_fsm->to(i);
1450
1452
1453 if(!to) {
1454 // No transition, only do the action.
1455 if(m_fsm->hasInput(i))
1456 zth_dbg(fsm, "[%s] Have input %s, no transition", id_str(),
1457 m_fsm->input(i).str());
1458 else
1459 zth_dbg(fsm, "[%s] Guard %s enabled, no transition", id_str(),
1460 m_fsm->guard(i).name().c_str());
1462 setFlag(Flag::input, m_fsm->hasInput(i));
1463 enter();
1464 } else {
1465 if(m_fsm->hasInput(i))
1466 zth_dbg(fsm, "[%s] Have input %s, transition %s -> %s", id_str(),
1467 m_fsm->input(i).str(), m_fsm->state(state_()).str(),
1468 m_fsm->state(to).str());
1469 else
1470 zth_dbg(fsm, "[%s] Guard %s enabled, transition %s -> %s", id_str(),
1471 m_fsm->guard(i).name().c_str(),
1472 m_fsm->state(state_()).str(), m_fsm->state(to).str());
1473
1474 setFlag(Flag::selfloop, to == state_());
1476 if(state_())
1477 leave();
1478
1479 m_prev = state_();
1480 state_(to);
1481
1484 setFlag(Flag::input, m_fsm->hasInput(i));
1485 enter();
1486
1488 }
1489
1490 return true;
1491 }
1492
1502 void push()
1503 {
1504 zth_assert(valid());
1505
1506 m_stack.push_back(m_prev);
1508 zth_dbg(fsm, "[%s] Push %s", id_str(), m_fsm->state(state_()).str());
1509 }
1510
1523 virtual void pop()
1524 {
1525 zth_assert(valid());
1526 zth_assert(!m_stack.empty());
1527
1528 index_type to = m_stack.back();
1529 zth_assert(to != 0);
1530
1531 zth_dbg(fsm, "[%s] Pop %s -> %s", id_str(), m_fsm->state(state_()).str(),
1532 m_fsm->state(to).str());
1533
1535 setFlag(Flag::selfloop, state_() == to);
1536
1537 if(!flag(Flag::transition)) {
1538 // leave() was not called by step().
1540 leave();
1541 }
1542
1543 m_prev = state_();
1544 state_(to);
1545 m_stack.pop_back();
1546 m_transition = 0;
1547
1551 enter();
1552
1554 }
1555
1561 bool popped() const noexcept
1562 {
1563 return flag(Flag::popped);
1564 }
1565
1569 bool flag(Flag f) const noexcept
1570 {
1571 return m_flags.test(flagIndex(f));
1572 }
1573
1579 bool entry() noexcept
1580 {
1581 if(!flag(Flag::entry))
1582 return false;
1583
1585 return true;
1586 }
1587
1594 Symbol input() const noexcept
1595 {
1596 if(!flag(Flag::input))
1597 return Symbol{};
1598
1599 zth_assert(valid());
1600 zth_assert(m_fsm->hasInput(m_transition));
1601
1602 return m_fsm->input(m_transition);
1603 }
1604
1608 virtual void input(Symbol i)
1609 {
1610 if(!i.valid())
1611 return;
1612
1613 m_inputs.emplace_back(std::move(i));
1614 }
1615
1621 bool clearInput(Symbol i) noexcept
1622 {
1623 if(!i.valid())
1624 return false;
1625
1626 for(size_t j = 0; j < m_inputs.size(); j++)
1627 if(m_inputs[j] == i) {
1628 m_inputs[j] = std::move(m_inputs.back());
1629 m_inputs.pop_back();
1630 return true;
1631 }
1632
1633 return false;
1634 }
1635
1643 void clearInput() noexcept
1644 {
1645 clearInput(input());
1646 }
1647
1651 void clearInputs() noexcept
1652 {
1653 m_inputs.clear();
1654 }
1655
1661 void reserveInputs(size_t capacity)
1662 {
1663 if(m_inputs.capacity() > capacity * 2U)
1664 m_inputs.shrink_to_fit();
1665
1666 m_inputs.reserve(capacity);
1667 }
1668
1673 bool hasInput(Symbol i) const noexcept
1674 {
1675 if(!i.valid())
1676 return false;
1677
1678 for(size_t j = 0; j < m_inputs.size(); j++)
1679 if(m_inputs[j] == i)
1680 return true;
1681
1682 return false;
1683 }
1684
1685protected:
1689 bool setFlag(Flag f, bool value = true) noexcept
1690 {
1691 m_flags.set(flagIndex(f), value);
1692 return value;
1693 }
1694
1700 void clearFlag(Flag f) noexcept
1701 {
1702 setFlag(f, false);
1703 }
1704
1708 virtual void leave()
1709 {
1710 zth_dbg(fsm, "[%s] Leave %s", id_str(), state().str());
1711 }
1712
1720 virtual void enter()
1721 {
1722 if(m_transition) {
1723 zth_dbg(fsm, "[%s] Enter %s%s; run action %s", id_str(), state().str(),
1724 flag(Flag::selfloop) ? " (loop)" : "",
1725 m_fsm->action(m_transition).name().c_str());
1726
1727 m_fsm->action(m_transition).run(*this);
1728 } else {
1729 zth_dbg(fsm, "[%s] Enter %s (dummy transition)", id_str(), state().str());
1730 }
1731 }
1732
1733private:
1737 static constexpr size_t flagIndex(Flag f) noexcept
1738 {
1739 return static_cast<size_t>(f);
1740 }
1741
1745 void init(TransitionsBase const& c) noexcept
1746 {
1747 if(c.size() == 0)
1748 // Invalid.
1749 return;
1750
1751 m_fsm = &c;
1752 reset();
1753 }
1754
1755 friend TransitionsBase;
1756
1760 index_type state_() const noexcept
1761 {
1762 return m_state.load(std::memory_order_relaxed);
1763 }
1764
1768 void state_(index_type s) noexcept
1769 {
1770 return m_state.store(s, std::memory_order_relaxed);
1771 }
1772
1773private:
1775 TransitionsBase const* m_fsm{};
1777 std::bitset<static_cast<size_t>(Flag::flags)> m_flags;
1779 index_type m_prev{};
1781 index_type m_transition{};
1783 std::atomic<index_type> m_state{};
1785 vector_type<index_type>::type m_stack;
1788};
1789
1798class Fsm : public BasicFsm {
1800public:
1802
1806 explicit Fsm(cow_string const& name = "FSM")
1807 : base{name}
1808 {}
1809
1814 : Fsm{}
1815 {
1816 *this = std::move(f);
1817 }
1818
1822 Fsm& operator=(Fsm&& f) noexcept
1823 {
1824 base::operator=(std::move(f));
1825 return *this;
1826 }
1827
1831 virtual ~Fsm() override = default;
1832
1833 virtual void reset() noexcept override
1834 {
1835 base::reset();
1836 m_t = Timestamp::now();
1837 trigger();
1838 }
1839
1848 {
1849 zth_dbg(fsm, "[%s] Run for %s", id_str(), (until - Timestamp::now()).str().c_str());
1851
1852 while(true) {
1853 GuardPollInterval p = step();
1854
1855 if(flag(Flag::stop))
1856 // Return now, but we could continue anyway.
1857 return p;
1858
1859 auto now = Timestamp::now();
1860 if(now > until)
1861 return p;
1862
1863 if(p)
1864 continue;
1865
1866 auto p_end = now + p;
1867 m_trigger.wait(std::min(p_end, until), now);
1868 }
1869 }
1870
1878 GuardPollInterval run(bool returnWhenBlocked = false)
1879 {
1880 zth_dbg(fsm, "[%s] Run%s", id_str(), returnWhenBlocked ? " until blocked" : "");
1882
1883 while(true) {
1884 GuardPollInterval p = step();
1885
1886 if(flag(Flag::stop))
1887 // Return now, but we could continue anyway.
1888 return p;
1889
1890 if(p)
1891 continue;
1892
1893 if(returnWhenBlocked)
1894 // cppcheck-suppress identicalConditionAfterEarlyExit
1895 return p;
1896
1897 m_trigger.wait(p);
1898 }
1899 }
1900
1908 void trigger() noexcept
1909 {
1910 m_trigger.signal(false);
1911 }
1912
1919 void stop() noexcept
1920 {
1921 zth_dbg(fsm, "[%s] Stop requested", id_str());
1923 }
1924
1931 virtual void input(Symbol i) override
1932 {
1933 if(!i.valid())
1934 return;
1935
1936 base::input(i);
1937 trigger();
1938 }
1939
1946 Timestamp const& t() const noexcept
1947 {
1948 return m_t;
1949 }
1950
1956 {
1957 return t().passed();
1958 }
1959
1963 template <time_t s>
1965 {
1966 return TimeInterval{s} - fsm.dt();
1967 }
1968
1972 template <uint64_t ms>
1974 {
1975 return TimeInterval{
1976 (time_t)(ms / 1'000ULL), (long)(ms * 1'000'000ULL) % 1'000'000'000L}
1977 - fsm.dt();
1978 }
1979
1983 template <uint64_t us>
1985 {
1986 return TimeInterval{
1987 (time_t)(us / 1'000'000ULL), (long)(us * 1'000ULL) % 1'000'000'000L}
1988 - fsm.dt();
1989 }
1990
1991protected:
1999 virtual void enter() override
2000 {
2002 m_t = Timestamp::now();
2003
2004 base::enter();
2005 }
2006
2007private:
2009 Signal m_trigger;
2011 Timestamp m_t;
2012};
2013
2019inline17 constexpr auto entry = guard(&BasicFsm::entry, "entry");
2020
2027inline17 constexpr auto push = action(&BasicFsm::push, "push");
2028
2034inline17 constexpr auto pop = action(&BasicFsm::pop, "pop");
2035
2041inline17 constexpr auto popped = guard(&BasicFsm::popped, "popped");
2042
2048inline17 constexpr auto stop = action(&Fsm::stop, "stop");
2049
2061inline17 constexpr auto consume =
2062 action<void (BasicFsm::*)() noexcept>(&BasicFsm::clearInput, "consume");
2063
2070template <time_t s>
2071inline17 constexpr auto timeout_s = guard(&Fsm::timeoutGuard_s<s>, "timeout");
2072
2079template <uint64_t ms>
2080inline17 constexpr auto timeout_ms = guard(&Fsm::timeoutGuard_ms<ms>, "timeout");
2081
2088template <uint64_t us>
2089inline17 constexpr auto timeout_us = guard(&Fsm::timeoutGuard_us<us>, "timeout");
2090
2092{
2093 return fsm.hasInput(m_input);
2094}
2095
2097{
2098 if(isInput())
2099 return fsm.hasInput(input());
2100 else
2101 return m_guard.enabled(fsm);
2102}
2103
2104template <size_t Size>
2106{
2107 if(isInput(i))
2108 return fsm.hasInput(input(i));
2109 else
2110 return guard(i).enabled(fsm);
2111}
2112
2114{
2115 Fsm fsm_;
2116 fsm_.init(*this);
2117 return fsm_;
2118}
2119
2120} // namespace fsm
2121} // namespace zth
2122#endif // C++14
2123#endif // ZTH_FSM14_H
Fiber-aware signal.
Definition sync.h:512
void signal(bool queue=true, bool queueEveryTime=false) noexcept
Definition sync.h:565
void wait()
Definition sync.h:529
Convenient wrapper around struct timespec that contains a time interval.
Definition time.h:55
static constexpr TimeInterval null() noexcept
Definition time.h:69
static constexpr TimeInterval infinity() noexcept
Definition time.h:64
constexpr bool hasPassed() const noexcept
Definition time.h:261
Convenient wrapper around struct timespec that contains an absolute timestamp.
Definition time.h:568
static Timestamp now()
Definition time.h:595
TimeInterval passed() const
Definition time.h:661
friend cow_string str(UniqueIDBase const &)
Keeps track of a process-wide unique ID within the type T.
Definition util.h:715
virtual char const * id_str() const override
Definition util.h:809
string const & name() const noexcept
Definition util.h:783
UniqueID & operator=(UniqueID const &)=delete
Copy-on-write string.
Definition util.h:319
char const * c_str() const
Definition util.h:391
constexpr Action()=default
~Action()=default
void operator()(BasicFsm &fsm) const
Definition fsm14.h:603
virtual void run(BasicFsm &fsm) const
Definition fsm14.h:596
Action(Action &&) noexcept=default
virtual cow_string name() const
Definition fsm14.h:598
Basic FSM base class.
Definition fsm14.h:1295
void push()
Push the current state onto the state stack.
Definition fsm14.h:1502
TransitionsBase::index_type index_type
Definition fsm14.h:1298
Flag
Flags that indicate properties of the current state.
Definition fsm14.h:1304
@ 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:1708
void reserveInputs(size_t capacity)
Reserve memory for the given amount of input symbols.
Definition fsm14.h:1661
Symbol input() const noexcept
Return the input symbol that triggered the transition to the current state.
Definition fsm14.h:1594
bool setFlag(Flag f, bool value=true) noexcept
Set or clear the given flag.
Definition fsm14.h:1689
virtual ~BasicFsm() override=default
Dtor.
virtual void enter()
Called when the current state was just entered.
Definition fsm14.h:1720
BasicFsm(BasicFsm &&f)
Move ctor.
Definition fsm14.h:1328
bool flag(Flag f) const noexcept
Check if the given flag is set.
Definition fsm14.h:1569
void clearInput() noexcept
Clear the input symbol that triggered the current state.
Definition fsm14.h:1643
bool valid() const noexcept
Checks if the FSM was initialized.
Definition fsm14.h:1373
virtual void pop()
Pop the previous state from the state stack.
Definition fsm14.h:1523
void clearInputs() noexcept
Clear all inputs.
Definition fsm14.h:1651
bool popped() const noexcept
Check if the current state was reached via pop().
Definition fsm14.h:1561
State const & prev() const noexcept
Return the previous state.
Definition fsm14.h:1405
void clearFlag(Flag f) noexcept
Set the given flag.
Definition fsm14.h:1700
bool clearInput(Symbol i) noexcept
Remove the given input symbol.
Definition fsm14.h:1621
State const & state() const noexcept
Return the current state.
Definition fsm14.h:1364
bool entry() noexcept
Check if the state was just entered.
Definition fsm14.h:1579
BasicFsm(cow_string const &name="FSM")
Ctor.
Definition fsm14.h:1319
void reserveStack(size_t capacity)
Reserve memory to push() a amount of states.
Definition fsm14.h:1382
GuardPollInterval step()
Try to take one step in the state machine.
Definition fsm14.h:1424
virtual void input(Symbol i)
Register the given input symbol.
Definition fsm14.h:1608
BasicFsm & operator=(BasicFsm &&f) noexcept
Move assignment.
Definition fsm14.h:1337
virtual void reset() noexcept
Reset the state machine.
Definition fsm14.h:1393
bool hasInput(Symbol i) const noexcept
Checks if the given input symbol was registered before.
Definition fsm14.h:1673
constexpr Callback(T_ &&c, char const *name=nullptr)
Definition fsm14.h:339
constexpr Callback(T_ &&c, char const *name=nullptr)
Definition fsm14.h:401
constexpr Callback(T_ &&c, char const *name=nullptr)
Definition fsm14.h:360
FSM base class.
Definition fsm14.h:1798
Timestamp const & t() const noexcept
Returns the time stamp when the current state was entered.
Definition fsm14.h:1946
virtual void enter() override
Called when the current state was just entered.
Definition fsm14.h:1999
virtual void input(Symbol i) override
Register the given input symbol.
Definition fsm14.h:1931
GuardPollInterval run(Timestamp const &until)
Run the state machine until the given time stamp.
Definition fsm14.h:1847
static GuardPollInterval timeoutGuard_us(Fsm &fsm)
Callback function for the zth::fsm::timeout_us guard.
Definition fsm14.h:1984
void stop() noexcept
Interrupt a running FSM.
Definition fsm14.h:1919
void trigger() noexcept
Trigger a currently running FSM to reevaluate the guards immediately.
Definition fsm14.h:1908
GuardPollInterval run(bool returnWhenBlocked=false)
Run the state machine.
Definition fsm14.h:1878
static GuardPollInterval timeoutGuard_ms(Fsm &fsm)
Callback function for the zth::fsm::timeout_ms guard.
Definition fsm14.h:1973
TimeInterval dt() const
Returns the time since the current state was entered.
Definition fsm14.h:1955
virtual ~Fsm() override=default
Dtor.
virtual void reset() noexcept override
Reset the state machine.
Definition fsm14.h:1833
Fsm(Fsm &&f)
Move ctor.
Definition fsm14.h:1813
static GuardPollInterval timeoutGuard_s(Fsm &fsm)
Callback function for the zth::fsm::timeout_s guard.
Definition fsm14.h:1964
Fsm & operator=(Fsm &&f) noexcept
Move assignment.
Definition fsm14.h:1822
Fsm(cow_string const &name="FSM")
Ctor.
Definition fsm14.h:1806
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:424
constexpr GuardPollInterval(GuardPollInterval &&) noexcept=default
constexpr GuardedActionBase()=default
GuardPollInterval operator()(BasicFsm &fsm) const
Definition fsm14.h:682
virtual GuardPollInterval tryRun(BasicFsm &fsm) const
Definition fsm14.h:672
constexpr bool hasGuard() const noexcept
Definition fsm14.h:726
virtual cow_string name() const final
Definition fsm14.h:754
constexpr GuardedAction(Guard const &guard, Action const &action) noexcept
Definition fsm14.h:691
constexpr GuardedAction(Action const &action) noexcept
Definition fsm14.h:713
constexpr GuardedAction(Symbol &&input) noexcept
Definition fsm14.h:708
constexpr auto const & guard() const noexcept
Definition fsm14.h:738
constexpr GuardedAction() noexcept
Definition fsm14.h:717
virtual GuardPollInterval enabled(BasicFsm &fsm) const final
Definition fsm14.h:2096
constexpr bool isInput() const noexcept
Definition fsm14.h:721
constexpr GuardedAction(Symbol &&input, Action const &action) noexcept
Definition fsm14.h:696
constexpr auto const & action() const noexcept
Definition fsm14.h:744
constexpr Symbol const & input() const noexcept
Definition fsm14.h:731
virtual void run(BasicFsm &fsm) const final
Definition fsm14.h:749
constexpr GuardedAction(Guard const &guard) noexcept
Definition fsm14.h:703
constexpr InputGuard(Symbol &&input)
Definition fsm14.h:491
virtual GuardPollInterval enabled(BasicFsm &fsm) const final
Definition fsm14.h:2091
virtual cow_string name() const final
Definition fsm14.h:497
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:306
constexpr Named(char const *name) noexcept
Definition fsm14.h:298
A input/state symbol.
Definition fsm14.h:164
constexpr char const * symbol() const noexcept
Return the symbol string.
Definition fsm14.h:229
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 char const * str() const noexcept
Return a string representation of this symbol.
Definition fsm14.h:220
constexpr bool valid() const noexcept
Check if the symbol is valid.
Definition fsm14.h:245
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 auto const & action() const noexcept
Definition fsm14.h:986
virtual GuardPollInterval enabled(BasicFsm &fsm) const final
Definition fsm14.h:951
constexpr auto const & guard() const noexcept
Definition fsm14.h:981
constexpr Symbol const & input() const noexcept
Definition fsm14.h:976
virtual cow_string name() const final
Definition fsm14.h:996
constexpr bool isInput() const noexcept
Definition fsm14.h:966
virtual GuardPollInterval tryRun(BasicFsm &fsm) const final
Definition fsm14.h:956
constexpr bool hasGuard() const noexcept
Definition fsm14.h:971
constexpr Transition(F &&from, T &&to) noexcept
Definition fsm14.h:946
constexpr auto const & from() const noexcept
Definition fsm14.h:961
constexpr auto const & to() const noexcept
Definition fsm14.h:991
constexpr Transition(F &&from) noexcept
Definition fsm14.h:940
constexpr auto const & action() const noexcept
Definition fsm14.h:865
constexpr TransitionStart(GuardedAction &&ga) noexcept
Definition fsm14.h:830
constexpr TransitionStart(State &&state, GuardedAction &&guardedAction) noexcept
Definition fsm14.h:835
constexpr auto const & guard() const noexcept
Definition fsm14.h:860
virtual GuardPollInterval tryRun(BasicFsm &fsm) const final
Definition fsm14.h:875
constexpr auto const & state() const noexcept
Definition fsm14.h:840
constexpr bool hasGuard() const noexcept
Definition fsm14.h:850
constexpr bool isInput() const noexcept
Definition fsm14.h:845
constexpr Symbol const & input() const noexcept
Definition fsm14.h:855
constexpr TransitionStart(State &&state) noexcept
Definition fsm14.h:812
constexpr TransitionStart(Guard const &guard) noexcept
Definition fsm14.h:818
constexpr TransitionStart(Action const &action) noexcept
Definition fsm14.h:824
virtual cow_string name() const override
Definition fsm14.h:880
virtual GuardPollInterval enabled(BasicFsm &fsm) const final
Definition fsm14.h:870
F & init(F &fsm) const noexcept
Definition fsm14.h:1059
virtual bool hasInput(index_type i) const noexcept
Definition fsm14.h:1044
virtual GuardPollInterval enabled(index_type i, BasicFsm &fsm) const =0
virtual size_t size() const noexcept=0
virtual Guard const & guard(index_type i) const noexcept=0
void uml(FILE *f=stdout) const
Definition fsm14.h:1076
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:1065
virtual Action const & action(index_type i) const noexcept=0
virtual State const & state(index_type i) const noexcept=0
virtual Action const & action(index_type i) const noexcept final
Definition fsm14.h:1230
virtual index_type to(index_type i) const noexcept final
Definition fsm14.h:1236
virtual State const & state(index_type i) const noexcept final
Definition fsm14.h:1242
virtual size_t size() const noexcept final
Definition fsm14.h:1199
constexpr Transitions()=default
static constexpr Transitions compile(std::initializer_list< Transition > l)
Definition fsm14.h:1157
virtual Symbol input(index_type i) const noexcept final
Definition fsm14.h:1221
typename smallest_uint< Size >::type Index
Definition fsm14.h:1128
virtual GuardPollInterval enabled(index_type i, BasicFsm &fsm) const final
Definition fsm14.h:2105
virtual bool isInput(index_type i) const noexcept final
Definition fsm14.h:1204
virtual Guard const & guard(index_type i) const noexcept final
Definition fsm14.h:1214
virtual bool hasGuard(index_type i) const noexcept final
Definition fsm14.h:1209
constexpr TypedAction(T_ &&a, char const *name=nullptr)
Definition fsm14.h:616
virtual void run(BasicFsm &fsm) const final
Definition fsm14.h:620
virtual cow_string name() const final
Definition fsm14.h:625
virtual cow_string name() const final
Definition fsm14.h:482
virtual GuardPollInterval enabled(BasicFsm &fsm) const final
Definition fsm14.h:477
constexpr TypedGuard(T_ &&g, char const *name=nullptr)
Definition fsm14.h:473
constexpr auto consume
Action consume the current input symbol.
Definition fsm14.h:2061
constexpr auto never
Trivial guard that is never enabled.
Definition fsm14.h:582
constexpr auto guard(T &&g, char const *name=nullptr)
Create a guard from a function.
Definition fsm14.h:536
constexpr auto entry
Guard that is only enabled upon entry of a state.
Definition fsm14.h:2019
constexpr auto timeout_us
A guard that is enabled after a us microseconds after entering the current state.
Definition fsm14.h:2089
constexpr auto nothing
Trivial action that does nothing.
Definition fsm14.h:665
constexpr auto action(T &&a, char const *name=nullptr)
Create an action from a function.
Definition fsm14.h:653
constexpr auto timeout_s
A guard that is enabled after a s seconds after entering the current state.
Definition fsm14.h:2071
constexpr auto pop
Action to pop the current state from the stack.
Definition fsm14.h:2034
constexpr auto push
Action to push the new state onto the stack.
Definition fsm14.h:2027
constexpr auto stop
Action to return from Fsm::run().
Definition fsm14.h:2048
constexpr auto input(Symbol &&symbol) noexcept
Create a guard from an input symbol.
Definition fsm14.h:786
constexpr auto popped
Guard to indicate that the current state was reached via pop.
Definition fsm14.h:2041
constexpr auto timeout_ms
A guard that is enabled after a ms milliseconds after entering the current state.
Definition fsm14.h:2080
constexpr auto always
Trivial guard that is always enabled.
Definition fsm14.h:570
constexpr auto compile(T &&... t)
Compile a transition description.
Definition fsm14.h:1283
#define zth_dbg(group, fmt, a...)
Debug printf()-like function.
Definition util.h:189
#define ZTH_CLASS_NEW_DELETE(T)
Define new/delete operators for a class, which are allocator-aware.
Definition allocator.h:143
#define zth_throw(...)
Definition macros.h:229
#define UNUSED_PAR(name)
Definition macros.h:78
STL namespace.
constexpr auto operator/(Guard const &g, Action const &a) noexcept
Definition fsm14.h:767
void nothing_action()
Definition fsm14.h:658
constexpr auto operator+(Symbol &&input) noexcept
Definition fsm14.h:772
bool never_guard() noexcept
Definition fsm14.h:572
constexpr Transition operator>>=(TransitionStart &&from, State &&to) noexcept
Definition fsm14.h:1006
bool always_guard() noexcept
Definition fsm14.h:557
cow_string str(T value)
Returns an zth::string representation of the given value.
Definition util.h:492
string format(char const *fmt,...)
Format like sprintf(), but save the result in an zth::string.
Definition util.h:478
constexpr CompiledTransition() noexcept
Definition fsm14.h:1135
Exception thrown when the FSM description is incorrect.
Definition fsm14.h:151
invalid_fsm(char const *str)
Definition fsm14.h:152
std::vector< T, typename Config::Allocator< T >::type > type
Definition allocator.h:174
#define zth_assert(expr)
assert(), but better integrated in Zth.
Definition util.h:212