// Copyright 2024 Christophe Henry // henry UNDERSCORE christophe AT hotmail DOT com // This is an extended version of the state machine available in the boost::mpl library // Distributed under the same license as the original. // Copyright for the original version: // Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed // under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #include // back-end #include //front-end #include #include using namespace std; namespace msm = boost::msm; using namespace msm::front; using namespace msm::front::puml; // State, events, if non-default must be specialized in boost::msm::front::puml namespace // Actions and guard must be specialized in boost::msm::front::puml namespace namespace boost::msm::front::puml { // A "complicated" event type that carries some data. enum DiskTypeEnum { DISK_CD = 0, DISK_DVD = 1 }; template<> struct Event { Event(std::string name, DiskTypeEnum diskType = DISK_CD) : name(name), disc_type(diskType) {} std::string name; DiskTypeEnum disc_type; }; //Actions template<> struct Action { template void operator()(EVT const&, FSM&, SourceState&, TargetState&) { std::cout << "open drawer" << std::endl; } }; template<> struct Action { template void operator()(EVT const&, FSM&, SourceState&, TargetState&) { std::cout << "close drawer" << std::endl; } }; template<> struct Action { template void operator()(EVT const&, FSM& fsm, SourceState&, TargetState&) { std::cout << "start playback" << std::endl; } }; template<> struct Action { template void operator()(EVT const&, FSM& fsm, SourceState&, TargetState&) { std::cout << "stop playback" << std::endl; } }; template<> struct Action { template void operator()(EVT const&, FSM& fsm, SourceState&, TargetState&) { std::cout << "resume playback" << std::endl; } }; template<> struct Action { template void operator()(EVT const&, FSM& fsm, SourceState&, TargetState&) { std::cout << "pause playback" << std::endl; } }; template<> struct Action { template void operator()(EVT const&, FSM& fsm, SourceState&, TargetState&) { fsm.process_event(Event{}); } }; // Guards template<> struct Guard { template bool operator()(EVT const&, FSM& fsm, SourceState&, TargetState&) { return true; } }; template<> struct Guard { template bool operator()(EVT const&, FSM&, SourceState&, TargetState&) { return true; } }; template<> struct Guard { template bool operator()(EVT& evt, FSM&, SourceState&, TargetState&) { // to test a guard condition, let's say we understand only CDs, not DVD if (evt.disc_type != DISK_CD) { return false; } return true; } }; } namespace { // front-end: define the FSM structure struct player_ : public msm::front::state_machine_def { // here is the whole FSM structure defined: // Initial states [*] // Transitions with actions starting with / and separated by , // and guards between []. Supported are !, &&, || and () // State Entry / Exit with guards // Flags // -> can have different lengths for nicer PlantUML Viewer preview BOOST_MSM_PUML_DECLARE_TABLE( R"( @startuml Player skinparam linetype polyline state Player{ [*]-> Empty Stopped -> Playing : play Stopped -> Open : open_close / open_drawer Stopped -> Stopped : stop Open -> Empty : open_close / close_drawer [can_close_drawer] Empty --> Open : open_close / open_drawer Empty ---> Stopped : cd_detected / store_cd_info [good_disk_format && always_true] Playing --> Stopped : stop / stop_playback Playing -> Paused : pause Playing --> Open : open_close / stop_playback, open_drawer Paused -> Playing : end_pause / resume_playback Paused --> Stopped : stop / stop_playback Paused --> Open : open_close / stop_playback, open_drawer Playing : flag CDLoaded Playing : entry start_playback [always_true] Paused : entry pause_playback Paused : flag CDLoaded Stopped : flag CDLoaded } @enduml )" ) // Replaces the default no-transition response. template void no_transition(Event const& e, FSM&, int state) { std::cout << "no transition from state " << state << " on event " << typeid(e).name() << std::endl; } }; // Pick a back-end typedef msm::back11::state_machine player; void test() { player p; p.start(); p.process_event(Event{}); // Open is active p.process_event(Event{}); // Empty is active p.process_event(Event{"louie, louie", DISK_DVD}); // Empty is active because disk format rejected p.process_event(Event{"louie, louie", DISK_CD}); // Playing is active because disk format accepted, flag is also set assert(p.is_flag_active < Flag>()); p.process_event(Event{}); // Paused is active // go back to Playing p.process_event(Event{}); // Playing is active p.process_event(Event{}); // Stopped is active p.stop(); } } int main() { test(); return 0; }