Skip to content

Commit

Permalink
🆕 [core] Support for member-function like guards/actions
Browse files Browse the repository at this point in the history
Problem:
- Only lambda-expression like guards/actions are supported.

Solution:
- Add support for member-function like guards/actions using
 `&type::member_function` syntax.
  • Loading branch information
krzysztof-jusiak committed Jul 8, 2020
1 parent 434837b commit e6d0685
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 50 deletions.
82 changes: 32 additions & 50 deletions example/actions_guards.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,76 +14,57 @@
namespace sml = boost::sml;

namespace {
template <class R, class... Ts>
auto call_impl(R (*f)(Ts...)) {
return [f](Ts... args) { return f(args...); };
}
template <class T, class R, class... Ts>
auto call_impl(T* self, R (T::*f)(Ts...)) {
return [self, f](Ts... args) { return (self->*f)(args...); };
}
template <class T, class R, class... Ts>
auto call_impl(const T* self, R (T::*f)(Ts...) const) {
return [self, f](Ts... args) { return (self->*f)(args...); };
}
template <class T, class R, class... Ts>
auto call_impl(const T* self, R (T::*f)(Ts...)) {
return [self, f](Ts... args) { return (self->*f)(args...); };
}
/**
* Simple wrapper to call free/member functions
* @param args function, [optional] this
* @return function(args...)
*/
auto call = [](auto... args) { return call_impl(args...); };
//->

struct e1 {};
struct e2 {};
struct e3 {};
struct e4 {};
struct e5 {};

auto guard1 = [] {
std::cout << "guard1" << std::endl;
return true;
};
struct actions_guards {
using self = actions_guards;

auto guard2 = [](int i) {
assert(42 == i);
std::cout << "guard2" << std::endl;
return false;
};
auto operator()() {
using namespace sml;

bool guard3(int i) {
assert(42 == i);
std::cout << "guard3" << std::endl;
return true;
}
auto guard1 = [] {
std::cout << "guard1" << std::endl;
return true;
};

auto action1 = [](auto e) { std::cout << "action1: " << typeid(e).name() << std::endl; };
struct action2 {
void operator()(int i) {
assert(42 == i);
std::cout << "action2" << std::endl;
}
};
auto guard2 = [](int i) {
assert(42 == i);
std::cout << "guard2" << std::endl;
return false;
};

auto action1 = [](auto e) { std::cout << "action1: " << typeid(e).name() << std::endl; };

struct action2 {
void operator()(int i) {
assert(42 == i);
std::cout << "action2" << std::endl;
}
};

struct actions_guards {
auto operator()() noexcept {
using namespace sml;
// clang-format off
return make_transition_table(
*"idle"_s + event<e1> = "s1"_s
, "s1"_s + event<e2> [ guard1 ] / action1 = "s2"_s
, "s2"_s + event<e3> [ guard1 && ![] { return false;} ] / (action1, action2{}) = "s3"_s
, "s3"_s + event<e4> [ !guard1 || guard2 ] / (action1, [] { std::cout << "action3" << std::endl; }) = "s4"_s
, "s3"_s + event<e4> [ guard1 ] / ([] { std::cout << "action4" << std::endl; }, [this] { action4(); }) = "s5"_s
, "s5"_s + event<e5> [ call(guard3) || guard2 ] / call(this, &actions_guards::action5) = X
, "s3"_s + event<e4> [ guard1 ] / ([] { std::cout << "action4" << std::endl; }, [this] { action4(); } ) = "s5"_s
, "s5"_s + event<e5> [ &self::guard3 ] / &self::action5 = X
);
// clang-format on
}

bool guard3(int i) const {
assert(42 == i);
std::cout << "guard3" << std::endl;
return true;
}

void action4() const { std::cout << "action4" << std::endl; }

void action5(int i, const e5&) {
Expand All @@ -94,7 +75,8 @@ struct actions_guards {
} // namespace

int main() {
sml::sm<actions_guards> sm{42};
actions_guards ag{};
sml::sm<actions_guards> sm{ag, 42};
sm.process_event(e1{});
sm.process_event(e2{});
sm.process_event(e3{});
Expand Down
20 changes: 20 additions & 0 deletions include/boost/sml.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,22 @@ struct zero_wrapper : TExpr {
explicit zero_wrapper(const TExpr &expr) : TExpr(expr) {}
const TExpr &get() const { return *this; }
};
template <class R, class TBase, class... TArgs>
struct zero_wrapper<R (TBase::*)(TArgs...)> {
explicit zero_wrapper(R (TBase::*ptr)(TArgs...)) : ptr{ptr} {}
auto operator()(TBase &self, TArgs... args) { return (self.*ptr)(args...); }

private:
R (TBase::*ptr)(TArgs...){};
};
template <class R, class TBase, class... TArgs>
struct zero_wrapper<R (TBase::*)(TArgs...) const> {
explicit zero_wrapper(R (TBase::*ptr)(TArgs...) const) : ptr{ptr} {}
auto operator()(TBase &self, TArgs... args) { return (self.*ptr)(args...); }

private:
R (TBase::*ptr)(TArgs...) const {};
};
template <class, class>
struct zero_wrapper_impl;
template <class TExpr, class... TArgs>
Expand Down Expand Up @@ -1247,6 +1263,10 @@ aux::true_type test_callable(...);
template <class, class T>
struct callable
: decltype(test_callable<aux::inherit<aux::conditional_t<__is_class(T), T, aux::none_type>, callable_fallback>>(0)) {};
template <class T, class R, class TBase, class... TArgs>
struct callable<T, R (TBase::*)(TArgs...)> : aux::true_type {};
template <class T, class R, class TBase, class... TArgs>
struct callable<T, R (TBase::*)(TArgs...) const> : aux::true_type {};
}
namespace concepts {
template <class T>
Expand Down
18 changes: 18 additions & 0 deletions include/boost/sml/aux_/utility.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,24 @@ struct zero_wrapper : TExpr {
const TExpr &get() const { return *this; }
};

template <class R, class TBase, class... TArgs>
struct zero_wrapper<R (TBase::*)(TArgs...)> {
explicit zero_wrapper(R (TBase::*ptr)(TArgs...)) : ptr{ptr} {}
auto operator()(TBase &self, TArgs... args) { return (self.*ptr)(args...); }

private:
R (TBase::*ptr)(TArgs...){};
};

template <class R, class TBase, class... TArgs>
struct zero_wrapper<R (TBase::*)(TArgs...) const> {
explicit zero_wrapper(R (TBase::*ptr)(TArgs...) const) : ptr{ptr} {}
auto operator()(TBase &self, TArgs... args) { return (self.*ptr)(args...); }

private:
R (TBase::*ptr)(TArgs...) const {};
};

template <class, class>
struct zero_wrapper_impl;

Expand Down
6 changes: 6 additions & 0 deletions include/boost/sml/concepts/callable.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ template <class, class T>
struct callable
: decltype(test_callable<aux::inherit<aux::conditional_t<__is_class(T), T, aux::none_type>, callable_fallback>>(0)) {};

template <class T, class R, class TBase, class... TArgs>
struct callable<T, R (TBase::*)(TArgs...)> : aux::true_type {};

template <class T, class R, class TBase, class... TArgs>
struct callable<T, R (TBase::*)(TArgs...) const> : aux::true_type {};

} // namespace concepts

#endif
24 changes: 24 additions & 0 deletions test/ft/transition_table.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,30 @@ test uml_notation = [] {
expect(sm.is(sml::X));
};

test member_functions = [] {
struct c {
auto operator()() noexcept {
using namespace sml;
// clang-format off
return make_transition_table(
*idle + event<e1> [ &c::guard ] / &c::action = X
);
// clang-format on
}

auto guard() const -> bool { return true; }
void action() { action_ = true; }
bool action_{};
};

c c_{};
sml::sm<c> sm{c_};
expect(!c_.action_);
sm.process_event(e1{});
expect(sm.is(sml::X));
expect(c_.action_);
};

struct c_guard {
template <class T>
bool operator()(const T &) const noexcept {
Expand Down

0 comments on commit e6d0685

Please sign in to comment.