/* * TinyFSM - Tiny Finite State Machine Processor * * Copyright (c) 2012-2018 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.2 * * API documentation: see "../doc/50-API.md" * * The official TinyFSM website is located at: * https://digint.ch/tinyfsm/ * * Author: * Axel Burri * --------------------------------------------------------------------- */ #ifndef TINYFSM_HPP_INCLUDED #define TINYFSM_HPP_INCLUDED #ifndef TINYFSM_NOSTDLIB #include #endif // #include // #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 struct is_same_fsm { static constexpr bool value = true; }; #else // check if both fsm and state class share same fsmtype template struct is_same_fsm : std::is_same< typename F::fsmtype, typename S::fsmtype > { }; #endif template struct _state_instance { using value_type = S; using type = _state_instance; static S value; }; template typename _state_instance::value_type _state_instance::value; // -------------------------------------------------------------------------- template class Fsm { public: using fsmtype = Fsm; using state_ptr_t = F *; static state_ptr_t current_state_ptr; // public, leaving ability to access state instance (e.g. on reset) template static constexpr S & state(void) { static_assert(is_same_fsm::value, "accessing state of different state machine"); return _state_instance::value; } template static constexpr bool is_in_state(void) { static_assert(is_same_fsm::value, "accessing state of different state machine"); return current_state_ptr == &_state_instance::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 static void dispatch(E const & event) { current_state_ptr->react(event); } /// state transition functions protected: template void transit(void) { static_assert(is_same_fsm::value, "transit to different state machine"); current_state_ptr->exit(); current_state_ptr = &_state_instance::value; current_state_ptr->entry(); } template void transit(ActionFunction action_function) { static_assert(is_same_fsm::value, "transit to different state machine"); current_state_ptr->exit(); // NOTE: we get into deep trouble if the action_function sends a new event. // TODO: implement a mechanism to check for reentrancy action_function(); current_state_ptr = &_state_instance::value; current_state_ptr->entry(); } template void transit(ActionFunction action_function, ConditionFunction condition_function) { if(condition_function()) { transit(action_function); } } }; template typename Fsm::state_ptr_t Fsm::current_state_ptr; // -------------------------------------------------------------------------- template struct FsmList; template<> struct FsmList<> { static void set_initial_state() { } static void reset() { } static void enter() { } template static void dispatch(E const &) { } }; template struct FsmList { using fsmtype = Fsm; static void set_initial_state() { fsmtype::set_initial_state(); FsmList::set_initial_state(); } static void reset() { F::reset(); FsmList::reset(); } static void enter() { fsmtype::enter(); FsmList::enter(); } static void start() { set_initial_state(); enter(); } template static void dispatch(E const & event) { fsmtype::template dispatch(event); FsmList::template dispatch(event); } }; // -------------------------------------------------------------------------- template struct StateList; template<> struct StateList<> { static void reset() { } }; template struct StateList { static void reset() { _state_instance::value = S(); StateList::reset(); } }; // -------------------------------------------------------------------------- template struct MooreMachine : tinyfsm::Fsm { virtual void entry(void) { }; /* entry actions in some states */ void exit(void) { }; /* no exit actions */ }; template struct MealyMachine : tinyfsm::Fsm { // 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 */