Skip to content

Commit

Permalink
context level callbacks for action and guard, but updated to c++20
Browse files Browse the repository at this point in the history
  • Loading branch information
Sriram Sundararajan committed Jan 18, 2025
1 parent 54d1708 commit 04eb981
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 15 deletions.
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ include (CMakePackageConfigHelpers)
include (GNUInstallDirs)

#Compiler MUST be at least CXX 11 compliant
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

if (NOT CMAKE_ARCHIVE_OUTPUT_DIRECTORY)
Expand Down
33 changes: 20 additions & 13 deletions include/tsm.h
Original file line number Diff line number Diff line change
Expand Up @@ -122,13 +122,15 @@ struct BaseTransition {
template<typename From,
typename Event,
typename To,
typename Action = void (*)(),
typename Guard = bool (*)()>
auto Action = []() {},
auto Guard = []() { return true; }>
struct Transition : BaseTransition<From, Event, To> {
using action = Action;
using guard = Guard;
Action action_{}; // for lambdas
Guard guard_{};
Transition() = default;
~Transition() = default;
using action_t = decltype(Action);
using guard_t = decltype(Guard);
static constexpr action_t action = Action;
static constexpr guard_t guard = Guard;
};

struct ClockTickEvent {
Expand All @@ -137,8 +139,8 @@ struct ClockTickEvent {

template<typename From,
typename To,
typename Guard = bool (*)(void),
typename Action = void (*)(void)>
auto Action = []() {},
auto Guard = []() { return true; }>
struct ClockedTransition
: Transition<From, ClockTickEvent, To, Action, Guard> {};

Expand Down Expand Up @@ -194,8 +196,8 @@ struct is_transition_match : std::false_type {};
template<typename From,
typename Event,
typename To,
typename Action,
typename Guard>
auto Action,
auto Guard>
struct is_transition_match<Transition<From, Event, To, Action, Guard>,
From,
Event> : std::true_type {};
Expand Down Expand Up @@ -479,6 +481,9 @@ struct Hsm : T {
State*>) {
return state->guard();
}
} else if constexpr (std::is_member_function_pointer_v<decltype(Tn::guard)>) {
auto& ctx = static_cast<T&>(*this);
return std::invoke(Tn::guard, ctx);
}
return true;
}
Expand All @@ -503,6 +508,10 @@ struct Hsm : T {
state->action();
}
}
else if constexpr (std::is_member_function_pointer_v<decltype(Tn::action)>) {
auto& ctx = static_cast<T&>(*this);
std::invoke(Tn::action, ctx);
}
}

template<typename transition,
Expand Down Expand Up @@ -576,15 +585,13 @@ struct wrap_transition {
using from = typename T::from;
using event = typename T::event;
using to = typename T::to;
using action = typename T::action;
using guard = typename T::guard;

using wrap_from =
std::conditional_t<is_state_trait_v<from>, make_hsm_t<from>, from>;
using wrap_to =
std::conditional_t<is_state_trait_v<to>, make_hsm_t<to>, to>;

using type = Transition<wrap_from, event, wrap_to, action, guard>;
using type = Transition<wrap_from, event, wrap_to, T::action, T::guard>;
};

template<typename T>
Expand Down
49 changes: 48 additions & 1 deletion test/test_hsm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,54 @@ TEST_CASE("SwitchHsm") {
REQUIRE(std::holds_alternative<SwitchHsmContext::On*>(hsm.current_state_));
}

struct SwitchHsmContextWithActions {
struct Off {};
struct On {};
struct Toggle {};

// optional guard or action
bool canTurnOn() {
// Decide if we can turn on...
return true;
}
void onTurnOn() { on_count_++; }

// Static wrappers that forward to the instance
static bool canTurnOnGuard(SwitchHsmContextWithActions& ctx) {
return ctx.canTurnOn();
}
static void onTurnOnAction(SwitchHsmContextWithActions& ctx) {
ctx.onTurnOn();
}

int on_count() const { return on_count_; }

using transitions =
std::tuple<Transition<Off,
Toggle,
On,
&SwitchHsmContextWithActions::onTurnOn,
&SwitchHsmContextWithActions::canTurnOn>,
Transition<On, Toggle, Off>>;

private:
int on_count_{};
};

using SwitchHsmWithActions = make_hsm_t<SwitchHsmContextWithActions>;

TEST_CASE("SwitchHsmWithActions") {
using SwitchHsm = make_hsm_t<SwitchHsmContextWithActions>;
SwitchHsm hsm;
REQUIRE(std::holds_alternative<SwitchHsmContextWithActions::Off*>(
hsm.current_state_));
hsm.handle(SwitchHsmContextWithActions::Toggle());
REQUIRE(hsm.on_count() == 1);
REQUIRE(std::holds_alternative<SwitchHsmContextWithActions::On*>(
hsm.current_state_));
hsm.handle(SwitchHsmContextWithActions::Toggle());
}

// Traffic Light HSM

namespace TrafficLight {
Expand Down Expand Up @@ -438,7 +486,6 @@ struct LightContext {
bool guard(LightContext&, ClockTickEvent& t) { return t.ticks_ >= 5; }
void action(LightContext& l, ClockTickEvent&) {
l.walk_pressed_ = false;
std::cout << "Y2\n";
}
};
using transitions = std::tuple<ClockedTransition<G1, Y1>,
Expand Down

0 comments on commit 04eb981

Please sign in to comment.