Skip to content

Commit 8f24199

Browse files
authored
fix: Make internal transition recognition compile time (#159)
1 parent e02b126 commit 8f24199

File tree

6 files changed

+77
-26
lines changed

6 files changed

+77
-26
lines changed

include/hsm/details/dispatch_table.h

Lines changed: 17 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,15 @@ template <class T> auto get(std::reference_wrapper<T> ref) -> auto&
1919
return ref.get();
2020
}
2121

22-
// NOLINTNEXTLINE(cppcoreguidelines-special-member-functions)
22+
// NOLINTNEXTLINE(cppcoreguidelines-special-member-functions)
2323
template <class Event> struct IDispatchTableEntry {
2424
virtual ~IDispatchTableEntry() = default;
2525
virtual void executeAction(Event& event) = 0;
2626
virtual auto executeGuard(Event& event) -> bool = 0;
2727
};
2828

2929
template <
30-
class Transition,
30+
bool Internal,
3131
class Action,
3232
class Guard,
3333
class SourcePtr,
@@ -37,14 +37,12 @@ template <
3737
class DispatchTableEntry final : public IDispatchTableEntry<Event> {
3838
public:
3939
constexpr DispatchTableEntry(
40-
Transition transition,
4140
Action action,
4241
Guard guard,
4342
SourcePtr source,
4443
TargetPtr target,
4544
OptionalDependency optionalDependency)
46-
: m_transition(std::move(transition))
47-
, m_action(action)
45+
: m_action(action)
4846
, m_guard(guard)
4947
, m_source(std::move(source))
5048
, m_target(std::move(target))
@@ -56,18 +54,17 @@ class DispatchTableEntry final : public IDispatchTableEntry<Event> {
5654
{
5755
// clang-format off
5856
if constexpr(is_action<Action>()){
59-
[](auto& transition,
60-
auto& action,
57+
[](auto& action,
6158
auto& event,
6259
const auto& source,
6360
auto& target,
6461
const auto& optionalDependency) {
6562
bh::unpack(optionalDependency,
66-
[&transition, &action, &event, &source, &target](const auto&... optionalDependency){
67-
(void) transition;
63+
[&action, &event, &source, &target](const auto&... optionalDependency){
6864
using Target = typename TargetPtr::element_type::element_type;
65+
(void) target;
6966
if constexpr(is_default_constructable(bh::type_c<Target>)){
70-
if (transition.internal()){
67+
if constexpr (Internal){
7168
action(event, **source, **source, get(optionalDependency)...);
7269
}
7370
else
@@ -79,7 +76,7 @@ class DispatchTableEntry final : public IDispatchTableEntry<Event> {
7976
action(event, **source, target, get(optionalDependency)...);
8077
}
8178
});
82-
}(m_transition, m_action, event, m_source, m_target, m_optionalDependency);
79+
}(m_action, event, m_source, m_target, m_optionalDependency);
8380
}
8481
// clang-format on
8582
}
@@ -89,16 +86,16 @@ class DispatchTableEntry final : public IDispatchTableEntry<Event> {
8986
// clang-format off
9087
if constexpr(is_guard<Guard>()){
9188
return [](
92-
auto& transition,
9389
auto& guard,
9490
auto& event,
9591
const auto& source,
9692
const auto& target,
9793
const auto& optionalDependency) {
9894
return bh::unpack(
9995
optionalDependency,
100-
[&transition, &guard, &event, &source, &target](const auto&... optionalDependency) {
101-
if (transition.internal())
96+
[&guard, &event, &source, &target](const auto&... optionalDependency) {
97+
(void) target;
98+
if constexpr (Internal)
10299
{
103100
return guard(event, **source, **source, get(optionalDependency)...);
104101
}
@@ -108,7 +105,7 @@ class DispatchTableEntry final : public IDispatchTableEntry<Event> {
108105
}
109106

110107
});
111-
}(m_transition, m_guard, event, m_source, m_target, m_optionalDependency);
108+
}(m_guard, event, m_source, m_target, m_optionalDependency);
112109
}
113110
else {
114111
return true;
@@ -117,7 +114,6 @@ class DispatchTableEntry final : public IDispatchTableEntry<Event> {
117114
}
118115

119116
private:
120-
Transition m_transition;
121117
Action m_action;
122118
Guard m_guard;
123119
SourcePtr m_source;
@@ -145,20 +141,19 @@ constexpr auto make_transition(
145141
using Event = typename decltype(eventTypeid)::type;
146142

147143
return std::make_unique<DispatchTableEntry<
148-
Transition,
144+
transition.internal(),
149145
Action,
150146
Guard,
151147
Source,
152148
Target,
153149
Event,
154-
decltype(optionalDependency)>>(
155-
transition, action, guard, source, target, optionalDependency);
150+
decltype(optionalDependency)>>(action, guard, source, target, optionalDependency);
156151
}
157152

158153
template <class Event> struct NextState {
159-
StateIdx combinedState{};
160-
bool history{};
161-
bool defer{};
154+
StateIdx combinedState {};
155+
bool history {};
156+
bool defer {};
162157
bool valid = false;
163158
bool internal = false;
164159
std::unique_ptr<IDispatchTableEntry<Event>> transition;

include/hsm/details/fill_dispatch_table.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,10 @@ constexpr auto addDispatchTableEntryOfSubMachineExits(
119119
(void)eventTypeid;
120120
(void)optionalDependency;
121121

122+
if constexpr (transition.internal()) {
123+
static_assert(transition.internal());
124+
}
125+
122126
constexpr auto parentState = transition.source();
123127
if constexpr (has_transition_table(parentState)) {
124128
bh::for_each(collect_child_state_typeids(parentState), [=, &dispatchTables](auto state) {

include/hsm/details/transition.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,15 @@
22

33
#include <type_traits>
44

5+
#include <boost/hana/bool.hpp>
6+
57
namespace hsm {
68
namespace details {
9+
10+
namespace bh {
11+
using namespace boost::hana;
12+
}
13+
714
template <class Source, class Event, class Guard, class Action, class Target> struct Transition {
815
constexpr Transition(Source, Event, Guard guard, Action action, Target)
916
: m_guard(guard)

test/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ add_executable(
4545
unit/has_action_tests.cpp
4646
unit/make_dispatch_tables_tests.cpp
4747
unit/unexpected_event_handler_tables_tests.cpp
48+
unit/transition_tests.cpp
4849
)
4950
target_compile_features(hsmUnitTests PRIVATE cxx_std_17)
5051
target_link_libraries(hsmUnitTests PRIVATE hsm::hsm GTest::gtest_main)

test/integration/guards_actions.cpp

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,10 @@ namespace hsm::test {
1515

1616
// States
1717
struct S1 {
18+
bool isS1 = true;
1819
};
1920
struct S2 {
21+
bool isS2 = true;
2022
};
2123
struct S3 {
2224
};
@@ -47,6 +49,8 @@ struct e9 {
4749
};
4850
struct e10 {
4951
};
52+
struct e11 {
53+
};
5054

5155
template <class Event, class Source, class Target>
5256
auto print_transition(Event event, Source source, Target target, std::string context)
@@ -64,6 +68,11 @@ const auto success = [](auto /*event*/, auto /*source*/, auto /*target*/) { retu
6468
// Actions
6569
const auto a2 = [](auto event, auto /*source*/, auto /*target*/) { event.called->set_value(); };
6670

71+
const auto accessSourceAndTarget = [](auto /*event*/, auto source, auto target) {
72+
EXPECT_TRUE(source.isS1);
73+
EXPECT_TRUE(target.isS2);
74+
};
75+
6776
struct Functor {
6877
template <class Event, class Source, class Target>
6978
auto operator()(Event event, Source source, Target target) const
@@ -118,15 +127,16 @@ struct MainState {
118127
, hsm::state<S1> + hsm::event<e2> = hsm::state<SubState>
119128
, hsm::state<S1> + hsm::event<e3> [fail] = hsm::state<S2>
120129
, hsm::state<S1> + hsm::event<e4> [success] = hsm::state<S2>
130+
, hsm::state<S1> + hsm::event<e9> [fail] = hsm::state<S2>
131+
, hsm::state<S1> + hsm::event<e9> [success] = hsm::state<S2>
132+
, hsm::state<S1> + hsm::event<e10> = hsm::state<S3>
133+
, hsm::state<S1> + hsm::event<e11> / accessSourceAndTarget = hsm::state<S2>
134+
, hsm::state<S3> [fail] = hsm::state<S1>
121135
// The following transitions show different possibilities to provide actions
122136
, hsm::state<S1> + hsm::event<e5> / Functor{} = hsm::state<S2>
123137
, hsm::state<S1> + hsm::event<e6> / free_function_adapter = hsm::state<S2>
124138
, hsm::state<S1> + hsm::event<e7> / lambda = hsm::state<S2>
125139
, hsm::state<S1> + hsm::event<e8> / member_function_adapter = hsm::state<S2>
126-
, hsm::state<S1> + hsm::event<e9> [fail] = hsm::state<S2>
127-
, hsm::state<S1> + hsm::event<e9> [success] = hsm::state<S2>
128-
, hsm::state<S1> + hsm::event<e10> = hsm::state<S3>
129-
, hsm::state<S3> [fail] = hsm::state<S1>
130140
);
131141
// clang-format on
132142
}
@@ -210,4 +220,10 @@ TEST_F(GuardsActionsTests, should_guard_anon_transition)
210220
sm.process_event(e10 {});
211221
ASSERT_TRUE(sm.is(hsm::state<S3>));
212222
}
223+
224+
TEST_F(GuardsActionsTests, should_access_source_and_target_member)
225+
{
226+
sm.process_event(e11 {});
227+
ASSERT_TRUE(sm.is(hsm::state<S2>));
228+
}
213229
}

test/unit/transition_tests.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#include "hsm/details/transition.h"
2+
3+
#include <gtest/gtest.h>
4+
5+
using namespace ::testing;
6+
7+
namespace hsm::tests {
8+
9+
class TransitionTests : public Test {
10+
};
11+
12+
class S1 {
13+
};
14+
class S2 {
15+
};
16+
17+
TEST_F(TransitionTests, should_recognize_internal_transition)
18+
{
19+
auto transition = hsm::details::transition("source", "event", "guard", "avtion", "target");
20+
auto internalExtendedTransition
21+
= hsm::details::internal_extended_transition("parent", transition);
22+
auto externalExtendedTransition = hsm::details::extended_transition("parent", transition);
23+
24+
static_assert(!externalExtendedTransition.internal(), "");
25+
static_assert(internalExtendedTransition.internal(), "");
26+
}
27+
28+
}

0 commit comments

Comments
 (0)