19 #if defined(__cplusplus) && __cplusplus >= 201402L
28 # include <functional>
31 # include <type_traits>
50 template <
typename R,
typename C,
typename A>
51 struct function_traits_detail {
52 using return_type = R;
56 static constexpr
bool is_member = !std::is_same<C, void>::value;
57 static constexpr
bool is_functor =
false;
63 static constexpr
bool is_functor =
true;
68 struct function_traits<R (&)()> : function_traits_detail<R, void, void> {};
70 # if defined(__cpp_noexcept_function_type) && __cpp_noexcept_function_type >= 201510
72 struct function_traits<R (&)() noexcept> : function_traits_detail<R,
void,
void> {};
76 template <
typename R,
typename A>
77 struct function_traits<R (&)(A)> : function_traits_detail<R, void, A> {};
79 # if defined(__cpp_noexcept_function_type) && __cpp_noexcept_function_type >= 201510
80 template <
typename R,
typename A>
81 struct function_traits<R (&)(A) noexcept> : function_traits_detail<R, void, A> {};
86 struct function_traits<R (*)()> : function_traits_detail<R, void, void> {};
88 # if defined(__cpp_noexcept_function_type) && __cpp_noexcept_function_type >= 201510
90 struct function_traits<R (*)() noexcept> : function_traits_detail<R,
void,
void> {};
94 template <
typename R,
typename A>
95 struct function_traits<R (*)(A)> : function_traits_detail<R, void, A> {};
97 # if defined(__cpp_noexcept_function_type) && __cpp_noexcept_function_type >= 201510
98 template <
typename R,
typename A>
99 struct function_traits<R (*)(A) noexcept> : function_traits_detail<R, void, A> {};
103 template <
typename R,
typename C>
104 struct function_traits<R (C::*)()> : function_traits_detail<R, C, void> {};
106 # if defined(__cpp_noexcept_function_type) && __cpp_noexcept_function_type >= 201510
107 template <
typename R,
typename C>
108 struct function_traits<R (C::*)() noexcept> : function_traits_detail<R, C, void> {};
112 template <
typename R,
typename C,
typename A>
113 struct function_traits<R (C::*)(A)> : function_traits_detail<R, C, A> {};
115 # if defined(__cpp_noexcept_function_type) && __cpp_noexcept_function_type >= 201510
116 template <
typename R,
typename C,
typename A>
117 struct function_traits<R (C::*)(A) noexcept> : function_traits_detail<R, C, A> {};
121 template <
typename R,
typename C>
122 struct function_traits<R (C::*)() const> : function_traits_detail<R, C, void> {};
124 # if defined(__cpp_noexcept_function_type) && __cpp_noexcept_function_type >= 201510
125 template <
typename R,
typename C>
126 struct function_traits<R (C::*)() const noexcept> : function_traits_detail<R, C, void> {};
130 template <
typename R,
typename C,
typename A>
131 struct function_traits<R (C::*)(A) const> : function_traits_detail<R, C, A> {};
133 # if defined(__cpp_noexcept_function_type) && __cpp_noexcept_function_type >= 201510
134 template <
typename R,
typename C,
typename A>
135 struct function_traits<R (C::*)(A) const noexcept> : function_traits_detail<R, C, A> {};
154 : std::logic_error{
str}
172 constexpr
Symbol(
char const* s =
nullptr) noexcept
181 char const* a = m_symbol;
182 char const* b = s.m_symbol;
193 while(*a && *a == *b) {
203 return *
this == s.m_symbol;
211 template <
typename S>
214 return !(*
this == std::forward<S>(s));
220 __attribute__((returns_nonnull)) constexpr
char const*
str() const noexcept
222 return m_symbol ? m_symbol :
"-";
229 constexpr
char const*
symbol() const noexcept
237 __attribute__((returns_nonnull))
operator char const *()
const noexcept
245 constexpr
bool valid() const noexcept
253 operator bool() const noexcept
260 char const* m_symbol{};
278 template <
bool enable = Config::NamedFsm>
291 return format(
"%p",
this);
298 constexpr
explicit Named(
char const* name) noexcept
318 typename T,
typename R,
319 bool haveArg = !std::is_same<typename function_traits<T>::arg_type,
void>::value,
321 !haveArg && function_traits<T>::is_member && !function_traits<T>::is_functor
322 && std::is_base_of<BasicFsm, typename function_traits<T>::class_type>::value,
324 std::is_convertible<typename function_traits<T>::return_type, R>::value
326 || std::is_convertible<BasicFsm&, typename function_traits<T>::arg_type>::value
329 std::remove_reference_t<typename function_traits<T>::arg_type>>::value)>
332 template <
typename T,
typename R>
336 template <
typename T_>
337 constexpr
Callback(T_&& c,
char const* name =
nullptr)
339 , m_callback{std::forward<T_>(c)}
351 template <
typename T,
typename R>
355 template <
typename T_>
356 constexpr
Callback(T_&& c,
char const* name =
nullptr)
358 , m_callback{std::forward<T_>(c)}
365 return m_callback(
static_cast<A
>(fsm));
372 std::is_base_of<BasicFsm, std::remove_reference_t<A>>::value,
int> = 0>
376 zth_assert(
dynamic_cast<std::remove_reference_t<A>*
>(&fsm) !=
nullptr);
383 !std::is_base_of<BasicFsm, std::remove_reference_t<A>>::value,
int> = 0>
384 static void check(BasicFsm& fsm)
391 template <
typename T,
typename R>
395 template <
typename T_>
396 constexpr
Callback(T_&& c,
char const* name =
nullptr)
398 , m_callback{std::forward<T_>(c)}
405 zth_assert(
dynamic_cast<C*
>(&fsm) !=
nullptr);
407 return ((
static_cast<C&
>(fsm)).*m_callback)();
426 template <typename... A>
431 constexpr
operator bool() const noexcept
447 void operator=(
Guard const&) = delete;
458 template <
typename T>
461 ,
protected Callback<T, GuardPollInterval> {
466 template <
typename T_>
473 return this->call(fsm);
478 return Callback_type::name();
486 : m_input{std::move(
input)}
493 return m_input.
str();
529 template <
typename T>
530 constexpr
auto guard(T&& g,
char const* name =
nullptr)
594 return format(
"%p\n",
this);
603 template <
typename T>
611 template <
typename T_>
623 return Callback_type::name();
648 template <
typename T>
649 constexpr
auto action(T&& a,
char const* name =
nullptr)
695 , m_input{std::move(
input)}
717 return m_input.
valid();
732 constexpr
auto const&
guard() const noexcept
738 constexpr
auto const&
action() const noexcept
757 Guard const& m_guard;
795 if(!(ga.isInput() && &ga.action() == &
nothing))
807 : m_state{std::move(
state)}
823 , m_guardedAction{std::move(ga)}
827 : m_state{std::move(
state)}
828 , m_guardedAction{std::move(guardedAction)}
831 constexpr
auto const&
state() const noexcept
838 return m_guardedAction.
isInput();
848 return m_guardedAction.
input();
851 constexpr
auto const&
guard() const noexcept
853 return m_guardedAction.
guard();
856 constexpr
auto const&
action() const noexcept
858 return m_guardedAction.
action();
863 return m_guardedAction.
enabled(fsm);
868 return m_guardedAction.
tryRun(fsm);
920 if(!(t.hasGuard() && &t.guard() == &
always))
929 template <
typename F>
931 : m_from{std::forward<F>(
from)}
935 template <
typename F,
typename T>
937 : m_from{std::forward<F>(
from)}
938 , m_to{std::forward<T>(
to)}
948 return m_from.
tryRun(fsm);
951 constexpr
auto const&
from() const noexcept
953 return m_from.
state();
968 return m_from.
input();
971 constexpr
auto const&
guard() const noexcept
973 return m_from.
guard();
976 constexpr
auto const&
action() const noexcept
981 constexpr
auto const&
to() const noexcept
998 return Transition{std::move(from), std::move(to)};
1030 virtual size_t size() const noexcept = 0;
1048 template <typename F, std::enable_if_t<std::is_base_of<BasicFsm, F>::value,
int> = 0>
1057 size_t size_ =
size();
1059 fprintf(f,
"%3zu: %-16s + %s %-18s / %-18s >>= %3zu\n", i,
state(i).
str(),
1066 void uml(FILE* f = stdout)
const
1068 fprintf(f,
"@startuml\n");
1070 size_t size_ =
size();
1074 if(
state(i).valid()) {
1076 fprintf(f,
"state \"%s\" as s%zu\n",
state(statei).
str(), statei);
1085 edge +=
format(
"s%zu", statei);
1088 edge +=
format(
" --> s%zu",
to(i));
1090 edge +=
format(
" : (%zu)", t);
1106 fprintf(f,
"%s\n", edge.c_str());
1110 fprintf(f,
"@enduml\n");
1114 template <
size_t Size>
1118 using Index =
typename smallest_uint<Size>::type;
1159 f.m_transitions[0].
to = 1;
1161 if(!l.begin()->from().valid())
1168 for(
auto const& t : l) {
1170 prev = f.m_transitions[i].
from = t.from();
1171 if(find(t.from(), l) != (
size_t)i)
1173 "State transitions are not contiguous"});
1176 f.m_transitions[i].
guard = &t.guard();
1178 f.m_transitions[i].
guard = t.input().symbol();
1181 f.m_transitions[i].
action = &t.action();
1182 f.m_transitions[i].
to =
static_cast<Index>(find(t.to(), l));
1189 virtual size_t size() const noexcept final
1196 return (m_transitions[i].flags &
static_cast<uint8_t
>(
Flag::input)) != 0;
1208 return *
static_cast<Guard const*
>(m_transitions[i].
guard);
1215 return static_cast<char const*
>(m_transitions[i].
guard);
1223 return *m_transitions[i].
action;
1235 return m_transitions[i].
from;
1239 static constexpr
size_t find(
State const&
state, std::initializer_list<Transition> l)
1245 for(
auto const& t : l) {
1246 if(t.from().constexpr_eq(
state))
1251 # if GCC_VERSION < 90000
1256 zth_throw(invalid_fsm{
"Target state not found"});
1263 static_assert(std::numeric_limits<Index>::max() > Size,
"");
1264 CompiledTransition m_transitions[Size + 1u];
1272 template <
typename... T>
1312 zth_assert(std::atomic_is_lock_free(&m_state));
1321 *
this = std::move(f);
1330 m_flags = f.m_flags;
1332 m_transition = f.m_transition;
1334 m_stack = std::move(f.m_stack);
1335 m_inputs = std::move(f.m_inputs);
1357 return m_fsm->
state(state_());
1374 if(m_stack.capacity() > capacity * 2U)
1375 m_stack.shrink_to_fit();
1377 m_stack.reserve(capacity);
1387 m_prev = m_transition = 0;
1398 return m_fsm->
state(m_prev);
1419 auto size = m_fsm->
size();
1424 while(!(p = m_fsm->
enabled(i, *
this))) {
1432 zth_dbg(fsm,
"[%s] Blocked for %s",
id_str(), again.str().c_str());
1439 auto to = m_fsm->
to(i);
1449 zth_dbg(fsm,
"[%s] Guard %s enabled, no transition",
id_str(),
1456 zth_dbg(fsm,
"[%s] Have input %s, transition %s -> %s",
id_str(),
1460 zth_dbg(fsm,
"[%s] Guard %s enabled, transition %s -> %s",
id_str(),
1496 m_stack.push_back(m_prev);
1561 return m_flags.test(flagIndex(f));
1592 return m_fsm->
input(m_transition);
1603 m_inputs.emplace_back(std::move(i));
1616 for(
size_t j = 0; j < m_inputs.size(); j++)
1617 if(m_inputs[j] == i) {
1618 m_inputs[j] = std::move(m_inputs.back());
1619 m_inputs.pop_back();
1653 if(m_inputs.capacity() > capacity * 2U)
1654 m_inputs.shrink_to_fit();
1656 m_inputs.reserve(capacity);
1668 for(
size_t j = 0; j < m_inputs.size(); j++)
1669 if(m_inputs[j] == i)
1681 m_flags.set(flagIndex(f), value);
1727 static constexpr
size_t flagIndex(
Flag f) noexcept
1729 return static_cast<size_t>(f);
1735 void init(TransitionsBase
const& c) noexcept
1745 friend TransitionsBase;
1752 return m_state.load(std::memory_order_relaxed);
1760 return m_state.store(s, std::memory_order_relaxed);
1765 TransitionsBase
const* m_fsm{};
1767 std::bitset<static_cast<size_t>(
Flag::flags)> m_flags;
1773 std::atomic<index_type> m_state{};
1806 *
this = std::move(f);
1856 auto p_end = now + p;
1857 m_trigger.
wait(std::min(p_end, until), now);
1870 zth_dbg(fsm,
"[%s] Run%s",
id_str(), returnWhenBlocked ?
" until blocked" :
"");
1883 if(returnWhenBlocked)
1961 template <u
int64_t ms>
1965 (time_t)(ms / 1'000ULL), (long)(ms * 1'000'000ULL) % 1'000'000'000L}
1972 template <u
int64_t us>
1976 (time_t)(us / 1'000'000ULL), (long)(us * 1'000ULL) % 1'000'000'000L}
2068 template <u
int64_t ms>
2077 template <u
int64_t us>
2093 template <
size_t Size>
2099 return guard(i).enabled(fsm);
void signal(bool queue=true, bool queueEveryTime=false) noexcept
Convenient wrapper around struct timespec that contains a time interval.
constexpr bool hasPassed() const noexcept
constexpr static TimeInterval infinity() noexcept
constexpr static TimeInterval null() noexcept
Convenient wrapper around struct timespec that contains an absolute timestamp.
TimeInterval passed() const
friend cow_string str(UniqueIDBase const &)
Keeps track of a process-wide unique ID within the type T.
virtual char const * id_str() const override
string const & name() const noexcept
UniqueID & operator=(UniqueID const &)=delete
char const * c_str() const
constexpr Action()=default
void operator()(BasicFsm &fsm) const
virtual void run(BasicFsm &fsm) const
Action(Action &&) noexcept=default
virtual cow_string name() const
void push()
Push the current state onto the state stack.
TransitionsBase::index_type index_type
Flag
Flags that indicate properties of the current state.
@ 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.
void reserveInputs(size_t capacity)
Reserve memory for the given amount of input symbols.
virtual ~BasicFsm()=default
Dtor.
Symbol input() const noexcept
Return the input symbol that triggered the transition to the current state.
bool setFlag(Flag f, bool value=true) noexcept
Set or clear the given flag.
virtual void enter()
Called when the current state was just entered.
BasicFsm & operator=(BasicFsm &&f) noexcept
Move assignment.
BasicFsm(BasicFsm &&f)
Move ctor.
bool flag(Flag f) const noexcept
Check if the given flag is set.
void clearInput() noexcept
Clear the input symbol that triggered the current state.
bool valid() const noexcept
Checks if the FSM was initialized.
virtual void pop()
Pop the previous state from the state stack.
void clearInputs() noexcept
Clear all inputs.
State const & prev() const noexcept
Return the previous state.
bool popped() const noexcept
Check if the current state was reached via pop().
void clearFlag(Flag f) noexcept
Set the given flag.
bool clearInput(Symbol i) noexcept
Remove the given input symbol.
bool entry() noexcept
Check if the state was just entered.
BasicFsm(cow_string const &name="FSM")
Ctor.
State const & state() const noexcept
Return the current state.
void reserveStack(size_t capacity)
Reserve memory to push() a amount of states.
GuardPollInterval step()
Try to take one step in the state machine.
virtual void input(Symbol i)
Register the given input symbol.
virtual void reset() noexcept
Reset the state machine.
bool hasInput(Symbol i) const noexcept
Checks if the given input symbol was registered before.
constexpr Callback(T_ &&c, char const *name=nullptr)
R call(BasicFsm &fsm) const
constexpr Callback(T_ &&c, char const *name=nullptr)
R call(BasicFsm &fsm) const
R call(BasicFsm &fsm) const
constexpr Callback(T_ &&c, char const *name=nullptr)
Timestamp const & t() const noexcept
Returns the time stamp when the current state was entered.
virtual void enter() override
Called when the current state was just entered.
virtual void input(Symbol i) override
Register the given input symbol.
GuardPollInterval run(Timestamp const &until)
Run the state machine until the given time stamp.
static GuardPollInterval timeoutGuard_us(Fsm &fsm)
Callback function for the zth::fsm::timeout_us guard.
void stop() noexcept
Interrupt a running FSM.
void trigger() noexcept
Trigger a currently running FSM to reevaluate the guards immediately.
GuardPollInterval run(bool returnWhenBlocked=false)
Run the state machine.
static GuardPollInterval timeoutGuard_ms(Fsm &fsm)
Callback function for the zth::fsm::timeout_ms guard.
TimeInterval dt() const
Returns the time since the current state was entered.
virtual ~Fsm() override=default
Dtor.
virtual void reset() noexcept override
Reset the state machine.
static GuardPollInterval timeoutGuard_s(Fsm &fsm)
Callback function for the zth::fsm::timeout_s guard.
Fsm(cow_string const &name="FSM")
Ctor.
Fsm & operator=(Fsm &&f) noexcept
Move assignment.
Guard(Guard &&) noexcept=default
constexpr Guard()=default
virtual GuardPollInterval enabled(BasicFsm &fsm) const =0
virtual cow_string name() const =0
constexpr GuardPollInterval(bool enabled=false)
constexpr GuardPollInterval(GuardPollInterval &&) noexcept=default
constexpr GuardedActionBase()=default
GuardPollInterval operator()(BasicFsm &fsm) const
virtual GuardPollInterval tryRun(BasicFsm &fsm) const
constexpr bool hasGuard() const noexcept
virtual cow_string name() const final
constexpr Symbol const & input() const noexcept
constexpr GuardedAction(Guard const &guard, Action const &action) noexcept
constexpr GuardedAction(Action const &action) noexcept
constexpr auto const & guard() const noexcept
constexpr GuardedAction(Symbol &&input) noexcept
constexpr GuardedAction() noexcept
virtual GuardPollInterval enabled(BasicFsm &fsm) const final
constexpr bool isInput() const noexcept
constexpr GuardedAction(Symbol &&input, Action const &action) noexcept
constexpr auto const & action() const noexcept
virtual void run(BasicFsm &fsm) const final
constexpr GuardedAction(Guard const &guard) noexcept
constexpr Named(char const *name=nullptr) noexcept
constexpr Named(char const *name) noexcept
bool operator!=(S &&s) const noexcept
bool operator==(Symbol const &s) const noexcept
constexpr Symbol(char const *s=nullptr) noexcept
Ctor.
constexpr bool valid() const noexcept
Check if the symbol is valid.
constexpr char const * str() const noexcept
Return a string representation of this symbol.
bool operator==(char const *s) const noexcept
constexpr bool constexpr_eq(Symbol const &s) const noexcept
Like == operator, but constexpr.
constexpr char const * symbol() const noexcept
Return the symbol string.
virtual GuardPollInterval enabled(BasicFsm &fsm) const final
constexpr Symbol const & input() const noexcept
virtual cow_string name() const final
constexpr auto const & guard() const noexcept
constexpr auto const & to() const noexcept
constexpr auto const & action() const noexcept
constexpr bool isInput() const noexcept
virtual GuardPollInterval tryRun(BasicFsm &fsm) const final
constexpr bool hasGuard() const noexcept
constexpr Transition(F &&from, T &&to) noexcept
constexpr auto const & from() const noexcept
constexpr Transition(F &&from) noexcept
constexpr TransitionStart(GuardedAction &&ga) noexcept
constexpr TransitionStart(State &&state, GuardedAction &&guardedAction) noexcept
constexpr auto const & guard() const noexcept
virtual GuardPollInterval tryRun(BasicFsm &fsm) const final
constexpr auto const & action() const noexcept
constexpr auto const & state() const noexcept
constexpr bool hasGuard() const noexcept
constexpr bool isInput() const noexcept
constexpr Symbol const & input() const noexcept
constexpr TransitionStart(State &&state) noexcept
constexpr TransitionStart(Guard const &guard) noexcept
constexpr TransitionStart(Action const &action) noexcept
virtual cow_string name() const override
virtual GuardPollInterval enabled(BasicFsm &fsm) const final
virtual State const & state(index_type i) const noexcept=0
virtual bool hasInput(index_type i) const noexcept
virtual Guard const & guard(index_type i) const noexcept=0
virtual GuardPollInterval enabled(index_type i, BasicFsm &fsm) const =0
virtual size_t size() const noexcept=0
F & init(F &fsm) const noexcept
void uml(FILE *f=stdout) const
virtual Action const & action(index_type i) const noexcept=0
virtual index_type to(index_type i) const noexcept=0
virtual bool hasGuard(index_type i) const noexcept=0
virtual Symbol input(index_type i) const noexcept=0
void dump(FILE *f=stdout) const
virtual index_type to(index_type i) const noexcept final
virtual size_t size() const noexcept final
virtual Action const & action(index_type i) const noexcept final
constexpr Transitions()=default
static constexpr Transitions compile(std::initializer_list< Transition > l)
virtual Symbol input(index_type i) const noexcept final
virtual State const & state(index_type i) const noexcept final
typename smallest_uint< Size >::type Index
virtual GuardPollInterval enabled(index_type i, BasicFsm &fsm) const final
virtual bool isInput(index_type i) const noexcept final
virtual Guard const & guard(index_type i) const noexcept final
virtual bool hasGuard(index_type i) const noexcept final
constexpr TypedAction(T_ &&a, char const *name=nullptr)
virtual void run(BasicFsm &fsm) const final
virtual cow_string name() const final
virtual cow_string name() const final
virtual GuardPollInterval enabled(BasicFsm &fsm) const final
constexpr TypedGuard(T_ &&g, char const *name=nullptr)
constexpr auto consume
Action consume the current input symbol.
constexpr auto never
Trivial guard that is never enabled.
constexpr auto guard(T &&g, char const *name=nullptr)
Create a guard from a function.
constexpr auto entry
Guard that is only enabled upon entry of a state.
constexpr auto timeout_us
A guard that is enabled after a us microseconds after entering the current state.
constexpr auto nothing
Trivial action that does nothing.
constexpr auto action(T &&a, char const *name=nullptr)
Create an action from a function.
constexpr auto timeout_s
A guard that is enabled after a s seconds after entering the current state.
constexpr auto pop
Action to pop the current state from the stack.
constexpr auto push
Action to push the new state onto the stack.
constexpr auto stop
Action to return from Fsm::run().
constexpr auto input(Symbol &&symbol) noexcept
Create a guard from an input symbol.
constexpr auto popped
Guard to indicate that the current state was reached via pop.
constexpr auto timeout_ms
A guard that is enabled after a ms milliseconds after entering the current state.
constexpr auto always
Trivial guard that is always enabled.
constexpr auto compile(T &&... t)
Compile a transition description.
#define zth_dbg(group, fmt, a...)
Debug printf()-like function.
#define ZTH_CLASS_NEW_DELETE(T)
Define new/delete operators for a class, which are allocator-aware.
constexpr auto operator/(Guard const &g, Action const &a) noexcept
constexpr auto operator+(Symbol &&input) noexcept
bool never_guard() noexcept
constexpr Transition operator>>=(TransitionStart &&from, State &&to) noexcept
bool always_guard() noexcept
cow_string str(T value)
Returns an zth::string representation of the given value.
string format(char const *fmt,...)
Format like sprintf(), but save the result in an zth::string.
constexpr CompiledTransition() noexcept
Exception thrown when the FSM description is incorrect.
invalid_fsm(char const *str)
std::vector< index_type, typename Config::Allocator< index_type >::type > type
#define zth_assert(expr)
assert(), but better integrated in Zth.