// Copyright 2010 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 #include #include #include #include using namespace std; namespace msm = boost::msm; namespace // Concrete FSM implementation { // events struct OneSong { OneSong(string const& asong):m_Song(asong){} const string& get_data() const {return m_Song;} private: string m_Song; }; template struct NotFound { DATA get_data() const {return m_Data;} DATA m_Data; }; template struct Found { DATA get_data() const {return m_Data;} DATA m_Data; }; struct Done {}; template struct Insert : public boost::msm::front::state { template void on_entry(Event const& evt,FSM& ) { //TODO other containers if (m_Cont) { m_Cont->insert(evt.get_data()); } m_fsm->process_event(Done()); } void set_sm_ptr(FSMType* fsm){m_fsm=fsm;} void set_container(Container* cont){m_Cont=cont;} Container* m_Cont; private: FSMType* m_fsm; }; template struct StringFind : public boost::msm::front::state { template void on_entry(Event const& evt,FSM& ) { //TODO other containers // if the element in the event is found if (evt.get_data().find(m_Cont) != std::string::npos ) { Found res; res.m_Data = evt.get_data(); m_fsm->process_event(res); } // data not found else { NotFound res; res.m_Data = evt.get_data(); m_fsm->process_event(res); } } void set_sm_ptr(FSMType* fsm){m_fsm=fsm;} void set_container(const char* cont){m_Cont=cont;} private: std::string m_Cont; FSMType* m_fsm; }; template struct Foreach : public boost::msm::front::state { template void on_entry(Event const& evt,FSM& ) { //TODO other containers if (!m_Cont.empty()) { typename Container::value_type next_event = *m_Cont.begin(); m_Cont.erase(m_Cont.begin()); m_fsm->process_event(EventType(next_event)); } } void set_sm_ptr(FSMType* fsm){m_fsm=fsm;} void set_container(Container* cont){m_Cont=*cont;} private: Container m_Cont; FSMType* m_fsm; }; // Concrete FSM implementation struct iPodSearch_ : public msm::front::state_machine_def { typedef msm::back::state_machine iPodSearch; // The list of FSM states typedef std::set Songset; typedef Insert MyInsert; typedef StringFind MyFind; typedef Foreach MyForeach; // the initial state of the player SM. Must be defined typedef MyForeach initial_state; // transition actions // guard conditions typedef iPodSearch_ fsm; // makes transition table cleaner // Transition table for player struct transition_table : mpl::vector4< // Start Event Next Action Guard // +-----------+------------------+------------+---------------------+----------------------+ _row < MyForeach , OneSong , MyFind >, _row < MyFind , NotFound , MyForeach >, _row < MyFind , Found , MyInsert >, _row < MyInsert , Done , MyForeach > // +-----------+------------------+------------+---------------------+----------------------+ > {}; iPodSearch_():m_AllSongs(),m_ResultSearch() { // add a few songs for testing m_AllSongs.insert("Let it be"); m_AllSongs.insert("Yellow submarine"); m_AllSongs.insert("Twist and Shout"); m_AllSongs.insert("She Loves You"); } template void on_entry(Event const&,FSM& fsm) { fsm.template get_state().set_container(&m_AllSongs); fsm.template get_state().set_container(&m_ResultSearch); } const Songset& get_result(){return m_ResultSearch;} void reset_search(){m_ResultSearch.clear();} // 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; } private: Songset m_AllSongs; Songset m_ResultSearch; }; typedef msm::back::state_machine iPodSearch; void test() { iPodSearch search; // look for "She Loves You" using the first letters search.get_state()->set_container("Sh");// will find 2 songs // needed to start the highest-level SM. This will call on_entry and mark the start of the SM search.start(); // display all the songs const iPodSearch::Songset& res = search.get_result(); for (iPodSearch::Songset::const_iterator it = res.begin();it != res.end();++it) { cout << "candidate song:" << *it << endl; } cout << "search using more letters" << endl; // look for "She Loves You" using more letters search.reset_search(); search.get_state()->set_container("She");// will find 1 song search.start(); const iPodSearch::Songset& res2 = search.get_result(); for (iPodSearch::Songset::const_iterator it = res2.begin();it != res2.end();++it) { cout << "candidate song:" << *it << endl; } } } int main() { test(); return 0; }