-
Notifications
You must be signed in to change notification settings - Fork 0
WIP: baseline issues triage matrix (real bug vs user error) #6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
77364d6
5a33db7
2d5c5e9
370d88a
e9699f6
ddec026
39651c3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -763,6 +763,10 @@ template <class R, class T, int N> | |
| constexpr R get_id(type_id_type<N, T> *) { | ||
| return static_cast<R>(N); | ||
| } | ||
| template <class T, int N> | ||
| constexpr true_type has_type_id(type_id_type<N, T> *); | ||
| template <class T> | ||
| constexpr false_type has_type_id(...); | ||
| template <template <class...> class, class T> | ||
| struct is : false_type {}; | ||
| template <template <class...> class T, class... Ts> | ||
|
|
@@ -1299,6 +1303,15 @@ struct transitions<aux::false_type> { | |
| return false; | ||
| } | ||
| }; | ||
| template <class TEvent, class...> | ||
| struct has_terminate_parent_transition : aux::false_type {}; | ||
| template <class TEvent, class TTransition, class... TRest> | ||
| struct has_terminate_parent_transition<TEvent, TTransition, TRest...> | ||
| : aux::conditional< | ||
| aux::is_same<terminate_state, typename TTransition::dst_state>::value && | ||
| aux::is_same<TEvent, typename TTransition::event>::value, | ||
| aux::true_type, | ||
| has_terminate_parent_transition<TEvent, TRest...>>::type {}; | ||
| template <class Tsm, class T, class... Ts> | ||
| struct transitions_sub<sm<Tsm>, T, Ts...> { | ||
| template <class TEvent, class SM, class TDeps, class TSubs> | ||
|
|
@@ -1314,7 +1327,14 @@ struct transitions_sub<sm<Tsm>, T, Ts...> { | |
| } | ||
| template <class TEvent, class SM, class TDeps, class TSubs> | ||
| constexpr static bool execute_impl(const TEvent &event, SM &sm, TDeps &deps, TSubs &subs, typename SM::state_t ¤t_state) { | ||
| const auto handled = sub_sm<sm_impl<Tsm>>::get(&subs).process_event(event, deps, subs); | ||
| auto &sub = sub_sm<sm_impl<Tsm>>::get(&subs); | ||
| const auto handled = sub.process_event(event, deps, subs); | ||
|
|
||
| if (handled && sub.is_terminated() && has_terminate_parent_transition<TEvent, T, Ts...>::value) { | ||
| const auto propagated = transitions<T, Ts...>::execute(event, sm, deps, subs, current_state); | ||
| return propagated ? propagated : handled; | ||
| } | ||
|
Comment on lines
+1333
to
+1336
|
||
|
|
||
| return handled ? handled : transitions<T, Ts...>::execute(event, sm, deps, subs, current_state); | ||
| } | ||
| template <class _, class TEvent, class SM, class TDeps, class TSubs> | ||
|
|
@@ -2088,12 +2108,17 @@ struct sm_impl : aux::conditional_t<aux::should_not_subclass_statemachine_class< | |
| constexpr static void visit_state(const TVisitor &visitor) { | ||
| visitor(aux::string<TState>{}); | ||
| } | ||
| constexpr bool is_terminated() const { return is_terminated_impl(aux::make_index_sequence<regions>{}); } | ||
| constexpr bool is_terminated_impl(aux::index_sequence<0>) const { | ||
| return current_state_[0] == aux::get_id<state_t, terminate_state>((states_ids_t *)0); | ||
| constexpr bool is_terminated() const { | ||
| return is_terminated_impl(aux::make_index_sequence<regions>{}, | ||
| decltype(aux::has_type_id<terminate_state>((states_ids_t *)0)){}); | ||
| } | ||
| template <int... Ns> | ||
| constexpr bool is_terminated_impl(aux::index_sequence<Ns...>, aux::false_type) const { | ||
| (void)sizeof...(Ns); | ||
| return false; | ||
| } | ||
| template <int... Ns> | ||
| constexpr bool is_terminated_impl(aux::index_sequence<Ns...>) const { | ||
| constexpr bool is_terminated_impl(aux::index_sequence<Ns...>, aux::true_type) const { | ||
| #if defined(__cpp_fold_expressions) | ||
| return ((current_state_[Ns] == aux::get_id<state_t, terminate_state>((states_ids_t *)0)) && ...); | ||
| #else | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -101,7 +101,14 @@ auto make_dispatch_table(SM &fsm, const aux::index_sequence<Ns...> &) { | |||||
| using dispatch_table_t = bool (*)(SM &, const TEvent &); | ||||||
| const static dispatch_table_t dispatch_table[sizeof...(Ns) ? sizeof...(Ns) : 1] = { | ||||||
| &get_event_t<Ns + EventRangeBegin, events_ids_t>::template execute<SM, TEvent>...}; | ||||||
| return dispatch_table[id - EventRangeBegin](fsm, e); | ||||||
| const auto id64 = static_cast<long long>(id); | ||||||
| const auto begin64 = static_cast<long long>(EventRangeBegin); | ||||||
| const auto count64 = static_cast<long long>(sizeof...(Ns)); | ||||||
| if (id64 < begin64 || id64 >= begin64 + count64) { | ||||||
| return false; | ||||||
| } | ||||||
| const auto dispatch_id = static_cast<int>(id64 - begin64); | ||||||
|
||||||
| const auto dispatch_id = static_cast<int>(id64 - begin64); | |
| const auto dispatch_id = static_cast<std::size_t>(id64 - begin64); |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -527,6 +527,9 @@ fi | |||||
| if [[ -n "${EXTRA_CXX_FLAGS}" ]]; then | ||||||
| BASE_CXX_FLAGS="${BASE_CXX_FLAGS} ${EXTRA_CXX_FLAGS}" | ||||||
| fi | ||||||
| if [[ "${COMPILER_FAMILY}" == "clang" ]]; then | ||||||
| BASE_CXX_FLAGS="${BASE_CXX_FLAGS} -Wno-unknown-warning-option -Wno-error=mismatched-tags -Wno-error=stack-exhausted -Wno-stack-exhausted" | ||||||
|
||||||
| BASE_CXX_FLAGS="${BASE_CXX_FLAGS} -Wno-unknown-warning-option -Wno-error=mismatched-tags -Wno-error=stack-exhausted -Wno-stack-exhausted" | |
| BASE_CXX_FLAGS="${BASE_CXX_FLAGS} -Wno-unknown-warning-option -Wno-error=mismatched-tags" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,198 @@ | ||
| #include <algorithm> | ||
| #include <boost/sml.hpp> | ||
| #include <string> | ||
| #include <vector> | ||
|
|
||
| namespace sml = boost::sml; | ||
|
|
||
| inline std::string canonical_state_name(std::string state) { | ||
| if (0 == state.find("class ")) { | ||
| state.erase(0, 6); | ||
| } else if (0 == state.find("struct ")) { | ||
| state.erase(0, 7); | ||
| } | ||
| const auto scope_pos = state.rfind("::"); | ||
| if (scope_pos != std::string::npos) { | ||
| state.erase(0, scope_pos + 2); | ||
| } | ||
| return state; | ||
| } | ||
|
|
||
| template <class TSM> | ||
| std::vector<std::string> sorted_current_states(const TSM& sm) { | ||
| std::vector<std::string> states; | ||
| sm.visit_current_states([&](auto state) { states.push_back(canonical_state_name(state.c_str())); }); | ||
| std::sort(states.begin(), states.end()); | ||
| return states; | ||
| } | ||
|
|
||
| template <class TSM> | ||
| bool all_regions_terminated(const TSM& sm) { | ||
| const auto states = sorted_current_states(sm); | ||
| return std::all_of(states.cbegin(), states.cend(), [](const auto& state) { return state == "terminate"; }); | ||
| } | ||
|
|
||
| struct e_single_start {}; | ||
| struct e_single_finish {}; | ||
| struct e_single_idle {}; | ||
|
|
||
| struct e_ortho_left_start {}; | ||
| struct e_ortho_left_finish {}; | ||
| struct e_ortho_right_start {}; | ||
| struct e_ortho_right_finish {}; | ||
| struct e_ortho_unused {}; | ||
| struct e_ortho_right_done {}; | ||
|
|
||
| const auto qa_idle = sml::state<class qa_idle>; | ||
| const auto qa_active = sml::state<class qa_active>; | ||
| const auto qb_idle = sml::state<class qb_idle>; | ||
| const auto qb_active = sml::state<class qb_active>; | ||
|
|
||
| const auto qb_region_left = sml::state<class qb_region_left>; | ||
| const auto qb_region_right = sml::state<class qb_region_right>; | ||
| const auto qb_region_left_next = sml::state<class qb_region_left_next>; | ||
| const auto qb_region_right_next = sml::state<class qb_region_right_next>; | ||
| const auto qb_region_right_done = sml::state<class qb_region_right_done>; | ||
|
|
||
| struct e_return_left {}; | ||
| struct e_return_right {}; | ||
| struct e_return_unused {}; | ||
|
|
||
| test is_single_region_termination_and_event_visibility = [] { | ||
| struct machine { | ||
| auto operator()() { | ||
| using namespace sml; | ||
| // clang-format off | ||
| return make_transition_table( | ||
| *qa_idle + event<e_single_start> = qa_active | ||
| , qa_active + event<e_single_finish> = sml::X | ||
| ); | ||
| // clang-format on | ||
| } | ||
| }; | ||
|
|
||
| sml::sm<machine> sm{}; | ||
| expect((std::vector<std::string>{"qa_idle"} == sorted_current_states(sm))); | ||
| expect(sm.process_event(e_single_start{})); | ||
| expect((std::vector<std::string>{"qa_active"} == sorted_current_states(sm))); | ||
| expect(!sm.process_event(e_single_idle{})); | ||
| expect(sm.process_event(e_single_finish{})); | ||
| expect((std::vector<std::string>{"terminate"} == sorted_current_states(sm))); | ||
| expect(sm.is(sml::X)); | ||
| expect(!sm.process_event(e_single_idle{})); | ||
| }; | ||
|
|
||
| test orthogonal_regions_partial_termination = [] { | ||
| struct machine { | ||
| auto operator()() { | ||
| using namespace sml; | ||
| // clang-format off | ||
| return make_transition_table( | ||
| *qb_region_left + event<e_ortho_left_start> = qb_region_left_next | ||
| , qb_region_left_next + event<e_ortho_left_finish> = sml::X | ||
| , *qb_region_right + event<e_ortho_right_start> = qb_region_right_next | ||
| , qb_region_right_next + event<e_ortho_right_finish> = sml::X | ||
| ); | ||
| // clang-format on | ||
| } | ||
| }; | ||
|
|
||
| sml::sm<machine> sm{}; | ||
| expect((std::vector<std::string>{"qb_region_left", "qb_region_right"} == sorted_current_states(sm))); | ||
| expect(!all_regions_terminated(sm)); | ||
|
|
||
| expect(sm.process_event(e_ortho_left_start{})); | ||
| expect((std::vector<std::string>{"qb_region_left_next", "qb_region_right"} == sorted_current_states(sm))); | ||
| expect(!all_regions_terminated(sm)); | ||
|
|
||
| expect(sm.process_event(e_ortho_left_finish{})); | ||
| expect((std::vector<std::string>{"qb_region_right", "terminate"} == sorted_current_states(sm))); | ||
| expect(!all_regions_terminated(sm)); | ||
| expect(!sm.process_event(e_ortho_unused{})); | ||
| expect(!all_regions_terminated(sm)); | ||
|
|
||
| expect(sm.process_event(e_ortho_right_start{})); | ||
| expect((std::vector<std::string>{"qb_region_right_next", "terminate"} == sorted_current_states(sm))); | ||
| expect(sm.process_event(e_ortho_right_finish{})); | ||
| expect((std::vector<std::string>{"terminate", "terminate"} == sorted_current_states(sm))); | ||
| expect(all_regions_terminated(sm)); | ||
| }; | ||
|
|
||
| test visit_current_states_reports_all_regions = [] { | ||
| struct machine { | ||
| auto operator()() { | ||
| using namespace sml; | ||
| // clang-format off | ||
| return make_transition_table( | ||
| *qb_region_left + event<e_ortho_left_start> = qb_region_left_next | ||
| , qb_region_left_next + event<e_ortho_left_finish> = sml::X | ||
| , *qb_region_right + event<e_ortho_right_start> = qb_region_right_next | ||
| , qb_region_right_next + event<e_ortho_right_finish> = sml::X | ||
| ); | ||
| // clang-format on | ||
| } | ||
| }; | ||
|
|
||
| sml::sm<machine> sm{}; | ||
|
|
||
| expect((std::vector<std::string>{"qb_region_left", "qb_region_right"} == sorted_current_states(sm))); | ||
| sm.process_event(e_ortho_left_start{}); | ||
| sm.process_event(e_ortho_right_start{}); | ||
| expect((std::vector<std::string>{"qb_region_left_next", "qb_region_right_next"} == sorted_current_states(sm))); | ||
|
|
||
| sm.process_event(e_ortho_left_finish{}); | ||
| sm.process_event(e_ortho_right_finish{}); | ||
| expect((std::vector<std::string>{"terminate", "terminate"} == sorted_current_states(sm))); | ||
| }; | ||
|
|
||
| test process_event_return_value_in_orthogonal_regions = [] { | ||
| struct machine { | ||
| auto operator()() { | ||
| using namespace sml; | ||
| // clang-format off | ||
| return make_transition_table( | ||
| *qa_idle + event<e_return_left> = qa_active | ||
| , qa_active + event<e_return_left> = qa_active | ||
| , *qb_idle + event<e_return_right> = qb_active | ||
| ); | ||
| // clang-format on | ||
| } | ||
| }; | ||
|
|
||
| sml::sm<machine> sm{}; | ||
| expect(sm.process_event(e_return_left{})); | ||
| expect((std::vector<std::string>{"qa_active", "qb_idle"} == sorted_current_states(sm))); | ||
| expect(!sm.process_event(e_return_unused{})); | ||
| expect((std::vector<std::string>{"qa_active", "qb_idle"} == sorted_current_states(sm))); | ||
| expect(sm.process_event(e_return_right{})); | ||
| expect((std::vector<std::string>{"qa_active", "qb_active"} == sorted_current_states(sm))); | ||
| }; | ||
|
|
||
| test orthogonal_regions_still_process_after_partial_termination = [] { | ||
| struct machine { | ||
| auto operator()() { | ||
| using namespace sml; | ||
| // clang-format off | ||
| return make_transition_table( | ||
| *qb_region_left + event<e_ortho_left_start> = qb_region_left_next | ||
| , qb_region_left_next + event<e_ortho_left_finish> = sml::X | ||
| , *qb_region_right + event<e_ortho_right_start> = qb_region_right_next | ||
| , qb_region_right_next + event<e_ortho_right_done> = qb_region_right_done | ||
| ); | ||
| // clang-format on | ||
| } | ||
| }; | ||
|
|
||
| sml::sm<machine> sm{}; | ||
| expect((std::vector<std::string>{"qb_region_left", "qb_region_right"} == sorted_current_states(sm))); | ||
| expect(sm.process_event(e_ortho_left_start{})); | ||
| expect((std::vector<std::string>{"qb_region_left_next", "qb_region_right"} == sorted_current_states(sm))); | ||
| expect(sm.process_event(e_ortho_left_finish{})); | ||
| expect((std::vector<std::string>{"qb_region_right", "terminate"} == sorted_current_states(sm))); | ||
|
|
||
| expect(sm.process_event(e_ortho_right_start{})); | ||
| expect((std::vector<std::string>{"qb_region_right_next", "terminate"} == sorted_current_states(sm))); | ||
| expect(sm.process_event(e_ortho_right_done{})); | ||
| expect((std::vector<std::string>{"qb_region_right_done", "terminate"} == sorted_current_states(sm))); | ||
| expect(!sm.process_event(e_ortho_unused{})); | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new
has_type_idfunctions (lines 767-769) are added but only declared, not defined. The pointer overload should returntrue_typebut there's no function body. While this works for SFINAE anddecltypecontexts (as used on line 2113), the lack of definition could cause linker errors if someone tries to actually call these functions. Consider adding= deleteor{}bodies to make the intent clearer and prevent accidental ODR violations.