diff --git a/include/boost/sml.hpp b/include/boost/sml.hpp index dbdc88c9..d4579b9e 100644 --- a/include/boost/sml.hpp +++ b/include/boost/sml.hpp @@ -856,18 +856,8 @@ template struct transitions { template static bool execute(const TEvent &event, SM &sm, TDeps &deps, TSubs &subs, typename SM::state_t ¤t_state) { - return execute_impl(event, sm, deps, subs, current_state); - } - template - static bool execute_impl(const TEvent &event, SM &sm, TDeps &deps, TSubs &subs, typename SM::state_t ¤t_state) { return aux::get(sm.transitions_).execute(event, sm, deps, subs, current_state, typename SM::has_entry_exits{}); } - template - static bool execute_impl(const on_exit<_, TEvent> &event, SM &sm, TDeps &deps, TSubs &subs, - typename SM::state_t ¤t_state) { - aux::get(sm.transitions_).execute(event, sm, deps, subs, current_state, typename SM::has_entry_exits{}); - return false; - } }; template <> struct transitions { @@ -910,12 +900,23 @@ struct transitions_sub, T, Ts...> { sub_sm>::get(&subs).process_event(event, deps, subs); return true; } + template + static bool execute_impl(const back::on_exit<_, TEvent> &event, SM &sm, TDeps &deps, TSubs &subs, + typename SM::state_t ¤t_state) { + sub_sm>::get(&subs).process_event(event, deps, subs); + transitions::execute(event, sm, deps, subs, current_state); + return true; + } }; template struct transitions_sub> { template static bool execute(const TEvent &event, SM &, TDeps &deps, TSubs &subs, typename SM::state_t &) { - return sub_sm>::get(&subs).template process_event(event, deps, subs); + return sub_sm>::get(&subs).process_event(event, deps, subs); + } + template + static bool execute(const anonymous &, SM &, TDeps &, TSubs &, typename SM::state_t &) { + return false; } }; } // namespace back @@ -1336,20 +1337,13 @@ struct sm_impl : aux::conditional_t::value, aux: } template bool process_event(const TEvent &event, TDeps &deps, TSubs &subs) { - policies::log_process_event(aux::type{}, deps, event); -#if BOOST_SML_DISABLE_EXCEPTIONS - const auto handled = process_event_impl, mappings>>( - event, deps, subs, states_t{}, aux::make_index_sequence{}); -#else - const auto handled = - process_event_noexcept, mappings>>(event, deps, subs, has_exceptions{}); -#endif + bool handled = process_internal_events(event, deps, subs); do { - while (process_internal_events(anonymous{}, deps, subs)) { - } - process_defer_events(deps, subs, handled, aux::type>{}, events_t{}); - } while (process_queued_events(deps, subs, aux::type>{}, events_t{}) || - process_internal_events(anonymous{}, deps, subs)); + do { + while (process_internal_events(anonymous{}, deps, subs)) { + } + } while (process_defer_events(deps, subs, handled, aux::type>{}, events_t{})); + } while (process_queued_events(deps, subs, aux::type>{}, events_t{})); return handled; } void initialize(const aux::type_list<> &) {} @@ -1368,13 +1362,7 @@ struct sm_impl : aux::conditional_t::value, aux: } template void start(TDeps &deps, TSubs &subs) { - process_internal_events(on_entry<_, initial>{}, deps, subs); - do { - while (process_internal_events(anonymous{}, deps, subs)) { - } - process_defer_events(deps, subs, true, aux::type>{}, events_t{}); - } while (process_queued_events(deps, subs, aux::type>{}, events_t{}) || - process_internal_events(anonymous{}, deps, subs)); + process_event(on_entry<_, initial>{}, deps, subs); } template , events_ids_t>::value && @@ -1458,15 +1446,15 @@ struct sm_impl : aux::conditional_t::value, aux: aux::index_sequence) { const auto lock = thread_safety_.create_lock(); (void)lock; + auto handled = false; #if defined(__cpp_fold_expressions) - return ((dispatch_t::template dispatch<0, TMappings>(*this, current_state_[Ns], event, deps, subs, states)), ...); + ((handled |= dispatch_t::template dispatch<0, TMappings>(*this, current_state_[Ns], event, deps, subs, states)), ...); #else - auto handled = false; (void)aux::swallow{ 0, (handled |= dispatch_t::template dispatch<0, TMappings>(*this, current_state_[Ns], event, deps, subs, states), 0)...}; - return handled; #endif + return handled; } template bool process_event_impl(const TEvent &event, TDeps &deps, TSubs &subs, const aux::type_list &states, @@ -1522,15 +1510,10 @@ struct sm_impl : aux::conditional_t::value, aux: template bool process_event_no_defer(TDeps &deps, TSubs &subs, const void *data) { const auto &event = *static_cast(data); - policies::log_process_event(aux::type{}, deps, event); -#if BOOST_SML_DISABLE_EXCEPTIONS - const auto handled = process_event_impl>(event, deps, subs, states_t{}, - aux::make_index_sequence{}); -#else - const auto handled = process_event_noexcept>(event, deps, subs, has_exceptions{}); -#endif + bool handled = process_internal_events(event, deps, subs); if (handled && defer_again_) { ++defer_it_; + return false; } else { defer_.erase(defer_it_); defer_it_ = defer_.begin(); @@ -1550,9 +1533,8 @@ struct sm_impl : aux::conditional_t::value, aux: defer_again_ = false; defer_it_ = defer_.begin(); defer_end_ = defer_.end(); - processed_events = defer_it_ != defer_end_; while (defer_it_ != defer_end_) { - (this->*dispatch_table[defer_it_->id])(deps, subs, defer_it_->data); + processed_events |= (this->*dispatch_table[defer_it_->id])(deps, subs, defer_it_->data); defer_again_ = false; } defer_processing_ = false; @@ -2404,14 +2386,6 @@ void update_current_state(SM &, TDeps &deps, TSubs &subs, typename SM::state_t & update_composite_states>(subs, typename back::sm_impl::has_history_states{}, typename back::sm_impl::history_states_t{}); } -template -void process_internal_transitions(TDeps &, TSubs &, const TDstState &) {} -template -void process_internal_transitions(TDeps &deps, TSubs &subs, const state> &) { - auto &sm = back::sub_sm>::get(&subs); - while (sm.process_internal_events(back::anonymous{}, deps, subs)) { - } -} template struct transition, state, front::event, G, A> { static constexpr auto initial = state::initial; @@ -2432,7 +2406,6 @@ struct transition, state, front::event, G, A> { state{}); call, typename SM::logger_t>::execute(a, event, sm, deps, subs); sm.process_internal_event(back::on_entry{event}, deps, subs, current_state); - process_internal_transitions(deps, subs, state{}); return true; } return false; @@ -2444,7 +2417,6 @@ struct transition, state, front::event, G, A> { aux::get_id((typename SM::states_ids_t *)0), state{}, state{}); call, typename SM::logger_t>::execute(a, event, sm, deps, subs); - process_internal_transitions(deps, subs, state{}); return true; } return false; @@ -2493,7 +2465,6 @@ struct transition, state, front::event, always, A> { state{}); call, typename SM::logger_t>::execute(a, event, sm, deps, subs); sm.process_internal_event(back::on_entry{event}, deps, subs, current_state); - process_internal_transitions(deps, subs, state{}); return true; } template @@ -2502,7 +2473,6 @@ struct transition, state, front::event, always, A> { aux::get_id((typename SM::states_ids_t *)0), state{}, state{}); call, typename SM::logger_t>::execute(a, event, sm, deps, subs); - process_internal_transitions(deps, subs, state{}); return true; } A a; @@ -2544,7 +2514,6 @@ struct transition, state, front::event, G, none> { aux::get_id((typename SM::states_ids_t *)0), state{}, state{}); sm.process_internal_event(back::on_entry{event}, deps, subs, current_state); - process_internal_transitions(deps, subs, state{}); return true; } return false; @@ -2555,7 +2524,6 @@ struct transition, state, front::event, G, none> { update_current_state(sm, deps, subs, current_state, aux::get_id((typename SM::states_ids_t *)0), state{}, state{}); - process_internal_transitions(deps, subs, state{}); return true; } return false; @@ -2597,7 +2565,6 @@ struct transition, state, front::event, always, none> { aux::get_id((typename SM::states_ids_t *)0), state{}, state{}); sm.process_internal_event(back::on_entry{event}, deps, subs, current_state); - process_internal_transitions(deps, subs, state{}); return true; } template @@ -2605,7 +2572,6 @@ struct transition, state, front::event, always, none> { update_current_state(sm, deps, subs, current_state, aux::get_id((typename SM::states_ids_t *)0), state{}, state{}); - process_internal_transitions(deps, subs, state{}); return true; } __BOOST_SML_ZERO_SIZE_ARRAY(aux::byte); diff --git a/include/boost/sml/back/state_machine.hpp b/include/boost/sml/back/state_machine.hpp index 3a723ae9..ed9db1a1 100644 --- a/include/boost/sml/back/state_machine.hpp +++ b/include/boost/sml/back/state_machine.hpp @@ -77,22 +77,15 @@ struct sm_impl : aux::conditional_t::value, aux: template bool process_event(const TEvent &event, TDeps &deps, TSubs &subs) { - policies::log_process_event(aux::type{}, deps, event); + bool handled = process_internal_events(event, deps, subs); -#if BOOST_SML_DISABLE_EXCEPTIONS // __pph__ - const auto handled = process_event_impl, mappings>>( - event, deps, subs, states_t{}, aux::make_index_sequence{}); -#else // __pph__ - const auto handled = - process_event_noexcept, mappings>>(event, deps, subs, has_exceptions{}); -#endif // __pph__ // Repeat internal transition until there is no more to process. do { - while (process_internal_events(anonymous{}, deps, subs)) { - } - process_defer_events(deps, subs, handled, aux::type>{}, events_t{}); - } while (process_queued_events(deps, subs, aux::type>{}, events_t{}) || - process_internal_events(anonymous{}, deps, subs)); + do { + while (process_internal_events(anonymous{}, deps, subs)) { + } + } while (process_defer_events(deps, subs, handled, aux::type>{}, events_t{})); + } while (process_queued_events(deps, subs, aux::type>{}, events_t{})); return handled; } @@ -116,13 +109,7 @@ struct sm_impl : aux::conditional_t::value, aux: template void start(TDeps &deps, TSubs &subs) { - process_internal_events(on_entry<_, initial>{}, deps, subs); - do { - while (process_internal_events(anonymous{}, deps, subs)) { - } - process_defer_events(deps, subs, true, aux::type>{}, events_t{}); - } while (process_queued_events(deps, subs, aux::type>{}, events_t{}) || - process_internal_events(anonymous{}, deps, subs)); + process_event(on_entry<_, initial>{}, deps, subs); } template ::value, aux: const auto lock = thread_safety_.create_lock(); (void)lock; + auto handled = false; #if defined(__cpp_fold_expressions) // __pph__ - return ((dispatch_t::template dispatch<0, TMappings>(*this, current_state_[Ns], event, deps, subs, states)), ...); + ((handled |= dispatch_t::template dispatch<0, TMappings>(*this, current_state_[Ns], event, deps, subs, states)), ...); #else // __pph__ - auto handled = false; (void)aux::swallow{ 0, (handled |= dispatch_t::template dispatch<0, TMappings>(*this, current_state_[Ns], event, deps, subs, states), 0)...}; - return handled; #endif // __pph__ + return handled; } template @@ -290,15 +277,11 @@ struct sm_impl : aux::conditional_t::value, aux: template bool process_event_no_defer(TDeps &deps, TSubs &subs, const void *data) { const auto &event = *static_cast(data); - policies::log_process_event(aux::type{}, deps, event); -#if BOOST_SML_DISABLE_EXCEPTIONS // __pph__ - const auto handled = process_event_impl>(event, deps, subs, states_t{}, - aux::make_index_sequence{}); -#else // __pph__ - const auto handled = process_event_noexcept>(event, deps, subs, has_exceptions{}); -#endif // __pph__ + bool handled = process_internal_events(event, deps, subs); + if (handled && defer_again_) { ++defer_it_; + return false; } else { defer_.erase(defer_it_); defer_it_ = defer_.begin(); @@ -319,9 +302,8 @@ struct sm_impl : aux::conditional_t::value, aux: defer_again_ = false; defer_it_ = defer_.begin(); defer_end_ = defer_.end(); - processed_events = defer_it_ != defer_end_; while (defer_it_ != defer_end_) { - (this->*dispatch_table[defer_it_->id])(deps, subs, defer_it_->data); + processed_events |= (this->*dispatch_table[defer_it_->id])(deps, subs, defer_it_->data); defer_again_ = false; } defer_processing_ = false; diff --git a/include/boost/sml/back/transitions.hpp b/include/boost/sml/back/transitions.hpp index a4fdce00..5307c70c 100644 --- a/include/boost/sml/back/transitions.hpp +++ b/include/boost/sml/back/transitions.hpp @@ -39,20 +39,8 @@ template struct transitions { template static bool execute(const TEvent& event, SM& sm, TDeps& deps, TSubs& subs, typename SM::state_t& current_state) { - return execute_impl(event, sm, deps, subs, current_state); - } - - template - static bool execute_impl(const TEvent& event, SM& sm, TDeps& deps, TSubs& subs, typename SM::state_t& current_state) { return aux::get(sm.transitions_).execute(event, sm, deps, subs, current_state, typename SM::has_entry_exits{}); } - - template - static bool execute_impl(const on_exit<_, TEvent>& event, SM& sm, TDeps& deps, TSubs& subs, - typename SM::state_t& current_state) { - aux::get(sm.transitions_).execute(event, sm, deps, subs, current_state, typename SM::has_entry_exits{}); - return false; // from bottom to top - } }; template <> @@ -72,6 +60,7 @@ struct transitions { } }; +/// @brief Event executor on a sub state machine with transition in parent state machine. template struct transitions_sub, T, Ts...> { template @@ -101,13 +90,29 @@ struct transitions_sub, T, Ts...> { sub_sm>::get(&subs).process_event(event, deps, subs); return true; // from top to bottom } + + template + static bool execute_impl(const back::on_exit<_, TEvent>& event, SM& sm, TDeps& deps, TSubs& subs, + typename SM::state_t& current_state) { + sub_sm>::get(&subs).process_event(event, deps, subs); + transitions::execute(event, sm, deps, subs, current_state); + return true; // from bottom to top + } }; +/// @brief Event executor on a sub state machine without transition in parent state machine. template struct transitions_sub> { template static bool execute(const TEvent& event, SM&, TDeps& deps, TSubs& subs, typename SM::state_t&) { - return sub_sm>::get(&subs).template process_event(event, deps, subs); + return sub_sm>::get(&subs).process_event(event, deps, subs); + } + + template + static bool execute(const anonymous&, SM&, TDeps&, TSubs&, typename SM::state_t&) { + // Do not propagate anonymous events to sub state machine + // Anonymous events will be generated inside sub-statemachine by initial process_event. + return false; } }; diff --git a/include/boost/sml/front/transition.hpp b/include/boost/sml/front/transition.hpp index c51c2995..a86ba216 100644 --- a/include/boost/sml/front/transition.hpp +++ b/include/boost/sml/front/transition.hpp @@ -272,16 +272,6 @@ void update_current_state(SM &, TDeps &deps, TSubs &subs, typename SM::state_t & typename back::sm_impl::history_states_t{}); } -template -void process_internal_transitions(TDeps &, TSubs &, const TDstState &) {} - -template -void process_internal_transitions(TDeps &deps, TSubs &subs, const state> &) { - auto &sm = back::sub_sm>::get(&subs); - while (sm.process_internal_events(back::anonymous{}, deps, subs)) { - } -} - template struct transition, state, front::event, G, A> { static constexpr auto initial = state::initial; @@ -305,7 +295,6 @@ struct transition, state, front::event, G, A> { state{}); call, typename SM::logger_t>::execute(a, event, sm, deps, subs); sm.process_internal_event(back::on_entry{event}, deps, subs, current_state); - process_internal_transitions(deps, subs, state{}); return true; } return false; @@ -318,7 +307,6 @@ struct transition, state, front::event, G, A> { aux::get_id((typename SM::states_ids_t *)0), state{}, state{}); call, typename SM::logger_t>::execute(a, event, sm, deps, subs); - process_internal_transitions(deps, subs, state{}); return true; } return false; @@ -377,7 +365,6 @@ struct transition, state, front::event, always, A> { state{}); call, typename SM::logger_t>::execute(a, event, sm, deps, subs); sm.process_internal_event(back::on_entry{event}, deps, subs, current_state); - process_internal_transitions(deps, subs, state{}); return true; } @@ -387,7 +374,6 @@ struct transition, state, front::event, always, A> { aux::get_id((typename SM::states_ids_t *)0), state{}, state{}); call, typename SM::logger_t>::execute(a, event, sm, deps, subs); - process_internal_transitions(deps, subs, state{}); return true; } @@ -439,7 +425,6 @@ struct transition, state, front::event, G, none> { aux::get_id((typename SM::states_ids_t *)0), state{}, state{}); sm.process_internal_event(back::on_entry{event}, deps, subs, current_state); - process_internal_transitions(deps, subs, state{}); return true; } return false; @@ -451,7 +436,6 @@ struct transition, state, front::event, G, none> { update_current_state(sm, deps, subs, current_state, aux::get_id((typename SM::states_ids_t *)0), state{}, state{}); - process_internal_transitions(deps, subs, state{}); return true; } return false; @@ -503,7 +487,6 @@ struct transition, state, front::event, always, none> { aux::get_id((typename SM::states_ids_t *)0), state{}, state{}); sm.process_internal_event(back::on_entry{event}, deps, subs, current_state); - process_internal_transitions(deps, subs, state{}); return true; } @@ -512,7 +495,6 @@ struct transition, state, front::event, always, none> { update_current_state(sm, deps, subs, current_state, aux::get_id((typename SM::states_ids_t *)0), state{}, state{}); - process_internal_transitions(deps, subs, state{}); return true; } diff --git a/test/ft/actions_process_n_defer.cpp b/test/ft/actions_process_n_defer.cpp index 64565300..0ec1a974 100644 --- a/test/ft/actions_process_n_defer.cpp +++ b/test/ft/actions_process_n_defer.cpp @@ -1,6 +1,8 @@ #include #include +#include #include +#include namespace sml = boost::sml; @@ -60,8 +62,48 @@ test mix_process_n_defer = [] { // clang-format on } }; // internal, defer, process, defer, internal, process, internal - sml::sm, sml::defer_queue> sm{}; sm.process_event(e1{}); expect(sm.is(sml::X)); +}; + +test process_n_defer_again = [] { + struct sub { + auto operator()() { + using namespace sml; + // clang-format off + return make_transition_table( + * s2 + event / defer + , s2 + event / defer + , s2 + event = s3 + , s3 + on_entry<_> / [](std::string & calls){calls+="|s3_entry";} + , s3 + event / [](std::string & calls){calls+="|e1";} + ); + // clang-format on + } + }; + + struct c { + auto operator()() { + using namespace sml; + // clang-format off + return make_transition_table( + * s1 = state + // Check that deferred events are only propagated inside sub state machine. + , state + event / [](std::string & calls){calls+="|e3";} + ); + // clang-format on + } + }; + + std::string calls; + sml::sm, sml::defer_queue> sm{calls}; + sm.process_event(e1{}); + sm.process_event(e1{}); + sm.process_event(e1{}); + sm.process_event(e3{}); + expect(calls == ""); + sm.process_event(e2{}); + std::cout << calls << "\n"; + expect(calls == "|s3_entry|e1|e1|e1"); }; \ No newline at end of file diff --git a/test/ft/composite.cpp b/test/ft/composite.cpp index 71edffa9..b3ab5a64 100644 --- a/test/ft/composite.cpp +++ b/test/ft/composite.cpp @@ -884,6 +884,74 @@ test composite_with_names = [] { expect(sm.is(sml::X)); }; +test composite_sub_guards = [] { + enum class calls { a1_entry, a1_exit, a2_entry, a2_exit, b1_entry, b1_exit }; + + struct A { + auto operator()() const noexcept { + using namespace sml; + const auto a1 = state; + const auto a2 = state; + // clang-format off + return make_transition_table( + (*a1) [([](int& guard_counter, bool& is_ok) {guard_counter++; return is_ok;})] = a2, + a1 + sml::on_entry<_> / [](std::vector& c) { c.push_back(calls::a1_entry); }, + a1 + sml::on_exit<_> / [](std::vector& c) { c.push_back(calls::a1_exit); }, + a2 + sml::on_entry<_> / [](std::vector& c) { c.push_back(calls::a2_entry); }, + a2 + sml::on_exit<_> / [](std::vector& c) { c.push_back(calls::a2_exit); } + ); + // clang-format on + } + }; + + struct B { + auto operator()() const noexcept { + using namespace sml; + const auto b1 = state; + const auto b2 = state; + // clang-format off + return make_transition_table( + *b1 + event = b2, + b1 + sml::on_entry<_> / [](std::vector& c) { c.push_back(calls::b1_entry); }, + b1 + sml::on_exit<_> / [](std::vector& c) { c.push_back(calls::b1_exit); } + ); + // clang-format on + } + }; + + struct SM { + auto operator()() const noexcept { + using namespace sml; + // clang-format off + return make_transition_table( + *state = state, + state + event = state, + state + event = X + ); + // clang-format on + } + }; + + std::vector c_; + int guard_counter = 0; + bool is_ok = false; + sml::sm sm{c_, guard_counter, is_ok}; + expect(std::vector{calls::a1_entry} == c_); + expect(guard_counter == 1); + guard_counter = {}; + sm.process_event(e2{}); + expect(guard_counter == 1); + guard_counter = {}; + is_ok = true; + sm.process_event(e2{}); + expect(guard_counter == 1); + expect(std::vector{calls::a1_entry, calls::a1_exit, calls::a2_entry} == c_); + guard_counter = {}; + sm.process_event(e1{}); + expect(guard_counter == 0); + expect(std::vector{calls::a1_entry, calls::a1_exit, calls::a2_entry, calls::a2_exit, calls::b1_entry} == c_); +}; + #if !defined(_MSC_VER) test composite_with_string_names = [] { struct sub { diff --git a/test/ft/policies_logging.cpp b/test/ft/policies_logging.cpp index 3f7f5ad8..5dc8760d 100644 --- a/test/ft/policies_logging.cpp +++ b/test/ft/policies_logging.cpp @@ -180,13 +180,11 @@ test log_sub_sm = [] { // clang-format off std::vector messages_expected = { "[c_log_sub_sm] e1" - , "[sub] e1" , "[c_log_sub_sm] sub(a) -> sub(b)" , "[c_log_sub_sm] e2" , "[sub] e2" , "[sub] idle -> terminate" , "[c_log_sub_sm] e3" - , "[sub] e3" , "[c_log_sub_sm] e3[guard]: true" , "[c_log_sub_sm] sub(b) -> terminate" , "[c_log_sub_sm] / action" @@ -220,13 +218,11 @@ test log_sub_sm_mix = [] { // clang-format off std::vector messages_expected = { "[c_log_sub_sm_mix] e1" - , "[sub] e1" , "[c_log_sub_sm_mix] sub(a) -> sub" , "[c_log_sub_sm_mix] e2" , "[sub] e2" , "[sub] idle -> terminate" , "[c_log_sub_sm_mix] e3" - , "[sub] e3" , "[c_log_sub_sm_mix] e3[guard]: true" , "[c_log_sub_sm_mix] sub -> terminate" , "[c_log_sub_sm_mix] / action" diff --git a/test/ft/transitions.cpp b/test/ft/transitions.cpp index 5dd032e7..42243811 100644 --- a/test/ft/transitions.cpp +++ b/test/ft/transitions.cpp @@ -120,6 +120,8 @@ test subsequent_anonymous_transitions_composite = [] { // clang-format off return make_transition_table( *idle / [] (V& v) { v+="ss1|"; } = s1 + ,s1 + sml::on_entry<_> / [] (V& v) { v+="ss1en|"; } + ,s1 + sml::on_exit<_> / [] (V& v) { v+="ss1ex|"; } ,s1 / [] (V& v) { v+="ss2|"; } = s2 ,s2 / [] (V& v) { v+="ss3|"; } = X ); @@ -157,7 +159,7 @@ test subsequent_anonymous_transitions_composite = [] { sml::sm sm{calls}; expect(sm.is)>(X)); expect(sm.is(s3)); - std::string expected("11|12|s1|s2|s3|ssen|ss1|ss2|ss3|ssex|s4|13|14|"); + std::string expected("11|12|s1|s2|s3|ssen|ss1|ss1en|ss1ex|ss2|ss3|ssex|s4|13|14|"); expect(calls == expected); }; @@ -550,20 +552,59 @@ test initial_nontrivial_entry = [] { using namespace sml; // clang-format off return make_transition_table( - *idle + sml::on_entry / [] {} + *idle + sml::on_entry / [this] { calls+="e2|"; } + ,idle + sml::on_entry<_> / [this] { calls+="_|"; } ,idle + event = s1 - ,s1 + on_entry<_> / [this] { ++entry_calls; } + ,s1 + on_entry<_> / [this] { calls+="_|"; } + ,s1 + event = s2 + ,s2 + on_entry / [this] { calls+="e3|"; } + ,s2 + on_entry / [this] { calls+="e2|"; } + ,s2 + on_entry / [this] { calls+="e1|"; } + ,s2 + on_entry<_> / [this] { calls+="_|"; } + ,s2 + event = s3 + ,s3 + on_entry / [this] { calls+="e2|"; } + ,s3 + on_entry / [this] { calls+="e1|"; } + ,s3 + on_entry<_> / [this] { calls+="_|"; } ); // clang-format on } - int entry_calls = 0; + std::string calls; }; - sml::sm sm{}; - const c& c_ = sm; - sm.process_event(e2{}); - expect(1 == c_.entry_calls); + struct d { + auto operator()() noexcept { + using namespace sml; + // clang-format off + return make_transition_table( + *idle + event = state + ); + // clang-format on + } + }; + { + sml::sm sm{}; + const c& c_ = sm; + expect("_|" == c_.calls); + sm.process_event(e2{}); + expect("_|_|" == c_.calls); + sm.process_event(e3{}); + expect("_|_|e3|" == c_.calls); + sm.process_event(e3{}); + expect("_|_|e3|_|" == c_.calls); + } + { + sml::sm sm{}; + const c& c_ = sm; + sm.process_event(e2{}); + expect("e2|" == c_.calls); + sm.process_event(e2{}); + expect("e2|_|" == c_.calls); + sm.process_event(e3{}); + expect("e2|_|e3|" == c_.calls); + sm.process_event(e3{}); + expect("e2|_|e3|_|" == c_.calls); + } }; test initial_nontrivial_exit = [] { @@ -572,20 +613,69 @@ test initial_nontrivial_exit = [] { using namespace sml; // clang-format off return make_transition_table( - *idle + sml::on_exit<_> / [this] { ++entry_calls; } + *idle + sml::on_exit<_> / [](std::string& calls) { calls+="_|"; } + ,idle + sml::on_exit / [](std::string& calls) { calls+="e2|"; } ,idle + event = s1 - ,s1 + sml::on_exit / [] {} + ,idle + event = s1 + ,s1 + sml::on_exit / [](std::string& calls) { calls+="e2|"; } + ,s1 + sml::on_exit / [](std::string& calls) { calls+="e1|"; } + ,s1 + sml::on_exit<_> / [](std::string& calls) { calls+="_|"; } + ,s1 + event = s2 + ,s1 + event = s2 + ,s2 + sml::on_exit / [](std::string& calls) { calls+="e4|"; } + ,s2 + sml::on_exit / [](std::string& calls) { calls+="e3|"; } + ,s2 + sml::on_exit / [](std::string& calls) { calls+="e2|"; } + ,s2 + sml::on_exit / [](std::string& calls) { calls+="e1|"; } + ,s2 + sml::on_exit<_> / [](std::string& calls) { calls+="_|"; } + ,s2 + event = s3 ); // clang-format on } - - int entry_calls = 0; }; - sml::sm sm{}; - const c& c_ = sm; - sm.process_event(e1{}); - expect(1 == c_.entry_calls); + struct d { + auto operator()() noexcept { + using namespace sml; + // clang-format off + return make_transition_table( + *state + event = idle + ,state + sml::on_exit / [](std::string& calls) { calls+="ce4|"; } + ); + // clang-format on + } + }; + struct e { + auto operator()() noexcept { + using namespace sml; + // clang-format off + return make_transition_table( + *state + event = idle + ); + // clang-format on + } + }; + { + // Test with a simple sm + std::string s; + sml::sm sm{s}; + sm.process_event(e1{}); + expect("_|" == s); + sm.process_event(e3{}); + expect("_|_|" == s); + sm.process_event(e3{}); + expect("_|_|e3|" == s); + } + { + // Test with a composite sm + std::string s; + sml::sm sm{s}; + sm.process_event(e1{}); + expect("_|" == s); + sm.process_event(e1{}); + expect("_|e1|" == s); + sm.process_event(e4{}); + expect("_|e1|e4|ce4|" == s); + } }; #if !defined(_MSC_VER)