19#if defined(__cplusplus) && __cplusplus >= 201402L
30# include <type_traits>
49template <
typename R,
typename C,
typename A>
50struct function_traits_detail {
51 using return_type = R;
55 static constexpr bool is_member = !std::is_same<C, void>::value;
56 static constexpr bool is_functor =
false;
63 static constexpr bool is_functor =
true;
68struct function_traits<R (&)()> : function_traits_detail<R, void, void> {};
70# if defined(__cpp_noexcept_function_type) && __cpp_noexcept_function_type >= 201510
72struct function_traits<R (&)() noexcept> : function_traits_detail<R,
void,
void> {};
76template <
typename R,
typename A>
77struct function_traits<R (&)(A)> : function_traits_detail<R, void, A> {};
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> {};
86struct function_traits<R (*)()> : function_traits_detail<R, void, void> {};
88# if defined(__cpp_noexcept_function_type) && __cpp_noexcept_function_type >= 201510
90struct function_traits<R (*)() noexcept> : function_traits_detail<R,
void,
void> {};
94template <
typename R,
typename A>
95struct function_traits<R (*)(A)> : function_traits_detail<R, void, A> {};
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> {};
103template <
typename R,
typename C>
104struct function_traits<R (C::*)()> : function_traits_detail<R, C, void> {};
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> {};
112template <
typename R,
typename C,
typename A>
113struct function_traits<R (C::*)(A)> : function_traits_detail<R, C, A> {};
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> {};
121template <
typename R,
typename C>
122struct function_traits<R (C::*)() const> : function_traits_detail<R, C, void> {};
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> {};
130template <
typename R,
typename C,
typename A>
131struct function_traits<R (C::*)(A) const> : function_traits_detail<R, C, A> {};
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> {};
173 constexpr Symbol(
char const* s =
nullptr) noexcept
182 char const* a = m_symbol;
183 char const* b = s.m_symbol;
194 while(*a && *a == *b) {
204 return *
this == s.m_symbol;
212 template <
typename S>
215 return !(*
this == std::forward<S>(s));
221 __attribute__((returns_nonnull))
constexpr char const*
str() const noexcept
223 return m_symbol ? m_symbol :
"-";
230 constexpr char const*
symbol() const noexcept
238 __attribute__((returns_nonnull))
operator char const*()
const noexcept
246 constexpr bool valid() const noexcept
254 operator bool() const noexcept
261 char const* m_symbol{};
279template <
bool enable = Config::NamedFsm>
292 return format(
"%p",
this);
299 constexpr explicit Named(
char const* name) noexcept
320 typename T,
typename R,
321 bool haveArg = !std::is_same<typename function_traits<T>::arg_type,
void>::value,
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,
326 std::is_convertible<typename function_traits<T>::return_type, R>::value
328 || std::is_convertible<BasicFsm&, typename function_traits<T>::arg_type>::value
331 std::remove_reference_t<typename function_traits<T>::arg_type>>::value)>
334template <
typename T,
typename R>
338 template <
typename T_>
340 constexpr Callback(T_&& c,
char const* name =
nullptr)
342 , m_callback{
std::forward<T_>(c)}
355template <
typename T,
typename R>
359 template <
typename T_>
361 constexpr Callback(T_&& c,
char const* name =
nullptr)
363 , m_callback{
std::forward<T_>(c)}
370 return m_callback(
static_cast<A
>(fsm));
377 std::is_base_of<BasicFsm, std::remove_reference_t<A>>::value,
int> = 0>
381 zth_assert(
dynamic_cast<std::remove_reference_t<A>*
>(&fsm) !=
nullptr);
388 !std::is_base_of<BasicFsm, std::remove_reference_t<A>>::value,
int> = 0>
389 static void check(BasicFsm& fsm)
396template <
typename T,
typename R>
400 template <
typename T_>
402 constexpr Callback(T_&& c,
char const* name =
nullptr)
404 , m_callback{
std::forward<T_>(c)}
412 zth_assert(
dynamic_cast<C*
>(&fsm) !=
nullptr);
414 return ((
static_cast<C&
>(fsm)).*m_callback)();
434 template <typename... A>
440 constexpr operator bool() const noexcept
456 void operator=(
Guard const&) = delete;
473 template <
typename T_>
480 return this->call(fsm);
485 return Callback_type::name();
500 return m_input.
str();
537constexpr auto guard(T&& g,
char const* name =
nullptr)
601 return format(
"%p\n",
this);
616 template <
typename T_>
628 return Callback_type::name();
654constexpr auto action(T&& a,
char const* name =
nullptr)
699 , m_input{std::move(
input)}
724 return m_input.
valid();
739 constexpr auto const&
guard() const noexcept
745 constexpr auto const&
action() const noexcept
763 Guard const& m_guard;
801 if(!(ga.isInput() && &ga.action() == &
nothing))
814 : m_state{std::move(
state)}
833 , m_guardedAction{std::move(ga)}
837 : m_state{std::move(
state)}
838 , m_guardedAction{std::move(guardedAction)}
841 constexpr auto const&
state() const noexcept
848 return m_guardedAction.
isInput();
858 return m_guardedAction.
input();
861 constexpr auto const&
guard() const noexcept
863 return m_guardedAction.
guard();
866 constexpr auto const&
action() const noexcept
868 return m_guardedAction.
action();
873 return m_guardedAction.
enabled(fsm);
878 return m_guardedAction.
tryRun(fsm);
930 if(!(t.hasGuard() && &t.guard() == &
always))
939 template <
typename F>
942 : m_from{std::forward<F>(
from)}
946 template <
typename F,
typename T>
948 : m_from{std::forward<F>(
from)}
949 , m_to{std::forward<T>(
to)}
959 return m_from.
tryRun(fsm);
962 constexpr auto const&
from() const noexcept
964 return m_from.
state();
979 return m_from.
input();
982 constexpr auto const&
guard() const noexcept
984 return m_from.
guard();
987 constexpr auto const&
action() const noexcept
992 constexpr auto const&
to() const noexcept
1009 return Transition{std::move(from), std::move(to)};
1041 virtual size_t size() const noexcept = 0;
1059 template <typename F, std::enable_if_t<std::is_base_of<BasicFsm, F>::value,
int> = 0>
1068 size_t size_ =
size();
1070 fprintf(f,
"%3zu: %-16s + %s %-18s / %-18s >>= %3zu\n", i,
state(i).
str(),
1077 void uml(FILE* f = stdout)
const
1079 fprintf(f,
"@startuml\n");
1081 size_t size_ =
size();
1085 if(
state(i).valid()) {
1087 fprintf(f,
"state \"%s\" as s%zu\n",
state(statei).
str(), statei);
1096 edge +=
format(
"s%zu", statei);
1099 edge +=
format(
" --> s%zu",
to(i));
1101 edge +=
format(
" : (%zu)", t);
1117 fprintf(f,
"%s\n", edge.c_str());
1121 fprintf(f,
"@enduml\n");
1125template <
size_t Size>
1129 using Index =
typename smallest_uint<Size>::type;
1161 if(l.size() != Size)
1168 f.m_transitions[0].action = &
nothing;
1170 f.m_transitions[0].guard = &
always;
1171 f.m_transitions[0].action = &
nothing;
1172 f.m_transitions[0].to = 1;
1174 if(!l.begin()->from().valid())
1181 for(
auto const& t : l) {
1186 prev = f.m_transitions[i].from = t.from();
1187 if(find(t.from(), l) != (
size_t)i)
1189 "State transitions are not contiguous"});
1192 f.m_transitions[i].guard = &t.guard();
1194 f.m_transitions[i].guard = t.input().
symbol();
1195 f.m_transitions[i].flags =
static_cast<uint8_t
>(
Flag::input);
1197 f.m_transitions[i].action = &t.action();
1198 f.m_transitions[i].to =
static_cast<Index>(find(t.to(), l));
1205 virtual size_t size() const noexcept final
1212 return (m_transitions[i].flags &
static_cast<uint8_t
>(
Flag::input)) != 0;
1224 return *
static_cast<Guard const*
>(m_transitions[i].
guard);
1231 return static_cast<char const*
>(m_transitions[i].
guard);
1239 return *m_transitions[i].
action;
1251 return m_transitions[i].
from;
1255 static constexpr size_t find(
State const&
state, std::initializer_list<Transition> l)
1261 for(
auto const& t : l) {
1262 if(t.from().constexpr_eq(
state))
1267# if GCC_VERSION < 90000
1272 zth_throw(invalid_fsm{
"Target state not found"});
1279 static_assert(std::numeric_limits<Index>::max() > Size,
"");
1280 CompiledTransition m_transitions[Size + 1U];
1288template <
typename... T>
1328 zth_assert(std::atomic_is_lock_free(&m_state));
1337 *
this = std::move(f);
1346 m_flags = f.m_flags;
1348 m_transition = f.m_transition;
1350 m_stack = std::move(f.m_stack);
1351 m_inputs = std::move(f.m_inputs);
1373 return m_fsm->
state(state_());
1390 if(m_stack.capacity() > capacity * 2U)
1391 m_stack.shrink_to_fit();
1393 m_stack.reserve(capacity);
1403 m_prev = m_transition = 0;
1414 return m_fsm->
state(m_prev);
1435 auto size = m_fsm->
size();
1440 while(!(p = m_fsm->
enabled(i, *
this))) {
1448 zth_dbg(fsm,
"[%s] Blocked for %s",
id_str(), again.str().c_str());
1455 auto to = m_fsm->
to(i);
1465 zth_dbg(fsm,
"[%s] Guard %s enabled, no transition",
id_str(),
1472 zth_dbg(fsm,
"[%s] Have input %s, transition %s -> %s",
id_str(),
1476 zth_dbg(fsm,
"[%s] Guard %s enabled, transition %s -> %s",
id_str(),
1512 m_stack.push_back(m_prev);
1577 return m_flags.test(flagIndex(f));
1608 return m_fsm->
input(m_transition);
1619 m_inputs.emplace_back(std::move(i));
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();
1669 if(m_inputs.capacity() > capacity * 2U)
1670 m_inputs.shrink_to_fit();
1672 m_inputs.reserve(capacity);
1684 for(
size_t j = 0; j < m_inputs.size(); j++)
1685 if(m_inputs[j] == i)
1697 m_flags.set(flagIndex(f), value);
1743 static constexpr size_t flagIndex(
Flag f)
noexcept
1745 return static_cast<size_t>(f);
1751 void init(TransitionsBase
const& c)
noexcept
1761 friend TransitionsBase;
1768 return m_state.load(std::memory_order_relaxed);
1776 return m_state.store(s, std::memory_order_relaxed);
1781 TransitionsBase
const* m_fsm{};
1783 std::bitset<static_cast<size_t>(
Flag::flags)> m_flags;
1789 std::atomic<index_type> m_state{};
1791 vector_type<index_type>::type m_stack;
1822 *
this = std::move(f);
1872 auto p_end = t_now + p;
1873 m_trigger.
wait(std::min(p_end, until), t_now);
1886 zth_dbg(fsm,
"[%s] Run%s",
id_str(), returnWhenBlocked ?
" until blocked" :
"");
1899 if(returnWhenBlocked)
1978 template <u
int64_t ms>
1982 (time_t)(ms / 1'000ULL), (long)(ms * 1'000'000ULL) % 1'000'000'000L}
1989 template <u
int64_t us>
1993 (time_t)(us / 1'000'000ULL), (long)(us * 1'000ULL) % 1'000'000'000L}
2085template <u
int64_t ms>
2094template <u
int64_t us>
2110template <
size_t Size>
2116 return guard(i).enabled(fsm);
virtual char const * id_str() const noexcept override
string const & name() const noexcept
void signal(bool queue=true, bool queueEveryTime=false) noexcept
Convenient wrapper around struct timespec that contains a time interval.
static constexpr TimeInterval null() noexcept
static constexpr TimeInterval infinity() noexcept
constexpr bool hasPassed() const 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.
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.
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 ~BasicFsm() override=default
Dtor.
virtual void enter()
Called when the current state was just entered.
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.
bool popped() const noexcept
Check if the current state was reached via pop().
State const & prev() const noexcept
Return the previous state.
void clearFlag(Flag f) noexcept
Set the given flag.
bool clearInput(Symbol i) noexcept
Remove the given input symbol.
State const & state() const noexcept
Return the current state.
bool entry() noexcept
Check if the state was just entered.
BasicFsm(cow_string const &name="FSM")
Ctor.
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.
BasicFsm & operator=(BasicFsm &&f) noexcept
Move assignment.
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 & operator=(Fsm &&f) noexcept
Move assignment.
Fsm(cow_string const &name="FSM")
Ctor.
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 GuardedAction(Guard const &guard, Action const &action) noexcept
constexpr GuardedAction(Action const &action) noexcept
constexpr GuardedAction(Symbol &&input) noexcept
constexpr auto const & guard() const 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
constexpr Symbol const & input() 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
constexpr char const * symbol() const noexcept
Return the symbol string.
bool operator!=(S &&s) const noexcept
bool operator==(Symbol const &s) const noexcept
constexpr Symbol(char const *s=nullptr) noexcept
Ctor.
constexpr char const * str() const noexcept
Return a string representation of this symbol.
constexpr bool valid() const noexcept
Check if the symbol is valid.
bool operator==(char const *s) const noexcept
constexpr bool constexpr_eq(Symbol const &s) const noexcept
Like == operator, but constexpr.
constexpr auto const & action() const noexcept
virtual GuardPollInterval enabled(BasicFsm &fsm) const final
constexpr auto const & guard() const noexcept
constexpr Symbol const & input() const noexcept
virtual cow_string name() const final
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 auto const & to() const noexcept
constexpr Transition(F &&from) noexcept
constexpr auto const & action() const 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 & 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
F & init(F &fsm) const noexcept
virtual bool hasInput(index_type i) const noexcept
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
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 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
virtual index_type to(index_type i) const noexcept final
virtual State const & state(index_type i) const noexcept final
virtual size_t size() const noexcept final
constexpr Transitions()=default
static constexpr Transitions compile(std::initializer_list< Transition > l)
virtual Symbol input(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< T, typename Config::Allocator< T >::type > type
#define zth_assert(expr)
assert(), but better integrated in Zth.