Skip to content

Commit

Permalink
fix: Make internal transition recognition compile time (#159)
Browse files Browse the repository at this point in the history
  • Loading branch information
erikzenker committed Apr 18, 2021
1 parent e02b126 commit 8f24199
Show file tree
Hide file tree
Showing 6 changed files with 77 additions and 26 deletions.
39 changes: 17 additions & 22 deletions include/hsm/details/dispatch_table.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,15 @@ template <class T> auto get(std::reference_wrapper<T> ref) -> auto&
return ref.get();
}

// NOLINTNEXTLINE(cppcoreguidelines-special-member-functions)
// NOLINTNEXTLINE(cppcoreguidelines-special-member-functions)
template <class Event> struct IDispatchTableEntry {
virtual ~IDispatchTableEntry() = default;
virtual void executeAction(Event& event) = 0;
virtual auto executeGuard(Event& event) -> bool = 0;
};

template <
class Transition,
bool Internal,
class Action,
class Guard,
class SourcePtr,
Expand All @@ -37,14 +37,12 @@ template <
class DispatchTableEntry final : public IDispatchTableEntry<Event> {
public:
constexpr DispatchTableEntry(
Transition transition,
Action action,
Guard guard,
SourcePtr source,
TargetPtr target,
OptionalDependency optionalDependency)
: m_transition(std::move(transition))
, m_action(action)
: m_action(action)
, m_guard(guard)
, m_source(std::move(source))
, m_target(std::move(target))
Expand All @@ -56,18 +54,17 @@ class DispatchTableEntry final : public IDispatchTableEntry<Event> {
{
// clang-format off
if constexpr(is_action<Action>()){
[](auto& transition,
auto& action,
[](auto& action,
auto& event,
const auto& source,
auto& target,
const auto& optionalDependency) {
bh::unpack(optionalDependency,
[&transition, &action, &event, &source, &target](const auto&... optionalDependency){
(void) transition;
[&action, &event, &source, &target](const auto&... optionalDependency){
using Target = typename TargetPtr::element_type::element_type;
(void) target;
if constexpr(is_default_constructable(bh::type_c<Target>)){
if (transition.internal()){
if constexpr (Internal){
action(event, **source, **source, get(optionalDependency)...);
}
else
Expand All @@ -79,7 +76,7 @@ class DispatchTableEntry final : public IDispatchTableEntry<Event> {
action(event, **source, target, get(optionalDependency)...);
}
});
}(m_transition, m_action, event, m_source, m_target, m_optionalDependency);
}(m_action, event, m_source, m_target, m_optionalDependency);
}
// clang-format on
}
Expand All @@ -89,16 +86,16 @@ class DispatchTableEntry final : public IDispatchTableEntry<Event> {
// clang-format off
if constexpr(is_guard<Guard>()){
return [](
auto& transition,
auto& guard,
auto& event,
const auto& source,
const auto& target,
const auto& optionalDependency) {
return bh::unpack(
optionalDependency,
[&transition, &guard, &event, &source, &target](const auto&... optionalDependency) {
if (transition.internal())
[&guard, &event, &source, &target](const auto&... optionalDependency) {
(void) target;
if constexpr (Internal)
{
return guard(event, **source, **source, get(optionalDependency)...);
}
Expand All @@ -108,7 +105,7 @@ class DispatchTableEntry final : public IDispatchTableEntry<Event> {
}

});
}(m_transition, m_guard, event, m_source, m_target, m_optionalDependency);
}(m_guard, event, m_source, m_target, m_optionalDependency);
}
else {
return true;
Expand All @@ -117,7 +114,6 @@ class DispatchTableEntry final : public IDispatchTableEntry<Event> {
}

private:
Transition m_transition;
Action m_action;
Guard m_guard;
SourcePtr m_source;
Expand Down Expand Up @@ -145,20 +141,19 @@ constexpr auto make_transition(
using Event = typename decltype(eventTypeid)::type;

return std::make_unique<DispatchTableEntry<
Transition,
transition.internal(),
Action,
Guard,
Source,
Target,
Event,
decltype(optionalDependency)>>(
transition, action, guard, source, target, optionalDependency);
decltype(optionalDependency)>>(action, guard, source, target, optionalDependency);
}

template <class Event> struct NextState {
StateIdx combinedState{};
bool history{};
bool defer{};
StateIdx combinedState {};
bool history {};
bool defer {};
bool valid = false;
bool internal = false;
std::unique_ptr<IDispatchTableEntry<Event>> transition;
Expand Down
4 changes: 4 additions & 0 deletions include/hsm/details/fill_dispatch_table.h
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,10 @@ constexpr auto addDispatchTableEntryOfSubMachineExits(
(void)eventTypeid;
(void)optionalDependency;

if constexpr (transition.internal()) {
static_assert(transition.internal());
}

constexpr auto parentState = transition.source();
if constexpr (has_transition_table(parentState)) {
bh::for_each(collect_child_state_typeids(parentState), [=, &dispatchTables](auto state) {
Expand Down
7 changes: 7 additions & 0 deletions include/hsm/details/transition.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,15 @@

#include <type_traits>

#include <boost/hana/bool.hpp>

namespace hsm {
namespace details {

namespace bh {
using namespace boost::hana;
}

template <class Source, class Event, class Guard, class Action, class Target> struct Transition {
constexpr Transition(Source, Event, Guard guard, Action action, Target)
: m_guard(guard)
Expand Down
1 change: 1 addition & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ add_executable(
unit/has_action_tests.cpp
unit/make_dispatch_tables_tests.cpp
unit/unexpected_event_handler_tables_tests.cpp
unit/transition_tests.cpp
)
target_compile_features(hsmUnitTests PRIVATE cxx_std_17)
target_link_libraries(hsmUnitTests PRIVATE hsm::hsm GTest::gtest_main)
Expand Down
24 changes: 20 additions & 4 deletions test/integration/guards_actions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@ namespace hsm::test {

// States
struct S1 {
bool isS1 = true;
};
struct S2 {
bool isS2 = true;
};
struct S3 {
};
Expand Down Expand Up @@ -47,6 +49,8 @@ struct e9 {
};
struct e10 {
};
struct e11 {
};

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

const auto accessSourceAndTarget = [](auto /*event*/, auto source, auto target) {
EXPECT_TRUE(source.isS1);
EXPECT_TRUE(target.isS2);
};

struct Functor {
template <class Event, class Source, class Target>
auto operator()(Event event, Source source, Target target) const
Expand Down Expand Up @@ -118,15 +127,16 @@ struct MainState {
, hsm::state<S1> + hsm::event<e2> = hsm::state<SubState>
, hsm::state<S1> + hsm::event<e3> [fail] = hsm::state<S2>
, hsm::state<S1> + hsm::event<e4> [success] = hsm::state<S2>
, hsm::state<S1> + hsm::event<e9> [fail] = hsm::state<S2>
, hsm::state<S1> + hsm::event<e9> [success] = hsm::state<S2>
, hsm::state<S1> + hsm::event<e10> = hsm::state<S3>
, hsm::state<S1> + hsm::event<e11> / accessSourceAndTarget = hsm::state<S2>
, hsm::state<S3> [fail] = hsm::state<S1>
// The following transitions show different possibilities to provide actions
, hsm::state<S1> + hsm::event<e5> / Functor{} = hsm::state<S2>
, hsm::state<S1> + hsm::event<e6> / free_function_adapter = hsm::state<S2>
, hsm::state<S1> + hsm::event<e7> / lambda = hsm::state<S2>
, hsm::state<S1> + hsm::event<e8> / member_function_adapter = hsm::state<S2>
, hsm::state<S1> + hsm::event<e9> [fail] = hsm::state<S2>
, hsm::state<S1> + hsm::event<e9> [success] = hsm::state<S2>
, hsm::state<S1> + hsm::event<e10> = hsm::state<S3>
, hsm::state<S3> [fail] = hsm::state<S1>
);
// clang-format on
}
Expand Down Expand Up @@ -210,4 +220,10 @@ TEST_F(GuardsActionsTests, should_guard_anon_transition)
sm.process_event(e10 {});
ASSERT_TRUE(sm.is(hsm::state<S3>));
}

TEST_F(GuardsActionsTests, should_access_source_and_target_member)
{
sm.process_event(e11 {});
ASSERT_TRUE(sm.is(hsm::state<S2>));
}
}
28 changes: 28 additions & 0 deletions test/unit/transition_tests.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#include "hsm/details/transition.h"

#include <gtest/gtest.h>

using namespace ::testing;

namespace hsm::tests {

class TransitionTests : public Test {
};

class S1 {
};
class S2 {
};

TEST_F(TransitionTests, should_recognize_internal_transition)
{
auto transition = hsm::details::transition("source", "event", "guard", "avtion", "target");
auto internalExtendedTransition
= hsm::details::internal_extended_transition("parent", transition);
auto externalExtendedTransition = hsm::details::extended_transition("parent", transition);

static_assert(!externalExtendedTransition.internal(), "");
static_assert(internalExtendedTransition.internal(), "");
}

}

0 comments on commit 8f24199

Please sign in to comment.