yams/third_party/tinyfsm/include/tinyfsm.hpp

220 lines
7.2 KiB
C++

/*
* TinyFSM - Tiny Finite State Machine Processor
*
* Copyright (c) 2012-2022 Axel Burri
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/* ---------------------------------------------------------------------
* Version: 0.3.3
*
* API documentation: see "../doc/50-API.md"
*
* The official TinyFSM website is located at:
* https://digint.ch/tinyfsm/
*
* Author:
* Axel Burri <axel@tty0.ch>
* ---------------------------------------------------------------------
*/
#ifndef TINYFSM_HPP_INCLUDED
#define TINYFSM_HPP_INCLUDED
#ifndef TINYFSM_NOSTDLIB
#include <type_traits>
#endif
// #include <iostream>
// #define DBG(str) do { std::cerr << str << std::endl; } while( false )
// DBG("*** dbg_example *** " << __PRETTY_FUNCTION__);
namespace tinyfsm {
// --------------------------------------------------------------------------
struct Event {};
// --------------------------------------------------------------------------
#ifdef TINYFSM_NOSTDLIB
// remove dependency on standard library (silent fail!).
// useful in conjunction with -nostdlib option, e.g. if your compiler
// does not provide a standard library.
// NOTE: this silently disables all static_assert() calls below!
template <typename F, typename S> struct is_same_fsm {
static constexpr bool value = true;
};
#else
// check if both fsm and state class share same fsmtype
template <typename F, typename S>
struct is_same_fsm : std::is_same<typename F::fsmtype, typename S::fsmtype> {};
#endif
template <typename S> struct _state_instance {
using value_type = S;
using type = _state_instance<S>;
static S value;
};
template <typename S> typename _state_instance<S>::value_type _state_instance<S>::value;
// --------------------------------------------------------------------------
template <typename F> class Fsm {
public:
using fsmtype = Fsm<F>;
using state_ptr_t = F*;
static state_ptr_t current_state_ptr;
// public, leaving ability to access state instance (e.g. on reset)
template <typename S> static constexpr S& state(void) {
static_assert(is_same_fsm<F, S>::value, "accessing state of different state machine");
return _state_instance<S>::value;
}
template <typename S> static constexpr bool is_in_state(void) {
static_assert(is_same_fsm<F, S>::value, "accessing state of different state machine");
return current_state_ptr == &_state_instance<S>::value;
}
/// state machine functions
public:
// explicitely specialized in FSM_INITIAL_STATE macro
static void set_initial_state();
static void reset() {};
static void enter() { current_state_ptr->entry(); }
static void start() {
set_initial_state();
enter();
}
template <typename E> static void dispatch(E const& event) { current_state_ptr->react(event); }
/// state transition functions
protected:
template <typename S> void transit(void) {
static_assert(is_same_fsm<F, S>::value, "transit to different state machine");
current_state_ptr->exit();
current_state_ptr = &_state_instance<S>::value;
current_state_ptr->entry();
}
template <typename S, typename ActionFunction> void transit(ActionFunction action_function) {
static_assert(is_same_fsm<F, S>::value, "transit to different state machine");
current_state_ptr->exit();
// NOTE: do not send events in action_function definisions.
action_function();
current_state_ptr = &_state_instance<S>::value;
current_state_ptr->entry();
}
template <typename S, typename ActionFunction, typename ConditionFunction>
void transit(ActionFunction action_function, ConditionFunction condition_function) {
if (condition_function()) {
transit<S>(action_function);
}
}
};
template <typename F> typename Fsm<F>::state_ptr_t Fsm<F>::current_state_ptr;
// --------------------------------------------------------------------------
template <typename... FF> struct FsmList;
template <> struct FsmList<> {
static void set_initial_state() {}
static void reset() {}
static void enter() {}
template <typename E> static void dispatch(E const&) {}
};
template <typename F, typename... FF> struct FsmList<F, FF...> {
using fsmtype = Fsm<F>;
static void set_initial_state() {
fsmtype::set_initial_state();
FsmList<FF...>::set_initial_state();
}
static void reset() {
F::reset();
FsmList<FF...>::reset();
}
static void enter() {
fsmtype::enter();
FsmList<FF...>::enter();
}
static void start() {
set_initial_state();
enter();
}
template <typename E> static void dispatch(E const& event) {
fsmtype::template dispatch<E>(event);
FsmList<FF...>::template dispatch<E>(event);
}
};
// --------------------------------------------------------------------------
template <typename... SS> struct StateList;
template <> struct StateList<> {
static void reset() {}
};
template <typename S, typename... SS> struct StateList<S, SS...> {
static void reset() {
_state_instance<S>::value = S();
StateList<SS...>::reset();
}
};
// --------------------------------------------------------------------------
template <typename F> struct MooreMachine : tinyfsm::Fsm<F> {
virtual void entry(void) {}; /* entry actions in some states */
void exit(void) {}; /* no exit actions */
};
template <typename F> struct MealyMachine : tinyfsm::Fsm<F> {
// input actions are modeled in react():
// - conditional dependent of event type or payload
// - transit<>(ActionFunction)
void entry(void) {}; /* no entry actions */
void exit(void) {}; /* no exit actions */
};
} /* namespace tinyfsm */
#define FSM_INITIAL_STATE(_FSM, _STATE) \
namespace tinyfsm { \
template <> void Fsm<_FSM>::set_initial_state(void) { \
current_state_ptr = &_state_instance<_STATE>::value; \
} \
}
#endif /* TINYFSM_HPP_INCLUDED */