Skip to content

Commit bf447ce

Browse files
jefftrullkris-jusiak
authored andcommitted
Update initial submachines of composite states upon re-entry
When a composite state is re-entered due to an event any submachines within the state also need to be initialized, or they will remain in their previous terminated state. - Recursively call update_composite_state on any initial states that are themselves composite - Add a test for this situation
1 parent 5877b84 commit bf447ce

File tree

2 files changed

+123
-0
lines changed

2 files changed

+123
-0
lines changed

include/boost/sml.hpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,14 @@ using remove_pointer_t = typename remove_pointer<T>::type;
258258
} // namespace aux
259259
namespace aux {
260260
using swallow = int[];
261+
template<typename F, template<class...> class L, class... Ts>
262+
void for_each(L<Ts...>, F f) {
263+
#if defined(__cpp_fold_expressions)
264+
(f(identity<Ts>{}),...);
265+
#else
266+
(void)aux::swallow{0, (f(identity<Ts>{}), 0)...};
267+
#endif
268+
}
261269
template <int...>
262270
struct index_sequence {
263271
using type = index_sequence;
@@ -2598,9 +2606,31 @@ constexpr void update_composite_states(TSubs &subs, aux::true_type, const aux::t
25982606
0)...};
25992607
#endif
26002608
}
2609+
namespace detail {
2610+
template<class T>
2611+
struct is_composite_state {
2612+
constexpr static bool value = false;
2613+
};
2614+
template<class T>
2615+
struct is_composite_state<back::sm<T>> {
2616+
constexpr static bool value = true;
2617+
};
2618+
template<class T>
2619+
constexpr bool is_composite_state_v = is_composite_state<T>::value;
2620+
} // namespace detail
2621+
26012622
template <class T, class TSubs, class... Ts>
26022623
constexpr void update_composite_states(TSubs &subs, aux::false_type, Ts &&...) {
26032624
back::sub_sm<T>::get(&subs).initialize(typename T::initial_states_t{});
2625+
2626+
aux::for_each(typename T::initial_states_t{},
2627+
[&](auto state_identity){
2628+
using state_t = typename decltype(state_identity)::type;
2629+
if constexpr (detail::is_composite_state_v<state_t>) {
2630+
using sm_impl_t = aux::apply_t<back::sm_impl, state_t>;
2631+
update_composite_states<sm_impl_t>(subs, aux::false_type{}, Ts{}...);
2632+
}
2633+
});
26042634
}
26052635
template <class SM, class TDeps, class TSubs, class TSrcState, class TDstState>
26062636
constexpr void update_current_state(SM &, TDeps &deps, TSubs &, typename SM::state_t &current_state,

test/ft/composite.cpp

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1049,3 +1049,96 @@ test nested_composite_anonymous = [] {
10491049
expect(sm.is(state<Leaf>));
10501050
expect(sm.is<decltype(state<Leaf>)>(X));
10511051
};
1052+
1053+
test composite_state_reentry = [] {
1054+
using namespace boost::sml;
1055+
1056+
// events
1057+
struct E {};
1058+
1059+
struct Inner {
1060+
// states
1061+
struct START {};
1062+
struct MID {};
1063+
struct END {};
1064+
1065+
auto operator()() const noexcept {
1066+
using namespace sml;
1067+
1068+
/* clang-format off */
1069+
return make_transition_table(
1070+
*state<START> = state<MID>,
1071+
state<MID> + event<E> = state<END>,
1072+
state<END> = X
1073+
);
1074+
/* clang-format on */
1075+
}
1076+
};
1077+
1078+
struct Outer {
1079+
// states
1080+
struct START {}; // for demonstrating the workaround
1081+
struct END {};
1082+
1083+
1084+
auto operator()() const noexcept {
1085+
using namespace sml;
1086+
1087+
// just runs the inner state machine
1088+
/* clang-format off */
1089+
return make_transition_table(
1090+
*state<Inner> = state<END>,
1091+
state<END> = X
1092+
);
1093+
/* clang-format on */
1094+
}
1095+
1096+
};
1097+
1098+
// events
1099+
struct CONTINUE {};
1100+
struct EXIT {};
1101+
1102+
struct Top {
1103+
// states
1104+
struct MID {};
1105+
struct END {};
1106+
1107+
auto operator()() const noexcept {
1108+
using namespace sml;
1109+
1110+
/* clang-format off */
1111+
return make_transition_table(
1112+
*state<Outer> = state<MID>,
1113+
state<MID> + event<CONTINUE> = state<Outer>,
1114+
state<MID> + event<EXIT> = state<END>,
1115+
state<END> = X
1116+
);
1117+
/* clang-format on */
1118+
}
1119+
1120+
};
1121+
1122+
sml::sm<Top> sm;
1123+
1124+
// we should be in the Inner state machine waiting for its event
1125+
expect(sm.is(state<Outer>));
1126+
expect(sm.is<decltype(state<Outer>)>(state<Inner>));
1127+
expect(sm.is<decltype(state<Inner>)>(state<Inner::MID>));
1128+
1129+
// fire the event Inner is waiting for
1130+
expect(sm.process_event(E{}));
1131+
1132+
// now Inner and Outer have both finished and we are sitting in the top level MID
1133+
expect(sm.is(state<Top::MID>));
1134+
expect(sm.is<decltype(state<Outer>)>(X));
1135+
expect(sm.is<decltype(state<Inner>)>(X));
1136+
1137+
// fire CONTINUE to re-enter Outer and therefore Inner
1138+
expect(sm.process_event(CONTINUE{}));
1139+
1140+
// back in Inner waiting for an event
1141+
expect(sm.is(state<Outer>));
1142+
expect(sm.is<decltype(state<Outer>)>(state<Inner>));
1143+
expect(sm.is<decltype(state<Inner>)>(state<Inner::MID>));
1144+
};

0 commit comments

Comments
 (0)