├── lib └── ansi-colors │ ├── README.md │ ├── src │ ├── CMakeLists.txt │ └── ansi_colors.cpp │ ├── example │ ├── CMakeLists.txt │ └── ansi_colors_example.cpp │ ├── .gitignore │ ├── include │ └── pushkin │ │ └── ansi_colors.hpp │ ├── CMakeLists.txt │ └── LICENSE ├── include ├── CMakeLists.txt └── afsm │ ├── fsm_fwd.hpp │ ├── detail │ ├── reject_policies.hpp │ ├── debug_io.hpp │ ├── event_identity.hpp │ ├── exception_safety_guarantees.hpp │ ├── tags.hpp │ ├── def_traits.hpp │ ├── helpers.hpp │ ├── observer.hpp │ └── orthogonal_regions.hpp │ └── definition_fwd.hpp ├── examples ├── CMakeLists.txt ├── vending.cpp ├── minimal.cpp ├── pausing.cpp ├── vending_starter.cpp ├── minimal_actions.cpp ├── minimal_entry_exit.cpp ├── vending_nested_sm.cpp └── vending_machine.hpp ├── .gitignore ├── cmake ├── CMakeLists.txt ├── modules │ └── FindExternalProjectZmijModules.cmake └── FindAFSM.cmake ├── test ├── cmake │ └── CMakeLists.txt ├── CMakeLists.txt ├── orthogonal_states_test.cpp ├── transaction_common.hpp ├── state_machine_with_internal_transitions.cpp ├── fsm_parts_test.cpp ├── static_tests.cpp ├── common_base_test.cpp ├── test_observer.hpp ├── vending_machine_test.cpp └── pushdown_tests.cpp ├── benchmark ├── CMakeLists.txt ├── vending_benchmark.cpp ├── vending_msm_benchmark.cpp ├── defer_benchmark.cpp ├── vending_msm_test.cpp └── vending_machine_msm.hpp ├── CMakeLists.txt ├── README.md └── LICENSE /lib/ansi-colors/README.md: -------------------------------------------------------------------------------- 1 | # ansi-colors 2 | ANSI console colors for C++ iostreams 3 | -------------------------------------------------------------------------------- /lib/ansi-colors/src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # /ansi-colors/src/CMakeLists.txt 2 | # 3 | # @author zmij 4 | # @date Jun 2, 2016 5 | 6 | cmake_minimum_required(VERSION 2.6) 7 | 8 | add_library(${PSST_${LIB_NAME}_LIB} SHARED ansi_colors.cpp) 9 | -------------------------------------------------------------------------------- /include/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # /afsm/include/CMakeLists.txt 2 | # 3 | # @author zmij 4 | # @date Nov 18, 2016 5 | 6 | cmake_minimum_required(VERSION 2.6) 7 | 8 | install( 9 | DIRECTORY afsm 10 | DESTINATION include 11 | FILES_MATCHING PATTERN *.hpp PATTERN *.inl 12 | ) 13 | -------------------------------------------------------------------------------- /lib/ansi-colors/example/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # /ansi-colors/example/CMakeLists.txt 2 | # 3 | # @author zmij 4 | # @date Jun 2, 2016 5 | 6 | cmake_minimum_required(VERSION 2.6) 7 | 8 | add_executable(psst-ansi-colors-example ansi_colors_example.cpp) 9 | target_link_libraries(psst-ansi-colors-example ${PSST_${LIB_NAME}_LIB}) 10 | -------------------------------------------------------------------------------- /examples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # /afsm/examples/CMakeLists.txt 2 | # 3 | # @author zmij 4 | # @date Nov 14, 2016 5 | 6 | cmake_minimum_required(VERSION 2.6) 7 | 8 | file(GLOB examples *.cpp) 9 | foreach(file ${examples}) 10 | get_filename_component(tgt_name ${file} NAME_WE) 11 | add_executable(${tgt_name} ${file}) 12 | endforeach() 13 | -------------------------------------------------------------------------------- /lib/ansi-colors/.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | *.obj 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Compiled Dynamic libraries 12 | *.so 13 | *.dylib 14 | *.dll 15 | 16 | # Fortran module files 17 | *.mod 18 | 19 | # Compiled Static libraries 20 | *.lai 21 | *.la 22 | *.a 23 | *.lib 24 | 25 | # Executables 26 | *.exe 27 | *.out 28 | *.app 29 | 30 | # Build trees 31 | build* 32 | 33 | # Eclipse files 34 | .cproject 35 | .project 36 | 37 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | *.obj 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Compiled Dynamic libraries 12 | *.so 13 | *.dylib 14 | *.dll 15 | 16 | # Fortran module files 17 | *.mod 18 | 19 | # Compiled Static libraries 20 | *.lai 21 | *.la 22 | *.a 23 | *.lib 24 | 25 | # Executables 26 | *.exe 27 | *.out 28 | *.app 29 | 30 | # Build trees 31 | build* 32 | 33 | # Eclipse files 34 | .project 35 | .cproject 36 | .settings* 37 | 38 | # CLion settings 39 | .idea 40 | 41 | -------------------------------------------------------------------------------- /examples/vending.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * vending.cpp 3 | * 4 | * Created on: Nov 15, 2016 5 | * Author: zmij 6 | */ 7 | 8 | #include 9 | #include "vending_machine.hpp" 10 | 11 | using vending_machine = vending::vending_machine; 12 | 13 | int 14 | main(int, char*[]) 15 | try { 16 | vending_machine vm; 17 | return 0; 18 | } catch (::std::exception const& e) { 19 | ::std::cerr << "Exception: " << e.what() << "\n"; 20 | return 1; 21 | } catch (...) { 22 | ::std::cerr << "Unexpected exception\n"; 23 | return 2; 24 | } 25 | -------------------------------------------------------------------------------- /cmake/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # /afsm/cmake/CMakeLists.txt 2 | # 3 | # @author zmij 4 | # @date Nov 18, 2016 5 | 6 | cmake_minimum_required(VERSION 2.6) 7 | 8 | if(NOT CMAKE_SCRIPT_INSTALL_DIR) 9 | find_path(CMAKE_SCRIPT_INSTALL_DIR "CheckLanguage.cmake" 10 | PATHS ${CMAKE_ROOT} 11 | PATH_SUFFIXES Modules ) 12 | endif() 13 | 14 | if (CMAKE_SCRIPT_INSTALL_DIR) 15 | file(GLOB find_scripts Find*.cmake) 16 | install( 17 | FILES ${find_scripts} 18 | DESTINATION ${CMAKE_SCRIPT_INSTALL_DIR}) 19 | else() 20 | message(WARNING "Failed to find target directory for installing cmake Find scripts") 21 | endif() 22 | -------------------------------------------------------------------------------- /test/cmake/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # /afsm/test/cmake/CMakeLists.txt 2 | # 3 | # @author zmij 4 | # @date Nov 18, 2016 5 | 6 | cmake_minimum_required(VERSION 2.6) 7 | 8 | set(_pname test-find-afsm) 9 | if (PROJECT_VERSION) 10 | set(_pversion ${PROJECT_VERSION}) 11 | else() 12 | set(_pversion 0.1.0) 13 | endif() 14 | 15 | if (${CMAKE_VERSION} VERSION_GREATER "3.0") 16 | cmake_policy(SET CMP0048 NEW) 17 | project(${_pname} VERSION ${_pversion}) 18 | else() 19 | project(${_pname}) 20 | set(PROJECT_VERSION ${_pversion}) 21 | endif() 22 | 23 | add_definitions("-std=c++11") 24 | 25 | set( CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} 26 | "${CMAKE_CURRENT_SOURCE_DIR}/../../cmake" 27 | "${CMAKE_CURRENT_SOURCE_DIR}/../../lib/meta/cmake" ) 28 | 29 | find_package(AFSM REQUIRED) 30 | -------------------------------------------------------------------------------- /cmake/modules/FindExternalProjectZmijModules.cmake: -------------------------------------------------------------------------------- 1 | #FindExternalProjectZmijModules.cmake 2 | # Created on: Dec 2, 2018 3 | # Author: ser-fedorov 4 | 5 | if (TARGET libzmijcmake) 6 | return() 7 | endif() 8 | 9 | find_program(GIT_EXECUTABLE git) 10 | 11 | if(EXISTS ${CMAKE_CURRENT_BINARY_DIR}/cmake-scripts) 12 | execute_process(COMMAND ${GIT_EXECUTABLE} pull 13 | WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/cmake-scripts) 14 | else() 15 | execute_process(COMMAND ${GIT_EXECUTABLE} clone -b develop https://github.com/zmij/cmake-scripts.git cmake-scripts 16 | WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) 17 | endif() 18 | 19 | set(ZMIJ_CMAKE_SCRIPTS ${CMAKE_CURRENT_BINARY_DIR}/cmake-scripts) 20 | add_library(libzmijcmake IMPORTED INTERFACE) 21 | set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${ZMIJ_CMAKE_SCRIPTS}/modules") 22 | -------------------------------------------------------------------------------- /include/afsm/fsm_fwd.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * fsm_fwd.hpp 3 | * 4 | * Created on: 28 мая 2016 г. 5 | * Author: sergey.fedorov 6 | */ 7 | 8 | #ifndef AFSM_FSM_FWD_HPP_ 9 | #define AFSM_FSM_FWD_HPP_ 10 | 11 | namespace afsm { 12 | 13 | struct none {}; 14 | 15 | namespace detail { 16 | template < typename Observer > 17 | class observer_wrapper; 18 | 19 | } 20 | 21 | template < typename FSM, typename T > 22 | class state; 23 | template < typename FSM, typename T > 24 | class inner_state_machine; 25 | template < typename T, typename Mutex = none, typename Observer = none, 26 | template class ObserverWrapper = detail::observer_wrapper > 27 | class state_machine; 28 | template < typename T, typename Mutex = none, typename Observer = none, 29 | template class ObserverWrapper = detail::observer_wrapper > 30 | class priority_state_machine; 31 | 32 | } /* namespace afsm */ 33 | 34 | #endif /* AFSM_FSM_FWD_HPP_ */ 35 | -------------------------------------------------------------------------------- /include/afsm/detail/reject_policies.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * reject_policies.hpp 3 | * 4 | * Created on: Nov 30, 2016 5 | * Author: zmij 6 | */ 7 | 8 | #ifndef AFSM_DETAIL_REJECT_POLICIES_HPP_ 9 | #define AFSM_DETAIL_REJECT_POLICIES_HPP_ 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | namespace afsm { 16 | namespace def { 17 | namespace tags { 18 | 19 | struct reject_throw_event { 20 | template < typename Event, typename FSM > 21 | actions::event_process_result 22 | reject_event(Event&& event, FSM&) 23 | { 24 | throw event; 25 | } 26 | }; 27 | 28 | struct reject_throw { 29 | template < typename Event, typename FSM > 30 | actions::event_process_result 31 | reject_event(Event&&, FSM&) 32 | { 33 | using ::psst::util::demangle; 34 | throw ::std::runtime_error{ 35 | "An instance of " + demangle() + " event was rejected" 36 | }; 37 | } 38 | }; 39 | 40 | } /* namespace tags */ 41 | } /* namespace def */ 42 | } /* namespace afsm */ 43 | 44 | 45 | #endif /* AFSM_DETAIL_REJECT_POLICIES_HPP_ */ 46 | -------------------------------------------------------------------------------- /cmake/FindAFSM.cmake: -------------------------------------------------------------------------------- 1 | # /afsm/cmake/FindAFSM.cmake 2 | # Try to find afsm library 3 | # 4 | # The following variables are optionally searched for defaults 5 | # AFSM_ROOT_DIR: Base directory where include directory can be found 6 | # 7 | # Once done this will define 8 | # AFSM_FOUND - System has afsm library 9 | # AFSM_INCLUDE_DIRS - The afsm include directories 10 | 11 | # Library requires metapushkin library and will additionally search for it 12 | 13 | find_package(MetaPushkin REQUIRED) 14 | 15 | if(NOT AFSM_FOUND) 16 | 17 | set(AFSM_ROOT_DIR "" CACHE PATH "Folder containing afsm library") 18 | 19 | find_path(AFSM_INCLUDE_DIR "afsm/fsm.hpp" 20 | PATHS ${AFSM_ROOT_DIR} 21 | PATH_SUFFIXES include 22 | NO_DEFAULT_PATH) 23 | 24 | find_path(AFSM_INCLUDE_DIR "afsm/fsm.hpp") 25 | 26 | include(FindPackageHandleStandardArgs) 27 | # handle the QUIETLY and REQUIRED arguments and set benchmark_FOUND to TRUE 28 | # if all listed variables are TRUE 29 | find_package_handle_standard_args(AFSM FOUND_VAR AFSM_FOUND 30 | REQUIRED_VARS AFSM_INCLUDE_DIR) 31 | 32 | if(AFSM_FOUND) 33 | set(AFSM_INCLUDE_DIRS ${AFSM_INCLUDE_DIR}) 34 | endif() 35 | 36 | mark_as_advanced(AFSM_INCLUDE_DIR) 37 | 38 | endif() 39 | -------------------------------------------------------------------------------- /include/afsm/detail/debug_io.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * debug_io.hpp 3 | * 4 | * Created on: 2 июня 2016 г. 5 | * Author: sergey.fedorov 6 | */ 7 | 8 | #ifndef AFSM_DETAIL_DEBUG_IO_HPP_ 9 | #define AFSM_DETAIL_DEBUG_IO_HPP_ 10 | 11 | #include 12 | #include 13 | 14 | namespace afsm { 15 | namespace actions { 16 | 17 | inline ::std::ostream& 18 | operator << (::std::ostream& os, event_process_result const& val) 19 | { 20 | ::std::ostream::sentry s (os); 21 | if (s) { 22 | switch (val) { 23 | case event_process_result::refuse: 24 | os << "refuse"; 25 | break; 26 | case event_process_result::process: 27 | os << "transit"; 28 | break; 29 | case event_process_result::process_in_state: 30 | os << "in-state"; 31 | break; 32 | case event_process_result::defer: 33 | os << "defer"; 34 | break; 35 | default: 36 | break; 37 | } 38 | } 39 | return os; 40 | } 41 | 42 | 43 | } /* namespace actions */ 44 | } /* namespace afsm */ 45 | 46 | #endif /* AFSM_DETAIL_DEBUG_IO_HPP_ */ 47 | -------------------------------------------------------------------------------- /include/afsm/detail/event_identity.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * event_identity.hpp 3 | * 4 | * Created on: Dec 20, 2016 5 | * Author: zmij 6 | */ 7 | 8 | #ifndef AFSM_DETAIL_EVENT_IDENTITY_HPP_ 9 | #define AFSM_DETAIL_EVENT_IDENTITY_HPP_ 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | namespace afsm { 16 | namespace detail { 17 | 18 | struct event_base { 19 | struct id_type {}; 20 | }; 21 | 22 | template < typename T > 23 | struct event : event_base { 24 | static constexpr id_type id{}; 25 | }; 26 | 27 | template < typename T > 28 | constexpr event_base::id_type event::id; 29 | 30 | template < typename T > 31 | struct event_identity { 32 | using event_type = typename ::std::decay::type; 33 | using type = event; 34 | }; 35 | 36 | using event_set = ::std::set< event_base::id_type const* >; 37 | 38 | template < typename ... T > 39 | event_set 40 | make_event_set( ::psst::meta::type_tuple const& ) 41 | { 42 | return event_set{ 43 | &event_identity::type::id... 44 | }; 45 | } 46 | 47 | } /* namespace detail */ 48 | } /* namespace afsm */ 49 | 50 | 51 | 52 | #endif /* AFSM_DETAIL_EVENT_IDENTITY_HPP_ */ 53 | -------------------------------------------------------------------------------- /examples/minimal.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * minimal.cpp 3 | * 4 | * Created on: Nov 14, 2016 5 | * Author: zmij 6 | */ 7 | 8 | #include 9 | #include 10 | 11 | namespace minimal { 12 | 13 | // Events 14 | struct start {}; 15 | struct stop {}; 16 | 17 | // State machine definition 18 | struct minimal_def : ::afsm::def::state_machine { 19 | struct initial : state {}; 20 | struct running : state {}; 21 | struct terminated : terminal_state {}; 22 | 23 | using initial_state = initial; 24 | using transitions = transition_table< 25 | /* State Event Next */ 26 | tr< initial, start, running >, 27 | tr< running, stop, terminated > 28 | >; 29 | }; 30 | 31 | // State machine object 32 | using minimal = ::afsm::state_machine; 33 | 34 | void 35 | use() 36 | { 37 | minimal fsm; 38 | fsm.process_event(start{}); 39 | fsm.process_event(stop{}); 40 | } 41 | 42 | } /* namespace minimal */ 43 | 44 | int 45 | main(int, char*[]) 46 | try { 47 | minimal::use(); 48 | return 0; 49 | } catch (::std::exception const& e) { 50 | ::std::cerr << "Exception: " << e.what() << "\n"; 51 | return 1; 52 | } catch (...) { 53 | ::std::cerr << "Unexpected exception\n"; 54 | return 2; 55 | } 56 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # /afsm/test/CMakeLists.txt 2 | # 3 | # @author zmij 4 | # @date May 26, 2016 5 | 6 | cmake_minimum_required(VERSION 2.6) 7 | if (NOT TARGET test-afsm-base) 8 | if (NOT CMAKE_THREAD_LIBS_INIT) 9 | find_package(Threads REQUIRED) 10 | endif() 11 | 12 | # Add your package dependencies for test here 13 | 14 | include_directories(${GTEST_INCLUDE_DIRS} 15 | ${CMAKE_CURRENT_SOURCE_DIR}/../examples 16 | ${CMAKE_CURRENT_SOURCE_DIR}/../lib/ansi-colors/include) 17 | 18 | set( 19 | test_program_SRCS 20 | # Add your sources here 21 | ${CMAKE_CURRENT_SOURCE_DIR}/../lib/ansi-colors/src/ansi_colors.cpp 22 | static_tests.cpp 23 | state_machine_with_internal_transitions.cpp 24 | fsm_parts_test.cpp 25 | orthogonal_states_test.cpp 26 | transaction_fsm.cpp 27 | transaction_priority_fsm.cpp 28 | common_base_test.cpp 29 | vending_machine_test.cpp 30 | pushdown_tests.cpp 31 | ) 32 | add_executable(test-afsm-base ${test_program_SRCS}) 33 | target_link_libraries( 34 | test-afsm-base 35 | ${GTEST_BOTH_LIBRARIES} 36 | ${CMAKE_THREAD_LIBS_INIT} 37 | ) 38 | 39 | if (GTEST_XML_OUTPUT) 40 | set ( 41 | TEST_ARGS 42 | --gtest_output=xml:test-utils-detail.xml 43 | ) 44 | endif() 45 | 46 | add_test( 47 | NAME test-afsm-base 48 | COMMAND test-afsm-base ${TEST_ARGS} 49 | ) 50 | endif() 51 | -------------------------------------------------------------------------------- /lib/ansi-colors/include/pushkin/ansi_colors.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * ansi_colors.hpp 3 | * 4 | * Created on: Jun 2, 2016 5 | * Author: zmij 6 | */ 7 | 8 | #ifndef PUSHKIN_ANSI_COLORS_HPP_ 9 | #define PUSHKIN_ANSI_COLORS_HPP_ 10 | 11 | #include 12 | 13 | namespace psst { 14 | 15 | enum class ansi_color { 16 | // Clear the color 17 | clear = 0x0000, 18 | // Color attributes 19 | bright = 0x0001, 20 | dim = 0x0002, 21 | underline = 0x0004, 22 | 23 | foreground = 0x0008, 24 | backgound = 0x0010, 25 | // Normal colors 26 | black = 0x0020, 27 | red = 0x0040, 28 | green = 0x0080, 29 | yellow = 0x0100, 30 | blue = 0x0200, 31 | magenta = 0x0400, 32 | cyan = 0x0800, 33 | white = 0x1000, 34 | // Color mask 35 | colors = black | red | green | yellow | blue | magenta | cyan | white, 36 | // Attributes mask 37 | attributes = ~colors 38 | }; 39 | 40 | ::std::ostream& 41 | operator << (::std::ostream& os, ansi_color val); 42 | 43 | inline ansi_color 44 | operator | (ansi_color a, ansi_color b) 45 | { 46 | return static_cast( static_cast(a) | static_cast(b) ); 47 | } 48 | 49 | } /* namespace psst */ 50 | 51 | #endif /* PUSHKIN_ANSI_COLORS_HPP_ */ 52 | -------------------------------------------------------------------------------- /include/afsm/detail/exception_safety_guarantees.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * exception_safety_guarantees.hpp 3 | * 4 | * Created on: Nov 30, 2016 5 | * Author: zmij 6 | */ 7 | 8 | #ifndef AFSM_DETAIL_EXCEPTION_SAFETY_GUARANTEES_HPP_ 9 | #define AFSM_DETAIL_EXCEPTION_SAFETY_GUARANTEES_HPP_ 10 | 11 | 12 | namespace afsm { 13 | namespace def { 14 | namespace tags { 15 | 16 | /** 17 | * Default exception safety guarantee 18 | * On an exception caused by 19 | * * a guard check nothing happens 20 | * * state exit action - only data modified by exit action before exception 21 | * * transition action - previous and data modified by the transition action 22 | * * state enter action - previous and data modified by the enter action 23 | * * source state constructor - all of the above 24 | */ 25 | struct basic_exception_safety {}; 26 | /** 27 | * Strong exception safety guarantee 28 | * If an exception is thrown in the course of transition, nothing will be modified. 29 | * 30 | * Requires non-throwing swap operations 31 | * The exception will be thrown. 32 | */ 33 | struct strong_exception_safety {}; 34 | /** 35 | * Same as the strong exception safety, but the exception will be consumed 36 | * The event will be rejected and further behavior is ruled by event rejection 37 | * policies. 38 | */ 39 | struct nothrow_guarantee {}; 40 | 41 | } /* namespace tags */ 42 | } /* namespace def */ 43 | } /* namespace afsm */ 44 | 45 | 46 | #endif /* AFSM_DETAIL_EXCEPTION_SAFETY_GUARANTEES_HPP_ */ 47 | -------------------------------------------------------------------------------- /include/afsm/definition_fwd.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * definition_fwd.hpp 3 | * 4 | * Created on: 1 июня 2016 г. 5 | * Author: sergey.fedorov 6 | */ 7 | 8 | #ifndef AFSM_DEFINITION_FWD_HPP_ 9 | #define AFSM_DEFINITION_FWD_HPP_ 10 | 11 | namespace afsm { 12 | namespace def { 13 | 14 | template < typename T, typename ... Tags > 15 | struct state_def; 16 | template < typename StateType, typename ... Tags > 17 | using state = state_def< StateType, Tags... >; 18 | 19 | template < typename T, typename ... Tags > 20 | struct terminal_state; 21 | 22 | template < typename T, typename Machine, typename ... Tags > 23 | struct pushdown; 24 | template < typename T, typename Machine, typename ... Tags > 25 | struct popup; 26 | 27 | template < typename T, typename ... Tags > 28 | struct state_machine_def; 29 | template < typename T, typename ... Tags > 30 | using state_machine = state_machine_def; 31 | 32 | template < typename SourceState, typename Event, typename TargetState, 33 | typename Action = none, typename Guard = none > 34 | struct transition; 35 | template < typename Event, typename Action = none, typename Guard = none > 36 | struct internal_transition; 37 | template < typename ... T > 38 | struct transition_table; 39 | 40 | } /* namespace def */ 41 | namespace detail { 42 | 43 | template 44 | struct pushdown_state; 45 | 46 | template 47 | struct popup_state; 48 | } /* namespace detail */ 49 | } /* namespace afsm */ 50 | 51 | #endif /* AFSM_DEFINITION_FWD_HPP_ */ 52 | -------------------------------------------------------------------------------- /lib/ansi-colors/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # CMakeLists.txt for ansi-colors library 2 | # 3 | # @author zmij 4 | # @date Nov 30, 2015 5 | 6 | cmake_minimum_required(VERSION 2.6) 7 | 8 | # Set library name here 9 | set(lib_name ansi-colors) 10 | set(LIB_NAME ANSI_COLORS) 11 | 12 | if (NOT PROJECT_PREFIX) 13 | set(PROJECT_PREFIX psst) 14 | endif() 15 | set(_pname ${PROJECT_PREFIX}-${lib_name}) 16 | if (PROJECT_VERSION) 17 | set(_pversion ${PROJECT_VERSION}) 18 | else() 19 | set(_pversion 0.1.0) 20 | endif() 21 | 22 | if (${CMAKE_VERSION} VERSION_GREATER "3.0") 23 | cmake_policy(SET CMP0048 NEW) 24 | project(${_pname} VERSION ${_pversion}) 25 | else() 26 | project(${_pname}) 27 | set(PROJECT_VERSION ${_pversion}) 28 | endif() 29 | 30 | option(BUILD_EXAMPLE "Build example application" OFF) 31 | 32 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include) 33 | add_definitions("-std=c++11") 34 | 35 | set(PSST_${LIB_NAME}_LIB ${PROJECT_PREFIX}-${lib_name}) 36 | 37 | # Add subdirectories here 38 | add_subdirectory(src) 39 | if(BUILD_EXAMPLE) 40 | add_subdirectory(example) 41 | endif() 42 | 43 | get_directory_property(has_parent PARENT_DIRECTORY) 44 | if (has_parent) 45 | set(PSST_${LIB_NAME}_LIB ${PROJECT_PREFIX}-${lib_name} CACHE INTERNAL "Name of ansi-colors library target") 46 | set(PSST_${LIB_NAME}_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/include CACHE INTERNAL "Path to ansi-colors library includes" ) 47 | set(PSST_${LIB_NAME}_LINK_DIR ${CMAKE_CURRENT_BINARY_DIR}/src CACHE INTERNAL "Path to ansi-colors library dir") 48 | endif() 49 | -------------------------------------------------------------------------------- /benchmark/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # /afsm/benchmark/CMakeLists.txt 2 | # 3 | # @author zmij 4 | # @date May 26, 2016 5 | 6 | cmake_minimum_required(VERSION 2.6) 7 | 8 | if (NOT TARGET benchmark-afsm) 9 | enable_testing() 10 | 11 | find_package(ExternalProjectGBench) 12 | 13 | if (NOT CMAKE_THREAD_LIBS_INIT) 14 | find_package(Threads REQUIRED) 15 | endif() 16 | 17 | # Add your package dependencies for test here 18 | 19 | include_directories(${GBENCH_INCLUDE_DIRS} ${CMAKE_CURRENT_SOURCE_DIR}/../examples) 20 | 21 | set(benchmark_SRCS vending_benchmark.cpp defer_benchmark.cpp) 22 | add_executable(benchmark-afsm ${benchmark_SRCS}) 23 | target_link_libraries(benchmark-afsm 24 | ${GBENCH_LIBRARIES} 25 | ${CMAKE_THREAD_LIBS_INIT}) 26 | 27 | add_test( 28 | NAME benchmark-afsm 29 | COMMAND benchmark-afsm 30 | ) 31 | 32 | # Benchmark MSM 33 | find_package(Boost 1.58) 34 | if (Boost_FOUND) 35 | message(STATUS "Making benchmark with boost::msm library") 36 | include_directories(SYSTEM ${Boost_INCLUDE_DIRS}) 37 | 38 | set(test_msm_SRCS vending_msm_test.cpp) 39 | add_executable(test-msm ${test_msm_SRCS}) 40 | target_link_libraries(test-msm 41 | ${GTEST_BOTH_LIBRARIES} 42 | ${CMAKE_THREAD_LIBS_INIT}) 43 | 44 | set(benchmark_msm_SRCS vending_msm_benchmark.cpp) 45 | add_executable(benchmark-msm ${benchmark_msm_SRCS}) 46 | target_link_libraries(benchmark-msm 47 | ${GBENCH_LIBRARIES} 48 | ${CMAKE_THREAD_LIBS_INIT}) 49 | 50 | add_test( 51 | NAME benchmark-msm 52 | COMMAND benchmark-msm 53 | ) 54 | endif() 55 | endif() 56 | -------------------------------------------------------------------------------- /examples/pausing.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * minimal.cpp 3 | * 4 | * Created on: Nov 14, 2016 5 | * Author: zmij 6 | */ 7 | 8 | #include 9 | #include 10 | 11 | namespace pausing { 12 | 13 | // Events 14 | struct start {}; 15 | struct stop {}; 16 | struct pause {}; 17 | 18 | // State machine definition 19 | struct pausing_def : ::afsm::def::state_machine { 20 | struct initial : state {}; 21 | struct running : state {}; 22 | struct paused : state {}; 23 | struct terminated : terminal_state {}; 24 | 25 | using initial_state = initial; 26 | using transitions = transition_table< 27 | /* State Event Next */ 28 | tr< initial, start, running >, 29 | tr< running, pause, paused >, 30 | tr< paused, stop, terminated >, 31 | tr< running, stop, terminated > 32 | >; 33 | }; 34 | 35 | // State machine object 36 | using pausing = ::afsm::state_machine; 37 | 38 | void 39 | use() 40 | { 41 | pausing fsm; 42 | fsm.process_event(start{}); 43 | fsm.process_event(pause{}); 44 | fsm.process_event(start{}); 45 | fsm.process_event(stop{}); 46 | } 47 | 48 | } /* namespace pausing */ 49 | 50 | int 51 | main(int, char*[]) 52 | try { 53 | pausing::use(); 54 | return 0; 55 | } catch (::std::exception const& e) { 56 | ::std::cerr << "Exception: " << e.what() << "\n"; 57 | return 1; 58 | } catch (...) { 59 | ::std::cerr << "Unexpected exception\n"; 60 | return 2; 61 | } 62 | -------------------------------------------------------------------------------- /examples/vending_starter.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * vending_starter.cpp 3 | * 4 | * Created on: Nov 15, 2016 5 | * Author: zmij 6 | */ 7 | 8 | #include 9 | #include 10 | 11 | namespace vending { 12 | 13 | namespace events { 14 | 15 | struct power_on {}; 16 | struct power_off {}; 17 | 18 | } /* namespace events */ 19 | 20 | struct vending_def : ::afsm::def::state_machine { 21 | //@{ 22 | /** @name Substates definition */ 23 | struct off : state {}; 24 | struct on : state {}; 25 | //@} 26 | 27 | /** Initial state machine state */ 28 | using initial_state = off; 29 | 30 | /** State transition table */ 31 | using transitions = transition_table < 32 | /* Start Event Next */ 33 | tr< off, events::power_on, on >, 34 | tr< on, events::power_off, off > 35 | >; 36 | }; 37 | 38 | using vending_sm = ::afsm::state_machine; 39 | 40 | ::std::ostream& 41 | operator << (::std::ostream& os, vending_sm const& val) 42 | { 43 | ::std::ostream::sentry s(os); 44 | if (s) { 45 | os << (val.is_in_state< vending_sm::on >() ? "ON" : "OFF"); 46 | } 47 | return os; 48 | } 49 | 50 | 51 | void 52 | use() 53 | { 54 | vending_sm vm; 55 | ::std::cout << "Machine is " << vm << "\n"; 56 | vm.process_event(events::power_on{}); 57 | ::std::cout << "Machine is " << vm << "\n"; 58 | vm.process_event(events::power_off{}); 59 | ::std::cout << "Machine is " << vm << "\n"; 60 | } 61 | 62 | } /* namespace vending */ 63 | 64 | int 65 | main(int, char*[]) 66 | try { 67 | vending::use(); 68 | return 0; 69 | } catch (::std::exception const& e) { 70 | ::std::cerr << "Exception: " << e.what() << "\n"; 71 | return 1; 72 | } catch (...) { 73 | ::std::cerr << "Unexpected exception\n"; 74 | return 2; 75 | } 76 | -------------------------------------------------------------------------------- /examples/minimal_actions.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * minimal.cpp 3 | * 4 | * Created on: Nov 14, 2016 5 | * Author: zmij 6 | */ 7 | 8 | #include 9 | #include 10 | 11 | namespace minimal { 12 | 13 | // Events 14 | struct start {}; 15 | struct stop {}; 16 | 17 | // Actions 18 | struct do_start{ 19 | template < typename Event, typename FSM, typename SourceState, typename TargetState > 20 | void 21 | operator()(Event const&, FSM&, SourceState&, TargetState&) const 22 | { 23 | ::std::cout << "do start\n"; 24 | } 25 | }; 26 | struct do_stop { 27 | template < typename Event, typename FSM, typename SourceState, typename TargetState > 28 | void 29 | operator()(Event const&, FSM&, SourceState&, TargetState&) const 30 | { 31 | ::std::cout << "do stop\n"; 32 | } 33 | }; 34 | 35 | // State machine definition 36 | struct minimal_def : ::afsm::def::state_machine { 37 | struct initial : state {}; 38 | struct running : state {}; 39 | struct terminated : terminal_state {}; 40 | 41 | using initial_state = initial; 42 | using transitions = transition_table< 43 | /* State Event Next Action */ 44 | tr< initial, start, running, do_start >, 45 | tr< running, stop, terminated, do_stop > 46 | >; 47 | }; 48 | 49 | // State machine object 50 | using minimal = ::afsm::state_machine; 51 | 52 | void 53 | use() 54 | { 55 | minimal fsm; 56 | fsm.process_event(start{}); 57 | fsm.process_event(stop{}); 58 | } 59 | 60 | } /* namespace minimal */ 61 | 62 | int 63 | main(int, char*[]) 64 | try { 65 | minimal::use(); 66 | return 0; 67 | } catch (::std::exception const& e) { 68 | ::std::cerr << "Exception: " << e.what() << "\n"; 69 | return 1; 70 | } catch (...) { 71 | ::std::cerr << "Unexpected exception\n"; 72 | return 2; 73 | } 74 | -------------------------------------------------------------------------------- /lib/ansi-colors/src/ansi_colors.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * ansi_colors.cpp 3 | * 4 | * Created on: Jun 2, 2016 5 | * Author: zmij 6 | */ 7 | 8 | #include 9 | #include 10 | 11 | namespace psst { 12 | 13 | namespace { 14 | 15 | int const multiply_de_bruijn_bit_position[32] = 16 | { 17 | 0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, 18 | 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9 19 | }; 20 | 21 | int 22 | lowest_bit_set(unsigned int v) 23 | { 24 | return multiply_de_bruijn_bit_position[((unsigned int)((v & -v) * 0x077CB531U)) >> 27]; 25 | } 26 | 27 | char ESC = '\033'; 28 | 29 | } /* namespace */ 30 | 31 | int 32 | operator & (ansi_color a, ansi_color b) 33 | { 34 | return static_cast(a) & static_cast(b); 35 | } 36 | 37 | ::std::ostream& 38 | operator << (::std::ostream& os, ansi_color val) 39 | { 40 | ::std::ostream::sentry s(os); 41 | if (s) { 42 | if (val == ansi_color::clear) { 43 | os << ESC << "[0m"; 44 | } else { 45 | if (val & ansi_color::bright) { 46 | os << ESC << "[1m"; 47 | } else if (val & ansi_color::dim) { 48 | os << ESC << "[2m"; 49 | } 50 | if (val & ansi_color::underline) { 51 | os << ESC << "[4m"; 52 | } 53 | char fg = '3'; 54 | if (val & ansi_color::backgound) { 55 | fg = '4'; 56 | } 57 | // clear attribute bits 58 | val = static_cast(val & ansi_color::colors); 59 | if (val != ansi_color::clear) { 60 | int color_pos = lowest_bit_set(static_cast(val)) - 61 | lowest_bit_set(static_cast(ansi_color::black)); 62 | os << ESC << '[' << fg << (char)('0' + color_pos) << 'm'; 63 | } 64 | } 65 | } 66 | return os; 67 | } 68 | 69 | 70 | } /* namespace psst */ 71 | -------------------------------------------------------------------------------- /examples/minimal_entry_exit.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * minimal.cpp 3 | * 4 | * Created on: Nov 14, 2016 5 | * Author: zmij 6 | */ 7 | 8 | #include 9 | #include 10 | 11 | namespace minimal { 12 | 13 | // Events 14 | struct start {}; 15 | struct stop {}; 16 | 17 | // State machine definition 18 | struct minimal_def : ::afsm::def::state_machine { 19 | struct initial : state { 20 | template < typename Event, typename FSM > 21 | void 22 | on_exit(Event const&, FSM&) 23 | { 24 | ::std::cout << "exit initial\n"; 25 | } 26 | }; 27 | struct running : state { 28 | template < typename Event, typename FSM > 29 | void 30 | on_enter(Event const&, FSM&) 31 | { 32 | ::std::cout << "enter running\n"; 33 | } 34 | template < typename Event, typename FSM > 35 | void 36 | on_exit(Event const&, FSM&) 37 | { 38 | ::std::cout << "exit running\n"; 39 | } 40 | }; 41 | struct terminated : terminal_state { 42 | template < typename Event, typename FSM > 43 | void 44 | on_enter(Event const&, FSM&) 45 | { 46 | ::std::cout << "enter terminated\n"; 47 | } 48 | }; 49 | 50 | using initial_state = initial; 51 | using transitions = transition_table< 52 | /* State Event Next */ 53 | tr< initial, start, running >, 54 | tr< running, stop, terminated > 55 | >; 56 | }; 57 | 58 | // State machine object 59 | using minimal = ::afsm::state_machine; 60 | 61 | void 62 | use() 63 | { 64 | minimal fsm; 65 | fsm.process_event(start{}); 66 | fsm.process_event(stop{}); 67 | } 68 | 69 | } /* namespace minimal */ 70 | 71 | int 72 | main(int, char*[]) 73 | try { 74 | minimal::use(); 75 | return 0; 76 | } catch (::std::exception const& e) { 77 | ::std::cerr << "Exception: " << e.what() << "\n"; 78 | return 1; 79 | } catch (...) { 80 | ::std::cerr << "Unexpected exception\n"; 81 | return 2; 82 | } 83 | -------------------------------------------------------------------------------- /include/afsm/detail/tags.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * tags.hpp 3 | * 4 | * Created on: 1 июня 2016 г. 5 | * Author: sergey.fedorov 6 | */ 7 | 8 | #ifndef AFSM_DETAIL_TAGS_HPP_ 9 | #define AFSM_DETAIL_TAGS_HPP_ 10 | 11 | namespace afsm { 12 | namespace def { 13 | namespace tags { 14 | 15 | /** 16 | * Tag for state class. 17 | * For internal use only. 18 | */ 19 | struct state {}; 20 | /** 21 | * Tag for state machine class. 22 | * For internal use only. 23 | */ 24 | struct state_machine{}; 25 | 26 | /** 27 | * Tag for marking states with history. 28 | */ 29 | struct has_history {}; 30 | /** 31 | * Tag for marking states having common base class. 32 | * For internal use. 33 | */ 34 | struct has_common_base {}; 35 | 36 | /** 37 | * Tag for marking state having common base. 38 | */ 39 | template < typename T > 40 | struct common_base : T, has_common_base { 41 | using common_base_type = T; 42 | using common_base_tag_type = common_base; 43 | }; 44 | 45 | template <> 46 | struct common_base { 47 | using common_base_type = void; 48 | using common_base_tag_type = common_base; 49 | }; 50 | 51 | /** 52 | * Tag for marking state having common virtual base. 53 | */ 54 | template < typename T > 55 | struct virtual_common_base : virtual T, has_common_base { 56 | using common_base_type = T; 57 | using common_base_tag_type = virtual_common_base; 58 | }; 59 | 60 | template <> 61 | struct virtual_common_base { 62 | using common_base_type = void; 63 | using common_base_tag_type = virtual_common_base; 64 | }; 65 | 66 | //@{ 67 | /** @name Push/pop tags */ 68 | struct pushdown_state {}; 69 | struct popup_state {}; 70 | 71 | template < typename T > 72 | struct pushdown : pushdown_state { 73 | using pushdown_machine_type = T; 74 | }; 75 | 76 | template < typename T > 77 | struct popup : popup_state { 78 | using pushdown_machine_type = T; 79 | }; 80 | //@} 81 | 82 | struct allow_empty_enter_exit {}; 83 | struct mandatory_empty_enter_exit {}; 84 | 85 | } /* namespace tags */ 86 | } /* namespace def */ 87 | } /* namespace afsm */ 88 | 89 | #endif /* AFSM_DETAIL_TAGS_HPP_ */ 90 | -------------------------------------------------------------------------------- /benchmark/vending_benchmark.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * vending_benchmark.cpp 3 | * 4 | * Created on: Nov 18, 2016 5 | * Author: zmij 6 | */ 7 | 8 | #include 9 | #include "vending_machine.hpp" 10 | 11 | namespace vending { 12 | 13 | void 14 | AFSM_ConstructDefault(::benchmark::State& state) 15 | { 16 | while (state.KeepRunning()) { 17 | vending_machine vm; 18 | } 19 | } 20 | 21 | void 22 | AFSM_ConstructWithData(::benchmark::State& state) 23 | { 24 | while (state.KeepRunning()) { 25 | vending_machine vm{ goods_storage{ 26 | { 0, { 10, 15.0f } }, 27 | { 1, { 100, 5.0f } } 28 | }}; 29 | } 30 | } 31 | 32 | void 33 | AFSM_ProcessSingleEvent(::benchmark::State& state) 34 | { 35 | vending_machine vm{ goods_storage{ 36 | { 0, { 10, 15.0f } }, 37 | { 1, { 100, 5.0f } } 38 | }}; 39 | vm.process_event(events::power_on{}); 40 | while (state.KeepRunning()) { 41 | vm.process_event(events::money{100}); 42 | } 43 | } 44 | 45 | void 46 | AFSM_OnOffEmpty(::benchmark::State& state) // With a default transition 47 | { 48 | vending_machine vm; 49 | while(state.KeepRunning()) { 50 | vm.process_event(events::power_on{}); 51 | vm.process_event(events::power_off{}); 52 | } 53 | } 54 | 55 | void 56 | AFSM_OnOffLoaded(::benchmark::State& state) // Without a default transition 57 | { 58 | vending_machine vm{ goods_storage{ 59 | { 0, { 10, 15.0f } }, 60 | { 1, { 100, 5.0f } } 61 | }}; 62 | while(state.KeepRunning()) { 63 | vm.process_event(events::power_on{}); 64 | vm.process_event(events::power_off{}); 65 | } 66 | } 67 | 68 | void 69 | AFSM_BuyItem(::benchmark::State& state) 70 | { 71 | vending_machine vm{ goods_storage{ 72 | { 0, { 1000000000, 15.0f } }, 73 | { 1, { 1000000000, 5.0f } } 74 | }}; 75 | vm.process_event(events::power_on{}); 76 | while(state.KeepRunning()) { 77 | vm.process_event(events::money{3}); 78 | vm.process_event(events::money{3}); 79 | vm.process_event(events::select_item{1}); 80 | } 81 | } 82 | 83 | BENCHMARK(AFSM_ConstructDefault); 84 | BENCHMARK(AFSM_ConstructWithData); 85 | BENCHMARK(AFSM_ProcessSingleEvent); 86 | BENCHMARK(AFSM_OnOffEmpty); 87 | BENCHMARK(AFSM_OnOffLoaded); 88 | BENCHMARK(AFSM_BuyItem); 89 | 90 | } /* namespace vending */ 91 | 92 | BENCHMARK_MAIN(); 93 | -------------------------------------------------------------------------------- /benchmark/vending_msm_benchmark.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * vending_msm_benchmark.cpp 3 | * 4 | * Created on: Nov 18, 2016 5 | * Author: zmij 6 | */ 7 | 8 | #include 9 | 10 | #include "vending_machine_msm.hpp" 11 | 12 | namespace vending_msm { 13 | 14 | void 15 | MSM_ConstructDefault(::benchmark::State& state) 16 | { 17 | while (state.KeepRunning()) { 18 | vending_machine vm; 19 | } 20 | } 21 | 22 | void 23 | MSM_ConstructWithData(::benchmark::State& state) 24 | { 25 | while (state.KeepRunning()) { 26 | vending_machine vm{ goods_storage{ 27 | { 0, { 10, 15.0f } }, 28 | { 1, { 100, 5.0f } } 29 | }}; 30 | } 31 | } 32 | 33 | void 34 | MSM_ProcessSingleEvent(::benchmark::State& state) 35 | { 36 | vending_machine vm{ goods_storage{ 37 | { 0, { 10, 15.0f } }, 38 | { 1, { 100, 5.0f } } 39 | }}; 40 | vm.start(); 41 | vm.process_event(events::power_on{}); 42 | while (state.KeepRunning()) { 43 | vm.process_event(events::money{100}); 44 | } 45 | } 46 | 47 | void 48 | MSM_OnOffEmpty(::benchmark::State& state) // With a default transition 49 | { 50 | vending_machine vm; 51 | vm.start(); 52 | while(state.KeepRunning()) { 53 | vm.process_event(events::power_on{}); 54 | vm.process_event(events::power_off{}); 55 | } 56 | } 57 | 58 | void 59 | MSM_OnOffLoaded(::benchmark::State& state) // Without a default transition 60 | { 61 | vending_machine vm{ goods_storage{ 62 | { 0, { 10, 15.0f } }, 63 | { 1, { 100, 5.0f } } 64 | }}; 65 | vm.start(); 66 | while(state.KeepRunning()) { 67 | vm.process_event(events::power_on{}); 68 | vm.process_event(events::power_off{}); 69 | } 70 | } 71 | 72 | void 73 | MSM_BuyItem(::benchmark::State& state) 74 | { 75 | vending_machine vm{ goods_storage{ 76 | { 0, { 1000000000, 15.0f } }, 77 | { 1, { 1000000000, 5.0f } } 78 | }}; 79 | vm.start(); 80 | vm.process_event(events::power_on{}); 81 | while(state.KeepRunning()) { 82 | vm.process_event(events::money{3}); 83 | vm.process_event(events::money{3}); 84 | vm.process_event(events::select_item{1}); 85 | } 86 | } 87 | 88 | BENCHMARK(MSM_ConstructDefault); 89 | BENCHMARK(MSM_ConstructWithData); 90 | BENCHMARK(MSM_ProcessSingleEvent); 91 | BENCHMARK(MSM_OnOffEmpty); 92 | BENCHMARK(MSM_OnOffLoaded); 93 | BENCHMARK(MSM_BuyItem); 94 | 95 | 96 | } /* namespace vending_msm */ 97 | 98 | BENCHMARK_MAIN(); 99 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # CMakeLists.txt for afsm library 2 | # 3 | # @author zmij 4 | # @date Nov 30, 2015 5 | 6 | cmake_minimum_required(VERSION 2.6) 7 | 8 | # Set library name here 9 | set(lib_name afsm) 10 | string(TOUPPER ${lib_name} LIB_NAME) 11 | 12 | if (PROJECT_VERSION) 13 | set(_pversion ${PROJECT_VERSION}) 14 | else() 15 | set(_pversion 0.1.0) 16 | endif() 17 | 18 | if (${CMAKE_VERSION} VERSION_GREATER "3.0") 19 | cmake_policy(SET CMP0048 NEW) 20 | project(${lib_name} VERSION ${_pversion}) 21 | else() 22 | project(${lib_name}) 23 | set(PROJECT_VERSION ${_pversion}) 24 | endif() 25 | 26 | option(AFSM_BUILD_TESTS "Build test programs" ON) 27 | option(AFSM_BUILD_BENCHMARKS "Build benchmarks" OFF) 28 | option(AFSM_BUILD_EXAMPLES "Build example programs" OFF) 29 | 30 | option(USE_CCACHE "Use ccache for build" ON) 31 | if (USE_CCACHE) 32 | find_program(CCACHE ccache) 33 | if (CCACHE) 34 | message(STATUS "ccache found and enabled") 35 | set(CMAKE_C_COMPILER_LAUNCHER ${CCACHE}) 36 | set(CMAKE_CXX_COMPILER_LAUNCHER ${CCACHE}) 37 | else() 38 | message(WARNING "ccache enabled, but not found") 39 | endif() 40 | else() 41 | message(STATUS "ccache disabled") 42 | endif() 43 | 44 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include) 45 | 46 | set( CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules") 47 | 48 | if (NOT CMAKE_CXX_STANDARD) 49 | set(CMAKE_CXX_STANDARD 14) 50 | endif() 51 | message(STATUS "AFSM C++ standard ${CMAKE_CXX_STANDARD}") 52 | add_definitions(-Wall -Werror -Wextra -pedantic -Weffc++ 53 | -Wno-non-virtual-dtor # I really know what I am exactly doing 54 | ) 55 | 56 | set(${LIB_NAME}_LIB ${lib_name}) 57 | 58 | find_package(ExternalProjectZmijModules) 59 | find_package(ExternalProjectMetapushkin) 60 | 61 | message(STATUS "Metapushkin include dir ${METAPUSHKIN_INCLUDE_DIRS}") 62 | include_directories(${METAPUSHKIN_INCLUDE_DIRS}) 63 | 64 | add_subdirectory(include) 65 | add_subdirectory(cmake) 66 | 67 | add_library(afsm INTERFACE) 68 | add_dependencies(afsm libmetapushkin) 69 | 70 | if (AFSM_BUILD_TESTS OR AFSM_BUILD_BENCHMARKS) 71 | find_package(ExternalProjectGTest) 72 | endif() 73 | 74 | if (AFSM_BUILD_TESTS) 75 | enable_testing() 76 | add_subdirectory(test) 77 | endif() 78 | if (AFSM_BUILD_BENCHMARKS) 79 | add_subdirectory(benchmark) 80 | endif() 81 | 82 | if (AFSM_BUILD_EXAMPLES) 83 | add_subdirectory(examples) 84 | endif() 85 | 86 | get_directory_property(has_parent PARENT_DIRECTORY) 87 | if (has_parent) 88 | set(${LIB_NAME}_INCLUDE_DIRS 89 | ${CMAKE_CURRENT_SOURCE_DIR}/include 90 | ${METAPUSHKIN_INCLUDE_DIRS} 91 | CACHE INTERNAL "Path to afsm libaray includes" ) 92 | endif() 93 | -------------------------------------------------------------------------------- /lib/ansi-colors/example/ansi_colors_example.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * ansi_colors_example.cpp 3 | * 4 | * Created on: Jun 2, 2016 5 | * Author: zmij 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | 16 | namespace { 17 | 18 | using psst::ansi_color; 19 | 20 | int const field_width = 20; 21 | 22 | ::std::array<::std::string, 8> const color_names {{ 23 | "Black", 24 | "Red", 25 | "Green", 26 | "Yellow", 27 | "Blue", 28 | "Magenta", 29 | "Cyan", 30 | "White" 31 | }}; 32 | 33 | struct color_data { 34 | ansi_color color; 35 | ansi_color dim_complement; 36 | ansi_color bright_complement; 37 | }; 38 | ::std::array< color_data, 8 > const colors {{ 39 | /* Color Dim complement Bright complement*/ 40 | { ansi_color::black, ansi_color::white, ansi_color::white }, 41 | { ansi_color::red, ansi_color::white, ansi_color::black }, 42 | { ansi_color::green, ansi_color::white, ansi_color::black }, 43 | { ansi_color::yellow, ansi_color::white, ansi_color::black }, 44 | { ansi_color::blue, ansi_color::white, ansi_color::white }, 45 | { ansi_color::magenta, ansi_color::white, ansi_color::black }, 46 | { ansi_color::cyan, ansi_color::white, ansi_color::black }, 47 | { ansi_color::white, ansi_color::black, ansi_color::black }, 48 | }}; 49 | 50 | ::std::string 51 | centered(::std::string const& str, int width) 52 | { 53 | auto lmargin = (width - str.size()) / 2; 54 | auto rmargin = width - str.size() - lmargin; 55 | 56 | ::std::ostringstream os; 57 | os << ::std::setw(lmargin) << " " << str << ::std::setw(rmargin) << " "; 58 | return os.str(); 59 | } 60 | 61 | void 62 | output_color(::std::string const& name, color_data const& data) 63 | { 64 | ::std::cout 65 | << "|" 66 | << (ansi_color::foreground | data.dim_complement) 67 | << (ansi_color::backgound | ansi_color::dim | data.color ) 68 | << centered(name, field_width) << ansi_color::clear 69 | << "|" 70 | << (ansi_color::foreground | data.bright_complement) 71 | << (ansi_color::backgound | ansi_color::bright | data.color) 72 | << centered(name, field_width) << ansi_color::clear 73 | << "|" 74 | << "\n" 75 | ; 76 | } 77 | 78 | } /* namespace */ 79 | 80 | int 81 | main(int argc, char* argv[]) 82 | try { 83 | using psst::ansi_color; 84 | 85 | ::std::cout 86 | << "|" << centered("Dim", field_width) 87 | << "|" << centered("Bright", field_width) 88 | << "|\n"; 89 | 90 | for (int i = 0; i < 8; ++i) { 91 | output_color(color_names[i], colors[i]); 92 | } 93 | 94 | return 0; 95 | } catch (::std::exception const& e) { 96 | ::std::cerr << "Exception: " << e.what() << "\n"; 97 | return 1; 98 | } catch (...) { 99 | ::std::cerr << "Unexpected exception\n"; 100 | return 2; 101 | } 102 | -------------------------------------------------------------------------------- /benchmark/defer_benchmark.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * defer_benchmark.cpp 3 | * 4 | * Created on: Dec 21, 2016 5 | * Author: zmij 6 | */ 7 | 8 | #include 9 | 10 | #include 11 | 12 | 13 | namespace afsm { 14 | namespace bench { 15 | 16 | namespace events { 17 | 18 | struct a_to_b {}; 19 | struct b_to_a {}; 20 | struct b_to_c {}; 21 | struct c_to_a {}; 22 | struct c_to_b {}; 23 | 24 | } /* namespace events */ 25 | 26 | struct defer_fsm_def : ::afsm::def::state_machine_def { 27 | 28 | struct state_a : state {}; 29 | struct state_b : state { 30 | using deferred_events = type_tuple< events::a_to_b >; 31 | }; 32 | struct state_c : state { 33 | using deferred_events = type_tuple< events::a_to_b >; 34 | }; 35 | 36 | using initial_state = state_a; 37 | 38 | using transitions = transition_table< 39 | tr< state_a, events::a_to_b, state_b >, 40 | tr< state_b, events::b_to_c, state_c >, 41 | tr< state_b, events::b_to_a, state_a >, 42 | tr< state_c, events::c_to_a, state_a >, 43 | tr< state_c, events::c_to_b, state_b > 44 | >; 45 | }; 46 | 47 | using defer_fsm = ::afsm::state_machine; 48 | 49 | namespace { 50 | 51 | void 52 | enqueue_events(defer_fsm& fsm, int n) 53 | { 54 | for (int i = 0; i < n; ++i) { 55 | fsm.process_event(events::a_to_b{}); 56 | } 57 | } 58 | 59 | } /* namespace */ 60 | 61 | void 62 | DeferNoDefer(::benchmark::State& state) 63 | { 64 | while(state.KeepRunning()) { 65 | defer_fsm fsm; 66 | fsm.process_event(events::a_to_b{}); 67 | } 68 | } 69 | 70 | void 71 | DeferReject(::benchmark::State& state) 72 | { 73 | while(state.KeepRunning()) { 74 | defer_fsm fsm; 75 | fsm.process_event(events::a_to_b{}); 76 | } 77 | } 78 | 79 | void 80 | DeferEnqueue(::benchmark::State& state) 81 | { 82 | defer_fsm fsm; 83 | 84 | fsm.process_event(events::a_to_b{}); 85 | 86 | while(state.KeepRunning()) { 87 | ::benchmark::DoNotOptimize(fsm.process_event(events::a_to_b{})); 88 | } 89 | } 90 | 91 | void 92 | DeferIgnore(::benchmark::State& state) 93 | { 94 | while(state.KeepRunning()) { 95 | state.PauseTiming(); 96 | defer_fsm fsm; 97 | fsm.process_event(events::a_to_b{}); 98 | // Enqueue N events 99 | enqueue_events(fsm, state.range(0)); 100 | state.ResumeTiming(); 101 | fsm.process_event(events::b_to_c{}); 102 | } 103 | state.SetComplexityN(state.range(0)); 104 | } 105 | 106 | void 107 | DeferProcessOne(::benchmark::State& state) 108 | { 109 | while(state.KeepRunning()) { 110 | state.PauseTiming(); 111 | defer_fsm fsm; 112 | fsm.process_event(events::a_to_b{}); 113 | // Enqueue N events 114 | enqueue_events(fsm, state.range(0)); 115 | state.ResumeTiming(); 116 | fsm.process_event(events::b_to_a{}); 117 | } 118 | state.SetComplexityN(state.range(0)); 119 | } 120 | 121 | BENCHMARK(DeferNoDefer); 122 | BENCHMARK(DeferReject); 123 | BENCHMARK(DeferEnqueue); 124 | BENCHMARK(DeferIgnore)->RangeMultiplier(10)->Range(1, 100000)->Complexity(); 125 | BENCHMARK(DeferProcessOne)->RangeMultiplier(10)->Range(1, 100000)->Complexity(); 126 | 127 | } /* namespace bench */ 128 | } /* namespace afsm */ 129 | 130 | -------------------------------------------------------------------------------- /test/orthogonal_states_test.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * orthogonal_states_test.cpp 3 | * 4 | * Created on: Nov 28, 2016 5 | * Author: zmij 6 | */ 7 | 8 | #include 9 | #include 10 | 11 | namespace afsm { 12 | namespace test { 13 | 14 | namespace events { 15 | 16 | struct power_on {}; 17 | struct power_off {}; 18 | 19 | struct do_work {}; 20 | struct error {}; 21 | 22 | } /* namespace events */ 23 | 24 | struct ortho_sm_def : def::state_machine { 25 | struct off : state {}; 26 | struct on : state_machine { 27 | struct work : state_machine { 28 | struct state_a : state {}; 29 | struct state_b : state {}; 30 | using initial_state = state_a; 31 | 32 | using transitions = transition_table< 33 | tr< state_a, events::do_work, state_b >, 34 | tr< state_b, events::do_work, state_a > 35 | >; 36 | }; 37 | struct error : state_machine { 38 | struct no : state {}; 39 | struct yes : state { 40 | using internal_transitions = transition_table< 41 | in 42 | >; 43 | }; 44 | using initial_state = no; 45 | using transitions = transition_table< 46 | tr< no, events::error, yes > 47 | >; 48 | }; 49 | using orthogonal_regions = type_tuple; 50 | }; 51 | 52 | using initial_state = off; 53 | using transitions = transition_table< 54 | tr< off, events::power_on, on >, 55 | tr< on, events::power_off, off > 56 | >; 57 | }; 58 | 59 | static_assert(def::traits::has_orthogonal_regions::value, ""); 60 | static_assert(def::traits::is_state_machine::value, ""); 61 | 62 | using work_fsm = state_machine; 63 | static_assert(!::std::is_same::value, ""); 64 | static_assert(!::std::is_same::value, ""); 65 | static_assert(::psst::meta::contains::value, ""); 66 | static_assert(::psst::meta::contains::value, ""); 67 | 68 | using work_inner_fsm = inner_state_machine>; 69 | static_assert(!::std::is_same::value, ""); 70 | static_assert(!::std::is_same::value, ""); 71 | static_assert(::psst::meta::contains::value, ""); 72 | static_assert(::psst::meta::contains::value, ""); 73 | 74 | using ortho_fsm = state_machine; 75 | static_assert(def::contains_substate::value, ""); 76 | static_assert(def::contains_substate::value, ""); 77 | static_assert(def::contains_substate::value, ""); 78 | 79 | TEST(OrthogonalRegions, Simple) 80 | { 81 | ortho_fsm fsm; 82 | EXPECT_TRUE(done(fsm.process_event(events::power_on{}))); 83 | EXPECT_TRUE(fsm.is_in_state()); 84 | EXPECT_TRUE(fsm.is_in_state()); 85 | EXPECT_TRUE(fsm.is_in_state()); 86 | EXPECT_TRUE(fsm.is_in_state()); 87 | EXPECT_TRUE(fsm.is_in_state()); 88 | 89 | EXPECT_TRUE(done(fsm.process_event(events::do_work{}))); 90 | EXPECT_TRUE(done(fsm.process_event(events::error{}))); 91 | EXPECT_TRUE(fsm.is_in_state()); 92 | EXPECT_TRUE(fsm.is_in_state()); 93 | EXPECT_TRUE(fsm.is_in_state()); 94 | EXPECT_TRUE(fsm.is_in_state()); 95 | EXPECT_TRUE(fsm.is_in_state()); 96 | 97 | EXPECT_TRUE(done(fsm.process_event(events::do_work{}))); 98 | EXPECT_TRUE(done(fsm.process_event(events::error{}))); 99 | EXPECT_TRUE(fsm.is_in_state()); 100 | EXPECT_TRUE(fsm.is_in_state()); 101 | EXPECT_TRUE(fsm.is_in_state()); 102 | EXPECT_TRUE(fsm.is_in_state()); 103 | EXPECT_TRUE(fsm.is_in_state()); 104 | } 105 | 106 | } /* namespace test */ 107 | } /* namespace afsm */ 108 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Another Finite State Machine 2 | 3 | `afsm` is a finite state machine C++14 library designed for usage in multithreaded asynchronous environment. 4 | 5 | ## Inspiration and Motivation 6 | 7 | The `afsm` library was inspired by [`::boost::msm`](http://www.boost.org/doc/libs/1_62_0/libs/msm/doc/HTML/index.html) library and implemented so that the migration from `::boost::msm` was a bunch of search and replace operations. The main motivation was to create a thread-safe FSM library and to achieve decent compile times for large and complex state machines not sacrificing performance. A state machine defined with `afms` library compiles several times faster than same library defined with `::boost::msm` and has similar (or better) performance. You can find some benchmark results [here](https://github.com/zmij/afsm/wiki/Performance-Benchmarks). 8 | 9 | ## Features 10 | 11 | * Statechart features 12 | * Hierarchical states 13 | * [Entry and exit actions](https://github.com/zmij/afsm/wiki/Entry-and-Exit-Actions) 14 | * Internal transitions 15 | * [Transition actions](https://github.com/zmij/afsm/wiki/Transition-Actions) 16 | * [Transition guards (conditions)](https://github.com/zmij/afsm/wiki/Transition-Guards) 17 | * [State history](https://github.com/zmij/afsm/wiki/History) 18 | * [Event deferring](https://github.com/zmij/afsm/wiki/Event-Deferring) 19 | * [Orthogonal regions](https://github.com/zmij/afsm/wiki/Orthogonal-Regions) 20 | * Statechart extensions 21 | * Optional [event priority](https://github.com/zmij/afsm/wiki/Event-Priority) 22 | * Optional [common base](https://github.com/zmij/afsm/wiki/Common-Base) for states and easy definition of dispatching common interface calls to current state 23 | * [Pushdown automaton](https://github.com/zmij/afsm/wiki/Pushdown-Automaton) 24 | * Compile-time checks 25 | * [Thread safety](https://github.com/zmij/afsm/wiki/Thread-Safety) 26 | * Exception safety 27 | * No vtables (unless common base feature is used) 28 | * Header only 29 | * Relatively fast compile time 30 | * No external dependencies except STL 31 | 32 | ### Planned features 33 | 34 | * State machine persistense 35 | 36 | ## Synopsis 37 | 38 | Here is a UML diagram of a trivial state machine and source code that it is mapped to. 39 | ![minimal](https://cloud.githubusercontent.com/assets/2694027/20274791/f352998c-aaa6-11e6-99ec-fc63300766d7.png) 40 | 41 | ```c++ 42 | #include 43 | // Events 44 | struct start {}; 45 | struct stop {}; 46 | 47 | // State machine definition 48 | struct minimal_def : ::afsm::def::state_machine { 49 | //@{ 50 | /** @name States */ 51 | struct initial : state {}; 52 | struct running : state {}; 53 | struct terminated : terminal_state {}; 54 | //@} 55 | 56 | using initial_state = initial; 57 | using transitions = transition_table< 58 | /* State Event Next */ 59 | tr< initial, start, running >, 60 | tr< running, stop, terminated > 61 | >; 62 | }; 63 | 64 | // State machine object 65 | using minimal = ::afsm::state_machine; 66 | 67 | void use() 68 | { 69 | mimimal fsm; 70 | fsm.process_event(start{}); 71 | fsm.process_event(stop{}); 72 | } 73 | ``` 74 | 75 | You can find a tutorial covering most of basic features [here](https://github.com/zmij/afsm/wiki/Tutorial:-Vending-machine-FSM). 76 | 77 | ## Documentation 78 | 79 | Please see [project wiki](https://github.com/zmij/afsm/wiki) for documentation. *TODO* doxygen generated documentation. 80 | 81 | ## Installation 82 | 83 | The library is header only and doesn't requre build or installation. Just add the `afsm/include` and `lib/meta/include` directories under the root of this repository to your include paths. 84 | 85 | ### CMake subproject 86 | 87 | You can add the library to your project as a subtree, e.g. `lib/afsm`, and in your root `CMakeLists.txt` file just do the following: 88 | 89 | ```cmake 90 | add_subdirectory(lib/afsm) 91 | include_directories(${AFSM_INCLUDE_DIRS}) 92 | ``` 93 | 94 | TODO write docs on gitrc subtree commands and link to the repository 95 | 96 | ### Installation to System Directories 97 | 98 | ```bash 99 | git clone git@github.com:zmij/afsm.git 100 | mkdir afsm/build 101 | cd afsm/build 102 | cmake .. 103 | sudo make install 104 | ``` 105 | 106 | ### Finding the AFSM Package 107 | 108 | ```cmake 109 | find_package(AFSM REQUIRED) # Will set AFSM_INCLUDE_DIRS variable 110 | ``` 111 | 112 | ## License 113 | 114 | [The Artistic License 2.0](https://github.com/zmij/afsm/blob/develop/LICENSE) 115 | -------------------------------------------------------------------------------- /test/transaction_common.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * transaction_events.hpp 3 | * 4 | * Created on: 28 нояб. 2016 г. 5 | * Author: sergey.fedorov 6 | */ 7 | 8 | #ifndef TEST_TRANSACTION_COMMON_HPP_ 9 | #define TEST_TRANSACTION_COMMON_HPP_ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include "test_observer.hpp" 18 | 19 | namespace afsm { 20 | namespace test { 21 | 22 | namespace events { 23 | 24 | struct connect { 25 | static ::std::string const name; 26 | }; 27 | struct complete { 28 | static ::std::string const name; 29 | }; 30 | struct ready_for_query { 31 | static ::std::string const name; 32 | }; 33 | struct begin { 34 | static ::std::string const name; 35 | }; 36 | struct terminate { 37 | static ::std::string const name; 38 | }; 39 | 40 | struct execute { 41 | static ::std::string const name; 42 | }; 43 | struct exec_prepared { 44 | static ::std::string const name; 45 | }; 46 | 47 | struct commit { 48 | static ::std::string const name; 49 | }; 50 | struct rollback { 51 | static ::std::string const name; 52 | }; 53 | 54 | struct conn_error { 55 | static ::std::string const name; 56 | }; 57 | struct query_error { 58 | static ::std::string const name; 59 | }; 60 | struct client_error { 61 | static ::std::string const name; 62 | }; 63 | 64 | struct row_description { 65 | static ::std::string const name; 66 | }; 67 | struct no_data { 68 | static ::std::string const name; 69 | }; 70 | struct row_event { 71 | static ::std::string const name; 72 | }; 73 | struct command_complete { 74 | static ::std::string const name; 75 | }; 76 | 77 | } /* namespace events */ 78 | 79 | struct transit_action { 80 | template < typename Event, typename FSM, typename SourceState, typename TargetState > 81 | void 82 | operator()(Event&&, FSM&, SourceState& src, TargetState& tgt) const 83 | { 84 | using ::psst::ansi_color; 85 | ::std::cerr 86 | << (ansi_color::cyan | ansi_color::bright) 87 | << ::std::setw(event_name_width) << ::std::left 88 | << Event::name << ansi_color::clear 89 | << ": " << src.name() << " -> " << tgt.name() << "\n"; 90 | } 91 | template < typename FSM, typename SourceState, typename TargetState > 92 | void 93 | operator()(none&&, FSM&, SourceState& src, TargetState& tgt) const 94 | { 95 | using ::psst::ansi_color; 96 | ::std::cerr 97 | << (ansi_color::red | ansi_color::bright) 98 | << ::std::setw(event_name_width) << ::std::left 99 | << "[default]" << ansi_color::clear 100 | << ": " << src.name() << " -> " << tgt.name() << "\n"; 101 | } 102 | }; 103 | 104 | struct state_name { 105 | virtual ~state_name() {} 106 | virtual ::std::string 107 | name() const = 0; 108 | 109 | template < typename Event, typename FSM > 110 | void 111 | on_enter(Event&&, FSM&) 112 | { 113 | using decayed_event = typename ::std::decay::type; 114 | using ::psst::ansi_color; 115 | ::std::cerr 116 | << (ansi_color::blue | ansi_color::bright) 117 | << ::std::setw(event_name_width) << ::std::left 118 | << decayed_event::name << ansi_color::clear 119 | << ": Enter " << name() << "\n"; 120 | } 121 | template < typename FSM > 122 | void 123 | on_enter(none&&, FSM&) 124 | { 125 | using ::psst::ansi_color; 126 | ::std::cerr 127 | << ansi_color::red 128 | << ::std::setw(event_name_width) << ::std::left 129 | << "[default]" << ansi_color::clear 130 | << ": Enter " << name() << "\n"; 131 | } 132 | template < typename Event, typename FSM > 133 | void 134 | on_exit(Event&&, FSM&) 135 | { 136 | using decayed_event = typename ::std::decay::type; 137 | using ::psst::ansi_color; 138 | ::std::cerr 139 | << (ansi_color::cyan | ansi_color::dim) 140 | << ::std::setw(event_name_width) << ::std::left 141 | << decayed_event::name << ansi_color::clear 142 | << ": Exit " << name() << "\n"; 143 | } 144 | template < typename FSM > 145 | void 146 | on_exit(none const&, FSM&) 147 | { 148 | using ::psst::ansi_color; 149 | ::std::cerr 150 | << (ansi_color::red | ansi_color::dim) 151 | << ::std::setw(event_name_width) << ::std::left 152 | << "[default]" << ansi_color::clear 153 | << ": Exit " << name() << "\n"; 154 | } 155 | }; 156 | 157 | } /* namespace test */ 158 | } /* namespace afsm */ 159 | 160 | 161 | #endif /* TEST_TRANSACTION_COMMON_HPP_ */ 162 | -------------------------------------------------------------------------------- /examples/vending_nested_sm.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * vending_starter.cpp 3 | * 4 | * Created on: Nov 15, 2016 5 | * Author: zmij 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | namespace vending { 14 | 15 | namespace events { 16 | 17 | struct power_on {}; 18 | struct power_off {}; 19 | 20 | struct start_maintenance {}; 21 | struct end_maintenance {}; 22 | struct out_of_goods {}; 23 | 24 | struct set_price { 25 | ::std::size_t p_no; 26 | float price; 27 | }; 28 | struct withdraw_money {}; 29 | 30 | } /* namespace events */ 31 | 32 | struct goods_entry { 33 | int amount; 34 | float price; 35 | }; 36 | 37 | using goods_storage = ::std::map<::std::size_t, goods_entry>; 38 | 39 | struct vending_def : ::afsm::def::state_machine { 40 | //@{ 41 | /** @name Substates definition */ 42 | struct off : state {}; 43 | struct on : state_machine { 44 | //@{ 45 | /** @name Substates definition */ 46 | struct serving : state {}; 47 | struct maintenance : state { 48 | //@{ 49 | /** @name Actions */ 50 | struct set_price { 51 | template < typename FSM, typename SourceState, typename TargetState > 52 | void 53 | operator()(events::set_price&& price, FSM& fsm, SourceState&, TargetState&) const 54 | { 55 | root_machine(fsm).set_price(price.p_no, price.price); 56 | } 57 | }; 58 | struct clear_balance { 59 | template < typename FSM, typename SourceState, typename TargetState > 60 | void 61 | operator()(events::withdraw_money&&, FSM& fsm, SourceState&, TargetState&) const 62 | { 63 | root_machine(fsm).clear_balance(); 64 | } 65 | }; 66 | //@} 67 | /** @name In-state transitions */ 68 | using internal_transitions = transition_table< 69 | /* Event Action */ 70 | in< events::set_price, set_price >, 71 | in< events::withdraw_money, clear_balance > 72 | >; 73 | }; 74 | struct out_of_service : state {}; 75 | //@} 76 | 77 | /** Initial state machine state */ 78 | using initial_state = serving; 79 | /** State transition table */ 80 | using transitions = transition_table< 81 | /* Start Event Next */ 82 | tr< serving, events::start_maintenance, maintenance >, 83 | tr< serving, events::out_of_goods, out_of_service >, 84 | tr< out_of_service, events::start_maintenance, maintenance >, 85 | tr< maintenance, events::end_maintenance, serving > 86 | >; 87 | }; 88 | //@} 89 | 90 | /** Initial state machine state */ 91 | using initial_state = off; 92 | 93 | /** State transition table */ 94 | using transitions = transition_table < 95 | /* Start Event Next */ 96 | tr< off, events::power_on, on >, 97 | tr< on, events::power_off, off > 98 | >; 99 | 100 | /** Default constructor */ 101 | vending_def() : goods{}, balance{0} {} 102 | /** Constructor, moving goods container to data member */ 103 | vending_def(goods_storage&& g) : goods{::std::move(g)}, balance{0} {} 104 | 105 | /** 106 | * Set price for an item 107 | * @param p_no 108 | * @param price 109 | */ 110 | void 111 | set_price(::std::size_t p_no, float price) 112 | { 113 | auto f = goods.find(p_no); 114 | if (f != goods.end()) { 115 | f->second.price = price; 116 | } 117 | } 118 | void 119 | clear_balance() 120 | { balance = 0; } 121 | 122 | goods_storage goods; 123 | float balance; 124 | }; 125 | 126 | using vending_sm = ::afsm::state_machine; 127 | 128 | ::std::ostream& 129 | operator << (::std::ostream& os, vending_sm const& val) 130 | { 131 | ::std::ostream::sentry s(os); 132 | if (s) { 133 | os << (val.is_in_state< vending_sm::on >() ? "ON" : "OFF"); 134 | } 135 | return os; 136 | } 137 | 138 | 139 | void 140 | use() 141 | { 142 | vending_sm vm{ goods_storage{ 143 | {1, {10, 15.0f}}, 144 | {5, {100, 5.0f}} 145 | }}; 146 | ::std::cout << "Machine is " << vm << "\n"; 147 | vm.process_event(events::power_on{}); 148 | ::std::cout << "Machine is " << vm << "\n"; 149 | vm.process_event(events::power_off{}); 150 | ::std::cout << "Machine is " << vm << "\n"; 151 | 152 | vm.process_event(events::power_on{}); 153 | ::std::cout << "Machine is " << vm << "\n"; 154 | vm.process_event(events::start_maintenance{}); 155 | if (vm.is_in_state()) { 156 | ::std::cout << "Machine is being maintained\n"; 157 | } else { 158 | ::std::cout << "Something went wrong\n"; 159 | } 160 | vm.process_event(events::power_off{}); 161 | ::std::cout << "Machine is " << vm << "\n"; 162 | vm.process_event(events::power_on{}); 163 | ::std::cout << "Machine is " << vm << "\n"; 164 | if (vm.is_in_state()) { 165 | ::std::cout << "Something went wrong\n"; 166 | } else { 167 | ::std::cout << "Machine is not being maintained\n"; 168 | } 169 | } 170 | 171 | } /* namespace vending */ 172 | 173 | int 174 | main(int, char*[]) 175 | try { 176 | vending::use(); 177 | return 0; 178 | } catch (::std::exception const& e) { 179 | ::std::cerr << "Exception: " << e.what() << "\n"; 180 | return 1; 181 | } catch (...) { 182 | ::std::cerr << "Unexpected exception\n"; 183 | return 2; 184 | } 185 | -------------------------------------------------------------------------------- /include/afsm/detail/def_traits.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * def_traits.hpp 3 | * 4 | * Created on: 1 июня 2016 г. 5 | * Author: sergey.fedorov 6 | */ 7 | 8 | #ifndef AFSM_DETAIL_DEF_TRAITS_HPP_ 9 | #define AFSM_DETAIL_DEF_TRAITS_HPP_ 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | namespace afsm { 16 | namespace def { 17 | namespace traits { 18 | 19 | template < typename T > 20 | struct is_transition : ::std::false_type {}; 21 | 22 | template < typename SourceState, typename Event, typename TargetState, 23 | typename Action, typename Guard> 24 | struct is_transition< transition > 25 | : ::std::true_type{}; 26 | template < typename Event, typename Action, typename Guard > 27 | struct is_transition< internal_transition< Event, Action, Guard > > 28 | : ::std::true_type {}; 29 | 30 | template < typename T > 31 | struct is_internal_transition : ::std::false_type {}; 32 | 33 | template < typename Event, typename Action, typename Guard > 34 | struct is_internal_transition< internal_transition< Event, Action, Guard > > 35 | : ::std::true_type {}; 36 | 37 | template < typename T > 38 | struct is_state 39 | : ::std::is_base_of< tags::state, T > {}; 40 | 41 | template < typename T > 42 | struct is_terminal_state 43 | : ::std::is_base_of< terminal_state, T > {}; 44 | 45 | template < typename T > 46 | struct is_state_machine 47 | : ::std::is_base_of< tags::state_machine, T > {}; 48 | 49 | template < typename T > 50 | struct is_pushdown 51 | : ::std::is_base_of< tags::pushdown_state, T > {}; 52 | template < typename T > 53 | struct is_popup 54 | : ::std::is_base_of< tags::popup_state, T > {}; 55 | 56 | namespace detail { 57 | 58 | template < typename T, typename Machine, bool IsPush > 59 | struct pushes : ::std::false_type {}; 60 | 61 | template < typename T, typename Machine > 62 | struct pushes 63 | : ::std::integral_constant::value> {}; 65 | 66 | template < typename T, typename Machine, bool IsPop > 67 | struct pops : ::std::false_type {}; 68 | 69 | template < typename T, typename Machine > 70 | struct pops 71 | : ::std::integral_constant::value> {}; 73 | 74 | } /* namespace detail */ 75 | 76 | template < typename T, typename Machine > 77 | struct pushes : detail::pushes::value> {}; 78 | template < typename T, typename Machine > 79 | struct pops : detail::pops::value> {}; 80 | 81 | template < typename T > 82 | struct has_common_base 83 | : ::std::is_base_of< tags::has_common_base, T > {}; 84 | 85 | template < typename T > 86 | struct has_history 87 | : ::std::is_base_of< tags::has_history, T > {}; 88 | 89 | template < typename T > 90 | struct allow_empty_transition_functions 91 | : ::std::is_base_of< tags::allow_empty_enter_exit, T > {}; 92 | 93 | template < typename T > 94 | struct has_orthogonal_regions 95 | : ::std::integral_constant::value> {}; 96 | 97 | template < typename T > 98 | struct exception_safety { 99 | using type = typename ::std::conditional< 100 | ::std::is_base_of::value, 101 | tags::nothrow_guarantee, 102 | typename ::std::conditional< 103 | ::std::is_base_of::value, 104 | tags::strong_exception_safety, 105 | tags::basic_exception_safety 106 | >::type 107 | >::type; 108 | }; 109 | 110 | namespace detail { 111 | template < typename T, bool HasCommonBase > 112 | struct inner_states_def { 113 | using definition_type = T; 114 | using common_base_tag = typename definition_type::common_base_tag_type; 115 | using exception_guarantee = typename exception_safety::type; 116 | 117 | template < typename U, typename ... Tags > 118 | using state = def::state_def; 119 | template < typename U, typename ... Tags > 120 | using terminal_state = def::terminal_state; 121 | template < typename U, typename ... Tags > 122 | using state_machine = def::state_machine; 123 | template < typename U, typename M, typename ... Tags > 124 | using push = def::pushdown; 125 | template < typename U, typename M, typename ... Tags > 126 | using pop = def::popup; 127 | }; 128 | 129 | template < typename T > 130 | struct inner_states_def { 131 | using definition_type = T; 132 | using common_base_type = void; 133 | using exception_guarantee = typename exception_safety::type; 134 | 135 | template < typename U, typename ... Tags > 136 | using state = def::state_def; 137 | template < typename U, typename ... Tags > 138 | using terminal_state = def::terminal_state; 139 | template < typename U, typename ... Tags > 140 | using state_machine = def::state_machine; 141 | template < typename U, typename M, typename ... Tags > 142 | using push = def::pushdown; 143 | template < typename U, typename M, typename ... Tags > 144 | using pop = def::popup; 145 | }; 146 | 147 | } /* namespace detail */ 148 | 149 | template < typename T > 150 | struct inner_states_definitions 151 | : detail::inner_states_def::value> {}; 152 | 153 | } /* namespace traits */ 154 | } /* namespace def */ 155 | } /* namespace afsm */ 156 | 157 | #endif /* AFSM_DETAIL_DEF_TRAITS_HPP_ */ 158 | -------------------------------------------------------------------------------- /include/afsm/detail/helpers.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * helpers.hpp 3 | * 4 | * Created on: 29 мая 2016 г. 5 | * Author: sergey.fedorov 6 | */ 7 | 8 | #ifndef AFSM_DETAIL_HELPERS_HPP_ 9 | #define AFSM_DETAIL_HELPERS_HPP_ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | namespace afsm { 19 | namespace detail { 20 | 21 | template 22 | struct not_a_state; 23 | 24 | template < typename T > 25 | struct not_a_state { 26 | static_assert( def::traits::is_state::value, "Type is not a state" ); 27 | }; 28 | 29 | template < typename T, typename FSM > 30 | struct front_state_type 31 | : ::std::conditional< 32 | def::traits::is_state_machine< T >::value, 33 | inner_state_machine< T, FSM >, 34 | typename ::std::conditional< 35 | def::traits::is_state< T >::value, 36 | state< T, FSM >, 37 | not_a_state< T > 38 | >::type 39 | > {}; 40 | 41 | template < typename T, typename ... Y, typename FSM > 42 | struct front_state_type<::psst::meta::type_tuple, FSM> 43 | : front_state_type< 44 | ::psst::meta::type_tuple, 45 | typename front_state_type< T, FSM >::type > {}; 46 | 47 | template < typename T, typename FSM > 48 | struct front_state_type<::psst::meta::type_tuple, FSM> 49 | : front_state_type {}; 50 | 51 | template < typename FSM, typename T > 52 | struct front_state_tuple; 53 | 54 | template < typename FSM > 55 | struct front_state_tuple< FSM, void> { 56 | using type = ::std::tuple<>; 57 | 58 | static type 59 | construct(FSM&) 60 | { return type{}; } 61 | }; 62 | 63 | template < typename FSM, typename ... T> 64 | struct front_state_tuple< FSM, ::psst::meta::type_tuple > { 65 | using type = ::std::tuple< typename front_state_type::type ... >; 66 | using index_tuple = typename ::psst::meta::index_builder< sizeof ... (T) >::type; 67 | 68 | static type 69 | construct(FSM& fsm) 70 | { return type( typename front_state_type::type{fsm}... ); } 71 | static type 72 | copy_construct(FSM& fsm, type const& rhs) 73 | { 74 | return copy_construct(fsm, rhs, index_tuple{}); 75 | } 76 | static type 77 | move_construct(FSM& fsm, type&& rhs) 78 | { 79 | return move_construct(fsm, ::std::forward(rhs), index_tuple{}); 80 | } 81 | private: 82 | template < ::std::size_t ... Indexes > 83 | static type 84 | copy_construct(FSM& fsm, type const& rhs, ::psst::meta::indexes_tuple const&) 85 | { 86 | return type( typename front_state_type::type{ 87 | fsm, ::std::get< Indexes >(rhs)}...); 88 | } 89 | template < ::std::size_t ... Indexes > 90 | static type 91 | move_construct(FSM& fsm, type&& rhs, ::psst::meta::indexes_tuple const&) 92 | { 93 | return type( typename front_state_type::type{ 94 | fsm, ::std::move(::std::get< Indexes >(rhs))}...); 95 | } 96 | }; 97 | 98 | template < typename FSM, typename State, bool Contains > 99 | struct substate_type_impl { 100 | using full_path = typename def::state_path::type; 101 | using path = typename ::psst::meta::pop_front::type; 102 | using type = typename front_state_type::type; 103 | using front = typename ::psst::meta::front::type; 104 | }; 105 | 106 | template < typename FSM, typename State, bool IsSelf > 107 | struct substate_type_self { 108 | using type = FSM; 109 | }; 110 | 111 | template < typename FSM, typename State > 112 | struct substate_type_self {}; 113 | 114 | template < typename FSM, typename State > 115 | struct substate_type_impl 116 | : substate_type_self ::value> {}; 117 | 118 | template < typename FSM, typename State > 119 | struct substate_type 120 | : substate_type_impl::value>{}; 121 | 122 | template < typename FSM, typename StateTable > 123 | struct stack_constructor { 124 | using state_table_type = StateTable; 125 | using stack_item = state_table_type; 126 | using type = ::std::deque; 127 | static constexpr ::std::size_t size = state_table_type::size; 128 | 129 | static type 130 | construct(FSM& fsm) 131 | { 132 | type res; 133 | res.emplace_back(state_table_type{fsm}); 134 | return res; 135 | } 136 | static type 137 | copy_construct(FSM& fsm, type const& rhs) 138 | { 139 | type res; 140 | ::std::transform(rhs.begin(), rhs.end(), ::std::back_inserter(res), 141 | [fsm](stack_item const& item) 142 | { 143 | return stack_item{ fsm, item }; 144 | }); 145 | return res; 146 | } 147 | static type 148 | move_construct(FSM& fsm, type&& rhs) 149 | { 150 | type res; 151 | ::std::transform(rhs.begin(), rhs.end(), ::std::back_inserter(res), 152 | [fsm](stack_item&& item) 153 | { 154 | return stack_item{fsm, ::std::move(item) }; 155 | }); 156 | return res; 157 | } 158 | }; 159 | 160 | struct no_lock { 161 | no_lock(none&) {} 162 | }; 163 | 164 | template < typename Mutex > 165 | struct lock_guard_type { 166 | using type = ::std::lock_guard; 167 | }; 168 | 169 | template <> 170 | struct lock_guard_type { 171 | using type = no_lock; 172 | }; 173 | template <> 174 | struct lock_guard_type { 175 | using type = no_lock; 176 | }; 177 | 178 | template 179 | struct size_type { 180 | using type = ::std::atomic<::std::size_t>; 181 | }; 182 | 183 | template <> 184 | struct size_type { 185 | using type = ::std::size_t; 186 | }; 187 | template <> 188 | struct size_type { 189 | using type = ::std::size_t; 190 | }; 191 | 192 | } /* namespace detail */ 193 | } /* namespace afsm */ 194 | 195 | #endif /* AFSM_DETAIL_HELPERS_HPP_ */ 196 | -------------------------------------------------------------------------------- /test/state_machine_with_internal_transitions.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * state_machine_with_internal_transitions.cpp 3 | * 4 | * Created on: Nov 14, 2016 5 | * Author: zmij 6 | */ 7 | 8 | #include 9 | #include 10 | #include "test_observer.hpp" 11 | 12 | namespace afsm { 13 | namespace test { 14 | 15 | namespace events { 16 | 17 | struct a_to_b {}; 18 | struct b_to_a {}; 19 | struct in_state {}; 20 | 21 | } /* namespace events */ 22 | 23 | struct outer_machine : def::state_machine { 24 | struct inner_action { 25 | template < typename FSM > 26 | void 27 | operator()(events::in_state const&, FSM&) const 28 | {} 29 | }; 30 | struct transit_action { 31 | template < typename Event, typename FSM > 32 | void 33 | operator()(Event const&, FSM&) const 34 | {} 35 | }; 36 | struct state_a : def::state {}; 37 | struct state_b : def::state { 38 | using deferred_events = type_tuple< events::a_to_b >; 39 | }; 40 | 41 | using initial_state = state_a; 42 | using internal_transitions = def::transition_table< 43 | in< events::in_state, inner_action, none > 44 | >; 45 | using transitions = def::transition_table< 46 | //in< events::in_state, inner_action, none >, 47 | tr< state_a, events::a_to_b, state_b, transit_action, none >, 48 | tr< state_b, events::b_to_a, state_a, transit_action, none > 49 | >; 50 | }; 51 | 52 | } /* namespace test */ 53 | // Instantiate it 54 | template class state_machine; 55 | 56 | namespace test { 57 | 58 | using test_sm = state_machine; 59 | 60 | TEST(FSM, MachineWithInstate) 61 | { 62 | test_sm tsm; 63 | tsm.make_observer("test"); 64 | // *** INITIAL STATE *** 65 | // Check current state 66 | EXPECT_TRUE(tsm.is_in_state< outer_machine::state_a >()); 67 | EXPECT_FALSE(tsm.is_in_state< outer_machine::state_b >()); 68 | // Process in-state 69 | EXPECT_EQ( actions::event_process_result::process_in_state, 70 | tsm.process_event(events::in_state{})); 71 | // Check current state 72 | EXPECT_TRUE(tsm.is_in_state< outer_machine::state_a >()); 73 | EXPECT_FALSE(tsm.is_in_state< outer_machine::state_b >()); 74 | // *** A -> B *** 75 | // Process with transition 76 | EXPECT_EQ( actions::event_process_result::process, 77 | tsm.process_event(events::a_to_b{})); 78 | // Check current state 79 | EXPECT_TRUE(tsm.is_in_state< outer_machine::state_b >()); 80 | EXPECT_FALSE(tsm.is_in_state< outer_machine::state_a >()); 81 | // Process in-state 82 | EXPECT_EQ( actions::event_process_result::process_in_state, 83 | tsm.process_event(events::in_state{})); 84 | // Check current state 85 | EXPECT_TRUE(tsm.is_in_state< outer_machine::state_b >()); 86 | EXPECT_FALSE(tsm.is_in_state< outer_machine::state_a >()); 87 | // *** B -> A *** 88 | // Process with transition 89 | EXPECT_EQ( actions::event_process_result::process, 90 | tsm.process_event(events::b_to_a{})); 91 | // Check current state 92 | EXPECT_TRUE(tsm.is_in_state< outer_machine::state_a >()); 93 | EXPECT_FALSE(tsm.is_in_state< outer_machine::state_b >()); 94 | // Process in-state 95 | EXPECT_EQ( actions::event_process_result::process_in_state, 96 | tsm.process_event(events::in_state{})); 97 | } 98 | 99 | TEST(FSM, MachineWithInstateDefers) 100 | { 101 | test_sm tsm; 102 | tsm.make_observer("test"); 103 | // *** INITIAL STATE *** 104 | // Check current state 105 | EXPECT_TRUE(tsm.is_in_state< outer_machine::state_a >()); 106 | EXPECT_FALSE(tsm.is_in_state< outer_machine::state_b >()); 107 | // *** A -> B *** 108 | // Process with transition 109 | EXPECT_EQ( actions::event_process_result::process, 110 | tsm.process_event(events::a_to_b{})); 111 | // Check current state 112 | EXPECT_TRUE(tsm.is_in_state< outer_machine::state_b >()); 113 | EXPECT_FALSE(tsm.is_in_state< outer_machine::state_a >()); 114 | // Process in-state 115 | EXPECT_EQ( actions::event_process_result::process_in_state, 116 | tsm.process_event(events::in_state{})); 117 | // Check current state 118 | EXPECT_TRUE(tsm.is_in_state< outer_machine::state_b >()); 119 | EXPECT_FALSE(tsm.is_in_state< outer_machine::state_a >()); 120 | // Defer events 121 | EXPECT_EQ( actions::event_process_result::defer, 122 | tsm.process_event(events::a_to_b{})); 123 | EXPECT_EQ( actions::event_process_result::defer, 124 | tsm.process_event(events::a_to_b{})); 125 | // Check current state 126 | EXPECT_TRUE(tsm.is_in_state< outer_machine::state_b >()); 127 | EXPECT_FALSE(tsm.is_in_state< outer_machine::state_a >()); 128 | // Process in-state 129 | EXPECT_EQ( actions::event_process_result::process_in_state, 130 | tsm.process_event(events::in_state{})); 131 | // Check current state 132 | EXPECT_TRUE(tsm.is_in_state< outer_machine::state_b >()); 133 | EXPECT_FALSE(tsm.is_in_state< outer_machine::state_a >()); 134 | // *** B -> A -> B *** 135 | // Process with transition 136 | EXPECT_EQ( actions::event_process_result::process, 137 | tsm.process_event(events::b_to_a{})); 138 | // Check current state 139 | EXPECT_TRUE(tsm.is_in_state< outer_machine::state_b >()); 140 | EXPECT_FALSE(tsm.is_in_state< outer_machine::state_a >()); 141 | // Process in-state 142 | EXPECT_EQ( actions::event_process_result::process_in_state, 143 | tsm.process_event(events::in_state{})); 144 | // Check current state 145 | EXPECT_TRUE(tsm.is_in_state< outer_machine::state_b >()); 146 | EXPECT_FALSE(tsm.is_in_state< outer_machine::state_a >()); 147 | // *** B -> A -> B *** 148 | // Process with transition 149 | EXPECT_EQ( actions::event_process_result::process, 150 | tsm.process_event(events::b_to_a{})); 151 | // Check current state 152 | EXPECT_TRUE(tsm.is_in_state< outer_machine::state_b >()); 153 | EXPECT_FALSE(tsm.is_in_state< outer_machine::state_a >()); 154 | } 155 | 156 | } /* namespace test */ 157 | } /* namespace afsm */ 158 | 159 | -------------------------------------------------------------------------------- /test/fsm_parts_test.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * fsm_parts_test.cpp 3 | * 4 | * Created on: 29 мая 2016 г. 5 | * Author: sergey.fedorov 6 | */ 7 | 8 | #include 9 | #include 10 | 11 | namespace afsm { 12 | namespace test { 13 | 14 | namespace a { 15 | 16 | struct event_a {}; 17 | struct event_b {}; 18 | struct event_c {}; 19 | 20 | struct dummy_action { 21 | template < typename FSM, typename SourceState, typename TargetState > 22 | void 23 | operator()(event_a&&, FSM&, SourceState& source, TargetState&) const 24 | { 25 | ::std::cerr << "Dummy action triggered (a)\n"; 26 | source.value = "a"; 27 | } 28 | template < typename FSM, typename SourceState, typename TargetState > 29 | void 30 | operator()(event_b&&, FSM&, SourceState& source, TargetState&) const 31 | { 32 | ::std::cerr << "Dummy action triggered (b)\n"; 33 | source.value = "b"; 34 | } 35 | template < typename FSM, typename SourceState, typename TargetState > 36 | void 37 | operator()(event_c&&, FSM&, SourceState& source, TargetState&) const 38 | { 39 | ::std::cerr << "Dummy action triggered (c)\n"; 40 | source.value = "c"; 41 | } 42 | }; 43 | 44 | struct dummy_action_a { 45 | template < typename FSM, typename SourceState, typename TargetState > 46 | void 47 | operator()(event_a const&, FSM&, SourceState& source, TargetState&) const 48 | { 49 | ::std::cerr << "Dummy action 2 triggered (a)\n"; 50 | source.value = "dummy"; 51 | } 52 | }; 53 | 54 | struct a_guard { 55 | template 56 | bool 57 | operator()(FSM const&, State const&, Event const&) const 58 | { return true; } 59 | }; 60 | 61 | struct internal_transitions_test : def::state< internal_transitions_test > { 62 | ::std::string value = "none"; 63 | 64 | using internal_transitions = transition_table < 65 | in< event_a, dummy_action, a_guard >, 66 | in< event_a, dummy_action_a, not_< a_guard > >, 67 | in< event_b, dummy_action, none >, 68 | in< event_c, dummy_action, none >, 69 | in< event_c, dummy_action, none > 70 | >; 71 | }; 72 | 73 | struct test_state : state { 74 | using base_state = state; 75 | using base_state::process_event; 76 | test_state(enclosing_fsm_type& fsm) : base_state{fsm} {} 77 | }; 78 | 79 | TEST(FSM, InnerStateTransitions) 80 | { 81 | none n; 82 | test_state ts{n}; 83 | EXPECT_EQ("none", ts.value); 84 | EXPECT_EQ(actions::event_process_result::process_in_state, ts.process_event(event_a{})); 85 | EXPECT_EQ("a", ts.value); 86 | EXPECT_EQ(actions::event_process_result::process_in_state, ts.process_event(event_b{})); 87 | EXPECT_EQ("b", ts.value); 88 | EXPECT_EQ(actions::event_process_result::process_in_state, ts.process_event(event_c{})); 89 | EXPECT_EQ("c", ts.value); 90 | } 91 | 92 | } /* namespace a */ 93 | 94 | namespace b { 95 | 96 | struct event_ab {}; 97 | struct event_bca {}; 98 | struct inner_event {}; 99 | 100 | struct is_none { 101 | template < typename FSM, typename State > 102 | bool 103 | operator()(FSM const& fsm, State const&) 104 | { 105 | return fsm.value == "none"; 106 | } 107 | }; 108 | 109 | struct dummy_sm : detail::null_observer { 110 | }; 111 | 112 | struct inner_dispatch_test : def::state_machine< inner_dispatch_test > { 113 | struct state_a; 114 | struct state_b; 115 | struct state_c; 116 | 117 | struct inner_action { 118 | template < typename FSM > 119 | void 120 | operator()(inner_event const&, FSM& fsm, state_a&, state_a&) const 121 | { 122 | ::std::cerr << "Dummy action triggered (inner_event - a)\n"; 123 | fsm.value = "in_a"; 124 | } 125 | template < typename FSM > 126 | void 127 | operator()(inner_event const&, FSM& fsm, state_b&, state_b&) const 128 | { 129 | ::std::cerr << "Dummy action triggered (inner_event - b)\n"; 130 | fsm.value = "in_b"; 131 | } 132 | template < typename FSM > 133 | void 134 | operator()(inner_event const&, FSM& fsm, state_c&, state_c&) const 135 | { 136 | ::std::cerr << "Dummy action triggered (inner_event - c)\n"; 137 | fsm.value = "in_c"; 138 | } 139 | }; 140 | 141 | struct state_a : state< state_a > { 142 | using internal_transitions = def::transition_table < 143 | in< inner_event, inner_action, none > 144 | >; 145 | }; 146 | 147 | struct state_b : state< state_b > { 148 | using internal_transitions = def::transition_table < 149 | in< inner_event, inner_action, none > 150 | >; 151 | }; 152 | 153 | struct state_c : state< state_c > { 154 | using internal_transitions = def::transition_table < 155 | in< inner_event, inner_action, none > 156 | >; 157 | }; 158 | 159 | using transitions = transition_table < 160 | tr< state_a, event_ab, state_b, none, none >, 161 | tr< state_b, event_bca, state_c, none, is_none >, 162 | tr< state_b, event_bca, state_a, none, not_ > 163 | >; 164 | using initial_state = state_a; 165 | 166 | ::std::string value = "none"; 167 | }; 168 | 169 | struct test_sm : inner_state_machine< inner_dispatch_test, dummy_sm > { 170 | using base_state = inner_state_machine; 171 | using base_state::process_event; 172 | test_sm(enclosing_fsm_type& fsm) : base_state{fsm} {} 173 | }; 174 | 175 | dummy_sm& 176 | root_machine(dummy_sm& sm) 177 | { 178 | return sm; 179 | } 180 | 181 | dummy_sm const& 182 | root_machine(dummy_sm const& sm) 183 | { 184 | return sm; 185 | } 186 | 187 | TEST(FSM, InnerEventDispatch) 188 | { 189 | dummy_sm n; 190 | test_sm tsm{n}; 191 | EXPECT_EQ("none", tsm.value); 192 | EXPECT_EQ(test_sm::initial_state_index, tsm.current_state()); 193 | EXPECT_EQ(actions::event_process_result::process_in_state, tsm.process_event(inner_event{})); 194 | EXPECT_EQ("in_a", tsm.value); 195 | EXPECT_EQ(actions::event_process_result::process, tsm.process_event(event_ab{})); 196 | EXPECT_NE(test_sm::initial_state_index, tsm.current_state()); 197 | EXPECT_EQ(actions::event_process_result::process_in_state, tsm.process_event(inner_event{})); 198 | EXPECT_EQ("in_b", tsm.value); 199 | EXPECT_EQ(actions::event_process_result::process, tsm.process_event(event_bca{})); 200 | EXPECT_EQ(actions::event_process_result::process_in_state, tsm.process_event(inner_event{})); 201 | } 202 | 203 | } /* namespace b */ 204 | 205 | } /* namespace test */ 206 | } /* namespace afsm */ 207 | -------------------------------------------------------------------------------- /test/static_tests.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * static_tests.cpp 3 | * 4 | * Created on: May 26, 2016 5 | * Author: zmij 6 | */ 7 | 8 | #include 9 | #include 10 | 11 | namespace afsm { 12 | namespace def { 13 | namespace test { 14 | 15 | struct state_interface { 16 | }; 17 | 18 | struct eventAB{}; 19 | struct eventBC{}; 20 | struct eventAC{}; 21 | 22 | struct stateA : state< stateA > {}; 23 | struct stateB : state< stateB, tags::strong_exception_safety > {}; 24 | struct stateC : state< stateC, tags::nothrow_guarantee > {}; 25 | 26 | static_assert( traits::is_state::value, "" ); 27 | static_assert( traits::is_state::value, "" ); 28 | static_assert( traits::is_state::value, "" ); 29 | static_assert( !traits::has_history::value, "" ); 30 | 31 | struct stateD : state< stateD, tags::has_history > {}; 32 | static_assert( traits::is_state::value, "" ); 33 | static_assert( traits::has_history::value, "" ); 34 | 35 | struct stateE : state< stateE, state_interface > {}; 36 | static_assert( traits::is_state::value, "" ); 37 | static_assert( !traits::has_history::value, "" ); 38 | 39 | struct stateF : state< stateF, state_interface, tags::has_history > {}; 40 | static_assert( traits::is_state::value, "" ); 41 | static_assert( traits::has_history::value, "" ); 42 | 43 | static_assert(::std::is_same< 44 | detail::source_state< transition >::type, 45 | stateA>::value, ""); 46 | static_assert(::std::is_same< 47 | detail::target_state< transition >::type, 48 | stateB>::value, ""); 49 | using state_set_1 = ::psst::meta::unique::type; 50 | static_assert(state_set_1::size == 3, ""); 51 | static_assert(::psst::meta::contains< stateA, state_set_1 >::value, ""); 52 | static_assert(::psst::meta::contains< stateB, state_set_1 >::value, ""); 53 | static_assert(::psst::meta::all_match< traits::is_state, state_set_1 >::value, ""); 54 | 55 | using transition_table_1 = transition_table< 56 | transition, 57 | transition, 58 | transition 59 | >; 60 | using empty_transition_table = transition_table<>; 61 | 62 | static_assert(transition_table_1::inner_states::size == 3, ""); 63 | static_assert(::psst::meta::contains::value, ""); 64 | static_assert(::psst::meta::contains::value, ""); 65 | static_assert(::psst::meta::contains::value, ""); 66 | static_assert(::psst::meta::all_match< traits::is_state, transition_table_1::inner_states >::value, ""); 67 | static_assert(transition_table_1::handled_events::size == 3, ""); 68 | 69 | static_assert(detail::has_transitions::value, ""); 70 | static_assert(!detail::has_transitions::value, ""); 71 | static_assert(!detail::has_transitions::value, ""); 72 | static_assert(detail::has_inner_states::value, ""); 73 | static_assert(!detail::has_inner_states::value, ""); 74 | 75 | using events_in_state_a = ::psst::meta::find_if< 76 | def::originates_from::template type, 77 | transition_table_1::transitions 78 | >; 79 | using events_in_state_b = ::psst::meta::find_if< 80 | def::originates_from::template type, 81 | transition_table_1::transitions 82 | >; 83 | using events_in_state_c = ::psst::meta::find_if< 84 | def::originates_from::template type, 85 | transition_table_1::transitions 86 | >; 87 | static_assert(events_in_state_a::type::size == 2, ""); 88 | static_assert(events_in_state_b::type::size == 1, ""); 89 | static_assert(events_in_state_c::type::size == 0, ""); 90 | 91 | struct my_state : state { 92 | using internal_transitions = transition_table < 93 | internal_transition< eventAB >, 94 | internal_transition< eventBC > 95 | >; 96 | }; 97 | struct my_stable_state : state< my_stable_state > { 98 | }; 99 | 100 | static_assert( !::std::is_same::value, "" ); 101 | static_assert(my_state::internal_transitions::transition_map::size == 2, ""); 102 | static_assert(my_state::internal_transitions::inner_states::size == 0, ""); 103 | static_assert(my_state::internal_transitions::handled_events::size == 2, ""); 104 | static_assert( ::std::is_same::value, "" ); 105 | static_assert(!detail::has_inner_states::value, ""); 106 | static_assert(!detail::has_inner_states::value, ""); 107 | 108 | struct my_fsm : state_machine { 109 | using transitions = transition_table<>; 110 | }; 111 | 112 | static_assert(traits::is_state::value, ""); 113 | static_assert(traits::is_state_machine::value, ""); 114 | static_assert(!traits::has_orthogonal_regions::value, ""); 115 | 116 | struct ortho_fsm : state_machine { 117 | struct region_a : state {}; 118 | struct region_b : state {}; 119 | using orthogonal_regions = type_tuple; 120 | }; 121 | 122 | static_assert(traits::is_state::value, ""); 123 | static_assert(traits::is_state_machine::value, ""); 124 | static_assert(traits::has_orthogonal_regions::value, ""); 125 | 126 | //---------------------------------------------------------------------------- 127 | // Actions 128 | //---------------------------------------------------------------------------- 129 | 130 | struct action_long { 131 | template < typename Event, typename FSM, typename SourceState, typename TargetState > 132 | void 133 | operator()(Event&&, FSM&, SourceState&, TargetState&) {} 134 | }; 135 | 136 | struct action_short { 137 | template < typename Event, typename FSM > 138 | void 139 | operator()(Event&&, FSM&) {} 140 | }; 141 | 142 | static_assert(actions::detail::action_long_signature::value, ""); 143 | static_assert(!actions::detail::action_long_signature::value, ""); 144 | 145 | static_assert(!actions::detail::action_short_signature::value, ""); 146 | static_assert(actions::detail::action_short_signature::value, ""); 147 | 148 | //---------------------------------------------------------------------------- 149 | // Exception safety 150 | //---------------------------------------------------------------------------- 151 | static_assert( 152 | ::std::is_same< 153 | traits::exception_safety::type, 154 | tags::basic_exception_safety 155 | >::value, "Default exception safety"); 156 | static_assert( 157 | ::std::is_same< 158 | traits::exception_safety::type, 159 | tags::strong_exception_safety 160 | >::value, "Strong exception safety"); 161 | static_assert( 162 | ::std::is_same< 163 | traits::exception_safety::type, 164 | tags::nothrow_guarantee 165 | >::value, "No-throw exception guarantee"); 166 | 167 | } /* namespace test */ 168 | } /* namespace def */ 169 | } /* namespace afsm */ 170 | 171 | -------------------------------------------------------------------------------- /test/common_base_test.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * common_base_test.cpp 3 | * 4 | * Created on: 31 мая 2016 г. 5 | * Author: sergey.fedorov 6 | */ 7 | 8 | #include 9 | #include 10 | 11 | #include 12 | #include "test_observer.hpp" 13 | 14 | namespace afsm { 15 | namespace test { 16 | 17 | struct wake {}; 18 | struct wash {}; 19 | struct food {}; 20 | struct do_work {}; 21 | 22 | struct alarm {}; 23 | struct pillow {}; 24 | struct sleep {}; 25 | 26 | struct human_interface { 27 | virtual ~human_interface() {} 28 | 29 | virtual void 30 | work() = 0; 31 | 32 | virtual void 33 | sleep() = 0; 34 | }; 35 | 36 | struct dummy_action { 37 | template< typename FSM > 38 | void 39 | operator()(wash const&, FSM&) 40 | { 41 | ::std::cerr << "Brrr!\n"; 42 | } 43 | template< typename FSM > 44 | void 45 | operator()(do_work const&, FSM&) 46 | { 47 | ::std::cerr << "Enough work!\n"; 48 | } 49 | }; 50 | 51 | struct sleep_action { 52 | template < typename FSM > 53 | void 54 | operator()(sleep const&, FSM& fsm) const 55 | { 56 | if (fsm.fatigue > 0) 57 | --fsm.fatigue; 58 | } 59 | }; 60 | 61 | struct work_action { 62 | template < typename FSM > 63 | void 64 | operator()(do_work const&, FSM& fsm) const 65 | { 66 | ::std::cerr << "Getting tired! " << ++fsm.fatigue << "\n"; 67 | } 68 | }; 69 | 70 | using common_base_tag = ::afsm::def::tags::common_base; 71 | 72 | struct human_def : ::afsm::def::state_machine< human_def, common_base_tag > { 73 | using fsm_type = ::afsm::state_machine; 74 | 75 | struct sleeping : state { 76 | void 77 | work() override 78 | { ::std::cerr << "Zzzzzz!\n"; } 79 | void 80 | sleep() override 81 | { ::std::cerr << "ZzzZ.Zzzz!\n"; } 82 | 83 | using internal_transitions = transition_table < 84 | in< test::sleep, sleep_action, none > 85 | >; 86 | }; 87 | 88 | struct awake : state_machine { 89 | using fsm_type = ::afsm::inner_state_machine< awake, human_def::fsm_type >; 90 | 91 | template < typename Event > 92 | void 93 | on_enter(Event&&, human_def::fsm_type& ) 94 | { 95 | ::std::cerr << "Woken up!\n"; 96 | } 97 | 98 | template < typename Event > 99 | void 100 | on_exit(Event&&, human_def::fsm_type&) 101 | { 102 | ::std::cerr << "Going to sleep\n"; 103 | } 104 | 105 | struct is_tired { 106 | template < typename FSM, typename State > 107 | bool 108 | operator()(FSM const& fsm, State const&) const 109 | { 110 | ::std::cerr << "Check tired " << fsm.fatigue << "\n"; 111 | return fsm.fatigue >= 5; 112 | } 113 | }; 114 | 115 | struct woken_up : state { 116 | using deferred_events = type_tuple< do_work >; 117 | void 118 | work() override 119 | { ::std::cerr << "Nay!\n"; } 120 | void 121 | sleep() override 122 | { ::std::cerr << "Nay!\n"; } 123 | 124 | using internal_transitions = transition_table< 125 | in< food, none, none > 126 | >; 127 | }; 128 | struct fresh : state { 129 | void 130 | work() override 131 | { ::std::cerr << "OK!\n"; } 132 | void 133 | sleep() override 134 | { ::std::cerr << "Nay!\n"; } 135 | 136 | using internal_transitions = transition_table< 137 | in< do_work, work_action, not_ > 138 | >; 139 | }; 140 | struct tired : state { 141 | void 142 | work() override 143 | { ::std::cerr << "Noooo!\n"; } 144 | void 145 | sleep() override 146 | { ::std::cerr << "Sooner the better!\n"; } 147 | }; 148 | struct sleepy : state { 149 | void 150 | work() override 151 | { ::std::cerr << "Noooo...\n"; } 152 | void 153 | sleep() override 154 | { ::std::cerr << "Yaaawn!\n"; } 155 | }; 156 | 157 | using initial_state = woken_up; 158 | using transitions = transition_table< 159 | tr< woken_up, wash, fresh, dummy_action >, 160 | tr< fresh, do_work, tired, dummy_action, is_tired >, 161 | tr< tired, food, sleepy, none > 162 | >; 163 | 164 | awake() 165 | : fatigue{0} 166 | { 167 | ::std::cerr << "Construct awake\n"; 168 | } 169 | fsm_type& 170 | fsm() 171 | { 172 | return static_cast(*this); 173 | } 174 | void 175 | work() override 176 | { 177 | fsm().current_state_base().work(); 178 | } 179 | void 180 | sleep() override 181 | { 182 | fsm().current_state_base().sleep(); 183 | } 184 | 185 | int fatigue = 0; 186 | }; 187 | 188 | using initial_state = sleeping; 189 | using transitions = transition_table < 190 | tr< sleeping, alarm, awake >, 191 | tr< awake, pillow, sleeping > 192 | >; 193 | 194 | fsm_type& 195 | fsm() 196 | { 197 | return static_cast(*this); 198 | } 199 | 200 | void 201 | work() override 202 | { 203 | fsm().current_state_base().work(); 204 | } 205 | void 206 | sleep() override 207 | { 208 | fsm().current_state_base().sleep(); 209 | } 210 | 211 | int fatigue = 0; 212 | }; 213 | 214 | using human_fsm = ::afsm::state_machine; 215 | 216 | TEST(FSM, CommonBase) 217 | { 218 | using afsm::actions::event_process_result; 219 | human_fsm hfsm; 220 | hfsm.make_observer("human_def"); 221 | 222 | EXPECT_TRUE(hfsm.is_in_state< human_fsm::sleeping >()); 223 | hfsm.work(); 224 | hfsm.sleep(); 225 | EXPECT_TRUE(hfsm.is_in_state< human_fsm::sleeping >()); 226 | 227 | EXPECT_EQ(event_process_result::process, hfsm.process_event(alarm{})); 228 | EXPECT_TRUE(hfsm.is_in_state< human_fsm::awake >()); 229 | EXPECT_TRUE(hfsm.is_in_state< human_fsm::awake::woken_up >()); 230 | 231 | hfsm.work(); 232 | hfsm.sleep(); 233 | EXPECT_EQ(event_process_result::defer, hfsm.process_event(do_work{})); 234 | EXPECT_EQ(event_process_result::defer, hfsm.process_event(do_work{})); 235 | EXPECT_EQ(event_process_result::defer, hfsm.process_event(do_work{})); 236 | EXPECT_EQ(event_process_result::process, hfsm.process_event(wash{})); 237 | hfsm.work(); 238 | hfsm.sleep(); 239 | EXPECT_EQ(event_process_result::process_in_state, hfsm.process_event(do_work{})); 240 | EXPECT_EQ(event_process_result::process_in_state, hfsm.process_event(do_work{})); 241 | EXPECT_EQ(event_process_result::process, hfsm.process_event(do_work{})); 242 | hfsm.work(); 243 | hfsm.sleep(); 244 | } 245 | 246 | } // namespace test 247 | } /* namespace afsm */ 248 | -------------------------------------------------------------------------------- /include/afsm/detail/observer.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * observer.hpp 3 | * 4 | * Created on: Jun 3, 2016 5 | * Author: zmij 6 | */ 7 | 8 | #ifndef AFSM_DETAIL_OBSERVER_HPP_ 9 | #define AFSM_DETAIL_OBSERVER_HPP_ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | namespace afsm { 17 | namespace detail { 18 | 19 | struct null_observer { 20 | template < typename FSM, typename Event > 21 | void 22 | start_process_event(FSM const&, Event const&) const noexcept {} 23 | 24 | template < typename FSM, typename State, typename Event > 25 | void 26 | state_entered(FSM const&, State const&, Event const&) const noexcept{} 27 | template < typename FSM, typename State, typename Event > 28 | void 29 | state_exited(FSM const&, State const&, Event const&) const noexcept{} 30 | template < typename FSM, typename State > 31 | void 32 | state_cleared(FSM const&, State const&) const noexcept{} 33 | 34 | template < typename FSM, typename SourceState, typename TargetState, typename Event> 35 | void 36 | state_changed(FSM const&, SourceState const&, TargetState const&, Event const&) const noexcept {} 37 | 38 | template < typename FSM, typename Event > 39 | void 40 | processed_in_state(FSM const&, Event const&) const noexcept {} 41 | 42 | template < typename FSM, typename Event > 43 | void 44 | enqueue_event(FSM const&, Event const&) const noexcept {} 45 | 46 | template < typename FSM > 47 | void 48 | start_process_events_queue(FSM const&) const noexcept {} 49 | template < typename FSM > 50 | void 51 | end_process_events_queue(FSM const&) const noexcept {} 52 | 53 | template < typename FSM, typename Event > 54 | void 55 | defer_event(FSM const&, Event const&) const noexcept {} 56 | 57 | template < typename FSM > 58 | void 59 | start_process_deferred_queue(FSM const&, ::std::size_t /*size*/) const noexcept {} 60 | template < typename FSM > 61 | void 62 | end_process_deferred_queue(FSM const&, ::std::size_t /*remain*/) const noexcept {} 63 | 64 | template < typename FSM > 65 | void 66 | skip_processing_deferred_queue(FSM const&) const noexcept {} 67 | template < typename FSM > 68 | void 69 | postpone_deferred_events(FSM const&, ::std::size_t /*count*/) const noexcept {} 70 | template < typename FSM > 71 | void 72 | drop_deferred_event(FSM const&) const noexcept{} 73 | 74 | template < typename FSM, typename Event > 75 | void 76 | reject_event(FSM const&, Event const&) const noexcept {} 77 | }; 78 | 79 | template < typename T > 80 | class observer_wrapper { 81 | public: 82 | using observer_ptr = ::std::shared_ptr; 83 | public: 84 | observer_wrapper() 85 | : observer_{} {} 86 | void 87 | set_observer(observer_ptr observer) 88 | { 89 | observer_ = observer; 90 | } 91 | 92 | template < typename ... Args > 93 | void 94 | make_observer(Args&& ... args) 95 | { 96 | observer_ = ::std::make_shared(::std::forward(args)...); 97 | } 98 | protected: 99 | template < typename FSM, typename FSM_DEF, typename Size > 100 | friend class transitions::state_transition_table; 101 | 102 | template < typename FSM, typename Event > 103 | void 104 | start_process_event(FSM const& fsm, Event const& event) const noexcept 105 | { 106 | if (observer_) 107 | observer_->start_process_event(fsm, event); 108 | } 109 | 110 | template < typename FSM, typename State, typename Event > 111 | void 112 | state_entered(FSM const& fsm, State const& state, Event const& event) const noexcept 113 | { 114 | if (observer_) 115 | observer_->state_entered(fsm, state, event); 116 | } 117 | template < typename FSM, typename State, typename Event > 118 | void 119 | state_exited(FSM const& fsm, State const& state, Event const& event) const noexcept 120 | { 121 | if (observer_) 122 | observer_->state_exited(fsm, state, event); 123 | } 124 | template < typename FSM, typename State > 125 | void 126 | state_cleared(FSM const& fsm, State const& state) const noexcept 127 | { 128 | if (observer_) 129 | observer_->state_cleared(fsm, state); 130 | } 131 | template < typename FSM, typename SourceState, typename TargetState, typename Event> 132 | void 133 | state_changed(FSM const& fsm, SourceState const& source, 134 | TargetState const& target, Event const& event) const noexcept 135 | { 136 | if (observer_) 137 | observer_->state_changed(fsm, source, target, event); 138 | } 139 | 140 | template < typename FSM, typename Event > 141 | void 142 | processed_in_state(FSM const& fsm, Event const& event) const noexcept 143 | { 144 | if (observer_) 145 | observer_->processed_in_state(fsm, event); 146 | } 147 | 148 | template < typename FSM, typename Event > 149 | void 150 | enqueue_event(FSM const& fsm, Event const& event) const noexcept 151 | { 152 | if (observer_) 153 | observer_->enqueue_event(fsm, event); 154 | } 155 | 156 | template < typename FSM > 157 | void 158 | start_process_events_queue(FSM const& fsm) const noexcept 159 | { 160 | if (observer_) 161 | observer_->start_process_events_queue(fsm); 162 | } 163 | 164 | template < typename FSM > 165 | void 166 | end_process_events_queue(FSM const& fsm) const noexcept 167 | { 168 | if (observer_) 169 | observer_->end_process_events_queue(fsm); 170 | } 171 | 172 | template < typename FSM, typename Event > 173 | void 174 | defer_event(FSM const& fsm, Event const& event) const noexcept 175 | { 176 | if (observer_) 177 | observer_->defer_event(fsm, event); 178 | } 179 | 180 | template < typename FSM > 181 | void 182 | start_process_deferred_queue(FSM const& fsm, ::std::size_t size) const noexcept 183 | { 184 | if (observer_) 185 | observer_->start_process_deferred_queue(fsm, size); 186 | } 187 | 188 | template < typename FSM > 189 | void 190 | end_process_deferred_queue(FSM const& fsm, ::std::size_t remain) const noexcept 191 | { 192 | if (observer_) 193 | observer_->end_process_deferred_queue(fsm, remain); 194 | } 195 | 196 | template < typename FSM > 197 | void 198 | skip_processing_deferred_queue(FSM const& fsm) const noexcept 199 | { 200 | if (observer_) 201 | observer_->skip_processing_deferred_queue(fsm); 202 | } 203 | template < typename FSM > 204 | void 205 | postpone_deferred_events(FSM const& fsm, ::std::size_t count) const noexcept 206 | { 207 | if (observer_) 208 | observer_->postpone_deferred_events(fsm, count); 209 | } 210 | template < typename FSM > 211 | void 212 | drop_deferred_event(FSM const& fsm) const noexcept 213 | { 214 | if (observer_) 215 | observer_->drop_deferred_event(fsm); 216 | } 217 | 218 | template < typename FSM, typename Event > 219 | void 220 | reject_event(FSM const& fsm, Event const& event) const noexcept 221 | { 222 | if (observer_) 223 | observer_->reject_event(fsm, event); 224 | } 225 | private: 226 | observer_ptr observer_; 227 | }; 228 | 229 | template <> 230 | struct observer_wrapper : null_observer { 231 | }; 232 | 233 | } /* namespace detail */ 234 | } /* namespace afsm */ 235 | 236 | 237 | 238 | #endif /* AFSM_DETAIL_OBSERVER_HPP_ */ 239 | -------------------------------------------------------------------------------- /benchmark/vending_msm_test.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * vending_msm_test.cpp 3 | * 4 | * Created on: 20 нояб. 2016 г. 5 | * Author: sergey.fedorov 6 | */ 7 | 8 | #include 9 | #include "vending_machine_msm.hpp" 10 | 11 | namespace vending_msm { 12 | 13 | TEST(VendingMSM, OnOff) 14 | { 15 | vending_machine vm; 16 | 17 | EXPECT_EQ(0ul, vm.count()) << "No goods were loaded by default constructor"; 18 | EXPECT_TRUE(vm.is_empty()) << "Vending machine is empty"; 19 | 20 | EXPECT_TRUE(vm.is_flag_active< vending_def::off >()) 21 | << "Vending machine is off"; 22 | EXPECT_FALSE(vm.is_flag_active< vending_def::on >()) 23 | << "Vending machine is not on"; 24 | 25 | EXPECT_TRUE(vm.process_event(events::power_on{})) 26 | << "Vending machine turns on correctly"; 27 | 28 | EXPECT_FALSE(vm.is_flag_active< vending_def::off >()) 29 | << "Vending machine is not off"; 30 | EXPECT_TRUE(vm.is_flag_active< vending_def::on >()) 31 | << "Vending machine is on"; 32 | EXPECT_TRUE(vm.is_flag_active< vending_def::on::out_of_service >()) 33 | << "Vending machine is out of service (empty)"; 34 | EXPECT_FALSE(vm.is_flag_active< vending_def::on::serving >()) 35 | << "Vending machine is not in serving state (empty)"; 36 | 37 | EXPECT_TRUE(vm.process_event(events::power_off{})) 38 | << "Vending machine turns off correctly"; 39 | 40 | EXPECT_TRUE(vm.is_flag_active< vending_def::off >()) 41 | << "Vending machine is off"; 42 | EXPECT_FALSE(vm.is_flag_active< vending_def::on >()) 43 | << "Vending machine is not on"; 44 | } 45 | 46 | TEST(VendingMSM, Maintenance) 47 | { 48 | vending_machine vm; 49 | 50 | // Try turn to maintenance when off 51 | EXPECT_FALSE(vm.process_event(events::start_maintenance{100500})) 52 | << "Vending machine allows maintenance only when on"; 53 | EXPECT_TRUE(vm.is_flag_active< vending_def::off >()) 54 | << "Vending machine stays off"; 55 | 56 | // Power on 57 | EXPECT_TRUE(vm.process_event(events::power_on{})) 58 | << "Vending machine turns on correctly"; 59 | 60 | // Try use an invalid code 61 | EXPECT_TRUE(vm.process_event(events::start_maintenance{100500})) 62 | << "Vending machine rejects incorrect code"; 63 | EXPECT_TRUE(vm.is_flag_active< vending_def::on >()) 64 | << "Vending machine stays on"; 65 | EXPECT_TRUE(vm.is_flag_active< vending_def::on::out_of_service >()) 66 | << "Vending machine stays in previous state"; 67 | 68 | // Use a correct code 69 | EXPECT_TRUE(vm.process_event(events::start_maintenance{vending_machine::factory_code})) 70 | << "Vending machine accepts factory code"; 71 | EXPECT_TRUE(vm.is_flag_active< vending_def::on >()) 72 | << "Vending machine stays on"; 73 | EXPECT_TRUE(vm.is_flag_active< vending_def::on::maintenance >()) 74 | << "Vending machine transits to maintenance mode"; 75 | 76 | // Exit maintenance 77 | EXPECT_TRUE(vm.process_event(events::end_maintenance{})) 78 | << "Vending machine exits maintenance"; 79 | EXPECT_TRUE(vm.is_flag_active< vending_def::on >()) 80 | << "Vending machine stays on"; 81 | EXPECT_TRUE(vm.is_flag_active< vending_def::on::out_of_service >()) 82 | << "Vending machine transits to out of service (empty)"; 83 | 84 | // Use a correct code 85 | EXPECT_TRUE(vm.process_event(events::start_maintenance{vending_machine::factory_code})) 86 | << "Vending machine accepts factory code"; 87 | EXPECT_TRUE(vm.is_flag_active< vending_def::on::maintenance >()) 88 | << "Vending machine transits to maintenance mode"; 89 | 90 | // Turn off while in maintenance mode 91 | EXPECT_TRUE(vm.process_event(events::power_off{})) 92 | << "Vending machine turns off correctly"; 93 | 94 | EXPECT_TRUE(vm.is_flag_active< vending_def::off >()) 95 | << "Vending machine is off"; 96 | EXPECT_TRUE(vm.is_flag_active< vending_def::off >()) 97 | << "Vending machine is off"; 98 | 99 | // Turn back on 100 | EXPECT_TRUE(vm.process_event(events::power_on{})) 101 | << "Vending machine turns on correctly"; 102 | EXPECT_TRUE(vm.is_flag_active< vending_def::on::maintenance >()) 103 | << "Vending machine transits to maintenance mode"; 104 | 105 | EXPECT_TRUE(vm.is_flag_active< vending_def::on::maintenance::idle >()) 106 | << "Vending machine is in idle maintenance mode"; 107 | // Load some goods 108 | EXPECT_TRUE(vm.process_event(events::load_goods{ 0, 10 })) 109 | << "Vending machine consumes goods"; 110 | EXPECT_TRUE(vm.is_flag_active< vending_def::on::maintenance::idle >()) 111 | << "Vending machine transits back to idle maintenance mode"; 112 | EXPECT_FALSE(vm.is_empty()); 113 | EXPECT_EQ(10, vm.count()); 114 | EXPECT_TRUE(vm.process_event(events::load_goods{ 1, 100 })) 115 | << "Vending machine consumes goods"; 116 | EXPECT_TRUE(vm.is_flag_active< vending_def::on::maintenance::idle >()) 117 | << "Vending machine transits back to idle maintenance mode"; 118 | EXPECT_FALSE(vm.is_empty()); 119 | EXPECT_EQ(110, vm.count()); 120 | EXPECT_FALSE(vm.prices_correct()); 121 | 122 | // Try leave maintenance mode without setting prices 123 | EXPECT_FALSE(vm.process_event(events::end_maintenance{})); 124 | 125 | // Set prices 126 | EXPECT_TRUE(vm.process_event(events::set_price{ 0, 10.0 })) 127 | << "Set price for item 0"; 128 | EXPECT_TRUE(vm.process_event(events::set_price{ 1, 5.0 })) 129 | << "Set price for item 1"; 130 | EXPECT_FALSE(vm.is_empty()); 131 | EXPECT_TRUE(vm.prices_correct()); 132 | // Leave maintenance mode 133 | //EXPECT_EQ(result::process, vm.process_event(events::end_maintenance{})); 134 | } 135 | 136 | TEST(VendingMSM, BuyItem) 137 | { 138 | vending_machine vm{ goods_storage{ 139 | { 0, { 10, 15.0f } }, 140 | { 1, { 100, 5.0f } } 141 | }}; 142 | 143 | auto initial_count = vm.count(); 144 | 145 | EXPECT_FALSE(vm.is_empty()) << "Vending machine is empty"; 146 | 147 | EXPECT_TRUE(vm.is_flag_active< vending_def::off >()) 148 | << "Vending machine is off"; 149 | EXPECT_FALSE(vm.is_flag_active< vending_def::on >()) 150 | << "Vending machine is not on"; 151 | 152 | EXPECT_TRUE(vm.process_event(events::power_on{})) 153 | << "Vending machine turns on correctly"; 154 | 155 | EXPECT_FALSE(vm.is_flag_active< vending_def::off >()) 156 | << "Vending machine is not off"; 157 | EXPECT_TRUE(vm.is_flag_active< vending_def::on >()) 158 | << "Vending machine is on"; 159 | EXPECT_TRUE(vm.is_flag_active< vending_def::on::serving >()) 160 | << "Vending machine is serving"; 161 | EXPECT_TRUE(vm.is_flag_active< vending_def::on::serving::idle >()) 162 | << "Vending machine is serving"; 163 | 164 | EXPECT_TRUE(vm.process_event(events::money{3})) 165 | << "Vending machine accepts money"; 166 | EXPECT_TRUE(vm.is_flag_active< vending_def::on::serving::active >()) 167 | << "Vending machine is serving"; 168 | EXPECT_TRUE(vm.process_event(events::select_item{0})) 169 | << "Vending machine doesn't allow dispensing when low balance"; 170 | EXPECT_TRUE(vm.process_event(events::select_item{42})) 171 | << "Vending machine doesn't allow dispensing non-existent items"; 172 | EXPECT_TRUE(vm.process_event(events::money{5})) 173 | << "Vending machine accepts money"; 174 | EXPECT_TRUE(vm.process_event(events::select_item{1})) 175 | << "Vending machine dispenses when enough money"; 176 | EXPECT_EQ(vm.get_price(1), vm.balance) 177 | << "Machine's balance increased by item's price"; 178 | EXPECT_EQ(initial_count - 1, vm.count()) << "Item count decreased by 1"; 179 | } 180 | 181 | } /* namespace vending_msm */ 182 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The Artistic License 2.0 2 | 3 | Copyright (c) 2000-2006, The Perl Foundation. 4 | 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | This license establishes the terms under which a given free software 11 | Package may be copied, modified, distributed, and/or redistributed. 12 | The intent is that the Copyright Holder maintains some artistic 13 | control over the development of that Package while still keeping the 14 | Package available as open source and free software. 15 | 16 | You are always permitted to make arrangements wholly outside of this 17 | license directly with the Copyright Holder of a given Package. If the 18 | terms of this license do not permit the full use that you propose to 19 | make of the Package, you should contact the Copyright Holder and seek 20 | a different licensing arrangement. 21 | 22 | Definitions 23 | 24 | "Copyright Holder" means the individual(s) or organization(s) 25 | named in the copyright notice for the entire Package. 26 | 27 | "Contributor" means any party that has contributed code or other 28 | material to the Package, in accordance with the Copyright Holder's 29 | procedures. 30 | 31 | "You" and "your" means any person who would like to copy, 32 | distribute, or modify the Package. 33 | 34 | "Package" means the collection of files distributed by the 35 | Copyright Holder, and derivatives of that collection and/or of 36 | those files. A given Package may consist of either the Standard 37 | Version, or a Modified Version. 38 | 39 | "Distribute" means providing a copy of the Package or making it 40 | accessible to anyone else, or in the case of a company or 41 | organization, to others outside of your company or organization. 42 | 43 | "Distributor Fee" means any fee that you charge for Distributing 44 | this Package or providing support for this Package to another 45 | party. It does not mean licensing fees. 46 | 47 | "Standard Version" refers to the Package if it has not been 48 | modified, or has been modified only in ways explicitly requested 49 | by the Copyright Holder. 50 | 51 | "Modified Version" means the Package, if it has been changed, and 52 | such changes were not explicitly requested by the Copyright 53 | Holder. 54 | 55 | "Original License" means this Artistic License as Distributed with 56 | the Standard Version of the Package, in its current version or as 57 | it may be modified by The Perl Foundation in the future. 58 | 59 | "Source" form means the source code, documentation source, and 60 | configuration files for the Package. 61 | 62 | "Compiled" form means the compiled bytecode, object code, binary, 63 | or any other form resulting from mechanical transformation or 64 | translation of the Source form. 65 | 66 | 67 | Permission for Use and Modification Without Distribution 68 | 69 | (1) You are permitted to use the Standard Version and create and use 70 | Modified Versions for any purpose without restriction, provided that 71 | you do not Distribute the Modified Version. 72 | 73 | 74 | Permissions for Redistribution of the Standard Version 75 | 76 | (2) You may Distribute verbatim copies of the Source form of the 77 | Standard Version of this Package in any medium without restriction, 78 | either gratis or for a Distributor Fee, provided that you duplicate 79 | all of the original copyright notices and associated disclaimers. At 80 | your discretion, such verbatim copies may or may not include a 81 | Compiled form of the Package. 82 | 83 | (3) You may apply any bug fixes, portability changes, and other 84 | modifications made available from the Copyright Holder. The resulting 85 | Package will still be considered the Standard Version, and as such 86 | will be subject to the Original License. 87 | 88 | 89 | Distribution of Modified Versions of the Package as Source 90 | 91 | (4) You may Distribute your Modified Version as Source (either gratis 92 | or for a Distributor Fee, and with or without a Compiled form of the 93 | Modified Version) provided that you clearly document how it differs 94 | from the Standard Version, including, but not limited to, documenting 95 | any non-standard features, executables, or modules, and provided that 96 | you do at least ONE of the following: 97 | 98 | (a) make the Modified Version available to the Copyright Holder 99 | of the Standard Version, under the Original License, so that the 100 | Copyright Holder may include your modifications in the Standard 101 | Version. 102 | 103 | (b) ensure that installation of your Modified Version does not 104 | prevent the user installing or running the Standard Version. In 105 | addition, the Modified Version must bear a name that is different 106 | from the name of the Standard Version. 107 | 108 | (c) allow anyone who receives a copy of the Modified Version to 109 | make the Source form of the Modified Version available to others 110 | under 111 | 112 | (i) the Original License or 113 | 114 | (ii) a license that permits the licensee to freely copy, 115 | modify and redistribute the Modified Version using the same 116 | licensing terms that apply to the copy that the licensee 117 | received, and requires that the Source form of the Modified 118 | Version, and of any works derived from it, be made freely 119 | available in that license fees are prohibited but Distributor 120 | Fees are allowed. 121 | 122 | 123 | Distribution of Compiled Forms of the Standard Version 124 | or Modified Versions without the Source 125 | 126 | (5) You may Distribute Compiled forms of the Standard Version without 127 | the Source, provided that you include complete instructions on how to 128 | get the Source of the Standard Version. Such instructions must be 129 | valid at the time of your distribution. If these instructions, at any 130 | time while you are carrying out such distribution, become invalid, you 131 | must provide new instructions on demand or cease further distribution. 132 | If you provide valid instructions or cease distribution within thirty 133 | days after you become aware that the instructions are invalid, then 134 | you do not forfeit any of your rights under this license. 135 | 136 | (6) You may Distribute a Modified Version in Compiled form without 137 | the Source, provided that you comply with Section 4 with respect to 138 | the Source of the Modified Version. 139 | 140 | 141 | Aggregating or Linking the Package 142 | 143 | (7) You may aggregate the Package (either the Standard Version or 144 | Modified Version) with other packages and Distribute the resulting 145 | aggregation provided that you do not charge a licensing fee for the 146 | Package. Distributor Fees are permitted, and licensing fees for other 147 | components in the aggregation are permitted. The terms of this license 148 | apply to the use and Distribution of the Standard or Modified Versions 149 | as included in the aggregation. 150 | 151 | (8) You are permitted to link Modified and Standard Versions with 152 | other works, to embed the Package in a larger work of your own, or to 153 | build stand-alone binary or bytecode versions of applications that 154 | include the Package, and Distribute the result without restriction, 155 | provided the result does not expose a direct interface to the Package. 156 | 157 | 158 | Items That are Not Considered Part of a Modified Version 159 | 160 | (9) Works (including, but not limited to, modules and scripts) that 161 | merely extend or make use of the Package, do not, by themselves, cause 162 | the Package to be a Modified Version. In addition, such works are not 163 | considered parts of the Package itself, and are not subject to the 164 | terms of this license. 165 | 166 | 167 | General Provisions 168 | 169 | (10) Any use, modification, and distribution of the Standard or 170 | Modified Versions is governed by this Artistic License. By using, 171 | modifying or distributing the Package, you accept this license. Do not 172 | use, modify, or distribute the Package, if you do not accept this 173 | license. 174 | 175 | (11) If your Modified Version has been derived from a Modified 176 | Version made by someone other than you, you are nevertheless required 177 | to ensure that your Modified Version complies with the requirements of 178 | this license. 179 | 180 | (12) This license does not grant you the right to use any trademark, 181 | service mark, tradename, or logo of the Copyright Holder. 182 | 183 | (13) This license includes the non-exclusive, worldwide, 184 | free-of-charge patent license to make, have made, use, offer to sell, 185 | sell, import and otherwise transfer the Package with respect to any 186 | patent claims licensable by the Copyright Holder that are necessarily 187 | infringed by the Package. If you institute patent litigation 188 | (including a cross-claim or counterclaim) against any party alleging 189 | that the Package constitutes direct or contributory patent 190 | infringement, then this Artistic License to you shall terminate on the 191 | date that such litigation is filed. 192 | 193 | (14) Disclaimer of Warranty: 194 | THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS 195 | IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. THE IMPLIED 196 | WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR 197 | NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY YOUR LOCAL 198 | LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR CONTRIBUTOR WILL 199 | BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 200 | DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE, EVEN IF 201 | ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 202 | -------------------------------------------------------------------------------- /lib/ansi-colors/LICENSE: -------------------------------------------------------------------------------- 1 | The Artistic License 2.0 2 | 3 | Copyright (c) 2000-2006, The Perl Foundation. 4 | 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | This license establishes the terms under which a given free software 11 | Package may be copied, modified, distributed, and/or redistributed. 12 | The intent is that the Copyright Holder maintains some artistic 13 | control over the development of that Package while still keeping the 14 | Package available as open source and free software. 15 | 16 | You are always permitted to make arrangements wholly outside of this 17 | license directly with the Copyright Holder of a given Package. If the 18 | terms of this license do not permit the full use that you propose to 19 | make of the Package, you should contact the Copyright Holder and seek 20 | a different licensing arrangement. 21 | 22 | Definitions 23 | 24 | "Copyright Holder" means the individual(s) or organization(s) 25 | named in the copyright notice for the entire Package. 26 | 27 | "Contributor" means any party that has contributed code or other 28 | material to the Package, in accordance with the Copyright Holder's 29 | procedures. 30 | 31 | "You" and "your" means any person who would like to copy, 32 | distribute, or modify the Package. 33 | 34 | "Package" means the collection of files distributed by the 35 | Copyright Holder, and derivatives of that collection and/or of 36 | those files. A given Package may consist of either the Standard 37 | Version, or a Modified Version. 38 | 39 | "Distribute" means providing a copy of the Package or making it 40 | accessible to anyone else, or in the case of a company or 41 | organization, to others outside of your company or organization. 42 | 43 | "Distributor Fee" means any fee that you charge for Distributing 44 | this Package or providing support for this Package to another 45 | party. It does not mean licensing fees. 46 | 47 | "Standard Version" refers to the Package if it has not been 48 | modified, or has been modified only in ways explicitly requested 49 | by the Copyright Holder. 50 | 51 | "Modified Version" means the Package, if it has been changed, and 52 | such changes were not explicitly requested by the Copyright 53 | Holder. 54 | 55 | "Original License" means this Artistic License as Distributed with 56 | the Standard Version of the Package, in its current version or as 57 | it may be modified by The Perl Foundation in the future. 58 | 59 | "Source" form means the source code, documentation source, and 60 | configuration files for the Package. 61 | 62 | "Compiled" form means the compiled bytecode, object code, binary, 63 | or any other form resulting from mechanical transformation or 64 | translation of the Source form. 65 | 66 | 67 | Permission for Use and Modification Without Distribution 68 | 69 | (1) You are permitted to use the Standard Version and create and use 70 | Modified Versions for any purpose without restriction, provided that 71 | you do not Distribute the Modified Version. 72 | 73 | 74 | Permissions for Redistribution of the Standard Version 75 | 76 | (2) You may Distribute verbatim copies of the Source form of the 77 | Standard Version of this Package in any medium without restriction, 78 | either gratis or for a Distributor Fee, provided that you duplicate 79 | all of the original copyright notices and associated disclaimers. At 80 | your discretion, such verbatim copies may or may not include a 81 | Compiled form of the Package. 82 | 83 | (3) You may apply any bug fixes, portability changes, and other 84 | modifications made available from the Copyright Holder. The resulting 85 | Package will still be considered the Standard Version, and as such 86 | will be subject to the Original License. 87 | 88 | 89 | Distribution of Modified Versions of the Package as Source 90 | 91 | (4) You may Distribute your Modified Version as Source (either gratis 92 | or for a Distributor Fee, and with or without a Compiled form of the 93 | Modified Version) provided that you clearly document how it differs 94 | from the Standard Version, including, but not limited to, documenting 95 | any non-standard features, executables, or modules, and provided that 96 | you do at least ONE of the following: 97 | 98 | (a) make the Modified Version available to the Copyright Holder 99 | of the Standard Version, under the Original License, so that the 100 | Copyright Holder may include your modifications in the Standard 101 | Version. 102 | 103 | (b) ensure that installation of your Modified Version does not 104 | prevent the user installing or running the Standard Version. In 105 | addition, the Modified Version must bear a name that is different 106 | from the name of the Standard Version. 107 | 108 | (c) allow anyone who receives a copy of the Modified Version to 109 | make the Source form of the Modified Version available to others 110 | under 111 | 112 | (i) the Original License or 113 | 114 | (ii) a license that permits the licensee to freely copy, 115 | modify and redistribute the Modified Version using the same 116 | licensing terms that apply to the copy that the licensee 117 | received, and requires that the Source form of the Modified 118 | Version, and of any works derived from it, be made freely 119 | available in that license fees are prohibited but Distributor 120 | Fees are allowed. 121 | 122 | 123 | Distribution of Compiled Forms of the Standard Version 124 | or Modified Versions without the Source 125 | 126 | (5) You may Distribute Compiled forms of the Standard Version without 127 | the Source, provided that you include complete instructions on how to 128 | get the Source of the Standard Version. Such instructions must be 129 | valid at the time of your distribution. If these instructions, at any 130 | time while you are carrying out such distribution, become invalid, you 131 | must provide new instructions on demand or cease further distribution. 132 | If you provide valid instructions or cease distribution within thirty 133 | days after you become aware that the instructions are invalid, then 134 | you do not forfeit any of your rights under this license. 135 | 136 | (6) You may Distribute a Modified Version in Compiled form without 137 | the Source, provided that you comply with Section 4 with respect to 138 | the Source of the Modified Version. 139 | 140 | 141 | Aggregating or Linking the Package 142 | 143 | (7) You may aggregate the Package (either the Standard Version or 144 | Modified Version) with other packages and Distribute the resulting 145 | aggregation provided that you do not charge a licensing fee for the 146 | Package. Distributor Fees are permitted, and licensing fees for other 147 | components in the aggregation are permitted. The terms of this license 148 | apply to the use and Distribution of the Standard or Modified Versions 149 | as included in the aggregation. 150 | 151 | (8) You are permitted to link Modified and Standard Versions with 152 | other works, to embed the Package in a larger work of your own, or to 153 | build stand-alone binary or bytecode versions of applications that 154 | include the Package, and Distribute the result without restriction, 155 | provided the result does not expose a direct interface to the Package. 156 | 157 | 158 | Items That are Not Considered Part of a Modified Version 159 | 160 | (9) Works (including, but not limited to, modules and scripts) that 161 | merely extend or make use of the Package, do not, by themselves, cause 162 | the Package to be a Modified Version. In addition, such works are not 163 | considered parts of the Package itself, and are not subject to the 164 | terms of this license. 165 | 166 | 167 | General Provisions 168 | 169 | (10) Any use, modification, and distribution of the Standard or 170 | Modified Versions is governed by this Artistic License. By using, 171 | modifying or distributing the Package, you accept this license. Do not 172 | use, modify, or distribute the Package, if you do not accept this 173 | license. 174 | 175 | (11) If your Modified Version has been derived from a Modified 176 | Version made by someone other than you, you are nevertheless required 177 | to ensure that your Modified Version complies with the requirements of 178 | this license. 179 | 180 | (12) This license does not grant you the right to use any trademark, 181 | service mark, tradename, or logo of the Copyright Holder. 182 | 183 | (13) This license includes the non-exclusive, worldwide, 184 | free-of-charge patent license to make, have made, use, offer to sell, 185 | sell, import and otherwise transfer the Package with respect to any 186 | patent claims licensable by the Copyright Holder that are necessarily 187 | infringed by the Package. If you institute patent litigation 188 | (including a cross-claim or counterclaim) against any party alleging 189 | that the Package constitutes direct or contributory patent 190 | infringement, then this Artistic License to you shall terminate on the 191 | date that such litigation is filed. 192 | 193 | (14) Disclaimer of Warranty: 194 | THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS 195 | IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. THE IMPLIED 196 | WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR 197 | NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY YOUR LOCAL 198 | LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR CONTRIBUTOR WILL 199 | BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 200 | DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE, EVEN IF 201 | ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 202 | -------------------------------------------------------------------------------- /test/test_observer.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * test_observer.hpp 3 | * 4 | * Created on: Dec 21, 2016 5 | * Author: zmij 6 | */ 7 | 8 | #ifndef TEST_OBSERVER_HPP_ 9 | #define TEST_OBSERVER_HPP_ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | namespace afsm { 20 | namespace test { 21 | 22 | namespace { 23 | 24 | int const event_name_width = 17; 25 | 26 | } /* namespace */ 27 | 28 | inline ::std::vector< ::std::string > 29 | split_qualified_name(::std::string const& name) 30 | { 31 | ::std::vector< ::std::string > names{::std::string{}}; 32 | for (auto c = name.begin(); c != name.end(); ++c) { 33 | if (*c == ':') { 34 | if (!names.back().empty()) 35 | names.push_back(::std::string{}); 36 | } else { 37 | names.back().push_back(*c); 38 | } 39 | } 40 | return names; 41 | } 42 | 43 | inline ::std::string 44 | get_name_components(::std::string const& name, int count) 45 | { 46 | auto names = split_qualified_name(name); 47 | ::std::ostringstream os; 48 | int offset = names.size() - count; 49 | if (offset < 0) 50 | offset = 0; 51 | auto b = names.begin() + offset; 52 | for (auto p = b; p != names.end(); ++p) { 53 | if (p != b) { 54 | os << "::"; 55 | } 56 | os << *p; 57 | } 58 | return os.str(); 59 | } 60 | 61 | inline ::std::string 62 | get_name_components_after(::std::string const& name, ::std::string const& start) 63 | { 64 | if (start.empty()) { 65 | return name; 66 | } else { 67 | auto names = split_qualified_name(name); 68 | ::std::ostringstream os; 69 | auto p = names.begin(); 70 | while (p != names.end() && *p != start) ++p; 71 | if (p != names.end()) { 72 | ++p; 73 | } else { 74 | p = names.begin(); 75 | } 76 | auto b = p; 77 | for (; p != names.end(); ++p) { 78 | if (p != b) 79 | os << "::"; 80 | os << *p; 81 | } 82 | 83 | return os.str(); 84 | } 85 | } 86 | 87 | struct test_fsm_observer : ::afsm::detail::null_observer { 88 | test_fsm_observer() 89 | : def_name{}{} 90 | test_fsm_observer(::std::string const& n) 91 | : def_name{n}{} 92 | 93 | template < typename FSM, typename Event > 94 | void 95 | start_process_event(FSM const&, Event const&) const noexcept 96 | { 97 | using ::psst::ansi_color; 98 | using ::psst::util::demangle; 99 | ::std::cerr 100 | << (ansi_color::green | ansi_color::dim) 101 | << ::std::setw(event_name_width) << ::std::left 102 | << get_name_components(demangle(), 1) 103 | << ansi_color::clear 104 | << ": Start processing\n"; 105 | } 106 | 107 | template < typename FSM > 108 | void 109 | start_process_event(FSM const&, none const&) const noexcept 110 | { 111 | using ::psst::ansi_color; 112 | ::std::cerr 113 | << (ansi_color::green | ansi_color::dim) 114 | << ::std::setw(event_name_width) << ::std::left 115 | << "[default]" << ansi_color::clear 116 | << ": Start processing\n"; 117 | } 118 | 119 | template < typename FSM, typename State > 120 | void 121 | state_cleared(FSM const&, State const&) const noexcept 122 | { 123 | using ::psst::ansi_color; 124 | using ::psst::util::demangle; 125 | ::std::cerr 126 | << (ansi_color::red | ansi_color::bright) 127 | << ::std::setw(event_name_width) << ::std::setfill('*') 128 | << "*" << ansi_color::clear << ::std::setfill(' ') 129 | << ": State cleared " 130 | << get_name_components_after( 131 | demangle< typename State::state_definition_type >(), 132 | def_name) <<"\n"; 133 | } 134 | template < typename FSM, typename SourceState, typename TargetState, typename Event > 135 | void 136 | state_changed(FSM const&, SourceState const&, TargetState const&, Event const&) const noexcept 137 | { 138 | using ::psst::ansi_color; 139 | using ::psst::util::demangle; 140 | ::std::cerr 141 | << (ansi_color::blue | ansi_color::bright) 142 | << ::std::setw(event_name_width) << ::std::setfill('*') 143 | << "*" << ansi_color::clear << ::std::setfill(' ') 144 | << ": State changed " 145 | << get_name_components_after( 146 | demangle< typename SourceState::state_definition_type >(), 147 | def_name) 148 | << " -> " 149 | << get_name_components_after( 150 | demangle< typename TargetState::state_definition_type >(), 151 | def_name) 152 | << " (" << get_name_components(demangle(), 1) << ")\n"; 153 | } 154 | 155 | template < typename FSM, typename Event > 156 | void 157 | processed_in_state(FSM const&, Event const&) const noexcept 158 | { 159 | using ::psst::ansi_color; 160 | using ::psst::util::demangle; 161 | ::std::cerr 162 | << (ansi_color::blue | ansi_color::dim) 163 | << ::std::setw(event_name_width) << ::std::left 164 | << get_name_components(demangle(), 1) 165 | << ansi_color::clear 166 | << ": Processed in state\n"; 167 | } 168 | 169 | template < typename FSM, typename Event > 170 | void 171 | enqueue_event(FSM const&, Event const&) const noexcept 172 | { 173 | using ::psst::ansi_color; 174 | using ::psst::util::demangle; 175 | ::std::cerr 176 | << (ansi_color::blue | ansi_color::dim) 177 | << ::std::setw(event_name_width) << ::std::left 178 | << get_name_components(demangle(), 1) 179 | << ansi_color::clear 180 | << ": Enqueue event\n"; 181 | } 182 | 183 | template < typename FSM > 184 | void 185 | start_process_events_queue(FSM const&) const noexcept 186 | { 187 | using ::psst::ansi_color; 188 | ::std::cerr 189 | << (ansi_color::blue | ansi_color::bright) 190 | << ::std::setw(event_name_width) << ::std::setfill('*') 191 | << "*" << ansi_color::clear << ::std::setfill(' ') 192 | << ": Start processing event queue\n"; 193 | } 194 | template < typename FSM > 195 | void 196 | end_process_events_queue(FSM const&) const noexcept 197 | { 198 | using ::psst::ansi_color; 199 | ::std::cerr 200 | << (ansi_color::blue | ansi_color::bright) 201 | << ::std::setw(event_name_width) << ::std::setfill('*') 202 | << "*" << ansi_color::clear << ::std::setfill(' ') 203 | << ": End processing event queue\n"; 204 | } 205 | 206 | template < typename FSM, typename Event > 207 | void 208 | defer_event(FSM const&, Event const&) const noexcept 209 | { 210 | using ::psst::ansi_color; 211 | using ::psst::util::demangle; 212 | ::std::cerr 213 | << (ansi_color::red | ansi_color::dim) 214 | << ::std::setw(event_name_width) << ::std::left 215 | << get_name_components(demangle(), 1) 216 | << ansi_color::clear 217 | << ": Defer event\n"; 218 | } 219 | 220 | template < typename FSM > 221 | void 222 | start_process_deferred_queue(FSM const&, ::std::size_t size) const noexcept 223 | { 224 | using ::psst::ansi_color; 225 | ::std::cerr 226 | << (ansi_color::blue | ansi_color::bright) 227 | << ::std::setw(event_name_width) << ::std::setfill('*') 228 | << "*" << ansi_color::clear << ::std::setfill(' ') 229 | << ": Start processing deferred event queue size " << size << "\n"; 230 | } 231 | template < typename FSM > 232 | void 233 | end_process_deferred_queue(FSM const&, ::std::size_t size) const noexcept 234 | { 235 | using ::psst::ansi_color; 236 | ::std::cerr 237 | << (ansi_color::blue | ansi_color::bright) 238 | << ::std::setw(event_name_width) << ::std::setfill('*') 239 | << "*" << ansi_color::clear << ::std::setfill(' ') 240 | << ": End processing deferred event queue remain " << size << "\n"; 241 | } 242 | template < typename FSM > 243 | void 244 | skip_processing_deferred_queue(FSM const& fsm) const noexcept 245 | { 246 | using ::psst::ansi_color; 247 | auto const& handled = fsm.current_handled_events(); 248 | auto const& deferred = fsm.current_deferred_events(); 249 | ::std::cerr 250 | << (ansi_color::yellow | ansi_color::bright) 251 | << ::std::setw(event_name_width) << ::std::setfill('*') << "*" 252 | << ansi_color::clear << ::std::setfill(' ') 253 | << ": Skip processing deferred event queue. FSM can handle " 254 | << handled.size() << " event types now. Deferred " 255 | << deferred.size() << " event types.\n" 256 | ; 257 | } 258 | template < typename FSM > 259 | void 260 | postpone_deferred_events(FSM const&, ::std::size_t count) const noexcept 261 | { 262 | using ::psst::ansi_color; 263 | ::std::cerr 264 | << (ansi_color::yellow) 265 | << ::std::setw(event_name_width) << ::std::setfill('*') << "*" 266 | << ansi_color::clear << ::std::setfill(' ') 267 | << ": Postpone " << count << " deferred events\n"; 268 | } 269 | template < typename FSM > 270 | void 271 | drop_deferred_event(FSM const&) const noexcept 272 | { 273 | using ::psst::ansi_color; 274 | ::std::cerr 275 | << (ansi_color::red | ansi_color::bright) 276 | << ::std::setw(event_name_width) << ::std::setfill('*') << "*" 277 | << ansi_color::clear << ::std::setfill(' ') 278 | << ": Drop deferred event\n"; 279 | } 280 | 281 | template < typename FSM, typename Event > 282 | void 283 | reject_event(FSM const&, Event const&) const noexcept 284 | { 285 | using ::psst::ansi_color; 286 | using ::psst::util::demangle; 287 | ::std::cerr 288 | << (ansi_color::red | ansi_color::bright) 289 | << ::std::setw(event_name_width) << ::std::left 290 | << get_name_components(demangle(), 1) 291 | << ansi_color::clear 292 | << ": Reject event.\n"; 293 | } 294 | 295 | ::std::string def_name; 296 | }; 297 | 298 | } /* namespace test */ 299 | } /* namespace afsm */ 300 | 301 | #endif /* TEST_OBSERVER_HPP_ */ 302 | -------------------------------------------------------------------------------- /test/vending_machine_test.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * vending_machine_test.cpp 3 | * 4 | * Created on: Nov 15, 2016 5 | * Author: zmij 6 | */ 7 | 8 | #include 9 | #include "vending_machine.hpp" 10 | 11 | namespace vending { 12 | 13 | using result = ::afsm::actions::event_process_result; 14 | template < typename T > 15 | using event = afsm::detail::event; 16 | 17 | TEST(Vending, OnOff) 18 | { 19 | vending_machine vm; 20 | 21 | EXPECT_EQ(0ul, vm.count()) << "No goods were loaded by default constructor"; 22 | EXPECT_TRUE(vm.is_empty()) << "Vending machine is empty"; 23 | 24 | EXPECT_TRUE(vm.is_in_state< vending_def::off >()) 25 | << "Vending machine is off"; 26 | EXPECT_FALSE(vm.is_in_state< vending_def::on >()) 27 | << "Vending machine is not on"; 28 | 29 | EXPECT_TRUE(done(vm.process_event(events::power_on{}))) 30 | << "Vending machine turns on correctly"; 31 | 32 | EXPECT_FALSE(vm.is_in_state< vending_def::off >()) 33 | << "Vending machine is not off"; 34 | EXPECT_TRUE(vm.is_in_state< vending_def::on >()) 35 | << "Vending machine is on"; 36 | EXPECT_TRUE(vm.is_in_state< vending_def::on::out_of_service >()) 37 | << "Vending machine is out of service (empty)"; 38 | 39 | EXPECT_TRUE(done(vm.process_event(events::power_off{}))) 40 | << "Vending machine turns off correctly"; 41 | 42 | EXPECT_TRUE(vm.is_in_state< vending_def::off >()) 43 | << "Vending machine is off"; 44 | EXPECT_FALSE(vm.is_in_state< vending_def::on >()) 45 | << "Vending machine is not on"; 46 | } 47 | 48 | TEST(Vending, Maintenance) 49 | { 50 | vending_machine vm; 51 | 52 | // Try turn to maintenance when off 53 | EXPECT_FALSE(done(vm.process_event(events::start_maintenance{100500}))) 54 | << "Vending machine allows maintenance only when on"; 55 | EXPECT_TRUE(vm.is_in_state< vending_def::off >()) 56 | << "Vending machine stays off"; 57 | 58 | // Power on 59 | EXPECT_TRUE(done(vm.process_event(events::power_on{}))) 60 | << "Vending machine turns on correctly"; 61 | 62 | // Try use an invalid code 63 | EXPECT_FALSE(ok(vm.process_event(events::start_maintenance{100500}))) 64 | << "Vending machine rejects incorrect code"; 65 | EXPECT_TRUE(vm.is_in_state< vending_def::on >()) 66 | << "Vending machine stays on"; 67 | EXPECT_TRUE(vm.is_in_state< vending_def::on::out_of_service >()) 68 | << "Vending machine stays in previous state"; 69 | 70 | // Use a correct code 71 | EXPECT_TRUE(done(vm.process_event(events::start_maintenance{vending_machine::factory_code}))) 72 | << "Vending machine accepts factory code"; 73 | EXPECT_TRUE(vm.is_in_state< vending_def::on >()) 74 | << "Vending machine stays on"; 75 | EXPECT_TRUE(vm.is_in_state< vending_def::on::maintenance >()) 76 | << "Vending machine transits to maintenance mode"; 77 | 78 | // Exit maintenance 79 | EXPECT_TRUE(done(vm.process_event(events::end_maintenance{}))) 80 | << "Vending machine exits maintenance"; 81 | EXPECT_TRUE(vm.is_in_state< vending_def::on >()) 82 | << "Vending machine stays on"; 83 | EXPECT_TRUE(vm.is_in_state< vending_def::on::out_of_service >()) 84 | << "Vending machine transits to out of service (empty)"; 85 | 86 | // Use a correct code 87 | EXPECT_TRUE(done(vm.process_event(events::start_maintenance{vending_machine::factory_code}))) 88 | << "Vending machine accepts factory code"; 89 | EXPECT_TRUE(vm.is_in_state< vending_def::on::maintenance >()) 90 | << "Vending machine transits to maintenance mode"; 91 | 92 | // Turn off while in maintenance mode 93 | EXPECT_TRUE(done(vm.process_event(events::power_off{}))) 94 | << "Vending machine turns off correctly"; 95 | 96 | EXPECT_TRUE(vm.is_in_state< vending_def::off >()) 97 | << "Vending machine is off"; 98 | EXPECT_TRUE(vm.is_in_state< vending_def::off >()) 99 | << "Vending machine is off"; 100 | 101 | // Turn back on 102 | EXPECT_TRUE(done(vm.process_event(events::power_on{}))) 103 | << "Vending machine turns on correctly"; 104 | EXPECT_TRUE(vm.is_in_state< vending_def::on::maintenance >()) 105 | << "Vending machine transits to maintenance mode"; 106 | 107 | EXPECT_TRUE(vm.is_in_state< vending_def::on::maintenance::idle >()) 108 | << "Vending machine is in idle maintenance mode"; 109 | // Load some goods 110 | EXPECT_TRUE(done(vm.process_event(events::load_goods{ 0, 10 }))) 111 | << "Vending machine consumes goods"; 112 | EXPECT_TRUE(vm.is_in_state< vending_def::on::maintenance::idle >()) 113 | << "Vending machine transits back to idle maintenance mode"; 114 | EXPECT_FALSE(vm.is_empty()); 115 | EXPECT_EQ(10UL, vm.count()); 116 | EXPECT_TRUE(done(vm.process_event(events::load_goods{ 1, 100 }))) 117 | << "Vending machine consumes goods"; 118 | EXPECT_TRUE(vm.is_in_state< vending_def::on::maintenance::idle >()) 119 | << "Vending machine transits back to idle maintenance mode"; 120 | EXPECT_FALSE(vm.is_empty()); 121 | EXPECT_EQ(110UL, vm.count()); 122 | EXPECT_FALSE(vm.prices_correct()); 123 | 124 | // Try leave maintenance mode without setting prices 125 | EXPECT_FALSE(ok(vm.process_event(events::end_maintenance{}))); 126 | 127 | // Set prices 128 | EXPECT_TRUE(done(vm.process_event(events::set_price{ 0, 10.0 }))) 129 | << "Set price for item 0"; 130 | EXPECT_TRUE(done(vm.process_event(events::set_price{ 1, 5.0 }))) 131 | << "Set price for item 1"; 132 | EXPECT_FALSE(vm.is_empty()); 133 | EXPECT_TRUE(vm.prices_correct()); 134 | // Leave maintenance mode 135 | EXPECT_EQ(result::process, vm.process_event(events::end_maintenance{})); 136 | } 137 | 138 | TEST(Vending, BuyItem) 139 | { 140 | vending_machine vm{ goods_storage{ 141 | { 0, { 10, 15.0f } }, 142 | { 1, { 100, 5.0f } } 143 | }}; 144 | 145 | auto initial_count = vm.count(); 146 | 147 | EXPECT_FALSE(vm.is_empty()) << "Vending machine is empty"; 148 | 149 | EXPECT_TRUE(vm.is_in_state< vending_def::off >()) 150 | << "Vending machine is off"; 151 | EXPECT_FALSE(vm.is_in_state< vending_def::on >()) 152 | << "Vending machine is not on"; 153 | 154 | EXPECT_TRUE(done(vm.process_event(events::power_on{}))) 155 | << "Vending machine turns on correctly"; 156 | 157 | EXPECT_FALSE(vm.is_in_state< vending_def::off >()) 158 | << "Vending machine is not off"; 159 | EXPECT_TRUE(vm.is_in_state< vending_def::on >()) 160 | << "Vending machine is on"; 161 | EXPECT_TRUE(vm.is_in_state< vending_def::on::serving >()) 162 | << "Vending machine is serving"; 163 | EXPECT_TRUE(vm.is_in_state< vending_def::on::serving::idle >()) 164 | << "Vending machine is serving"; 165 | 166 | EXPECT_TRUE(done(vm.process_event(events::money{3}))) 167 | << "Vending machine accepts money"; 168 | EXPECT_TRUE(vm.is_in_state< vending_def::on::serving::active >()) 169 | << "Vending machine is serving"; 170 | EXPECT_FALSE(ok(vm.process_event(events::select_item{0}))) 171 | << "Vending machine doesn't allow dispensing when low balance"; 172 | EXPECT_FALSE(ok(vm.process_event(events::select_item{42}))) 173 | << "Vending machine doesn't allow dispensing non-existent items"; 174 | EXPECT_TRUE(done(vm.process_event(events::money{5}))) 175 | << "Vending machine accepts money"; 176 | EXPECT_TRUE(ok(vm.process_event(events::select_item{1}))) 177 | << "Vending machine dispenses when enough money"; 178 | EXPECT_EQ(vm.get_price(1), vm.balance) 179 | << "Machine's balance increased by item's price"; 180 | EXPECT_EQ(initial_count - 1, vm.count()) << "Item count decreased by 1"; 181 | 182 | 183 | } 184 | 185 | TEST(Vending, HandledEvents) 186 | { 187 | vending_machine vm{ goods_storage{ 188 | { 0, { 10, 15.0f } }, 189 | { 1, { 100, 5.0f } } 190 | }}; 191 | 192 | EXPECT_TRUE(vm.is_in_state< vending_def::off >()) 193 | << "Vending machine is off"; 194 | EXPECT_TRUE(vm.static_handled_events().count(&event::id)) 195 | << "Handles on event"; 196 | EXPECT_TRUE(vm.static_handled_events().count(&event::id)) 197 | << "Handles off event"; 198 | EXPECT_TRUE(vm.current_handled_events().count(&event::id)) 199 | << "Handles on event"; 200 | 201 | EXPECT_FALSE(vm.current_handled_events().count(&event::id)) 202 | << "Handles off event"; 203 | 204 | // off -> on 205 | EXPECT_TRUE(done(vm.process_event(events::power_on{}))) 206 | << "Vending machine turns on correctly"; 207 | EXPECT_TRUE(vm.is_in_state< vending_def::on >()) 208 | << "Vending machine is on"; 209 | 210 | EXPECT_TRUE(vm.current_handled_events().count(&event::id)); 211 | EXPECT_FALSE(vm.current_handled_events().count(&event::id)); 212 | EXPECT_TRUE(vm.current_handled_events().count(&event::id)); 213 | EXPECT_FALSE(vm.current_handled_events().count(&event::id)); 214 | EXPECT_FALSE(vm.current_handled_events().count(&event::id)); 215 | 216 | // serving -> maintenance 217 | EXPECT_TRUE(done(vm.process_event(events::start_maintenance{vending_machine::factory_code}))) 218 | << "Vending machine accepts factory code"; 219 | EXPECT_TRUE(vm.is_in_state< vending_def::on::maintenance >()) 220 | << "Vending machine transits to maintenance mode"; 221 | 222 | EXPECT_FALSE(vm.current_handled_events().count(&event::id)); 223 | EXPECT_TRUE(vm.current_handled_events().count(&event::id)); 224 | EXPECT_FALSE(vm.current_handled_events().count(&event::id)); 225 | EXPECT_TRUE(vm.current_handled_events().count(&event::id)); 226 | EXPECT_TRUE(vm.current_handled_events().count(&event::id)); 227 | } 228 | 229 | 230 | } /* namespace vending */ 231 | -------------------------------------------------------------------------------- /examples/vending_machine.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * vending_machine.hpp 3 | * 4 | * Created on: Nov 15, 2016 5 | * Author: zmij 6 | */ 7 | 8 | #ifndef AFSM_EXAMPLES_VENDING_MACHINE_HPP_ 9 | #define AFSM_EXAMPLES_VENDING_MACHINE_HPP_ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | namespace vending { 17 | 18 | namespace events { 19 | 20 | struct power_on {}; 21 | struct power_off {}; 22 | 23 | struct start_maintenance { 24 | int secret; 25 | }; 26 | struct end_maintenance {}; 27 | struct withdraw_money {}; 28 | struct load_goods { 29 | ::std::size_t p_no; 30 | int amount; 31 | }; 32 | struct load_done {}; 33 | struct set_price { 34 | ::std::size_t p_no; 35 | float price; 36 | }; 37 | 38 | struct money { 39 | float amount; 40 | }; 41 | struct select_item { 42 | ::std::size_t p_no; 43 | }; 44 | 45 | } /* namespace events */ 46 | 47 | struct goods_entry { 48 | int amount; 49 | float price; 50 | }; 51 | 52 | using goods_storage = ::std::map<::std::size_t, goods_entry>; 53 | 54 | struct vending_def : ::afsm::def::state_machine { 55 | using vending_fsm = ::afsm::state_machine; 56 | using history = ::afsm::def::tags::has_history; 57 | 58 | //@{ 59 | /** @name Guards */ 60 | struct is_empty { 61 | template < typename FSM, typename State > 62 | bool 63 | operator()(FSM const& fsm, State const&) const 64 | { 65 | return root_machine(fsm).is_empty(); 66 | } 67 | }; 68 | struct prices_correct { 69 | template < typename FSM, typename State > 70 | bool 71 | operator()(FSM const& fsm, State const&) const 72 | { 73 | return root_machine(fsm).prices_correct(); 74 | } 75 | }; 76 | struct goods_exist { 77 | template < typename FSM, typename State, typename Event > 78 | bool 79 | operator()(FSM const& fsm, State const&, Event const& evt) const 80 | { 81 | return root_machine(fsm).goods_exist(evt.p_no); 82 | } 83 | }; 84 | //@} 85 | 86 | struct off : state {}; 87 | struct on : state_machine { 88 | // A type alias for actual state machine, that will be passed to actions 89 | // and guards, just for demonstration purposes 90 | using on_fsm = ::afsm::inner_state_machine; 91 | // Forward declaration 92 | struct maintenance; 93 | //@{ 94 | /** @name Guards */ 95 | struct check_secret { 96 | template < typename State > 97 | bool 98 | operator()(on_fsm const& fsm, State const&, events::start_maintenance const& evt) const 99 | { 100 | return root_machine(fsm).secret == evt.secret; 101 | } 102 | }; 103 | //@} 104 | //@{ 105 | /** @name Actions */ 106 | //@} 107 | //@{ 108 | /** @name Substates */ 109 | struct maintenance : state_machine { 110 | //@{ 111 | /** @name Guards */ 112 | struct check_amount { 113 | template < typename FSM, typename State > 114 | bool 115 | operator()(FSM const&, State const&, events::load_goods const& goods) const 116 | { 117 | return goods.amount >= 0; 118 | } 119 | }; 120 | struct check_price { 121 | template < typename FSM, typename State > 122 | bool 123 | operator()(FSM const&, State const&, events::set_price const& price) const 124 | { 125 | return price.price >= 0; 126 | } 127 | }; 128 | //@} 129 | //@{ 130 | struct set_price { 131 | template < typename FSM > 132 | void 133 | operator()(events::set_price&& price, FSM& fsm) const 134 | { 135 | root_machine(fsm).set_price(price.p_no, price.price); 136 | } 137 | }; 138 | //@} 139 | using internal_transitions = transition_table < 140 | /* Event Action Guard */ 141 | in< events::set_price, set_price, and_< goods_exist, check_price > >, 142 | in< events::withdraw_money, none, none > 143 | >; 144 | struct idle : state {}; 145 | struct loading : state { 146 | template < typename FSM > 147 | void 148 | on_enter(events::load_goods&& goods, FSM& fsm) const 149 | { 150 | root_machine(fsm).add_goods(::std::move(goods)); 151 | } 152 | }; 153 | 154 | using initial_state = idle; 155 | using transitions = transition_table < 156 | /* State Event Next Action Guard */ 157 | tr< idle, events::load_goods, loading, none, check_amount >, 158 | tr< loading, events::load_done, idle, none, none > 159 | >; 160 | }; 161 | struct serving : state_machine { 162 | //@{ 163 | /** @name Guards */ 164 | struct enough_money { 165 | template < typename FSM, typename State > 166 | bool 167 | operator()(FSM const& fsm, State const& state, events::select_item const& item) const 168 | { 169 | return root_machine(fsm).get_price(item.p_no) <= state.balance; 170 | } 171 | }; 172 | //@} 173 | //@{ 174 | /** @name Actions */ 175 | struct dispense { 176 | template < typename FSM > 177 | void 178 | operator()(events::select_item&& item, FSM& fsm) const 179 | { 180 | root_machine(fsm).dispense_product(item.p_no); 181 | } 182 | }; 183 | //@} 184 | //@{ 185 | /** @name Substates */ 186 | struct idle : state {}; 187 | struct active : state { 188 | template < typename FSM > 189 | void 190 | on_enter(events::money&& money, FSM&) 191 | { 192 | balance += money.amount; 193 | } 194 | template < typename FSM > 195 | void 196 | on_exit(events::select_item&& item, FSM& fsm) 197 | { 198 | // Subtract balance 199 | auto& root = root_machine(fsm); 200 | balance -= root.get_price(item.p_no); 201 | if (balance > 0) { 202 | // Give change 203 | } 204 | } 205 | 206 | float balance{0}; 207 | }; 208 | //@} 209 | using initial_state = idle; 210 | 211 | using transitions = transition_table< 212 | /* Start Event Next Action Guard */ 213 | tr< idle, events::money, active, none, none >, 214 | tr< active, events::money, active, none, none >, 215 | tr< active, events::select_item, idle, dispense, and_< goods_exist, enough_money > > 216 | >; 217 | }; 218 | struct out_of_service : state {}; 219 | //@} 220 | 221 | using initial_state = serving; 222 | using transitions = transition_table< 223 | /* Start Event Next Action Guard */ 224 | /* Default transitions */ 225 | /*-----------------+---------------------------+---------------+-------+--------------------------------*/ 226 | tr< serving, none, out_of_service, none, is_empty >, 227 | tr< out_of_service, none, serving, none, not_ >, 228 | /*-----------------+---------------------------+---------------+-------+--------------------------------*/ 229 | /* Normal transitions */ 230 | tr< serving, events::start_maintenance, maintenance, none, check_secret >, 231 | tr< out_of_service, events::start_maintenance, maintenance, none, check_secret >, 232 | tr< maintenance, events::end_maintenance, serving, none, or_< is_empty, prices_correct > > 233 | >; 234 | 235 | }; 236 | 237 | using initial_state = off; 238 | using transitions = transition_table< 239 | /* Start Event Next */ 240 | tr< off, events::power_on, on >, 241 | tr< on, events::power_off, off > 242 | >; 243 | 244 | static const int factory_code = 2147483647; 245 | 246 | vending_def() 247 | : secret{factory_code}, goods{}, balance{0} {} 248 | vending_def(int code) 249 | : secret{code}, goods{}, balance{0} {} 250 | vending_def(goods_storage&& goods) 251 | : secret{factory_code}, goods{::std::move(goods)}, balance{0} {} 252 | vending_def(int code, goods_storage&& goods) 253 | : secret{code}, goods{::std::move(goods)}, balance{0} {} 254 | 255 | bool 256 | is_empty() const 257 | { 258 | return count() == 0; 259 | } 260 | 261 | ::std::size_t 262 | count() const 263 | { 264 | return ::std::accumulate( 265 | goods.begin(), goods.end(), 0ul, 266 | [](::std::size_t cnt, goods_storage::value_type const& i) 267 | { 268 | return cnt + i.second.amount; 269 | } 270 | ); 271 | } 272 | 273 | void 274 | add_goods(events::load_goods&& entry) 275 | { 276 | auto f = goods.find(entry.p_no); 277 | if (f == goods.end()) { 278 | goods.emplace(entry.p_no, goods_entry{entry.amount, 0}); 279 | } else { 280 | f->second.amount += entry.amount; 281 | } 282 | rebind().process_event(events::load_done{}); 283 | } 284 | 285 | bool 286 | prices_correct() const 287 | { 288 | auto f = ::std::find_if(goods.begin(), goods.end(), 289 | [](goods_storage::value_type const& i) 290 | { 291 | return i.second.price <= 0; 292 | }); 293 | return f == goods.end(); 294 | } 295 | 296 | bool 297 | goods_exist(::std::size_t p_no) const 298 | { 299 | auto f = goods.find(p_no); 300 | return f != goods.end(); 301 | } 302 | 303 | void 304 | set_price(::std::size_t p_no, float price) 305 | { 306 | auto f = goods.find(p_no); 307 | if (f != goods.end()) { 308 | f->second.price = price; 309 | } 310 | } 311 | float 312 | get_price(::std::size_t p_no) const 313 | { 314 | auto f = goods.find(p_no); 315 | if (f != goods.end()) { 316 | return f->second.price; 317 | } 318 | return 0; 319 | } 320 | void 321 | dispense_product(::std::size_t p_no) 322 | { 323 | auto f = goods.find(p_no); 324 | if (f != goods.end() && f->second.amount > 0) { 325 | --f->second.amount; 326 | balance += f->second.price; 327 | } 328 | } 329 | void 330 | add_balance(float amount) 331 | { 332 | balance += amount; 333 | } 334 | void 335 | clear_balance() 336 | { balance = 0; } 337 | 338 | vending_fsm& 339 | rebind() 340 | { return static_cast(*this); } 341 | vending_fsm const& 342 | rebind() const 343 | { return static_cast(*this); } 344 | 345 | int secret; 346 | goods_storage goods; 347 | float balance; 348 | }; 349 | 350 | using vending_machine = ::afsm::state_machine; 351 | 352 | } /* namespace vending */ 353 | 354 | 355 | 356 | #endif /* AFSM_EXAMPLES_VENDING_MACHINE_HPP_ */ 357 | -------------------------------------------------------------------------------- /include/afsm/detail/orthogonal_regions.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * orthogonal_regions.hpp 3 | * 4 | * Created on: 27 нояб. 2016 г. 5 | * Author: sergey.fedorov 6 | */ 7 | 8 | #ifndef AFSM_DETAIL_ORTHOGONAL_REGIONS_HPP_ 9 | #define AFSM_DETAIL_ORTHOGONAL_REGIONS_HPP_ 10 | 11 | #include 12 | #include 13 | 14 | namespace afsm { 15 | namespace orthogonal { 16 | 17 | namespace detail { 18 | 19 | template < ::std::size_t N > 20 | struct invoke_nth { 21 | using previous = invoke_nth; 22 | static constexpr ::std::size_t index = N; 23 | using event_handler_type = actions::detail::process_event_handler; 24 | using event_set = ::afsm::detail::event_set; 25 | 26 | template < typename Regions, typename Event, typename FSM > 27 | static void 28 | enter(Regions& regions, Event&& event, FSM& fsm) 29 | { 30 | using state_type = typename ::std::tuple_element::type; 31 | using state_enter = transitions::detail::state_enter; 32 | 33 | previous::enter(regions, ::std::forward(event), fsm); 34 | state_enter{}(::std::get(regions), ::std::forward(event), fsm); 35 | } 36 | 37 | template < typename Regions, typename Event, typename FSM > 38 | static void 39 | exit(Regions& regions, Event&& event, FSM& fsm) 40 | { 41 | using state_type = typename ::std::tuple_element::type; 42 | using state_exit = transitions::detail::state_exit; 43 | 44 | // Reverse order of exit 45 | state_exit{}(::std::get(regions), ::std::forward(event), fsm); 46 | previous::exit(regions, ::std::forward(event), fsm); 47 | } 48 | 49 | template < typename Regions, typename Event > 50 | static actions::event_process_result 51 | process_event(Regions& regions, Event&& event) 52 | { 53 | auto res = previous::process_event(regions, ::std::forward(event)); 54 | return ::std::max(res, event_handler_type{}(regions, ::std::forward(event))); 55 | } 56 | 57 | template < typename Regions > 58 | static void 59 | collect_events( Regions const& regions, event_set& events ) 60 | { 61 | previous::collect_events(regions, events); 62 | auto const& region = ::std::get(regions); 63 | event_set evts = region.current_handled_events(); 64 | events.insert(evts.begin(), evts.end()); 65 | } 66 | template < typename Regions > 67 | static void 68 | collect_deferred_events( Regions const& regions, event_set& events ) 69 | { 70 | previous::collect_deferred_events(regions, events); 71 | auto const& region = ::std::get(regions); 72 | event_set evts = region.current_deferrable_events(); 73 | events.insert(evts.begin(), evts.end()); 74 | } 75 | }; 76 | 77 | template <> 78 | struct invoke_nth< 0 > { 79 | static constexpr ::std::size_t index = 0; 80 | using event_handler_type = actions::detail::process_event_handler; 81 | using event_set = ::afsm::detail::event_set; 82 | 83 | template < typename Regions, typename Event, typename FSM > 84 | static void 85 | enter(Regions& regions, Event&& event, FSM& fsm) 86 | { 87 | using state_type = typename ::std::tuple_element::type; 88 | using state_enter = transitions::detail::state_enter; 89 | state_enter{}(::std::get(regions), ::std::forward(event), fsm); 90 | } 91 | 92 | template < typename Regions, typename Event, typename FSM > 93 | static void 94 | exit(Regions& regions, Event&& event, FSM& fsm) 95 | { 96 | using state_type = typename ::std::tuple_element::type; 97 | using state_exit = transitions::detail::state_exit; 98 | state_exit{}(::std::get(regions), ::std::forward(event), fsm); 99 | } 100 | 101 | template < typename Regions, typename Event > 102 | static actions::event_process_result 103 | process_event(Regions& regions, Event&& event) 104 | { 105 | return event_handler_type{}(regions, ::std::forward(event)); 106 | } 107 | 108 | template < typename Regions > 109 | static void 110 | collect_events( Regions const& regions, event_set& events ) 111 | { 112 | auto const& region = ::std::get(regions); 113 | region.current_handled_events().swap(events); 114 | } 115 | template < typename Regions > 116 | static void 117 | collect_deferred_events( Regions const& regions, event_set& events ) 118 | { 119 | auto const& region = ::std::get(regions); 120 | region.current_deferrable_events().swap(events); 121 | } 122 | }; 123 | 124 | } /* namespace detail */ 125 | 126 | template < typename FSM, typename FSM_DEF, typename Size > 127 | class regions_table { 128 | public: 129 | using fsm_type = FSM; 130 | using state_machine_definition_type = FSM_DEF; 131 | using size_type = Size; 132 | 133 | using this_type = regions_table; 134 | 135 | using orthogonal_regions = typename state_machine_definition_type::orthogonal_regions; 136 | static_assert(!::std::is_same::value, 137 | "Orthogonal regions table is not defined for orthogonal state machine"); 138 | 139 | using regions_def = typename def::detail::inner_states< orthogonal_regions >::type; 140 | using regions_constructor = ::afsm::detail::front_state_tuple; 141 | using regions_tuple = typename regions_constructor::type; 142 | 143 | static constexpr ::std::size_t size = regions_def::size; 144 | 145 | using region_indexes = typename ::psst::meta::index_builder::type; 146 | using all_regions = detail::invoke_nth; 147 | using event_set = ::afsm::detail::event_set; 148 | public: 149 | regions_table(fsm_type& fsm) 150 | : fsm_{&fsm}, 151 | regions_{ regions_constructor::construct(fsm) } 152 | {} 153 | regions_table(fsm_type& fsm, regions_table const& rhs) 154 | : fsm_{&fsm}, 155 | regions_{ regions_constructor::copy_construct(fsm, rhs.regions_) } 156 | {} 157 | regions_table(fsm_type& fsm, regions_table&& rhs) 158 | : fsm_{&fsm}, 159 | regions_{ regions_constructor::move_construct(fsm, ::std::move(rhs.regions_)) } 160 | {} 161 | 162 | regions_table(regions_table const&) = delete; 163 | regions_table(regions_table&& rhs) 164 | : fsm_{rhs.fsm_}, 165 | regions_{::std::move(rhs.regions_)} 166 | { 167 | } 168 | regions_table& 169 | operator = (regions_table const&) = delete; 170 | regions_table& 171 | operator = (regions_table&&) = delete; 172 | 173 | void 174 | swap(regions_table& rhs) 175 | { 176 | using ::std::swap; 177 | swap(regions_, rhs.regions_); 178 | set_fsm(*fsm_); 179 | rhs.set_fsm(*rhs.fsm_); 180 | } 181 | 182 | void 183 | set_fsm(fsm_type& fsm) 184 | { 185 | fsm_ = &fsm; 186 | ::afsm::detail::set_enclosing_fsm::set(fsm, regions_); 187 | } 188 | 189 | regions_tuple& 190 | regions() 191 | { return regions_; } 192 | regions_tuple const& 193 | regions() const 194 | { return regions_; } 195 | 196 | template < ::std::size_t N> 197 | typename ::std::tuple_element< N, regions_tuple >::type& 198 | get_state() 199 | { return ::std::get(regions_); } 200 | template < ::std::size_t N> 201 | typename ::std::tuple_element< N, regions_tuple >::type const& 202 | get_state() const 203 | { return ::std::get(regions_); } 204 | 205 | template < typename Event > 206 | actions::event_process_result 207 | process_event(Event&& event) 208 | { 209 | // Pass event to all regions 210 | return all_regions::process_event(regions_, ::std::forward(event)); 211 | } 212 | event_set 213 | current_handled_events() const 214 | { 215 | event_set evts; 216 | all_regions::collect_events(regions_, evts); 217 | return evts; 218 | } 219 | event_set 220 | current_deferrable_events() const 221 | { 222 | event_set evts; 223 | all_regions::collect_deferred_events(regions_, evts); 224 | return evts; 225 | } 226 | 227 | template < typename Event > 228 | void 229 | enter(Event&& event) 230 | { 231 | // Call enter for all regions 232 | all_regions::enter(regions_, ::std::forward(event), *fsm_); 233 | } 234 | template < typename Event > 235 | void 236 | exit(Event&& event) 237 | { 238 | // Call exit for all regions 239 | all_regions::exit(regions_, ::std::forward(event), *fsm_); 240 | } 241 | private: 242 | fsm_type* fsm_; 243 | regions_tuple regions_; 244 | }; 245 | 246 | template < typename FSM, typename FSM_DEF, typename Size > 247 | class regions_stack { 248 | public: 249 | using region_table_type = regions_table; 250 | using this_type = regions_stack; 251 | 252 | using fsm_type = typename region_table_type::fsm_type; 253 | using state_machine_definition_type = typename region_table_type::state_machine_definition_type; 254 | using size_type = typename region_table_type::size_type; 255 | using regions_tuple = typename region_table_type::regions_tuple; 256 | 257 | using stack_constructor_type = afsm::detail::stack_constructor; 258 | using event_set = ::afsm::detail::event_set; 259 | public: 260 | regions_stack(fsm_type& fsm) 261 | : fsm_{&fsm}, 262 | state_stack_{ stack_constructor_type::construct(fsm) } 263 | {} 264 | regions_stack(fsm_type& fsm, regions_stack const& rhs) 265 | : fsm_{&fsm}, 266 | state_stack_{ stack_constructor_type::copy_construct(fsm, rhs.state_stack_) } 267 | {} 268 | regions_stack(fsm_type& fsm, regions_stack&& rhs) 269 | : fsm_{&fsm}, 270 | state_stack_{ stack_constructor_type::move_construct(fsm, ::std::move(rhs.state_stack_)) } 271 | {} 272 | 273 | regions_stack(regions_stack const&) = delete; 274 | regions_stack(regions_stack&&) = delete; 275 | regions_stack& 276 | operator = (regions_stack const&) = delete; 277 | regions_stack& 278 | operator = (regions_stack&&) = delete; 279 | 280 | void 281 | swap(regions_stack& rhs) 282 | { 283 | using ::std::swap; 284 | swap(state_stack_, rhs.state_stack_); 285 | set_fsm(*fsm_); 286 | rhs.set_fsm(*rhs.fsm_); 287 | } 288 | 289 | void 290 | set_fsm(fsm_type& fsm) 291 | { 292 | fsm_ = &fsm; 293 | for (auto& item : state_stack_) { 294 | item.set_fsm(fsm); 295 | } 296 | } 297 | 298 | regions_tuple& 299 | states() 300 | { return top().states(); } 301 | regions_tuple const& 302 | states() const 303 | { return top().states(); } 304 | 305 | template < ::std::size_t N > 306 | typename ::std::tuple_element< N, regions_tuple >::type& 307 | get_state() 308 | { return top().template get_state(); } 309 | template < ::std::size_t N > 310 | typename ::std::tuple_element< N, regions_tuple >::type const& 311 | get_state() const 312 | { return top().template get_state(); } 313 | 314 | template < typename Event > 315 | actions::event_process_result 316 | process_event(Event&& event) 317 | { 318 | return top().process_event(::std::forward(event)); 319 | } 320 | event_set 321 | current_handled_events() const 322 | { 323 | return top().current_handled_events(); 324 | } 325 | event_set 326 | current_deferrable_events() const 327 | { 328 | return top().current_deferrable_events(); 329 | } 330 | 331 | template < typename Event > 332 | void 333 | enter(Event&& event) 334 | { 335 | top().enter(::std::forward(event)); 336 | } 337 | template < typename Event > 338 | void 339 | exit(Event&& event) 340 | { 341 | top().exit(::std::forward(event)); 342 | } 343 | 344 | template < typename Event > 345 | void 346 | push(Event&& event) 347 | { 348 | state_stack_.emplace_back( *fsm_ ); 349 | enter(::std::forward(event)); 350 | } 351 | 352 | template < typename Event > 353 | void 354 | pop(Event&& event) 355 | { 356 | if (state_stack_.size() > 1) { 357 | exit(::std::forward(event)); 358 | state_stack_.pop_back(); 359 | } 360 | } 361 | 362 | ::std::size_t 363 | stack_size() const 364 | { 365 | return state_stack_.size(); 366 | } 367 | private: 368 | using stack_type = typename stack_constructor_type::type; 369 | 370 | region_table_type& 371 | top() 372 | { return state_stack_.back(); } 373 | region_table_type const& 374 | top() const 375 | { return state_stack_.back(); } 376 | private: 377 | fsm_type *fsm_; 378 | stack_type state_stack_; 379 | }; 380 | 381 | template < typename FSM, typename FSM_DEF, typename Size, bool HasPushdowns > 382 | struct region_container_selector { 383 | using type = regions_table; 384 | }; 385 | 386 | template < typename FSM, typename FSM_DEF, typename Size > 387 | struct region_container_selector { 388 | using type = regions_stack; 389 | }; 390 | 391 | namespace detail { 392 | 393 | template < typename FSM, typename FSM_DEF, typename Size, bool HasPushdowns > 394 | struct region_container { 395 | using region_tuple = typename region_container_selector::type; 396 | 397 | region_container(FSM* fsm) 398 | : regions_{*fsm} 399 | {} 400 | region_container(FSM* fsm, region_container const& rhs) 401 | : regions_{*fsm, rhs.regions_} 402 | {} 403 | region_container(FSM* fsm, region_container&& rhs) 404 | : regions_{*fsm, ::std::move(rhs.regions_)} 405 | {} 406 | protected: 407 | region_tuple regions_; 408 | }; 409 | 410 | template < typename FSM, typename FSM_DEF, typename Size > 411 | struct region_container { 412 | using region_tuple = typename region_container_selector::type; 413 | 414 | region_container(FSM* fsm) 415 | : regions_{*fsm} 416 | {} 417 | region_container(FSM* fsm, region_container const& rhs) 418 | : regions_{*fsm, rhs.regions_} 419 | {} 420 | region_container(FSM* fsm, region_container&& rhs) 421 | : regions_{*fsm, ::std::move(rhs.regions_)} 422 | {} 423 | 424 | ::std::size_t 425 | stack_size() const 426 | { return regions_.stack_size(); } 427 | 428 | protected: 429 | template < typename T > 430 | friend struct ::afsm::detail::pushdown_state; 431 | template < typename T > 432 | friend struct ::afsm::detail::popup_state; 433 | // Push/pop ops 434 | template < typename Event > 435 | void 436 | pushdown(Event&& event) 437 | { 438 | regions_.push(::std::forward(event)); 439 | } 440 | template < typename Event > 441 | void 442 | popup(Event&& event) 443 | { 444 | regions_.pop(::std::forward(event)); 445 | } 446 | protected: 447 | region_tuple regions_; 448 | }; 449 | 450 | } /* namespace detail */ 451 | 452 | template < typename FSM, typename FSM_DEF, typename Size > 453 | struct region_container 454 | : detail::region_container::value> { 456 | using base_type = detail::region_container::value>; 458 | using region_tuple = typename base_type::region_tuple; 459 | 460 | region_container(FSM* fsm) : base_type{fsm} {} 461 | region_container(FSM* fsm, region_container const& rhs) 462 | : base_type{fsm, rhs} {} 463 | region_container(FSM* fsm, region_container&& rhs) 464 | : base_type{fsm, ::std::move(rhs)} {} 465 | protected: 466 | using base_type::regions_; 467 | }; 468 | 469 | } /* namespace orthogonal */ 470 | } /* namespace afsm */ 471 | 472 | 473 | 474 | #endif /* AFSM_DETAIL_ORTHOGONAL_REGIONS_HPP_ */ 475 | -------------------------------------------------------------------------------- /test/pushdown_tests.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * pushdown_tests.cpp 3 | * 4 | * Created on: Nov 29, 2016 5 | * Author: zmij 6 | */ 7 | 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | 16 | namespace afsm { 17 | namespace test { 18 | 19 | namespace events { 20 | 21 | struct bool_literal {}; 22 | struct integer_literal {}; 23 | struct float_literal {}; 24 | struct string_literal {}; 25 | struct null_literal {}; 26 | 27 | struct start_array {}; 28 | struct end_array {}; 29 | 30 | struct start_object {}; 31 | struct end_object {}; 32 | 33 | struct comma{}; 34 | struct colon{}; 35 | 36 | } /* namespace events */ 37 | 38 | enum class value_context { 39 | none, 40 | array, 41 | array_first, 42 | object 43 | }; 44 | 45 | struct json_parser_def : def::state_machine< json_parser_def > { 46 | struct context : state_machine< context > { 47 | //@{ 48 | /** @name Substates */ 49 | struct start : state { 50 | template < typename Event, typename FSM > 51 | void 52 | on_enter(Event&&, FSM&) 53 | { 54 | using ::psst::ansi_color; 55 | using ::psst::util::demangle; 56 | ::std::cerr << (ansi_color::cyan | ansi_color::bright) 57 | << "start: " << ansi_color::clear 58 | << demangle() << "\n"; 59 | } 60 | 61 | template < typename FSM > 62 | void 63 | on_enter(events::start_array&&, FSM& fsm) 64 | { 65 | using ::psst::ansi_color; 66 | using ::psst::util::demangle; 67 | ::std::cerr << (ansi_color::cyan | ansi_color::bright) 68 | << "start: " << ansi_color::clear 69 | << "array context (first element)\n"; 70 | 71 | fsm.context_ = value_context::array_first; 72 | } 73 | template < typename FSM > 74 | void 75 | on_enter(events::comma&&, FSM& fsm) 76 | { 77 | using ::psst::ansi_color; 78 | using ::psst::util::demangle; 79 | ::std::cerr << (ansi_color::cyan | ansi_color::bright) 80 | << "start: " << ansi_color::clear 81 | << "array context\n"; 82 | fsm.context_ = value_context::array; 83 | } 84 | template < typename FSM > 85 | void 86 | on_enter(events::colon&&, FSM& fsm) 87 | { 88 | using ::psst::ansi_color; 89 | using ::psst::util::demangle; 90 | ::std::cerr << (ansi_color::cyan | ansi_color::bright) 91 | << "start: " << ansi_color::clear 92 | << "object context\n"; 93 | fsm.context_ = value_context::object; 94 | } 95 | }; 96 | 97 | struct array : state_machine< array > { 98 | struct value : push< value, json_parser_def > {}; 99 | 100 | using initial_state = value; 101 | 102 | using transitions = transition_table< 103 | tr< value, events::comma, value > 104 | >; 105 | }; 106 | 107 | struct object : state_machine< object > { 108 | struct name : state { 109 | template < typename FSM > 110 | void 111 | on_exit(events::string_literal const&, FSM& fsm) 112 | { 113 | ++fsm.size; 114 | } 115 | }; 116 | struct colon : state< colon > {}; 117 | struct value : push< value, json_parser_def > {}; 118 | 119 | using initial_state = name; 120 | 121 | using transitions = transition_table< 122 | tr< name, events::string_literal, colon >, 123 | tr< colon, events::colon, value >, 124 | tr< value, events::comma, name > 125 | >; 126 | ::std::size_t size = 0; 127 | }; 128 | 129 | struct end : pop< end, json_parser_def > {}; 130 | //@} 131 | //@{ 132 | /** @name Guards */ 133 | template < value_context Ctx > 134 | struct in_context { 135 | template < typename FSM, typename State > 136 | bool 137 | operator()(FSM const& fsm, State const&) const 138 | { 139 | return fsm.context_ == Ctx; 140 | } 141 | }; 142 | using in_first_element = in_context; 143 | struct can_close_object { 144 | template < typename FSM, typename State > 145 | bool 146 | operator()(FSM const& fsm, State const& state) const 147 | { 148 | return (state.size == 0 && fsm.template is_in_state< object::name >()) 149 | || fsm.template is_in_state< object::value >(); 150 | } 151 | }; 152 | //@} 153 | //@{ 154 | /** @name Actions */ 155 | struct dup_event { 156 | template < typename Event, typename FSM > 157 | void 158 | operator()(Event&& evt, FSM& fsm) 159 | { 160 | root_machine(fsm).process_event(::std::forward(evt)); 161 | } 162 | }; 163 | //@} 164 | 165 | using initial_state = start; 166 | using transitions = transition_table< 167 | /* Start | Event | Next | Action | Guard */ 168 | /*--------+---------------------------+-------+-----------+---------------------*/ 169 | tr< start , events::bool_literal , end , none , none >, 170 | tr< start , events::integer_literal , end , none , none >, 171 | tr< start , events::float_literal , end , none , none >, 172 | tr< start , events::string_literal , end , none , none >, 173 | tr< start , events::null_literal , end , none , none >, 174 | 175 | /*--------+---------------------------+-------+-----------+---------------------*/ 176 | tr< start , events::start_array , array , none , none >, 177 | tr< array , events::end_array , end , none , none >, 178 | tr< start , events::end_array , end , dup_event , in_first_element >, 179 | 180 | /*--------+---------------------------+-------+-----------+---------------------*/ 181 | tr< start , events::start_object , object, none , none >, 182 | tr< object, events::end_object , end , none , can_close_object > 183 | >; 184 | 185 | value_context context_ = value_context::none; 186 | }; 187 | 188 | using orthogonal_regions = type_tuple; 189 | }; 190 | 191 | using json_parser_fsm = state_machine; 192 | 193 | static_assert(def::traits::is_state::value, ""); 194 | static_assert(def::traits::is_pushdown::value, ""); 195 | static_assert(def::traits::is_pushdown::value, ""); 196 | static_assert(def::traits::is_popup::value, ""); 197 | 198 | static_assert(def::traits::pushes::value, ""); 199 | static_assert(def::traits::pushes::value, ""); 200 | 201 | static_assert(def::state_pushes::value, ""); 202 | static_assert(def::state_pushes::value, ""); 203 | 204 | static_assert(def::traits::pops::value, ""); 205 | static_assert(def::state_pops::value, ""); 206 | 207 | static_assert(def::contains_pushdowns::value, ""); 208 | static_assert(def::contains_pushdowns::value, ""); 209 | static_assert(!def::contains_popups::value, ""); 210 | static_assert(!def::contains_popups::value, ""); 211 | 212 | static_assert(def::contains_pushdowns::value, ""); 213 | static_assert(def::contains_popups::value, ""); 214 | 215 | static_assert(def::is_pushed::value, ""); 216 | static_assert(!def::is_pushed::value, ""); 217 | static_assert(!def::is_pushed::value, ""); 218 | 219 | static_assert(def::is_popped::value, ""); 220 | static_assert(!def::is_popped::value, ""); 221 | static_assert(!def::is_popped::value, ""); 222 | 223 | static_assert(def::has_pushdown_stack::value, ""); 224 | static_assert(!def::has_pushdown_stack::value, ""); 225 | static_assert(!def::has_pushdown_stack::value, ""); 226 | 227 | static_assert(::std::is_same< 228 | def::state_path::type, 229 | ::psst::meta::type_tuple 230 | >::value, ""); 231 | static_assert(::std::is_same< 232 | def::state_path::type, 233 | ::psst::meta::type_tuple 234 | >::value, ""); 235 | static_assert(::std::is_same< 236 | def::state_path::type, 237 | ::psst::meta::type_tuple< 238 | json_parser_def, 239 | json_parser_def::context, 240 | json_parser_def::context::start> 241 | >::value, ""); 242 | 243 | static_assert(::std::is_same< 244 | def::state_path::type, 245 | ::psst::meta::type_tuple< 246 | json_parser_def, 247 | json_parser_def::context, 248 | json_parser_def::context::object, 249 | json_parser_def::context::object::name 250 | > 251 | >::value, ""); 252 | 253 | static_assert(::std::is_same< 254 | def::state_path::type, 255 | ::psst::meta::type_tuple< 256 | json_parser_def, 257 | json_parser_def::context, 258 | json_parser_def::context::object, 259 | json_parser_def::context::object::name 260 | > 261 | >::value, ""); 262 | 263 | TEST(Pushdown, ToTheEnd) 264 | { 265 | json_parser_fsm fsm; 266 | 267 | EXPECT_TRUE(fsm.is_in_state()); 268 | EXPECT_TRUE(fsm.is_in_state()); 269 | EXPECT_TRUE(done(fsm.process_event(events::bool_literal{}))); 270 | EXPECT_TRUE(fsm.is_in_state()); 271 | } 272 | 273 | TEST(Pushdown, PushPopState) 274 | { 275 | json_parser_fsm fsm; 276 | 277 | EXPECT_TRUE(fsm.is_in_state()); 278 | EXPECT_TRUE(fsm.is_in_state()); 279 | EXPECT_TRUE(done(fsm.process_event(events::start_array{}))); 280 | EXPECT_TRUE(fsm.is_in_state()); 281 | EXPECT_EQ(2UL, fsm.stack_size()); 282 | EXPECT_TRUE(done(fsm.process_event(events::null_literal{}))); 283 | EXPECT_EQ(1UL, fsm.stack_size()); 284 | EXPECT_TRUE(fsm.is_in_state()); 285 | EXPECT_TRUE(done(fsm.process_event(events::end_array{}))); 286 | EXPECT_TRUE(fsm.is_in_state()); 287 | } 288 | 289 | TEST(Pushdown, PushTwo) 290 | { 291 | json_parser_fsm fsm; 292 | 293 | EXPECT_TRUE(fsm.is_in_state()); 294 | EXPECT_TRUE(fsm.is_in_state()); 295 | EXPECT_TRUE(done(fsm.process_event(events::start_array{}))); 296 | EXPECT_TRUE(fsm.is_in_state()); 297 | EXPECT_EQ(2UL, fsm.stack_size()); 298 | 299 | EXPECT_TRUE(done(fsm.process_event(events::start_array{}))); 300 | EXPECT_TRUE(fsm.is_in_state()); 301 | EXPECT_EQ(3UL, fsm.stack_size()); 302 | EXPECT_TRUE(done(fsm.process_event(events::null_literal{}))); 303 | EXPECT_EQ(2UL, fsm.stack_size()); 304 | EXPECT_TRUE(fsm.is_in_state()); 305 | 306 | EXPECT_TRUE(done(fsm.process_event(events::comma{}))); 307 | 308 | 309 | EXPECT_TRUE(fsm.is_in_state()); 310 | EXPECT_EQ(3UL, fsm.stack_size()); 311 | 312 | EXPECT_FALSE(ok(fsm.process_event(events::end_array{}))) 313 | << "Cannot close array immediately after comma"; 314 | 315 | EXPECT_TRUE(done(fsm.process_event(events::null_literal{}))); 316 | EXPECT_EQ(2UL, fsm.stack_size()); 317 | EXPECT_TRUE(fsm.is_in_state()); 318 | 319 | EXPECT_TRUE(done(fsm.process_event(events::end_array{}))); 320 | 321 | EXPECT_EQ(1UL, fsm.stack_size()); 322 | EXPECT_TRUE(fsm.is_in_state()); 323 | EXPECT_TRUE(done(fsm.process_event(events::end_array{}))); 324 | EXPECT_TRUE(fsm.is_in_state()); 325 | } 326 | 327 | TEST(Pushdown, EmptyArray) 328 | { 329 | json_parser_fsm fsm; 330 | 331 | EXPECT_TRUE(fsm.is_in_state()); 332 | EXPECT_TRUE(fsm.is_in_state()); 333 | EXPECT_TRUE(done(fsm.process_event(events::start_array{}))); 334 | EXPECT_TRUE(fsm.is_in_state()); 335 | EXPECT_EQ(2UL, fsm.stack_size()); 336 | 337 | EXPECT_TRUE(done(fsm.process_event(events::end_array{}))); 338 | EXPECT_EQ(1UL, fsm.stack_size()); 339 | EXPECT_TRUE(fsm.is_in_state()); 340 | } 341 | 342 | TEST(Pushdown, PushObject) 343 | { 344 | json_parser_fsm fsm; 345 | 346 | EXPECT_TRUE(fsm.is_in_state()); 347 | EXPECT_TRUE(fsm.is_in_state()); 348 | EXPECT_TRUE(done(fsm.process_event(events::start_object{}))); 349 | 350 | EXPECT_TRUE(fsm.is_in_state()); 351 | EXPECT_TRUE(fsm.is_in_state()); 352 | EXPECT_TRUE(done(fsm.process_event(events::string_literal{}))); 353 | EXPECT_TRUE(fsm.is_in_state()); 354 | EXPECT_TRUE(done(fsm.process_event(events::colon{}))); 355 | 356 | EXPECT_TRUE(fsm.is_in_state()); 357 | EXPECT_EQ(2UL, fsm.stack_size()); 358 | 359 | EXPECT_FALSE(ok(fsm.process_event(events::end_object{}))); 360 | EXPECT_TRUE(done(fsm.process_event(events::string_literal{}))); 361 | EXPECT_EQ(1UL, fsm.stack_size()); 362 | 363 | EXPECT_TRUE(done(fsm.process_event(events::comma{}))); 364 | EXPECT_TRUE(done(fsm.process_event(events::string_literal{}))); 365 | EXPECT_FALSE(ok(fsm.process_event(events::end_object{}))); 366 | 367 | EXPECT_TRUE(done(fsm.process_event(events::colon{}))); 368 | 369 | EXPECT_TRUE(fsm.is_in_state()); 370 | EXPECT_EQ(2UL, fsm.stack_size()); 371 | 372 | EXPECT_FALSE(ok(fsm.process_event(events::end_object{}))); 373 | EXPECT_TRUE(done(fsm.process_event(events::string_literal{}))); 374 | EXPECT_EQ(1UL, fsm.stack_size()); 375 | 376 | EXPECT_TRUE(fsm.is_in_state< json_parser_fsm::context::object::value >()); 377 | EXPECT_TRUE(done(fsm.process_event(events::end_object{}))); 378 | EXPECT_TRUE(fsm.is_in_state< json_parser_fsm::context::end >()); 379 | } 380 | 381 | TEST(Pushdown, EmptyObject) 382 | { 383 | json_parser_fsm fsm; 384 | 385 | EXPECT_TRUE(fsm.is_in_state()); 386 | EXPECT_TRUE(fsm.is_in_state()); 387 | EXPECT_TRUE(done(fsm.process_event(events::start_object{}))); 388 | 389 | EXPECT_TRUE(fsm.is_in_state()); 390 | EXPECT_TRUE(fsm.is_in_state()); 391 | 392 | EXPECT_TRUE(done(fsm.process_event(events::end_object{}))); 393 | EXPECT_TRUE(fsm.is_in_state< json_parser_fsm::context::end >()); 394 | } 395 | 396 | } /* namespace test */ 397 | } /* namespace afsm */ 398 | 399 | -------------------------------------------------------------------------------- /benchmark/vending_machine_msm.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * vending_machine_msm.hpp 3 | * 4 | * Created on: Nov 18, 2016 5 | * Author: zmij 6 | */ 7 | 8 | #ifndef VENDING_MACHINE_MSM_HPP_ 9 | #define VENDING_MACHINE_MSM_HPP_ 10 | 11 | #ifdef __clang__ 12 | #pragma clang diagnostic push 13 | #pragma clang diagnostic ignored "-Wunused-local-typedef" 14 | #pragma clang diagnostic ignored "-Wunused-parameter" 15 | #pragma clang diagnostic ignored "-Wunused-variable" 16 | #endif 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #ifdef __clang__ 24 | #pragma clang diagnostic pop 25 | #endif 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | namespace vending_msm { 34 | 35 | namespace events { 36 | 37 | struct power_on {}; 38 | struct power_off {}; 39 | 40 | struct start_maintenance { 41 | int secret; 42 | }; 43 | struct end_maintenance {}; 44 | struct withdraw_money {}; 45 | struct load_goods { 46 | ::std::size_t p_no; 47 | int amount; 48 | }; 49 | struct load_done {}; 50 | struct set_price { 51 | ::std::size_t p_no; 52 | float price; 53 | }; 54 | 55 | struct money { 56 | float amount; 57 | }; 58 | struct select_item { 59 | ::std::size_t p_no; 60 | }; 61 | 62 | } /* namespace events */ 63 | 64 | struct goods_entry { 65 | int amount; 66 | float price; 67 | }; 68 | 69 | using goods_storage = ::std::map<::std::size_t, goods_entry>; 70 | 71 | struct vending_def : public ::boost::msm::front::state_machine_def { 72 | template < typename ... T > 73 | using and_ = ::psst::meta::and_; 74 | template < typename ... T > 75 | using or_ = ::psst::meta::or_; 76 | template < typename T > 77 | using not_ = ::psst::meta::not_; 78 | 79 | template 80 | using state = ::boost::msm::front::state; 81 | template 82 | using state_machine = ::boost::msm::front::state_machine_def; 83 | template 84 | using back_machine = ::boost::msm::back::state_machine; 85 | template 86 | using mpl_vector = ::boost::mpl::vector; 87 | 88 | template 89 | using tr = ::boost::msm::front::Row; 90 | template < typename ... T > 91 | using in = ::boost::msm::front::Internal< T ... >; 92 | 93 | using none = ::boost::msm::front::none; 94 | 95 | using vending_fsm = ::boost::msm::back::state_machine; 96 | 97 | //@{ 98 | /** @name Guards */ 99 | struct is_empty { 100 | template < typename Event, typename FSM, typename SourceState, typename TargetState > 101 | bool 102 | operator()(Event const&, FSM& fsm, SourceState&, TargetState&) 103 | { 104 | return root_machine(fsm).is_empty(); 105 | } 106 | }; 107 | struct prices_correct { 108 | template < typename Event, typename FSM, typename SourceState, typename TargetState > 109 | bool 110 | operator()(Event const&, FSM& fsm, SourceState&, TargetState&) const 111 | { 112 | return root_machine(fsm).prices_correct(); 113 | } 114 | }; 115 | struct goods_exist { 116 | template < typename Event, typename FSM, typename SourceState, typename TargetState > 117 | bool 118 | operator()(Event const& evt, FSM& fsm, SourceState&, TargetState&) const 119 | { 120 | return root_machine(fsm).goods_exist(evt.p_no); 121 | } 122 | }; 123 | //@} 124 | 125 | struct off : state<> { 126 | using flag_list = mpl_vector; 127 | }; 128 | struct on_ : state_machine { 129 | using on = back_machine; 130 | using flag_list = mpl_vector; 131 | //@{ 132 | /** @name Guards */ 133 | struct check_secret { 134 | template < typename Event, typename FSM, typename SourceState, typename TargetState > 135 | bool 136 | operator()(Event const& evt, FSM& fsm, SourceState&, TargetState&) const 137 | { 138 | return root_machine(fsm).secret == evt.secret; 139 | } 140 | }; 141 | //@} 142 | //@{ 143 | /** @name Actions */ 144 | //@} 145 | //@{ 146 | /** @name Substates */ 147 | struct maintenance_ : state_machine { 148 | using maintenance = back_machine; 149 | using flag_list = mpl_vector; 150 | //@{ 151 | /** @name Guards */ 152 | struct check_amount { 153 | template < typename FSM, typename SourceState, typename TargetState > 154 | bool 155 | operator()(events::load_goods const& goods, FSM const&, SourceState&, TargetState&) const 156 | { 157 | return goods.amount >= 0; 158 | } 159 | }; 160 | struct check_price { 161 | template < typename FSM, typename SourceState, typename TargetState > 162 | bool 163 | operator()(events::set_price const& price, FSM const&, SourceState&, TargetState&) const 164 | { 165 | return price.price >= 0; 166 | } 167 | }; 168 | //@} 169 | //@{ 170 | struct set_price { 171 | template < typename FSM, typename SourceState, typename TargetState > 172 | void 173 | operator()(events::set_price const& price, FSM& fsm, SourceState&, TargetState&) const 174 | { 175 | root_machine(fsm).set_price(price.p_no, price.price); 176 | } 177 | }; 178 | //@} 179 | struct internal_transition_table : mpl_vector < 180 | /* Event Action Guard */ 181 | in< events::set_price, set_price, and_< goods_exist, check_price > >, 182 | in< events::withdraw_money, none, none > 183 | >{}; 184 | struct idle : state<> { 185 | using flag_list = mpl_vector; 186 | }; 187 | struct loading : state<> { 188 | template < typename FSM > 189 | void 190 | on_entry(events::load_goods const& goods, FSM& fsm) 191 | { 192 | root_machine(fsm).add_goods(goods); 193 | } 194 | template < typename Event, typename FSM > 195 | void 196 | on_entry(Event const&, FSM&) {} 197 | }; 198 | 199 | using initial_state = idle; 200 | struct transition_table : mpl_vector < 201 | /* State Event Next Action Guard */ 202 | tr< idle, events::load_goods, loading, none, check_amount >, 203 | tr< loading, events::load_done, idle, none, none > 204 | > {}; 205 | 206 | template < typename Event, typename FSM > 207 | void 208 | on_entry(Event const&, FSM& fsm) 209 | { 210 | enclosing = &fsm; 211 | } 212 | on* enclosing = nullptr; 213 | }; 214 | using maintenance = back_machine; 215 | 216 | struct serving_ : state_machine { 217 | using serving = back_machine; 218 | using flag_list = mpl_vector; 219 | //@{ 220 | /** @name Guards */ 221 | struct enough_money { 222 | template < typename FSM, typename SourceState, typename TargetState > 223 | bool 224 | operator()(events::select_item const& item, FSM const& fsm, 225 | SourceState& state, TargetState&) const 226 | { 227 | return root_machine(fsm).get_price(item.p_no) <= state.balance; 228 | } 229 | }; 230 | //@} 231 | //@{ 232 | /** @name Actions */ 233 | struct dispense { 234 | template < typename FSM, typename SourceState, typename TargetState > 235 | void 236 | operator()(events::select_item const& item, FSM& fsm, SourceState&, TargetState&) const 237 | { 238 | root_machine(fsm).dispense_product(item.p_no); 239 | } 240 | }; 241 | //@} 242 | //@{ 243 | /** @name Substates */ 244 | struct idle : state<> { 245 | using flag_list = mpl_vector; 246 | }; 247 | struct active : state<> { 248 | using flag_list = mpl_vector; 249 | template < typename Event, typename FSM > 250 | void 251 | on_entry(Event const&, FSM&) {} 252 | template < typename FSM > 253 | void 254 | on_entry(events::money const& money, FSM&) 255 | { 256 | balance += money.amount; 257 | } 258 | template < typename Event, typename FSM > 259 | void 260 | on_exit(Event const&, FSM&) {} 261 | template < typename FSM > 262 | void 263 | on_exit(events::select_item const& item, FSM& fsm) 264 | { 265 | // Subtract balance 266 | auto& root = root_machine(fsm); 267 | balance -= root.get_price(item.p_no); 268 | if (balance > 0) { 269 | // Give change 270 | } 271 | } 272 | 273 | float balance{0}; 274 | }; 275 | //@} 276 | using initial_state = idle; 277 | 278 | struct transition_table : mpl_vector< 279 | /* Start Event Next Action Guard */ 280 | tr< idle, events::money, active, none, none >, 281 | tr< active, events::money, active, none, none >, 282 | tr< active, events::select_item, idle, dispense, and_< goods_exist, enough_money > > 283 | >{}; 284 | template < typename Event, typename FSM > 285 | void 286 | on_entry(Event const&, FSM& fsm) 287 | { 288 | enclosing = &fsm; 289 | } 290 | on* enclosing = nullptr; 291 | }; 292 | using serving = back_machine; 293 | 294 | struct out_of_service : state<> { 295 | using flag_list = mpl_vector; 296 | // template < typename Event, typename FSM > 297 | // void 298 | // on_entry(Event const&, FSM&) 299 | // { 300 | // ::std::cerr << "Enter out_of_service state\n"; 301 | // } 302 | }; 303 | //@} 304 | 305 | using initial_state = serving; 306 | struct transition_table : mpl_vector < 307 | /* Start Event Next Action Guard */ 308 | /* Default transitions */ 309 | /*-----------------+---------------------------+---------------+-------+--------------------------------*/ 310 | tr< serving, none, out_of_service, none, is_empty >, 311 | tr< out_of_service, none, serving, none, not_ >, 312 | /*-----------------+---------------------------+---------------+-------+--------------------------------*/ 313 | /* Normal transitions */ 314 | tr< serving, events::start_maintenance, maintenance, none, check_secret >, 315 | tr< out_of_service, events::start_maintenance, maintenance, none, check_secret >, 316 | tr< maintenance, events::end_maintenance, serving, none, or_< is_empty, prices_correct > > 317 | >{}; 318 | 319 | template < typename Event > 320 | void 321 | on_entry(Event const&, vending_fsm& fsm) 322 | { 323 | enclosing = &fsm; 324 | } 325 | 326 | vending_fsm* enclosing = nullptr; 327 | }; 328 | using on = back_machine; 329 | 330 | using initial_state = off; 331 | struct transition_table : mpl_vector < 332 | /* Start Event Next */ 333 | tr< off, events::power_on, on, none, none>, 334 | tr< on, events::power_off, off, none, none> 335 | > {}; 336 | 337 | template 338 | void 339 | no_transition(Event const&, FSM&, int) {} 340 | 341 | static const int factory_code = 2147483647; 342 | 343 | vending_def() 344 | : secret{factory_code}, goods{}, balance{0} {} 345 | vending_def(int code) 346 | : secret{code}, goods{}, balance{0} {} 347 | vending_def(goods_storage const& goods) 348 | : secret{factory_code}, goods{goods}, balance{0} {} 349 | vending_def(int code, goods_storage&& goods) 350 | : secret{code}, goods{::std::move(goods)}, balance{0} {} 351 | 352 | bool 353 | is_empty() const 354 | { 355 | return count() == 0; 356 | } 357 | 358 | ::std::size_t 359 | count() const 360 | { 361 | return ::std::accumulate( 362 | goods.begin(), goods.end(), 0ul, 363 | [](::std::size_t cnt, goods_storage::value_type const& i) 364 | { 365 | return cnt + i.second.amount; 366 | } 367 | ); 368 | } 369 | 370 | void 371 | add_goods(events::load_goods const& entry) 372 | { 373 | auto f = goods.find(entry.p_no); 374 | if (f == goods.end()) { 375 | goods.emplace(entry.p_no, goods_entry{entry.amount, 0}); 376 | } else { 377 | f->second.amount += entry.amount; 378 | } 379 | rebind().process_event(events::load_done{}); 380 | } 381 | 382 | bool 383 | prices_correct() const 384 | { 385 | auto f = ::std::find_if(goods.begin(), goods.end(), 386 | [](goods_storage::value_type const& i) 387 | { 388 | return i.second.price <= 0; 389 | }); 390 | return f == goods.end(); 391 | } 392 | 393 | bool 394 | goods_exist(::std::size_t p_no) const 395 | { 396 | auto f = goods.find(p_no); 397 | return f != goods.end(); 398 | } 399 | 400 | void 401 | set_price(::std::size_t p_no, float price) 402 | { 403 | auto f = goods.find(p_no); 404 | if (f != goods.end()) { 405 | f->second.price = price; 406 | } 407 | } 408 | float 409 | get_price(::std::size_t p_no) const 410 | { 411 | auto f = goods.find(p_no); 412 | if (f != goods.end()) { 413 | return f->second.price; 414 | } 415 | return 0; 416 | } 417 | void 418 | dispense_product(::std::size_t p_no) 419 | { 420 | auto f = goods.find(p_no); 421 | if (f != goods.end() && f->second.amount > 0) { 422 | --f->second.amount; 423 | balance += f->second.price; 424 | } 425 | } 426 | void 427 | add_balance(float amount) 428 | { 429 | balance += amount; 430 | } 431 | void 432 | clear_balance() 433 | { balance = 0; } 434 | 435 | vending_fsm& 436 | rebind() 437 | { return static_cast(*this); } 438 | vending_fsm const& 439 | rebind() const 440 | { return static_cast(*this); } 441 | 442 | int secret; 443 | goods_storage goods; 444 | float balance; 445 | }; 446 | 447 | using vending_machine = ::boost::msm::back::state_machine; 448 | 449 | inline vending_machine& 450 | root_machine(vending_machine& m) 451 | { 452 | return m; 453 | } 454 | 455 | template< typename T > 456 | vending_machine& 457 | root_machine(T& state) 458 | { 459 | return root_machine(*state.enclosing); 460 | } 461 | 462 | 463 | 464 | } /* namespace vending_msm */ 465 | 466 | 467 | #endif /* VENDING_MACHINE_MSM_HPP_ */ 468 | --------------------------------------------------------------------------------