From a4ff410a5a874c3eea39df27711b5e065f124b39 Mon Sep 17 00:00:00 2001 From: Thomas Kemmer Date: Wed, 20 Feb 2019 19:21:25 +0100 Subject: [PATCH] Refactor row implementations. --- src/fsm.h | 331 +++++++++++++++------------------------ tests/Makefile.am | 4 +- tests/test_basic_row.cpp | 87 ++++++++++ tests/test_notrans.cpp | 2 +- tests/test_scoped.cpp | 2 +- tests/test_typed_row.cpp | 100 ------------ 6 files changed, 218 insertions(+), 308 deletions(-) create mode 100644 tests/test_basic_row.cpp delete mode 100644 tests/test_typed_row.cpp diff --git a/src/fsm.h b/src/fsm.h index bdab5b3..dc98cc7 100644 --- a/src/fsm.h +++ b/src/fsm.h @@ -25,6 +25,7 @@ #ifndef FSMLITE_FSM_H #define FSMLITE_FSM_H +#include #include #if !defined(NDEBUG) && (!__GNUC__ || __EXCEPTIONS) @@ -34,36 +35,12 @@ namespace fsmlite { namespace detail { #if __cplusplus >= 201703L - template - using bool_constant = std::bool_constant; - - template - using add_pointer_t = std::add_pointer_t; - - template - using remove_pointer_t = std::remove_pointer_t; - - template - using is_null_pointer = std::is_null_pointer; - template using invoke_result_t = std::invoke_result_t; template using is_invocable = std::is_invocable; #elif __cplusplus >= 201103L - template - using bool_constant = std::integral_constant; - - template - using add_pointer_t = typename std::add_pointer::type; - - template - using remove_pointer_t = typename std::remove_pointer::type; - - template - using is_null_pointer = std::is_same::type>; - template using invoke_result_t = typename std::result_of::type; @@ -78,28 +55,93 @@ namespace fsmlite { }; template - using is_invocable = typename std::conditional< - std::is_function::value, - bool_constant(0)) == 1>, - bool_constant(0)) == 1> + using is_invocable = typename std::integral_constant< + bool, + sizeof(is_invocable_test::test(0)) == 1 >::type; #else #error "fsmlite requires C++11 support." #endif + // C++11 std::forward() is in , which may not be + // present on freestanding implementations + template + constexpr T&& forward(typename std::remove_reference::type& t) noexcept { + return static_cast(t); + } - // similar to std::integral_constant, but without const template - struct typed_value { - using type = T; - static T value; + constexpr T&& forward(typename std::remove_reference::type&& t) noexcept { + return static_cast(t); + } + + // C++17 std::invoke() is in , which may not be + // present on freestanding implementations + template + invoke_result_t invoke(F&& f, Args&&... args) { + return f(args...); + } + + template + invoke_result_t invoke(M T::* f, T1&& obj, Args&&... args) { + return (obj.*f)(args...); + } + + // use any of F(), F(Arg1), F(Arg2), F(Arg1, Arg2) + template< + class F, class Arg1, class Arg2, + bool f1 = is_invocable::value, + bool f2 = is_invocable::value, + bool f3 = is_invocable::value, + bool f4 = is_invocable::value + > struct binary_fn_helper; + + template + struct binary_fn_helper { + using result_type = invoke_result_t; + + static result_type invoke(F&& f, Arg1&&, Arg2&&) { + return detail::invoke(f); + } }; - template - T typed_value::value; + template + struct binary_fn_helper { + using result_type = invoke_result_t; + + static result_type invoke(F&& f, Arg1&& a, Arg2&&) { + return detail::invoke(f, a); + } + }; + + template + struct binary_fn_helper { + using result_type = invoke_result_t; + + static result_type invoke(F&& f, Arg1&&, Arg2&& b) { + return detail::invoke(f, b); + } + }; + + template + struct binary_fn_helper { + using result_type = invoke_result_t; + + static result_type invoke(F&& f, Arg1&& a, Arg2&& b) { + return detail::invoke(f, a, b); + } + }; + + template + using invoke_as_binary_fn_result_t = typename binary_fn_helper::result_type; + + template + invoke_as_binary_fn_result_tinvoke_as_binary_fn(F&& f, Arg1&& a, Arg2&& b) { + return binary_fn_helper::invoke(forward(f), forward(a), forward(b)); + } // basic template metaprogramming stuff; note that we could - // use std::tuple instead of list, but std::tuple is not - // required to be available on freestanding implementations + // use std::tuple instead of list, but may nor be + // present on freestanding implementations template struct list {}; template struct concat; @@ -130,67 +172,6 @@ namespace fsmlite { struct filter { using type = list<>; }; - - // use any of fn(), fn(arg1), fn(arg2), fn(arg1, arg2) - template< - class T, T* fn, class Arg1, class Arg2, - bool f1 = is_invocable::value, - bool f2 = is_invocable::value, - bool f3 = is_invocable::value, - bool f4 = is_invocable::value - > struct make_binary_function; - - template - struct make_binary_function { - static struct type { - invoke_result_t operator()(Arg1, Arg2) const { - return (*fn)(); - } - } value; - }; - - template - typename make_binary_function::type - make_binary_function::value; - - template - struct make_binary_function { - static struct type { - invoke_result_t operator()(Arg1 arg1, Arg2) const { - return (*fn)(arg1); - } - } value; - }; - - template - typename make_binary_function::type - make_binary_function::value; - - template - struct make_binary_function { - static struct type { - invoke_result_t operator()(Arg1, Arg2 arg2) const { - return (*fn)(arg2); - } - } value; - }; - - template - typename make_binary_function::type - make_binary_function::value; - - template - struct make_binary_function { - static struct type { - invoke_result_t operator()(Arg1 arg1, Arg2 arg2) const { - return (*fn)(arg1, arg2); - } - } value; - }; - - template - typename make_binary_function::type - make_binary_function::value; } /** @@ -263,100 +244,31 @@ namespace fsmlite { } private: - template - struct no_action_type { - void operator()(Derived&, const Event&) const {} - }; - - template - struct no_guard_type { - bool operator()(const Derived&, const Event&) const { - return true; - } - }; - - template - struct mem_fn_action_type { - void operator()(Derived& self, const Event& event) const { - (self.*action)(event); - } - }; - - template - struct mem_fn_guard_type { - bool operator()(const Derived& self, const Event& event) const { - return (self.*guard)(event); - } - }; - - template - struct no_action: detail::typed_value> {}; - - template - struct no_guard: detail::typed_value> {}; - - template - struct mem_fn_action: detail::typed_value> {}; - - template - struct mem_fn_guard: detail::typed_value> {}; - - template - using make_action = typename std::conditional< - detail::is_null_pointer::value, - no_action, - detail::make_binary_function, fn, Derived&, const Event&> - >::type; - - template - using make_guard = typename std::conditional< - detail::is_null_pointer::value, - no_guard, - detail::make_binary_function, fn, const Derived&, const Event&> - >::type; - - template - struct make_mem_fn_action_type { - void operator()(Derived& self, const Event& event) { - if (action != nullptr) (self.*action)(event); - } - }; - - template - struct make_mem_fn_action: detail::typed_value> {}; - - template - struct make_mem_fn_guard_type { - bool operator()(const Derived& self, const Event& event) const { - return guard != nullptr ? (self.*guard)(event) : true; - } - }; - - template - struct make_mem_fn_guard: detail::typed_value> {}; - - template< - State start, - class Event, - State target, - class Action, - Action* action, - class Guard, - Guard* guard - > - struct basic_row { + template + struct row_base { using state_type = State; using event_type = Event; static constexpr state_type start_value() { return start; } static constexpr state_type target_value() { return target; } - static void process_event(Derived& self, const Event& event) { - (*action)(self, event); + protected: + template + static void process_event(Action&& action, Derived& self, const Event& event) { + detail::invoke_as_binary_fn(action, self, event); + } + + // clang++-50: constexpr function's return type 'void' is not a literal type + static /*constexpr*/ void process_event(std::nullptr_t, Derived& self, const Event& event) { + } + + template + static bool check_guard(Guard&& guard, const Derived& self, const Event& event) { + return detail::invoke_as_binary_fn(guard, self, event); } - static bool check_guard(const Derived& self, const Event& e) { - return (*guard)(self, e); + static constexpr bool check_guard(std::nullptr_t, const Derived&, const Event&) { + return true; } }; @@ -371,7 +283,7 @@ namespace fsmlite { template using table = detail::list; /** - * Generic transition class template. + * Basic transition class template. * * @tparam start the start state of the transition * @@ -379,30 +291,33 @@ namespace fsmlite { * * @tparam target the target state of the transition * - * @tparam Action an action functor pointer type, or `std::nullptr_t` + * @tparam Action an action function type, or `std::nullptr_t` * * @tparam action a static `Action` instance * - * @tparam Guard a guard functor pointer type, or `std::nullptr_t` + * @tparam Guard a guard function type, or `std::nullptr_t` * * @tparam guard a static `Guard` instance */ + template< State start, class Event, State target, - class Action = decltype(nullptr), + class Action = std::nullptr_t, Action action = nullptr, - class Guard = decltype(nullptr), + class Guard = std::nullptr_t, Guard guard = nullptr > - struct typed_row: basic_row< - start, Event, target, - typename make_action::type, - &make_action::value, - typename make_guard::type, - &make_guard::value - > {}; + struct basic_row : public row_base { + static void process_event(Derived& self, const Event& event) { + row_base::process_event(action, self, event); + } + + static bool check_guard(const Derived& self, const Event& event) { + return row_base::check_guard(guard, self, event); + } + }; /** * Member function transition class template. @@ -413,7 +328,7 @@ namespace fsmlite { * * @tparam target the target state of the transition * - * @tparam action an action member function + * @tparam action an action member function, or `nullptr` * * @tparam guard a guard member function, or `nullptr` */ @@ -421,16 +336,24 @@ namespace fsmlite { State start, class Event, State target, - void (Derived::*action)(const Event&), + void (Derived::*action)(const Event&) = nullptr, bool (Derived::*guard)(const Event&) const = nullptr > - struct mem_fn_row: basic_row< - start, Event, target, - typename make_mem_fn_action::type, - &make_mem_fn_action::value, - typename make_mem_fn_guard::type, - &make_mem_fn_guard::value - > {}; + struct mem_fn_row : public row_base { + static void process_event(Derived& self, const Event& event) { + if (action) { + row_base::process_event(action, self, event); + } + } + + static bool check_guard(const Derived& self, const Event& event) { + if (guard) { + return row_base::check_guard(guard, self, event); + } else { + return true; + } + } + }; private: template struct by_event_type; diff --git a/tests/Makefile.am b/tests/Makefile.am index 519ce71..9c75a68 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -3,12 +3,12 @@ AM_CPPFLAGS = -I$(top_srcdir)/src AM_DEFAULT_SOURCE_EXT = .cpp check_PROGRAMS = \ + test_basic_row \ test_player \ test_mem_fn_row \ test_notrans \ test_recursive \ test_scoped \ - test_traits \ - test_typed_row + test_traits TESTS = $(check_PROGRAMS) diff --git a/tests/test_basic_row.cpp b/tests/test_basic_row.cpp new file mode 100644 index 0000000..92db947 --- /dev/null +++ b/tests/test_basic_row.cpp @@ -0,0 +1,87 @@ +#include +#include +#include + +#include "fsm.h" + +using namespace std::placeholders; + +int value = 0; + +// global actions + +void store(int i) { value = i; } +void clear() { value = 0; } + +// global guards + +bool is1(int i) { return i == 1; } + +// fsm + +class state_machine: public fsmlite::fsm { + friend class fsm; // base class needs access to transition_table + +public: + enum states { Init, Running, Exit }; + + using event = int; + +public: + + static void store2() { value = 2; } + + static bool is2(int i) { return i == 2; } + + void store3() { value = 3; } + + bool is3(int i) const { return i == 3; } + +private: + using m = state_machine; + + using transition_table = table< +// Row-Type Start Event Target Action-Type Action Guard-Type Guard +// ----------+--------+------+--------+---------------------+-----------+------------------+-------+- + basic_row< Init, event, Running, decltype(&store), &store >, + basic_row< Running, event, Running, decltype(&store), &store, decltype(&is1), &is1 >, + basic_row< Running, event, Running, decltype(&m::store2), &m::store2, decltype(&m::is2), &m::is2 >, + basic_row< Running, event, Running, decltype(&m::store3), &m::store3, decltype(&m::is3), &m::is3 >, + basic_row< Running, event, Exit, decltype(&clear), &clear /* fallback */ >, + basic_row< Exit, event, Exit, decltype(nullptr), nullptr > +// ----------+--------+------+--------+---------------------+-----------+------------------+-------+- + >; +}; + + +int main() +{ + state_machine m; + assert(m.current_state() == state_machine::Init); + assert(value == 0); + + m.process_event(42); + assert(m.current_state() == state_machine::Running); + assert(value == 42); + + m.process_event(1); + assert(m.current_state() == state_machine::Running); + assert(value == 1); + + m.process_event(2); + assert(m.current_state() == state_machine::Running); + assert(value == 2); + + m.process_event(3); + assert(m.current_state() == state_machine::Running); + assert(value == 3); + + m.process_event(42); + assert(m.current_state() == state_machine::Exit); + assert(value == 0); + + m.process_event(42); + assert(m.current_state() == state_machine::Exit); + assert(value == 0); + return 0; +} diff --git a/tests/test_notrans.cpp b/tests/test_notrans.cpp index 74e9c4b..a842d76 100644 --- a/tests/test_notrans.cpp +++ b/tests/test_notrans.cpp @@ -25,7 +25,7 @@ class state_machine: public fsmlite::fsm { using transition_table = table< // Row-Type Start Event Target // ----------+-----+------+------+- - typed_row< Init, event, Exit > + basic_row< Init, event, Exit > // ----------+-----+------+------+- >; }; diff --git a/tests/test_scoped.cpp b/tests/test_scoped.cpp index 2c9f926..513b945 100644 --- a/tests/test_scoped.cpp +++ b/tests/test_scoped.cpp @@ -14,7 +14,7 @@ class state_machine: public fsmlite::fsm { using transition_table = table< // Row-Type Start Event Target // ----------+------------+------+-----------+- - typed_row< State::Init, event, State::Exit > + basic_row< State::Init, event, State::Exit > // ----------+------------+------+-----------+- >; diff --git a/tests/test_typed_row.cpp b/tests/test_typed_row.cpp deleted file mode 100644 index 9c05e0e..0000000 --- a/tests/test_typed_row.cpp +++ /dev/null @@ -1,100 +0,0 @@ -#include -#include -#include - -#include "fsm.h" - -using namespace std::placeholders; - -int value = 0; - -// global actions - -void store(int i) { value = i; } -void store1() { value = 1; } -auto store2 = [](){ value = 2; }; -struct { void operator()() { value = 3; } } store3; -std::function store4 = [](){ value = 4; }; -void clear() { value = 0; } - -// global guards - -bool is1(int i) { return i == 1; } -auto is2 = [](int i) { return i == 2; }; -struct { bool operator()(int i) const { return i == 3; } } is3; -auto is4 = std::bind(std::equal_to(), _1, 4); - -// fsm - -class state_machine: public fsmlite::fsm { - friend class fsm; // base class needs access to transition_table - -public: - enum states { Init, Running, Exit }; - - using event = int; - -public: - - static void store5() { value = 5; } - - static bool is5(int i) { return i == 5; } - -private: - using m = state_machine; - - using transition_table = table< -// Row-Type Start Event Target Action-Type Action Guard-Type Guard -// ----------+--------+------+--------+------------------+--------+---------------+-----+- - typed_row< Init, event, Running, decltype(&store), &store >, - typed_row< Running, event, Running, decltype(&store1), &store1, decltype(&is1), &is1 >, - typed_row< Running, event, Running, decltype(&store2), &store2, decltype(&is2), &is2 >, - typed_row< Running, event, Running, decltype(&store3), &store3, decltype(&is3), &is3 >, - typed_row< Running, event, Running, decltype(&store4), &store4, decltype(&is4), &is4 >, - typed_row< Running, event, Running, decltype(&store5), &store5, decltype(&is5), &is5 >, - typed_row< Running, event, Exit, decltype(&clear), &clear /* fallback */ >, - typed_row< Exit, event, Exit > -// ----------+--------+------+--------+------------------+--------+---------------+-----+- - >; -}; - - -int main() -{ - state_machine m; - assert(m.current_state() == state_machine::Init); - assert(value == 0); - - m.process_event(42); - assert(m.current_state() == state_machine::Running); - assert(value == 42); - - m.process_event(1); - assert(m.current_state() == state_machine::Running); - assert(value == 1); - - m.process_event(2); - assert(m.current_state() == state_machine::Running); - assert(value == 2); - - m.process_event(3); - assert(m.current_state() == state_machine::Running); - assert(value == 3); - - m.process_event(4); - assert(m.current_state() == state_machine::Running); - assert(value == 4); - - m.process_event(5); - assert(m.current_state() == state_machine::Running); - assert(value == 5); - - m.process_event(42); - assert(m.current_state() == state_machine::Exit); - assert(value == 0); - - m.process_event(42); - assert(m.current_state() == state_machine::Exit); - assert(value == 0); - return 0; -}