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 if(l.size() != Size)
1162 zth_throw(invalid_fsm{"Invalid number of transitions"});
1163
1164 Transitions f;
1165
1166 if(Size == 0) {
1167 f.m_transitions[0].guard = &never;
1168 f.m_transitions[0].action = &nothing;
1169 } else {
1170 f.m_transitions[0].guard = &always;
1171 f.m_transitions[0].action = &nothing;
1172 f.m_transitions[0].to = 1;
1173
1174 if(!l.begin()->from().valid())
1175 zth_throw(invalid_fsm{"First state must be valid"});
1176 }
1177
1178 index_type i = 1;
1179 State prev;
1180
1181 for(auto const& t : l) {
1182 if(i > Size)
1183 zth_throw(invalid_fsm{"State transitions exceed table size"});
1184
1185 if(!prev.constexpr_eq(t.from()) && t.from().valid()) {
1186 prev = f.m_transitions[i].from = t.from();
1187 if(find(t.from(), l) != (size_t)i)
1189 "State transitions are not contiguous"});
1190 }
1191 if(t.hasGuard()) {
1192 f.m_transitions[i].guard = &t.guard();
1193 } else {
1194 f.m_transitions[i].guard = t.input().symbol();
1195 f.m_transitions[i].flags = static_cast<uint8_t>(Flag::input);
1196 }
1197 f.m_transitions[i].action = &t.action();
1198 f.m_transitions[i].to = static_cast<Index>(find(t.to(), l));
1199 i++;
1200 }
1201
1202 return f;
1203 }
1204
1205 virtual size_t size() const noexcept final
1206 {
1207 return Size + 1U;
1208 }
1209
1210 virtual bool isInput(index_type i) const noexcept final
1211 {
1212 return (m_transitions[i].flags & static_cast<uint8_t>(Flag::input)) != 0;
1213 }
1214
1215 virtual bool hasGuard(index_type i) const noexcept final
1216 {
1217 return !isInput(i);
1218 }
1219
1220 virtual Guard const& guard(index_type i) const noexcept final
1221 {
1222 zth_assert(i < size());
1223 zth_assert(hasGuard(i));
1224 return *static_cast<Guard const*>(m_transitions[i].guard);
1225 }
1226
1227 virtual Symbol input(index_type i) const noexcept final
1228 {
1229 zth_assert(i < size());
1230 zth_assert(!hasGuard(i));
1231 return static_cast<char const*>(m_transitions[i].guard);
1232 }
1233
1234 virtual GuardPollInterval enabled(index_type i, BasicFsm& fsm) const final;
1235
1236 virtual Action const& action(index_type i) const noexcept final
1237 {
1238 zth_assert(i < size());
1239 return *m_transitions[i].action;
1240 }
1241
1242 virtual index_type to(index_type i) const noexcept final
1243 {
1244 zth_assert(i < size());
1245 return static_cast<index_type>(m_transitions[i].to);
1246 }
1247
1248 virtual State const& state(index_type i) const noexcept final
1249 {
1250 zth_assert(i < size());
1251 return m_transitions[i].from;
1252 }
1253
1254private:
1255 static constexpr size_t find(State const& state, std::initializer_list<Transition> l)
1256 {
1257 if(!state.valid())
1258 return 0;
1259
1260 size_t i = 0;
1261 for(auto const& t : l) {
1262 if(t.from().constexpr_eq(state))
1263 return i + 1;
1264 i++;
1265 }
1266
1267# if GCC_VERSION < 90000
1268 // This following if-statement is not required,
1269 // but triggers https://gcc.gnu.org/bugzilla/show_bug.cgi?id=67371 otherwise.
1270 if(i >= l.size())
1271# endif
1272 zth_throw(invalid_fsm{"Target state not found"});
1273
1274 // Unreachable.
1275 return Size + 1U;
1276 }
1277
1278private:
1279 static_assert(std::numeric_limits<Index>::max() > Size, "");
1280 CompiledTransition m_transitions[Size + 1U];
1281};
1282
1288template <typename... T>
1289constexpr auto compile(T&&... t)
1290{
1291 return Transitions<sizeof...(T)>::compile({Transition{std::forward<T>(t)}...});
1292}
1293
1301class BasicFsm : public UniqueID<BasicFsm> {
1303public:
1305
1310 enum class Flag : size_t {
1311 entry,
1312 selfloop,
1313 blocked,
1314 transition,
1315 pushed,
1316 popped,
1317 stop,
1318 input,
1319 flags,
1320 };
1321
1325 explicit BasicFsm(cow_string const& name = "FSM")
1326 : UniqueID{name}
1327 {
1328 zth_assert(std::atomic_is_lock_free(&m_state));
1329 }
1330
1335 : BasicFsm{}
1336 {
1337 *this = std::move(f);
1338 }
1339
1344 {
1345 m_fsm = f.m_fsm;
1346 m_flags = f.m_flags;
1347 m_prev = f.m_prev;
1348 m_transition = f.m_transition;
1349 state_(f.state_());
1350 m_stack = std::move(f.m_stack);
1351 m_inputs = std::move(f.m_inputs);
1352
1353 f.m_fsm = nullptr;
1354 f.state_(0);
1355
1356 UniqueID::operator=(std::move(f));
1357 return *this;
1358 }
1359
1363 virtual ~BasicFsm() override = default;
1364
1370 State const& state() const noexcept
1371 {
1372 zth_assert(valid());
1373 return m_fsm->state(state_());
1374 }
1375
1379 bool valid() const noexcept
1380 {
1381 return m_fsm;
1382 }
1383
1388 void reserveStack(size_t capacity)
1389 {
1390 if(m_stack.capacity() > capacity * 2U)
1391 m_stack.shrink_to_fit();
1392
1393 m_stack.reserve(capacity);
1394 }
1395
1399 virtual void reset() noexcept
1400 {
1401 zth_dbg(fsm, "[%s] Reset", id_str());
1402 state_(0);
1403 m_prev = m_transition = 0;
1404 m_flags.reset();
1405 m_stack.clear();
1406 }
1407
1411 State const& prev() const noexcept
1412 {
1413 zth_assert(valid());
1414 return m_fsm->state(m_prev);
1415 }
1416
1431 {
1432 zth_assert(valid());
1433
1434 auto i = state_();
1435 auto size = m_fsm->size();
1436
1437 // Find next enabled guard.
1438 GuardPollInterval again{false};
1440 while(!(p = m_fsm->enabled(i, *this))) {
1441 // Save the shortest poll interval.
1442 if(p < again)
1443 again = p;
1444
1445 if(++i == size || m_fsm->state(i).valid()) {
1446 // No enabled guards.
1448 zth_dbg(fsm, "[%s] Blocked for %s", id_str(), again.str().c_str());
1449 return again;
1450 }
1451 }
1452
1453 // Got it. Take transition.
1454 m_transition = i;
1455 auto to = m_fsm->to(i);
1456
1458
1459 if(!to) {
1460 // No transition, only do the action.
1461 if(m_fsm->hasInput(i))
1462 zth_dbg(fsm, "[%s] Have input %s, no transition", id_str(),
1463 m_fsm->input(i).str());
1464 else
1465 zth_dbg(fsm, "[%s] Guard %s enabled, no transition", id_str(),
1466 m_fsm->guard(i).name().c_str());
1468 setFlag(Flag::input, m_fsm->hasInput(i));
1469 enter();
1470 } else {
1471 if(m_fsm->hasInput(i))
1472 zth_dbg(fsm, "[%s] Have input %s, transition %s -> %s", id_str(),
1473 m_fsm->input(i).str(), m_fsm->state(state_()).str(),
1474 m_fsm->state(to).str());
1475 else
1476 zth_dbg(fsm, "[%s] Guard %s enabled, transition %s -> %s", id_str(),
1477 m_fsm->guard(i).name().c_str(),
1478 m_fsm->state(state_()).str(), m_fsm->state(to).str());
1479
1480 setFlag(Flag::selfloop, to == state_());
1482 if(state_())
1483 leave();
1484
1485 m_prev = state_();
1486 state_(to);
1487
1490 setFlag(Flag::input, m_fsm->hasInput(i));
1491 enter();
1492
1494 }
1495
1496 return true;
1497 }
1498
1508 void push()
1509 {
1510 zth_assert(valid());
1511
1512 m_stack.push_back(m_prev);
1514 zth_dbg(fsm, "[%s] Push %s", id_str(), m_fsm->state(state_()).str());
1515 }
1516
1529 virtual void pop()
1530 {
1531 zth_assert(valid());
1532 zth_assert(!m_stack.empty());
1533
1534 index_type to = m_stack.back();
1535 zth_assert(to != 0);
1536
1537 zth_dbg(fsm, "[%s] Pop %s -> %s", id_str(), m_fsm->state(state_()).str(),
1538 m_fsm->state(to).str());
1539
1541 setFlag(Flag::selfloop, state_() == to);
1542
1543 if(!flag(Flag::transition)) {
1544 // leave() was not called by step().
1546 leave();
1547 }
1548
1549 m_prev = state_();
1550 state_(to);
1551 m_stack.pop_back();
1552 m_transition = 0;
1553
1557 enter();
1558
1560 }
1561
1567 bool popped() const noexcept
1568 {
1569 return flag(Flag::popped);
1570 }
1571
1575 bool flag(Flag f) const noexcept
1576 {
1577 return m_flags.test(flagIndex(f));
1578 }
1579
1585 bool entry() noexcept
1586 {
1587 if(!flag(Flag::entry))
1588 return false;
1589
1591 return true;
1592 }
1593
1600 Symbol input() const noexcept
1601 {
1602 if(!flag(Flag::input))
1603 return Symbol{};
1604
1605 zth_assert(valid());
1606 zth_assert(m_fsm->hasInput(m_transition));
1607
1608 return m_fsm->input(m_transition);
1609 }
1610
1614 virtual void input(Symbol i)
1615 {
1616 if(!i.valid())
1617 return;
1618
1619 m_inputs.emplace_back(std::move(i));
1620 }
1621
1627 bool clearInput(Symbol i) noexcept
1628 {
1629 if(!i.valid())
1630 return false;
1631
1632 for(size_t j = 0; j < m_inputs.size(); j++)
1633 if(m_inputs[j] == i) {
1634 m_inputs[j] = std::move(m_inputs.back());
1635 m_inputs.pop_back();
1636 return true;
1637 }
1638
1639 return false;
1640 }
1641
1649 void clearInput() noexcept
1650 {
1651 clearInput(input());
1652 }
1653
1657 void clearInputs() noexcept
1658 {
1659 m_inputs.clear();
1660 }
1661
1667 void reserveInputs(size_t capacity)
1668 {
1669 if(m_inputs.capacity() > capacity * 2U)
1670 m_inputs.shrink_to_fit();
1671
1672 m_inputs.reserve(capacity);
1673 }
1674
1679 bool hasInput(Symbol i) const noexcept
1680 {
1681 if(!i.valid())
1682 return false;
1683
1684 for(size_t j = 0; j < m_inputs.size(); j++)
1685 if(m_inputs[j] == i)
1686 return true;
1687
1688 return false;
1689 }
1690
1691protected:
1695 bool setFlag(Flag f, bool value = true) noexcept
1696 {
1697 m_flags.set(flagIndex(f), value);
1698 return value;
1699 }
1700
1706 void clearFlag(Flag f) noexcept
1707 {
1708 setFlag(f, false);
1709 }
1710
1714 virtual void leave()
1715 {
1716 zth_dbg(fsm, "[%s] Leave %s", id_str(), state().str());
1717 }
1718
1726 virtual void enter()
1727 {
1728 if(m_transition) {
1729 zth_dbg(fsm, "[%s] Enter %s%s; run action %s", id_str(), state().str(),
1730 flag(Flag::selfloop) ? " (loop)" : "",
1731 m_fsm->action(m_transition).name().c_str());
1732
1733 m_fsm->action(m_transition).run(*this);
1734 } else {
1735 zth_dbg(fsm, "[%s] Enter %s (dummy transition)", id_str(), state().str());
1736 }
1737 }
1738
1739private:
1743 static constexpr size_t flagIndex(Flag f) noexcept
1744 {
1745 return static_cast<size_t>(f);
1746 }
1747
1751 void init(TransitionsBase const& c) noexcept
1752 {
1753 if(c.size() == 0)
1754 // Invalid.
1755 return;
1756
1757 m_fsm = &c;
1758 reset();
1759 }
1760
1761 friend TransitionsBase;
1762
1766 index_type state_() const noexcept
1767 {
1768 return m_state.load(std::memory_order_relaxed);
1769 }
1770
1774 void state_(index_type s) noexcept
1775 {
1776 return m_state.store(s, std::memory_order_relaxed);
1777 }
1778
1779private:
1781 TransitionsBase const* m_fsm{};
1783 std::bitset<static_cast<size_t>(Flag::flags)> m_flags;
1785 index_type m_prev{};
1787 index_type m_transition{};
1789 std::atomic<index_type> m_state{};
1791 vector_type<index_type>::type m_stack;
1794};
1795
1804class Fsm : public BasicFsm {
1806public:
1808
1812 explicit Fsm(cow_string const& name = "FSM")
1813 : base{name}
1814 {}
1815
1820 : Fsm{}
1821 {
1822 *this = std::move(f);
1823 }
1824
1828 Fsm& operator=(Fsm&& f) noexcept
1829 {
1830 base::operator=(std::move(f));
1831 return *this;
1832 }
1833
1837 virtual ~Fsm() override = default;
1838
1839 virtual void reset() noexcept override
1840 {
1841 base::reset();
1842 m_t = Timestamp::now();
1843 trigger();
1844 }
1845
1854 {
1855 zth_dbg(fsm, "[%s] Run for %s", id_str(), (until - Timestamp::now()).str().c_str());
1857
1858 while(true) {
1859 GuardPollInterval p = step();
1860
1861 if(flag(Flag::stop))
1862 // Return now, but we could continue anyway.
1863 return p;
1864
1865 auto t_now = Timestamp::now();
1866 if(t_now > until)
1867 return p;
1868
1869 if(p)
1870 continue;
1871
1872 auto p_end = t_now + p;
1873 m_trigger.wait(std::min(p_end, until), t_now);
1874 }
1875 }
1876
1884 GuardPollInterval run(bool returnWhenBlocked = false)
1885 {
1886 zth_dbg(fsm, "[%s] Run%s", id_str(), returnWhenBlocked ? " until blocked" : "");
1888
1889 while(true) {
1890 GuardPollInterval p = step();
1891
1892 if(flag(Flag::stop))
1893 // Return now, but we could continue anyway.
1894 return p;
1895
1896 if(p)
1897 continue;
1898
1899 if(returnWhenBlocked)
1900 // cppcheck-suppress identicalConditionAfterEarlyExit
1901 return p;
1902
1903 m_trigger.wait(p);
1904 }
1905 }
1906
1914 void trigger() noexcept
1915 {
1916 m_trigger.signal(false);
1917 }
1918
1925 void stop() noexcept
1926 {
1927 zth_dbg(fsm, "[%s] Stop requested", id_str());
1929 }
1930
1937 virtual void input(Symbol i) override
1938 {
1939 if(!i.valid())
1940 return;
1941
1942 base::input(i);
1943 trigger();
1944 }
1945
1952 Timestamp const& t() const noexcept
1953 {
1954 return m_t;
1955 }
1956
1962 {
1963 return t().passed();
1964 }
1965
1969 template <time_t s>
1971 {
1972 return TimeInterval{s} - fsm.dt();
1973 }
1974
1978 template <uint64_t ms>
1980 {
1981 return TimeInterval{
1982 (time_t)(ms / 1'000ULL), (long)(ms * 1'000'000ULL) % 1'000'000'000L}
1983 - fsm.dt();
1984 }
1985
1989 template <uint64_t us>
1991 {
1992 return TimeInterval{
1993 (time_t)(us / 1'000'000ULL), (long)(us * 1'000ULL) % 1'000'000'000L}
1994 - fsm.dt();
1995 }
1996
1997protected:
2005 virtual void enter() override
2006 {
2008 m_t = Timestamp::now();
2009
2010 base::enter();
2011 }
2012
2013private:
2015 Signal m_trigger;
2017 Timestamp m_t;
2018};
2019
2025inline17 constexpr auto entry = guard(&BasicFsm::entry, "entry");
2026
2033inline17 constexpr auto push = action(&BasicFsm::push, "push");
2034
2040inline17 constexpr auto pop = action(&BasicFsm::pop, "pop");
2041
2047inline17 constexpr auto popped = guard(&BasicFsm::popped, "popped");
2048
2054inline17 constexpr auto stop = action(&Fsm::stop, "stop");
2055
2067inline17 constexpr auto consume =
2068 action<void (BasicFsm::*)() noexcept>(&BasicFsm::clearInput, "consume");
2069
2076template <time_t s>
2077inline17 constexpr auto timeout_s = guard(&Fsm::timeoutGuard_s<s>, "timeout");
2078
2085template <uint64_t ms>
2086inline17 constexpr auto timeout_ms = guard(&Fsm::timeoutGuard_ms<ms>, "timeout");
2087
2094template <uint64_t us>
2095inline17 constexpr auto timeout_us = guard(&Fsm::timeoutGuard_us<us>, "timeout");
2096
2098{
2099 return fsm.hasInput(m_input);
2100}
2101
2103{
2104 if(isInput())
2105 return fsm.hasInput(input());
2106 else
2107 return m_guard.enabled(fsm);
2108}
2109
2110template <size_t Size>
2112{
2113 if(isInput(i))
2114 return fsm.hasInput(input(i));
2115 else
2116 return guard(i).enabled(fsm);
2117}
2118
2120{
2121 Fsm fsm_;
2122 fsm_.init(*this);
2123 return fsm_;
2124}
2125
2126} // namespace fsm
2127} // namespace zth
2128#endif // C++14
2129#endif // ZTH_FSM14_H
virtual char const * id_str() const noexcept override
Definition util.h:787
string const & name() const noexcept
Definition util.h:761
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:82
static constexpr TimeInterval null() noexcept
Definition time.h:96
static constexpr TimeInterval infinity() noexcept
Definition time.h:91
constexpr bool hasPassed() const noexcept
Definition time.h:323
Convenient wrapper around struct timespec that contains an absolute timestamp.
Definition time.h:629
static Timestamp now()
Definition time.h:656
TimeInterval passed() const
Definition time.h:721
friend cow_string str(UniqueIDBase const &)
Keeps track of a process-wide unique ID within the type T.
Definition util.h:875
UniqueID & operator=(UniqueID const &)=delete
Copy-on-write string.
Definition util.h:339
char const * c_str() const
Definition util.h:411
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:1301
void push()
Push the current state onto the state stack.
Definition fsm14.h:1508
TransitionsBase::index_type index_type
Definition fsm14.h:1304
Flag
Flags that indicate properties of the current state.
Definition fsm14.h:1310
@ 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:1714
void reserveInputs(size_t capacity)
Reserve memory for the given amount of input symbols.
Definition fsm14.h:1667
Symbol input() const noexcept
Return the input symbol that triggered the transition to the current state.
Definition fsm14.h:1600
bool setFlag(Flag f, bool value=true) noexcept
Set or clear the given flag.
Definition fsm14.h:1695
virtual ~BasicFsm() override=default
Dtor.
virtual void enter()
Called when the current state was just entered.
Definition fsm14.h:1726
BasicFsm(BasicFsm &&f)
Move ctor.
Definition fsm14.h:1334
bool flag(Flag f) const noexcept
Check if the given flag is set.
Definition fsm14.h:1575
void clearInput() noexcept
Clear the input symbol that triggered the current state.
Definition fsm14.h:1649
bool valid() const noexcept
Checks if the FSM was initialized.
Definition fsm14.h:1379
virtual void pop()
Pop the previous state from the state stack.
Definition fsm14.h:1529
void clearInputs() noexcept
Clear all inputs.
Definition fsm14.h:1657
bool popped() const noexcept
Check if the current state was reached via pop().
Definition fsm14.h:1567
State const & prev() const noexcept
Return the previous state.
Definition fsm14.h:1411
void clearFlag(Flag f) noexcept
Set the given flag.
Definition fsm14.h:1706
bool clearInput(Symbol i) noexcept
Remove the given input symbol.
Definition fsm14.h:1627
State const & state() const noexcept
Return the current state.
Definition fsm14.h:1370
bool entry() noexcept
Check if the state was just entered.
Definition fsm14.h:1585
BasicFsm(cow_string const &name="FSM")
Ctor.
Definition fsm14.h:1325
void reserveStack(size_t capacity)
Reserve memory to push() a amount of states.
Definition fsm14.h:1388
GuardPollInterval step()
Try to take one step in the state machine.
Definition fsm14.h:1430
virtual void input(Symbol i)
Register the given input symbol.
Definition fsm14.h:1614
BasicFsm & operator=(BasicFsm &&f) noexcept
Move assignment.
Definition fsm14.h:1343
virtual void reset() noexcept
Reset the state machine.
Definition fsm14.h:1399
bool hasInput(Symbol i) const noexcept
Checks if the given input symbol was registered before.
Definition fsm14.h:1679
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:1804
Timestamp const & t() const noexcept
Returns the time stamp when the current state was entered.
Definition fsm14.h:1952
virtual void enter() override
Called when the current state was just entered.
Definition fsm14.h:2005
virtual void input(Symbol i) override
Register the given input symbol.
Definition fsm14.h:1937
GuardPollInterval run(Timestamp const &until)
Run the state machine until the given time stamp.
Definition fsm14.h:1853
static GuardPollInterval timeoutGuard_us(Fsm &fsm)
Callback function for the zth::fsm::timeout_us guard.
Definition fsm14.h:1990
void stop() noexcept
Interrupt a running FSM.
Definition fsm14.h:1925
void trigger() noexcept
Trigger a currently running FSM to reevaluate the guards immediately.
Definition fsm14.h:1914
GuardPollInterval run(bool returnWhenBlocked=false)
Run the state machine.
Definition fsm14.h:1884
static GuardPollInterval timeoutGuard_ms(Fsm &fsm)
Callback function for the zth::fsm::timeout_ms guard.
Definition fsm14.h:1979
TimeInterval dt() const
Returns the time since the current state was entered.
Definition fsm14.h:1961
virtual ~Fsm() override=default
Dtor.
virtual void reset() noexcept override
Reset the state machine.
Definition fsm14.h:1839
Fsm(Fsm &&f)
Move ctor.
Definition fsm14.h:1819
static GuardPollInterval timeoutGuard_s(Fsm &fsm)
Callback function for the zth::fsm::timeout_s guard.
Definition fsm14.h:1970
Fsm & operator=(Fsm &&f) noexcept
Move assignment.
Definition fsm14.h:1828
Fsm(cow_string const &name="FSM")
Ctor.
Definition fsm14.h:1812
Guard(Guard &&) noexcept=default
~Guard()=default
constexpr Guard()=default
virtual GuardPollInterval enabled(BasicFsm &fsm) const =0
virtual cow_string name() const =0
constexpr GuardPollInterval(bool enabled=false)
Definition fsm14.h: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:2102
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:2097
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:1236
virtual index_type to(index_type i) const noexcept final
Definition fsm14.h:1242
virtual State const & state(index_type i) const noexcept final
Definition fsm14.h:1248
virtual size_t size() const noexcept final
Definition fsm14.h:1205
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:1227
typename smallest_uint< Size >::type Index
Definition fsm14.h:1129
virtual GuardPollInterval enabled(index_type i, BasicFsm &fsm) const final
Definition fsm14.h:2111
virtual bool isInput(index_type i) const noexcept final
Definition fsm14.h:1210
virtual Guard const & guard(index_type i) const noexcept final
Definition fsm14.h:1220
virtual bool hasGuard(index_type i) const noexcept final
Definition fsm14.h:1215
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:2067
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:2025
constexpr auto timeout_us
A guard that is enabled after a us microseconds after entering the current state.
Definition fsm14.h:2095
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:2077
constexpr auto pop
Action to pop the current state from the stack.
Definition fsm14.h:2040
constexpr auto push
Action to push the new state onto the stack.
Definition fsm14.h:2033
constexpr auto stop
Action to return from Fsm::run().
Definition fsm14.h:2054
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:2047
constexpr auto timeout_ms
A guard that is enabled after a ms milliseconds after entering the current state.
Definition fsm14.h:2086
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:1289
#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:160
#define zth_throw(...)
Definition macros.h:376
#define UNUSED_PAR(name)
Definition macros.h:79
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:512
string format(char const *fmt,...)
Format like sprintf(), but save the result in an zth::string.
Definition util.h:498
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:195
#define zth_assert(expr)
assert(), but better integrated in Zth.
Definition util.h:217