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;
1170 f.m_transitions[0].
to = 1;
1172 if(!l.begin()->from().valid())
1179 for(
auto const& t : l) {
1181 prev = f.m_transitions[i].
from = t.from();
1182 if(find(t.from(), l) != (size_t)i)
1184 "State transitions are not contiguous"});
1187 f.m_transitions[i].
guard = &t.guard();
1189 f.m_transitions[i].
guard = t.input().symbol();
1192 f.m_transitions[i].
action = &t.action();
1193 f.m_transitions[i].
to =
static_cast<Index>(find(t.to(), l));
1200 virtual size_t size() const noexcept final
1207 return (m_transitions[i].flags &
static_cast<uint8_t
>(
Flag::input)) != 0;
1219 return *
static_cast<Guard const*
>(m_transitions[i].
guard);
1226 return static_cast<char const*
>(m_transitions[i].
guard);
1234 return *m_transitions[i].
action;
1246 return m_transitions[i].
from;
1250 static constexpr size_t find(
State const&
state, std::initializer_list<Transition> l)
1256 for(
auto const& t : l) {
1257 if(t.from().constexpr_eq(
state))
1262# if GCC_VERSION < 90000
1267 zth_throw(invalid_fsm{
"Target state not found"});
1274 static_assert(std::numeric_limits<Index>::max() > Size,
"");
1275 CompiledTransition m_transitions[Size + 1U];
1283template <
typename... T>
1323 zth_assert(std::atomic_is_lock_free(&m_state));
1332 *
this = std::move(f);
1341 m_flags = f.m_flags;
1343 m_transition = f.m_transition;
1345 m_stack = std::move(f.m_stack);
1346 m_inputs = std::move(f.m_inputs);
1368 return m_fsm->
state(state_());
1385 if(m_stack.capacity() > capacity * 2U)
1386 m_stack.shrink_to_fit();
1388 m_stack.reserve(capacity);
1398 m_prev = m_transition = 0;
1409 return m_fsm->
state(m_prev);
1430 auto size = m_fsm->
size();
1435 while(!(p = m_fsm->
enabled(i, *
this))) {
1443 zth_dbg(fsm,
"[%s] Blocked for %s",
id_str(), again.str().c_str());
1450 auto to = m_fsm->
to(i);
1460 zth_dbg(fsm,
"[%s] Guard %s enabled, no transition",
id_str(),
1467 zth_dbg(fsm,
"[%s] Have input %s, transition %s -> %s",
id_str(),
1471 zth_dbg(fsm,
"[%s] Guard %s enabled, transition %s -> %s",
id_str(),
1507 m_stack.push_back(m_prev);
1572 return m_flags.test(flagIndex(f));
1603 return m_fsm->
input(m_transition);
1614 m_inputs.emplace_back(std::move(i));
1627 for(
size_t j = 0; j < m_inputs.size(); j++)
1628 if(m_inputs[j] == i) {
1629 m_inputs[j] = std::move(m_inputs.back());
1630 m_inputs.pop_back();
1664 if(m_inputs.capacity() > capacity * 2U)
1665 m_inputs.shrink_to_fit();
1667 m_inputs.reserve(capacity);
1679 for(
size_t j = 0; j < m_inputs.size(); j++)
1680 if(m_inputs[j] == i)
1692 m_flags.set(flagIndex(f), value);
1738 static constexpr size_t flagIndex(
Flag f)
noexcept
1740 return static_cast<size_t>(f);
1746 void init(TransitionsBase
const& c)
noexcept
1756 friend TransitionsBase;
1763 return m_state.load(std::memory_order_relaxed);
1771 return m_state.store(s, std::memory_order_relaxed);
1776 TransitionsBase
const* m_fsm{};
1778 std::bitset<static_cast<size_t>(
Flag::flags)> m_flags;
1784 std::atomic<index_type> m_state{};
1786 vector_type<index_type>::type m_stack;
1817 *
this = std::move(f);
1867 auto p_end = now + p;
1868 m_trigger.
wait(std::min(p_end, until), now);
1881 zth_dbg(fsm,
"[%s] Run%s",
id_str(), returnWhenBlocked ?
" until blocked" :
"");
1894 if(returnWhenBlocked)
1973 template <u
int64_t ms>
1977 (time_t)(ms / 1'000ULL), (long)(ms * 1'000'000ULL) % 1'000'000'000L}
1984 template <u
int64_t us>
1988 (time_t)(us / 1'000'000ULL), (long)(us * 1'000ULL) % 1'000'000'000L}
2080template <u
int64_t ms>
2089template <u
int64_t us>
2105template <
size_t Size>
2111 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.