diff --git a/include/hsm/details/collect_states.h b/include/hsm/details/collect_states.h index 1b7a6dd..b3f61cc 100644 --- a/include/hsm/details/collect_states.h +++ b/include/hsm/details/collect_states.h @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -51,6 +52,12 @@ constexpr auto extractStateTypeids = [](auto transition) { }; } +constexpr auto extractParentAndState = [](auto transition) { + return bh::make_basic_tuple( + bh::make_pair(transition.parent(), resolveInitialState(transition)), + bh::make_pair(transition.parent(), transition.target())); +}; + template constexpr auto collect_child_state_typeids_recursive(State parentState) { auto transitions = flatten_transition_table(parentState); @@ -62,7 +69,6 @@ template constexpr auto collect_child_state_typeids_recursive(Stat template constexpr auto collect_child_states_recursive(State parentState) { return bh::flatten(bh::transform(flatten_transition_table(parentState), extractExtendedStates)); - ; } template constexpr auto collect_state_typeids_recursive(State parentState) @@ -90,4 +96,11 @@ template constexpr auto collect_child_states(State state) return remove_duplicates( bh::flatten(bh::transform(make_transition_table2(state), extractStates))); } + +template constexpr auto collect_state_parentstate_pairs_recursively(State rootState) +{ + return remove_duplicates(bh::prepend( + bh::flatten(bh::transform(flatten_transition_table(rootState), extractParentAndState)), + bh::make_pair(rootState, rootState))); +} } \ No newline at end of file diff --git a/include/hsm/details/flatten_internal_transition_table.h b/include/hsm/details/flatten_internal_transition_table.h index 7392c79..89e7b09 100644 --- a/include/hsm/details/flatten_internal_transition_table.h +++ b/include/hsm/details/flatten_internal_transition_table.h @@ -76,29 +76,57 @@ constexpr auto extend_internal_transition(Transition internalTransition, States } /** - * Returns the internal transitions for each for each state + * Extends an internal transitions to all provided states + * + * @param internalTranstion Internal transition that should be extended + * @param states tuple of states + */ +template +constexpr auto +extend_internal_transition2(Transition internalTransition, ParentState parent, State state) +{ + return bh::make_basic_tuple(details::internal_extended_transition( + parent, + details::transition( + state, + internalTransition.event(), + internalTransition.guard(), + internalTransition.action(), + state))); +} + +/** + * Returns the internal transitions for each state * [[transition1, transition2], [transition3, transition4], []] * * @param states a tuple of states */ -constexpr auto get_internal_transitions = [](auto states) { +constexpr auto get_internal_transitions = [](auto stateAndParentStatePairs) { return bh::flatten(bh::filter( bh::transform( - states, - [](auto parentState) { - constexpr auto extend - = bh::capture(parentState)([](auto parentState, auto transition) { - // Folowing lines satisfies older gcc -Werror=unused-but-set-parameter - (void)transition; - if constexpr (has_transition_table(parentState)) { - return extend_internal_transition( - transition, collect_child_states(parentState)); - } else { - return bh::make_basic_tuple(); - } - }); + stateAndParentStatePairs, + [](auto stateAndParentStatePair) { + constexpr auto extend = bh::capture(stateAndParentStatePair)( + [](auto stateAndParentStatePair, auto internalTransition) { + // Folowing lines satisfies older gcc -Werror=unused-but-set-parameter + (void)internalTransition; + if constexpr (has_transition_table(bh::second(stateAndParentStatePair))) { + return extend_internal_transition( + internalTransition, + collect_child_states(bh::second(stateAndParentStatePair))); + } else if constexpr (has_internal_transition_table( + bh::second(stateAndParentStatePair))) { + return extend_internal_transition2( + internalTransition, + bh::first(stateAndParentStatePair), + bh::second(stateAndParentStatePair)); + } else { + return bh::make_basic_tuple(); + } + }); - return bh::transform(get_internal_transition_table(parentState), extend); + return bh::transform( + get_internal_transition_table(bh::second(stateAndParentStatePair)), extend); }), isNotEmpty)); }; @@ -110,9 +138,10 @@ constexpr auto get_internal_transitions = [](auto states) { */ template constexpr auto flatten_internal_transition_table(State rootState) { - return [](auto states) { - return bh::to(bh::flatten(get_internal_transitions(states))); - }(collect_states_recursive(rootState)); + return [](auto stateAndParentStatePairs) { + return remove_duplicate_types(bh::to( + bh::flatten(get_internal_transitions(stateAndParentStatePairs)))); + }(collect_state_parentstate_pairs_recursively(rootState)); } } // namespace hsm \ No newline at end of file diff --git a/include/hsm/details/flatten_transition_table.h b/include/hsm/details/flatten_transition_table.h index bf16e0e..920eae5 100644 --- a/include/hsm/details/flatten_transition_table.h +++ b/include/hsm/details/flatten_transition_table.h @@ -23,6 +23,12 @@ template constexpr auto flatten_sub_transition_table(State state); } +/** + * This function iterates recursively trough the transition table of a state provided by + * `make_transition_table()` and collects all transitions. + * + * @return a list of ExtendedTransition + */ template constexpr auto flatten_transition_table(State state) { auto flattenSubTransitionTable = [state](auto transition) { diff --git a/test/integration/internal_transition.cpp b/test/integration/internal_transition.cpp index cdc6d03..f931554 100644 --- a/test/integration/internal_transition.cpp +++ b/test/integration/internal_transition.cpp @@ -60,6 +60,8 @@ struct e8 { }; struct e9 { }; +struct e10 { }; +struct e11 { }; // Guards const auto fail = [](auto /*event*/, auto /*source*/, auto /*target*/) { return false; }; @@ -94,15 +96,27 @@ struct SubState { } }; +struct InternalOnlySubState { + static constexpr auto make_internal_transition_table() + { + // clang-format off + return hsm::transition_table( + + (hsm::event) + ); + // clang-format on + } +}; + struct MainState { static constexpr auto make_transition_table() { // clang-format off return hsm::transition_table( - * hsm::state + hsm::event = hsm::state - , hsm::state + hsm::event = hsm::state - , hsm::state + hsm::event = hsm::state - , hsm::state + hsm::event = hsm::state + * hsm::state + hsm::event = hsm::state + , hsm::state + hsm::event = hsm::state + , hsm::state + hsm::event = hsm::state + , hsm::state + hsm::event = hsm::state + , hsm::state + hsm::event = hsm::state ); // clang-format on } @@ -214,3 +228,12 @@ TEST_F(InternalTransitionTests, should_not_transit_to_initial_state) sm.process_event(e1 {}); ASSERT_TRUE(sm.is(hsm::state)); } + +TEST_F(InternalTransitionTests, should_recognize_substates_with_only_internal_transition_table) +{ + ASSERT_TRUE(sm.is(hsm::state)); + sm.process_event(e10 {}); + ASSERT_TRUE(sm.is(hsm::state)); + sm.process_event(e11 {}); + ASSERT_TRUE(sm.is(hsm::state)); +} \ No newline at end of file diff --git a/test/unit/collect_events_tests.cpp b/test/unit/collect_events_tests.cpp index f46aac3..5538636 100644 --- a/test/unit/collect_events_tests.cpp +++ b/test/unit/collect_events_tests.cpp @@ -62,7 +62,7 @@ TEST_F(CollectEventsTests, should_collect_event_typeids_recursive) { auto collectedEvents = hsm::collect_event_typeids_recursive(hsm::state_t {}); auto expectedEvents = bh::make_basic_tuple( - bh::typeid_(E1 {}), bh::typeid_(E2 {}), bh::typeid_(E4 {}), bh::typeid_(E3 {})); + bh::typeid_(E1 {}), bh::typeid_(E2 {}), bh::typeid_(E3 {}), bh::typeid_(E4 {})); ASSERT_EQ(bh::size(expectedEvents), bh::size(collectedEvents)); ASSERT_TRUE(bh::equal(expectedEvents, collectedEvents)); @@ -73,7 +73,7 @@ TEST_F(CollectEventsTests, should_collect_events_recursive) auto collectedEvents = bh::transform(hsm::collect_events_recursive(hsm::state_t {}), bh::typeid_); auto expectedEvents = bh::make_basic_tuple( - bh::typeid_(E1 {}), bh::typeid_(E2 {}), bh::typeid_(E4 {}), bh::typeid_(E3 {})); + bh::typeid_(E1 {}), bh::typeid_(E2 {}), bh::typeid_(E3 {}), bh::typeid_(E4 {})); ASSERT_EQ(bh::size(expectedEvents), bh::size(collectedEvents)); ASSERT_TRUE(bh::equal(expectedEvents, collectedEvents)); diff --git a/test/unit/collect_states_tests.cpp b/test/unit/collect_states_tests.cpp index dabe3bb..5fd6e63 100644 --- a/test/unit/collect_states_tests.cpp +++ b/test/unit/collect_states_tests.cpp @@ -8,6 +8,7 @@ #include #include +#include using namespace ::testing; @@ -300,4 +301,33 @@ TEST_F(CollectStatesTests, should_collect_parent_state_typeids) bh::typeid_); ASSERT_EQ(expectedParentStates, collectedParentStates); +} + +TEST_F(CollectStatesTests, should_collect_parent_state_and_state_typeids) +{ + auto collectedParentStatesAndStatesTypesids + = hsm::collect_state_parentstate_pairs_recursively(hsm::state_t {}); + + ASSERT_EQ(bh::size_c<11>, bh::size(collectedParentStatesAndStatesTypesids)); + + auto expected = bh::make_tuple( + bh::make_pair(hsm::state_t {}, hsm::state_t {}), + bh::make_pair(hsm::state_t {}, hsm::state_t {}), + bh::make_pair(hsm::state_t {}, hsm::state_t {}), + bh::make_pair(hsm::state_t {}, hsm::state_t {}), + bh::make_pair(hsm::state_t {}, hsm::state_t {}), + bh::make_pair(hsm::state_t {}, hsm::state_t {}), + bh::make_pair(hsm::state_t {}, hsm::history_t {}), + bh::make_pair(hsm::state_t {}, hsm::state_t {}), + bh::make_pair(hsm::state_t {}, hsm::state_t {}), + bh::make_pair(hsm::state_t {}, hsm::state_t {}), + bh::make_pair(hsm::state_t {}, hsm::state_t {})); + + auto toTypes = [](auto pairs) { + return bh::transform(pairs, [](auto pair) { + return bh::make_pair(bh::typeid_(bh::first(pair)), bh::typeid_(bh::second(pair))); + }); + }; + + ASSERT_EQ(toTypes(expected), toTypes(collectedParentStatesAndStatesTypesids)); } \ No newline at end of file diff --git a/test/unit/flatten_internal_transition_table_tests.cpp b/test/unit/flatten_internal_transition_table_tests.cpp index c7bf83e..92cb48f 100644 --- a/test/unit/flatten_internal_transition_table_tests.cpp +++ b/test/unit/flatten_internal_transition_table_tests.cpp @@ -6,24 +6,39 @@ #include #include +#include using namespace ::testing; using namespace hsm; namespace { -struct T { +struct PlainState { }; + +struct InternalOnly { static constexpr auto make_internal_transition_table() { return hsm::transition_table(hsm::internal_transition("event", "guard", "action")); } }; -struct P { +struct X { + static constexpr auto make_transition_table() + { + return hsm::transition_table(hsm::transition( + hsm::state_t {}, + "event", + "guard", + "action", + hsm::state_t {})); + } +}; + +struct Y { static constexpr auto make_transition_table() { - return hsm::transition_table( - hsm::transition(hsm::state_t {}, "event", "guard", "action", hsm::state_t {})); + return hsm::transition_table(hsm::transition( + hsm::state_t {}, "event", "guard", "action", hsm::state_t {})); } static constexpr auto make_internal_transition_table() @@ -32,11 +47,28 @@ struct P { } }; -struct S { +struct Z { static constexpr auto make_transition_table() { - return hsm::transition_table( - hsm::transition(hsm::state_t {}, "event", "guard", "action", hsm::state_t

{})); + return hsm::transition_table(hsm::transition( + hsm::state_t {}, + "event", + "guard", + "action", + hsm::state_t {})); + } + + static constexpr auto make_internal_transition_table() + { + return hsm::transition_table(hsm::internal_transition("event", "guard", "action")); + } +}; + +struct P { + static constexpr auto make_transition_table() + { + return hsm::transition_table(hsm::transition( + hsm::state_t {}, "event", "guard", "action", hsm::state_t {})); } static constexpr auto make_internal_transition_table() @@ -49,32 +81,55 @@ class FlattenInternalTransitionTableTests : public Test { }; } -TEST_F(FlattenInternalTransitionTableTests, should_flatten_internal_transition_table) +TEST_F(FlattenInternalTransitionTableTests, should_flatten_root_internal_transition_table) { - auto flattenInternalTransitionTable = flatten_internal_transition_table(hsm::state_t {}); - - ASSERT_EQ(boost::hana::size_c<3>, boost::hana::size(flattenInternalTransitionTable)); + auto flattenInternalTransitionTable = flatten_internal_transition_table(hsm::state_t {}); + ASSERT_EQ(boost::hana::size_c<1>, boost::hana::size(flattenInternalTransitionTable)); - // S{} State ASSERT_TRUE( - hsm::state_t

{} == boost::hana::at_c<0>(flattenInternalTransitionTable).parent()); + hsm::state_t {} == boost::hana::at_c<0>(flattenInternalTransitionTable).parent()); ASSERT_TRUE( - hsm::state_t {} == boost::hana::at_c<0>(flattenInternalTransitionTable).source()); + hsm::state_t {} + == boost::hana::at_c<0>(flattenInternalTransitionTable).source()); ASSERT_TRUE( - hsm::state_t {} == boost::hana::at_c<0>(flattenInternalTransitionTable).target()); + hsm::state_t {} + == boost::hana::at_c<0>(flattenInternalTransitionTable).target()); +} + +TEST_F(FlattenInternalTransitionTableTests, should_flatten_substate_internal_transition_table) +{ + auto flattenInternalTransitionTable = flatten_internal_transition_table(hsm::state_t {}); + ASSERT_EQ(boost::hana::size_c<1>, boost::hana::size(flattenInternalTransitionTable)); ASSERT_TRUE( - hsm::state_t {} == boost::hana::at_c<1>(flattenInternalTransitionTable).parent()); + hsm::state_t {} == boost::hana::at_c<0>(flattenInternalTransitionTable).parent()); ASSERT_TRUE( - hsm::state_t {} == boost::hana::at_c<1>(flattenInternalTransitionTable).source()); + hsm::state_t {} + == boost::hana::at_c<0>(flattenInternalTransitionTable).source()); ASSERT_TRUE( - hsm::state_t {} == boost::hana::at_c<1>(flattenInternalTransitionTable).target()); + hsm::state_t {} + == boost::hana::at_c<0>(flattenInternalTransitionTable).target()); +} + +TEST_F( + FlattenInternalTransitionTableTests, should_flatten_root_and_substate_internal_transition_table) +{ + auto flattenInternalTransitionTable = flatten_internal_transition_table(hsm::state_t {}); + ASSERT_EQ(boost::hana::size_c<1>, boost::hana::size(flattenInternalTransitionTable)); - // P{} State ASSERT_TRUE( - hsm::state_t {} == boost::hana::at_c<2>(flattenInternalTransitionTable).parent()); + hsm::state_t {} == boost::hana::at_c<0>(flattenInternalTransitionTable).parent()); ASSERT_TRUE( - hsm::state_t

{} == boost::hana::at_c<2>(flattenInternalTransitionTable).source()); + hsm::state_t {} + == boost::hana::at_c<0>(flattenInternalTransitionTable).source()); ASSERT_TRUE( - hsm::state_t

{} == boost::hana::at_c<2>(flattenInternalTransitionTable).target()); + hsm::state_t {} + == boost::hana::at_c<0>(flattenInternalTransitionTable).target()); +} + +TEST_F(FlattenInternalTransitionTableTests, should_flaten_internal_transition_table) +{ + auto flattenInternalTransitionTable = flatten_internal_transition_table(hsm::state_t

{}); + + ASSERT_EQ(boost::hana::size_c<3>, boost::hana::size(flattenInternalTransitionTable)); } \ No newline at end of file