18#if defined(__cplusplus) && __cplusplus >= 201402L
29# include <type_traits>
48template <
typename R,
typename C,
typename A>
49struct function_traits_detail {
50 using return_type = R;
54 static constexpr bool is_member = !std::is_same<C, void>::value;
55 static constexpr bool is_functor =
false;
62 static constexpr bool is_functor =
true;
67struct function_traits<R (&)()> : function_traits_detail<R, void, void> {};
69# if defined(__cpp_noexcept_function_type) && __cpp_noexcept_function_type >= 201510
71struct function_traits<R (&)() noexcept> : function_traits_detail<R,
void,
void> {};
75template <
typename R,
typename A>
76struct function_traits<R (&)(A)> : function_traits_detail<R, void, A> {};
78# if defined(__cpp_noexcept_function_type) && __cpp_noexcept_function_type >= 201510
79template <
typename R,
typename A>
80struct function_traits<R (&)(A)
noexcept> : function_traits_detail<R, void, A> {};
85struct function_traits<R (*)()> : function_traits_detail<R, void, void> {};
87# if defined(__cpp_noexcept_function_type) && __cpp_noexcept_function_type >= 201510
89struct function_traits<R (*)() noexcept> : function_traits_detail<R,
void,
void> {};
93template <
typename R,
typename A>
94struct function_traits<R (*)(A)> : function_traits_detail<R, void, A> {};
96# if defined(__cpp_noexcept_function_type) && __cpp_noexcept_function_type >= 201510
97template <
typename R,
typename A>
98struct function_traits<R (*)(A)
noexcept> : function_traits_detail<R, void, A> {};
102template <
typename R,
typename C>
103struct function_traits<R (C::*)()> : function_traits_detail<R, C, void> {};
105# if defined(__cpp_noexcept_function_type) && __cpp_noexcept_function_type >= 201510
106template <
typename R,
typename C>
107struct function_traits<R (C::*)() noexcept> : function_traits_detail<R, C, void> {};
111template <
typename R,
typename C,
typename A>
112struct function_traits<R (C::*)(A)> : function_traits_detail<R, C, A> {};
114# if defined(__cpp_noexcept_function_type) && __cpp_noexcept_function_type >= 201510
115template <
typename R,
typename C,
typename A>
116struct function_traits<R (C::*)(A) noexcept> : function_traits_detail<R, C, A> {};
120template <
typename R,
typename C>
121struct function_traits<R (C::*)() const> : function_traits_detail<R, C, void> {};
123# if defined(__cpp_noexcept_function_type) && __cpp_noexcept_function_type >= 201510
124template <
typename R,
typename C>
125struct function_traits<R (C::*)() const noexcept> : function_traits_detail<R, C, void> {};
129template <
typename R,
typename C,
typename A>
130struct function_traits<R (C::*)(A) const> : function_traits_detail<R, C, A> {};
132# if defined(__cpp_noexcept_function_type) && __cpp_noexcept_function_type >= 201510
133template <
typename R,
typename C,
typename A>
134struct function_traits<R (C::*)(A) const noexcept> : function_traits_detail<R, C, A> {};
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{};
278template <
bool enable = Config::NamedFsm>
291 return format(
"%p",
this);
298 constexpr explicit Named(
char const* name) noexcept
319 typename T,
typename R,
320 bool haveArg = !std::is_same<typename function_traits<T>::arg_type,
void>::value,
322 !haveArg && function_traits<T>::is_member && !function_traits<T>::is_functor
323 && std::is_base_of<BasicFsm, typename function_traits<T>::class_type>::value,
325 std::is_convertible<typename function_traits<T>::return_type, R>::value
327 || std::is_convertible<BasicFsm&, typename function_traits<T>::arg_type>::value
330 std::remove_reference_t<typename function_traits<T>::arg_type>>::value)>
333template <
typename T,
typename R>
337 template <
typename T_>
339 constexpr Callback(T_&& c,
char const* name =
nullptr)
341 , m_callback{
std::forward<T_>(c)}
354template <
typename T,
typename R>
358 template <
typename T_>
360 constexpr Callback(T_&& c,
char const* name =
nullptr)
362 , m_callback{
std::forward<T_>(c)}
369 return m_callback(
static_cast<A
>(fsm));
376 std::is_base_of<BasicFsm, std::remove_reference_t<A>>::value,
int> = 0>
380 zth_assert(
dynamic_cast<std::remove_reference_t<A>*
>(&fsm) !=
nullptr);
387 !std::is_base_of<BasicFsm, std::remove_reference_t<A>>::value,
int> = 0>
388 static void check(BasicFsm& fsm)
395template <
typename T,
typename R>
399 template <
typename T_>
401 constexpr Callback(T_&& c,
char const* name =
nullptr)
403 , m_callback{
std::forward<T_>(c)}
411 zth_assert(
dynamic_cast<C*
>(&fsm) !=
nullptr);
413 return ((
static_cast<C&
>(fsm)).*m_callback)();
433 template <typename... A>
439 constexpr operator bool() const noexcept
455 void operator=(
Guard const&) = delete;
472 template <
typename T_>
479 return this->call(fsm);
484 return Callback_type::name();
499 return m_input.
str();
536constexpr auto guard(T&& g,
char const* name =
nullptr)
600 return format(
"%p\n",
this);
615 template <
typename T_>
627 return Callback_type::name();
653constexpr auto action(T&& a,
char const* name =
nullptr)
698 , m_input{std::move(
input)}
723 return m_input.
valid();
738 constexpr auto const&
guard() const noexcept
744 constexpr auto const&
action() const noexcept
762 Guard const& m_guard;
800 if(!(ga.isInput() && &ga.action() == &
nothing))
813 : m_state{std::move(
state)}
832 , m_guardedAction{std::move(ga)}
836 : m_state{std::move(
state)}
837 , m_guardedAction{std::move(guardedAction)}
840 constexpr auto const&
state() const noexcept
847 return m_guardedAction.
isInput();
857 return m_guardedAction.
input();
860 constexpr auto const&
guard() const noexcept
862 return m_guardedAction.
guard();
865 constexpr auto const&
action() const noexcept
867 return m_guardedAction.
action();
872 return m_guardedAction.
enabled(fsm);
877 return m_guardedAction.
tryRun(fsm);
929 if(!(t.hasGuard() && &t.guard() == &
always))
938 template <
typename F>
941 : m_from{std::forward<F>(
from)}
945 template <
typename F,
typename T>
947 : m_from{std::forward<F>(
from)}
948 , m_to{std::forward<T>(
to)}
958 return m_from.
tryRun(fsm);
961 constexpr auto const&
from() const noexcept
963 return m_from.
state();
978 return m_from.
input();
981 constexpr auto const&
guard() const noexcept
983 return m_from.
guard();
986 constexpr auto const&
action() const noexcept
991 constexpr auto const&
to() const noexcept
1008 return Transition{std::move(from), std::move(to)};
1040 virtual size_t size() const noexcept = 0;
1058 template <typename F, std::enable_if_t<std::is_base_of<BasicFsm, F>::value,
int> = 0>
1067 size_t size_ =
size();
1069 fprintf(f,
"%3zu: %-16s + %s %-18s / %-18s >>= %3zu\n", i,
state(i).
str(),
1076 void uml(FILE* f = stdout)
const
1078 fprintf(f,
"@startuml\n");
1080 size_t size_ =
size();
1084 if(
state(i).valid()) {
1086 fprintf(f,
"state \"%s\" as s%zu\n",
state(statei).
str(), statei);
1095 edge +=
format(
"s%zu", statei);
1098 edge +=
format(
" --> s%zu",
to(i));
1100 edge +=
format(
" : (%zu)", t);
1116 fprintf(f,
"%s\n", edge.c_str());
1120 fprintf(f,
"@enduml\n");
1124template <
size_t Size>
1128 using Index =
typename smallest_uint<Size>::type;
1169 f.m_transitions[0].
to = 1;
1171 if(!l.begin()->from().valid())
1178 for(
auto const& t : l) {
1180 prev = f.m_transitions[i].
from = t.from();
1181 if(find(t.from(), l) != (size_t)i)
1183 "State transitions are not contiguous"});
1186 f.m_transitions[i].
guard = &t.guard();
1188 f.m_transitions[i].
guard = t.input().symbol();
1191 f.m_transitions[i].
action = &t.action();
1192 f.m_transitions[i].
to =
static_cast<Index>(find(t.to(), l));
1199 virtual size_t size() const noexcept final
1206 return (m_transitions[i].flags &
static_cast<uint8_t
>(
Flag::input)) != 0;
1218 return *
static_cast<Guard const*
>(m_transitions[i].
guard);
1225 return static_cast<char const*
>(m_transitions[i].
guard);
1233 return *m_transitions[i].
action;
1245 return m_transitions[i].
from;
1249 static constexpr size_t find(
State const&
state, std::initializer_list<Transition> l)
1255 for(
auto const& t : l) {
1256 if(t.from().constexpr_eq(
state))
1261# if GCC_VERSION < 90000
1266 zth_throw(invalid_fsm{
"Target state not found"});
1273 static_assert(std::numeric_limits<Index>::max() > Size,
"");
1274 CompiledTransition m_transitions[Size + 1U];
1282template <
typename... T>
1322 zth_assert(std::atomic_is_lock_free(&m_state));
1331 *
this = std::move(f);
1340 m_flags = f.m_flags;
1342 m_transition = f.m_transition;
1344 m_stack = std::move(f.m_stack);
1345 m_inputs = std::move(f.m_inputs);
1367 return m_fsm->
state(state_());
1384 if(m_stack.capacity() > capacity * 2U)
1385 m_stack.shrink_to_fit();
1387 m_stack.reserve(capacity);
1397 m_prev = m_transition = 0;
1408 return m_fsm->
state(m_prev);
1429 auto size = m_fsm->
size();
1434 while(!(p = m_fsm->
enabled(i, *
this))) {
1442 zth_dbg(fsm,
"[%s] Blocked for %s",
id_str(), again.str().c_str());
1449 auto to = m_fsm->
to(i);
1459 zth_dbg(fsm,
"[%s] Guard %s enabled, no transition",
id_str(),
1466 zth_dbg(fsm,
"[%s] Have input %s, transition %s -> %s",
id_str(),
1470 zth_dbg(fsm,
"[%s] Guard %s enabled, transition %s -> %s",
id_str(),
1506 m_stack.push_back(m_prev);
1571 return m_flags.test(flagIndex(f));
1602 return m_fsm->
input(m_transition);
1613 m_inputs.emplace_back(std::move(i));
1626 for(
size_t j = 0; j < m_inputs.size(); j++)
1627 if(m_inputs[j] == i) {
1628 m_inputs[j] = std::move(m_inputs.back());
1629 m_inputs.pop_back();
1663 if(m_inputs.capacity() > capacity * 2U)
1664 m_inputs.shrink_to_fit();
1666 m_inputs.reserve(capacity);
1678 for(
size_t j = 0; j < m_inputs.size(); j++)
1679 if(m_inputs[j] == i)
1691 m_flags.set(flagIndex(f), value);
1737 static constexpr size_t flagIndex(
Flag f)
noexcept
1739 return static_cast<size_t>(f);
1745 void init(TransitionsBase
const& c)
noexcept
1755 friend TransitionsBase;
1762 return m_state.load(std::memory_order_relaxed);
1770 return m_state.store(s, std::memory_order_relaxed);
1775 TransitionsBase
const* m_fsm{};
1777 std::bitset<static_cast<size_t>(
Flag::flags)> m_flags;
1783 std::atomic<index_type> m_state{};
1785 vector_type<index_type>::type m_stack;
1816 *
this = std::move(f);
1866 auto p_end = now + p;
1867 m_trigger.
wait(std::min(p_end, until), now);
1880 zth_dbg(fsm,
"[%s] Run%s",
id_str(), returnWhenBlocked ?
" until blocked" :
"");
1893 if(returnWhenBlocked)
1972 template <u
int64_t ms>
1976 (time_t)(ms / 1'000ULL), (long)(ms * 1'000'000ULL) % 1'000'000'000L}
1983 template <u
int64_t us>
1987 (time_t)(us / 1'000'000ULL), (long)(us * 1'000ULL) % 1'000'000'000L}
2079template <u
int64_t ms>
2088template <u
int64_t us>
2104template <
size_t Size>
2110 return guard(i).enabled(fsm);
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.
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.
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.