Skip to content

Commit

Permalink
Fix #30: Require C++17 support.
Browse files Browse the repository at this point in the history
  • Loading branch information
tkem committed Jul 22, 2021
1 parent daf0539 commit 4bbf2b4
Show file tree
Hide file tree
Showing 8 changed files with 17 additions and 468 deletions.
76 changes: 2 additions & 74 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,84 +1,12 @@
# fsmlite [![Build Status](https://travis-ci.com/tkem/fsmlite.svg?branch=master)](https://travis-ci.com/tkem/fsmlite/) [![Coverage Status](https://coveralls.io/repos/github/tkem/fsmlite/badge.svg?branch=master)](https://coveralls.io/github/tkem/fsmlite?branch=master) [![Documentation Status](https://readthedocs.org/projects/fsmlite/badge/?version=latest&style=flat)](http://fsmlite.readthedocs.io/en/latest/)

**fsmlite** is a lightweight finite state machine framework for C++11.
**fsmlite** is a lightweight finite state machine framework for C++17.
It is based on concepts first presented by David Abrahams and Aleksey
Gurtovoy in [C++ Template Metaprogramming][1], with additional ideas
taken liberally from Boost's [Meta State Machine][2] (MSM).

The canonical CD player example (with CD-Text and auto-play support!)
therefore looks somewhat like this:

```C++
#include "fsm.h"

#include <string>

class player: public fsmlite::fsm<player> {
// grant base class access to private transition_table
friend class fsmlite::fsm<player>;

std::string cd_title;
bool autoplay = false;

public:
enum states { Stopped, Open, Empty, Playing, Paused };

player(state_type init_state = Empty) : fsm(init_state) { }

void set_autoplay(bool f) { autoplay = f; }

bool is_autoplay() const { return autoplay; }

const std::string& get_cd_title() const { return cd_title; }

struct play {};
struct open_close {};
struct cd_detected { std::string title; };
struct stop {};
struct pause {};

private:
// guards
bool is_autoplay(const cd_detected&) const { return autoplay; }
bool is_bad_cd(const cd_detected& cd) const { return cd.title.empty(); }

// actions
void start_playback(const play&);
void start_autoplay(const cd_detected& cd);
void open_drawer(const open_close&);
void open_drawer(const cd_detected& cd);
void close_drawer(const open_close&);
void store_cd_info(const cd_detected& cd);
void stop_playback(const stop&);
void pause_playback(const pause&);
void stop_and_open(const open_close&);
void resume_playback(const play&);

private:
using m = player; // for brevity

using transition_table = table<
// Start Event Target Action Guard (optional)
// -----------+--------+------------+--------+-------------------+---------------+-
mem_fn_row< Stopped, play, Playing, &m::start_playback >,
mem_fn_row< Stopped, open_close, Open, &m::open_drawer >,
mem_fn_row< Open, open_close, Empty, &m::close_drawer >,
mem_fn_row< Empty, open_close, Open, &m::open_drawer >,
mem_fn_row< Empty, cd_detected, Open, &m::open_drawer, &m::is_bad_cd >,
mem_fn_row< Empty, cd_detected, Playing, &m::start_autoplay, &m::is_autoplay >,
mem_fn_row< Empty, cd_detected, Stopped, &m::store_cd_info /* fallback */ >,
mem_fn_row< Playing, stop, Stopped, &m::stop_playback >,
mem_fn_row< Playing, pause, Paused, &m::pause_playback >,
mem_fn_row< Playing, open_close, Open, &m::stop_and_open >,
mem_fn_row< Paused, play, Playing, &m::resume_playback >,
mem_fn_row< Paused, stop, Stopped, &m::stop_playback >,
mem_fn_row< Paused, open_close, Open, &m::stop_and_open >
// -----------+--------+------------+--------+-------------------+---------------+-
>;
};
```
C++17 will give you a little more flexibility:
looks somewhat like this:

