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