```C++
class player: public fsmlite::fsm<player> {
Expand Down
2 changes: 1 addition & 1 deletion configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ AC_PROG_INSTALL
AC_CHECK_PROGS([DOXYGEN], [doxygen], [false])
AC_CHECK_PROGS([SPHINXBUILD], [sphinx-build], [false])

AX_CXX_COMPILE_STDCXX([11])
AX_CXX_COMPILE_STDCXX([17])
AX_CXXFLAGS_WARN_ALL

AC_ARG_ENABLE([coverage],
Expand Down
125 changes: 13 additions & 112 deletions src/fsm.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,33 +34,8 @@

namespace fsmlite {
namespace detail {
#if __cplusplus >= 201703L
template<class F, class... Args>
using invoke_result_t = std::invoke_result_t<F, Args...>;

template <class F, class... Args>
using is_invocable = std::is_invocable<F, Args...>;
#elif __cplusplus >= 201103L
template<class F, class... Args>
using invoke_result_t = typename std::result_of<F&&(Args&&...)>::type;

struct is_invocable_test {
struct no_type { int a; int b; };

template<class F, class... Args, class = invoke_result_t<F, Args...>>
static char test(int);

template<class, class...>
static no_type test(...);
};

template<class F, class... Args>
using is_invocable = typename std::integral_constant<
bool,
sizeof(is_invocable_test::test<F, Args...>(0)) == 1
>::type;
#else
#error "fsmlite requires C++11 support."
#if __cplusplus < 201703L
#error "fsmlite requires C++17 support."
#endif
// C++11 std::forward() is in <utility>, which may not be
// present on freestanding implementations
Expand All @@ -77,27 +52,27 @@ namespace fsmlite {
// C++17 std::invoke() is in <functional>, which may not be
// present on freestanding implementations
template <class F, class... Args>
invoke_result_t<F, Args...> invoke(F&& f, Args&&... args) {
std::invoke_result_t<F, Args...> invoke(F&& f, Args&&... args) {
return f(args...);
}

template <class M, class T, class T1, class... Args>
invoke_result_t<M T::*, T1, Args...> invoke(M T::* f, T1&& obj, Args&&... args) {
std::invoke_result_t<M T::*, T1, Args...> 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<F>::value,
bool f2 = is_invocable<F, Arg1>::value,
bool f3 = is_invocable<F, Arg2>::value,
bool f4 = is_invocable<F, Arg1, Arg2>::value
bool f1 = std::is_invocable<F>::value,
bool f2 = std::is_invocable<F, Arg1>::value,
bool f3 = std::is_invocable<F, Arg2>::value,
bool f4 = std::is_invocable<F, Arg1, Arg2>::value
> struct binary_fn_helper;

template<class F, class Arg1, class Arg2>
struct binary_fn_helper<F, Arg1, Arg2, true, false, false, false> {
using result_type = invoke_result_t<F>;
using result_type = std::invoke_result_t<F>;

static result_type invoke(F&& f, Arg1&&, Arg2&&) {
return detail::invoke(f);
Expand All @@ -106,7 +81,7 @@ namespace fsmlite {

template<class F, class Arg1, class Arg2>
struct binary_fn_helper<F, Arg1, Arg2, false, true, false, false> {
using result_type = invoke_result_t<F, Arg1>;
using result_type = std::invoke_result_t<F, Arg1>;

static result_type invoke(F&& f, Arg1&& a, Arg2&&) {
return detail::invoke(f, a);
Expand All @@ -115,7 +90,7 @@ namespace fsmlite {

template<class F, class Arg1, class Arg2>
struct binary_fn_helper<F, Arg1, Arg2, false, false, true, false> {
using result_type = invoke_result_t<F, Arg2>;
using result_type = std::invoke_result_t<F, Arg2>;

static result_type invoke(F&& f, Arg1&&, Arg2&& b) {
return detail::invoke(f, b);
Expand All @@ -124,7 +99,7 @@ namespace fsmlite {

template<class F, class Arg1, class Arg2>
struct binary_fn_helper<F, Arg1, Arg2, false, false, false, true> {
using result_type = invoke_result_t<F, Arg1, Arg2>;
using result_type = std::invoke_result_t<F, Arg1, Arg2>;

static result_type invoke(F&& f, Arg1&& a, Arg2&& b) {
return detail::invoke(f, a, b);
Expand Down Expand Up @@ -286,80 +261,7 @@ namespace fsmlite {
template<class... Rows> using table = detail::list<Rows...>;

/**
* Basic transition class template.
*
* @tparam start the start state of the transition
*
* @tparam Event the event type triggering the transition
*
* @tparam target the target state of the transition
*
* @tparam Action an action function type, or `std::nullptr_t`
*
* @tparam action a static `Action` instance
*
* @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 = std::nullptr_t,
Action action = nullptr,
class Guard = std::nullptr_t,
Guard guard = nullptr
>
struct basic_row : public row_base<start, Event, target> {
static void process_event(Derived& self, const Event& event) {
row_base<start, Event, target>::process_event(action, self, event);
}

static bool check_guard(const Derived& self, const Event& event) {
return row_base<start, Event, target>::check_guard(guard, self, event);
}
};

/**
* Member function transition class template.
*
* @tparam start the start state of the transition
*
* @tparam Event the event type triggering the transition
*
* @tparam target the target state of the transition
*
* @tparam action an action member function, or `nullptr`
*
* @tparam guard a guard member function, or `nullptr`
*/
template<
State start,
class Event,
State target,
void (Derived::*action)(const Event&) = nullptr,
bool (Derived::*guard)(const Event&) const = nullptr
>
struct mem_fn_row : public row_base<start, Event, target> {
static void process_event(Derived& self, const Event& event) {
if (action != nullptr) {
row_base<start, Event, target>::process_event(action, self, event);
}
}

static bool check_guard(const Derived& self, const Event& event) {
if (guard != nullptr) {
return row_base<start, Event, target>::check_guard(guard, self, event);
} else {
return true;
}
}
};

#if __cplusplus >= 201703L
/**
* Generic transition class template (requires C++17).
* Transition class template.
*
* @tparam start the start state of the transition
*
Expand Down Expand Up @@ -387,7 +289,6 @@ namespace fsmlite {
return row_base<start, Event, target>::check_guard(guard, self, event);
}
};
#endif

private:
template<class Event, class...> struct by_event_type;
Expand Down
2 changes: 0 additions & 2 deletions tests/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@ 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_row \
Expand Down
82 changes: 0 additions & 82 deletions tests/test_basic_row.cpp

This file was deleted.

Loading

0 comments on commit 4bbf2b4

Please sign in to comment.