Skip to content

Latest commit

 

History

History
388 lines (327 loc) · 11.9 KB

214.md

File metadata and controls

388 lines (327 loc) · 11.9 KB
Info

Example

template<class... Ts>
struct sm {
  template<class T>
  constexpr auto process() {
    return std::array{
      [] {
        if constexpr (std::is_same_v<T, typename Ts::second_type>) {
          return Ts::first_type::value;
        } else {
          return 0;
        }
     }()...
   };
 }
};

struct T0{};
struct T1{};
struct T2{};

static_assert(std::array{0, 0} == sm<std::pair<std::integral_constant<int, 1>, T1>, std::pair<std::integral_constant<int, 2>, T2>>{}.process<T0>());
static_assert(std::array{1, 0} == sm<std::pair<std::integral_constant<int, 1>, T1>, std::pair<std::integral_constant<int, 2>, T2>>{}.process<T1>());
static_assert(std::array{0, 2} == sm<std::pair<std::integral_constant<int, 1>, T1>, std::pair<std::integral_constant<int, 2>, T2>>{}.process<T2>());

https://godbolt.org/z/9sT44a

Puzzle

template<class... Transitions>
struct sm {
  constexpr explicit(false) sm(const Transitions&...) {}
  template<class TMsg>
  constexpr auto process(const TMsg&) {
    /*TODO*/return std::array<int, sizeof...(Transitions)>{};
  }
};

template<class TSrc, class TMsg, class TDst>
struct transition {
  using src = TSrc;
  using msg = TMsg;
  using dst = TDst;
};

struct msg0{};
struct msg1{};
struct msg2{};
struct msg3{};
struct msg4{};

int main() {
  using namespace boost::ut;

  "state machine with orthogonal regions"_test = [] {
    sm sm{
      std::tuple{
        transition<class State1, msg1, class State2>{},
        transition<class State2, msg2, class State1>{},
      },
      std::tuple{
        transition<class State1, msg3, class State2>{},
        transition<class State2, msg4, class State1>{}
      }
    };

    should("state in the same state on unexpected message") = [&] {
      expect(std::array{0, 0} == sm.process(msg0{}));
      expect(std::array{0, 0} == sm.process(msg2{}));
      expect(std::array{0, 0} == sm.process(msg4{}));
    };

    should("transition to destination state on expected message") = [&] {
      expect(std::array{1, 0} == sm.process(msg1{}));
      expect(std::array{1, 1} == sm.process(msg3{}));
    };

    should("stay in the same state on unexpected message") = [&] {
      expect(std::array{1, 1} == sm.process(msg0{}));
      expect(std::array{1, 1} == sm.process(msg1{}));
      expect(std::array{1, 1} == sm.process(msg3{}));
    };

    should("transition to source state on expected message") = [&] {
      expect(std::array{0, 1} == sm.process(msg2{}));
      expect(std::array{0, 0} == sm.process(msg4{}));
    };
  };
}

https://godbolt.org/z/M7ra3b

Solutions

template<class Transitions>
struct sm_single {
    constexpr explicit(false) sm_single(const Transitions&) {}
    constexpr static std::size_t N = std::tuple_size_v<Transitions>;
    template<class TMsg>
    constexpr auto process(const TMsg&) {
        [&]<auto ... Is >( std::index_sequence<Is...> const & )
        {
            ( [&](){ using T = std::tuple_element_t<Is,Transitions>;
                     if( Is == current_state && std::is_same_v< typename T::msg , TMsg>  )
                     {
                         // assuming we can find a state index that the src match this dst
                         std::size_t next_state = []<auto ... Js >( std::index_sequence<Js...> const & ){
                             return ( [](){ if(std::is_same_v< typename std::tuple_element_t<Js,Transitions>::src, typename T::dst>)
                                                return Js;
                                            return 0ul;
                                          }() + ...);
                         }( std::make_index_sequence<N>{});
                         current_state = next_state;
                     }
                   }(), ...);
        }(std::make_index_sequence<N>{});
        return current_state;
    }
    int  current_state = 0;
};
template<class... Transitions>
struct sm {
  constexpr explicit(false) sm(const Transitions&... transitions)
  :sms{sm_single(transitions) ...}
  {}
  template<class TMsg>
  constexpr auto process(const TMsg& msg ) {
    return [&]<auto ... Is >( std::index_sequence<Is...> const & ){
            return std::array<int, sizeof...(Transitions)>{std::get<Is>(sms).process(msg)...};
        }(std::make_index_sequence<sizeof...(Transitions)>{});
  }
  std::tuple<sm_single<Transitions>...> sms;
};

https://godbolt.org/z/a54EfP

namespace detail {
template<class... Ts>
struct sm {
  using states_t = boost::mp11::mp_unique<boost::mp11::mp_list<typename Ts::src..., typename Ts::dst...>>;

  template<class TMsg>
  constexpr auto process(const TMsg& msg) {
    ([this] {
      if constexpr (std::is_same_v<TMsg, typename Ts::msg>) {
        if (state_ == boost::mp11::mp_find<states_t, typename Ts::src>{}) {
          state_ = boost::mp11::mp_find<states_t, typename Ts::dst>{};
        }
      }
    }(), ...);
    return state_;
  }

  constexpr explicit(false) sm(const std::tuple<Ts...>&) {}

 private:
  int state_{};
};
}

template<class... Ts>
struct sm : decltype(detail::sm{Ts{}})... {
  template<class TMsg>
  constexpr auto process(const TMsg& msg) {
    return std::array{static_cast<decltype(detail::sm{Ts{}})&>(*this).process(msg)...};
  }

  constexpr explicit(false) sm(const Ts&... ts)
    : decltype(detail::sm{Ts{}}){ts}...
  { }
};

https://godbolt.org/z/jxfY8q

template<class... Transitions>
struct sm {
  constexpr explicit(false) sm(const Transitions&...) {}
  template<class TMsg>
  constexpr auto process(const TMsg&) {
    [&] <auto... Is> (std::index_sequence<Is...>) {
      (process<TMsg, Is, Transitions>(), ...);
    }(std::index_sequence_for<Transitions...>{});
    return current_states;
  }

private:
  template <class TMsg, auto Index, class TTransitions>
  constexpr auto process() {
    [&] <template <class...> class TList, class... Ts> (TList<Ts...>) {
      ([&] {
        if constexpr (std::is_same_v<TMsg, typename Ts::msg>) {
          if (index_for<typename Ts::src, TTransitions>() == current_states[Index]) {
            current_states[Index] = index_for<typename Ts::dst, TTransitions>();
            return true;
          }
        }
        return false;
      }() or ...);
    }(TTransitions{});
  }

  template <class TMsg, class TTransitions>
  constexpr auto index_for() const {
    return [] <template <class...> class TList, class... Ts, auto... Is> (
        TList<Ts...>, std::index_sequence<Is...>) {
      return ([] () -> int {
        if constexpr (std::is_same_v<TMsg, typename Ts::src>) {
          return Is;
        } else {
          return 0;
        }
      }() + ... + 0);
    }(TTransitions{}, std::make_index_sequence<std::tuple_size_v<TTransitions>>{});
  }

  std::array<int, sizeof...(Transitions)> current_states{};
};

https://godbolt.org/z/cYv7j5

template<class... Transitions>
struct sm {
  template<class ...Ts>
  using states_impl_t = boost::mp11::mp_unique<boost::mp11::mp_list<typename Ts::src..., typename Ts::dst...>>;
  using states_t = std::tuple<boost::mp11::mp_rename<Transitions, states_impl_t>...>;
  using TransitionList = boost::mp11::mp_list<Transitions...>;

  constexpr explicit(false) sm(const Transitions&...) {}
  template<class TMsg>
  constexpr auto process(const TMsg&) {
    [this]<auto... Is>(std::index_sequence<Is...> const&) {
        ( [this]<class... Ts, auto I>(std::tuple<Ts...> const&, std::integral_constant<int, I>const&) {
            ( [this]() {
                if constexpr (std::is_same_v<TMsg, typename Ts::msg>) {
                  if ( states[I] == boost::mp11::mp_find< boost::mp11::mp_at_c<states_t, I>, typename Ts::src>{}) {
                    states[I] = boost::mp11::mp_find<boost::mp11::mp_at_c<states_t, I>, typename Ts::dst>{};
                  }
                }
             }  (), ...);
        }( boost::mp11::mp_at_c<TransitionList, Is>{}, std::integral_constant<int, Is>{} ), ...);
    }(std::make_index_sequence<sizeof...(Transitions)>{});

    return states;
  }
private:
  std::array<int, sizeof...(Transitions)> states{};
};

https://godbolt.org/z/eebYbG

template<class... Transitions>
struct sm {
  constexpr explicit(false) sm(const Transitions&...) {}

    template <class TDst, class TTransitions, typename T, T... ints>
    constexpr auto dest_index(std::integer_sequence<T, ints...> index_seq){
        return ([](){
            using element_type = std::tuple_element_t<ints, TTransitions>;
            if (std::is_same_v<TDst, typename element_type::src>)
            {
                return static_cast<int>(ints);
            }
            else
                return 0;
        }() + ...);
    }

    template<class TMsg, class TTransitions, class TIndex>
    constexpr auto matched(int state)
    {
        constexpr auto transitions_size = std::tuple_size<TTransitions>::value;
        using index_seq = std::make_index_sequence<transitions_size>;

        using element_type = std::tuple_element_t<TIndex::value, TTransitions>;
        if (state == TIndex::value && std::is_same_v<TMsg, typename element_type::msg>)
            return dest_index<typename element_type::dst, TTransitions>(index_seq{}) - TIndex::value;
        else
            return 0;
    }

    template<class TMsg, class TTransitions, typename T, T... ints>
    constexpr auto sum_matches(std::integer_sequence<T, ints...> index_seq, int state)
    {
        return (matched<TMsg, TTransitions, std::integral_constant<int, ints>>(state) + ...);
    }

    template<class TMsg, class TTransitions>
    constexpr auto get_state_for_transition_table(int i)
    {

        auto state = states[i];
        constexpr auto transitions_size = std::tuple_size<TTransitions>::value;
        using index_seq = std::make_index_sequence<transitions_size>;
        auto state_step = sum_matches<TMsg, TTransitions>(index_seq{}, state);

        state += state_step;
        return state;
    }

    template<class TMsg, class ...>
    constexpr auto process(const TMsg&) {
        std::ptrdiff_t i = 0;
        (void(states[i++] = get_state_for_transition_table<TMsg, Transitions>(i)) , ...);

        return states;
    }

    private:
        std::array<int, sizeof...(Transitions)> states{};
};

https://godbolt.org/z/8Khx77

template<class... Transitions>
struct sm : sm<Transitions>... {
  constexpr explicit(false) sm(const Transitions&... t) : sm<Transitions>(t)... {}
  template<class TMsg>
  constexpr auto process(const TMsg& msg) {
    return std::array{sm<Transitions>::process(msg)...};
  }
};

template<class Transitions>
struct sm<Transitions> {
  constexpr explicit(false) sm(const Transitions&) {}

  template<class TMsg>
  constexpr int process(const TMsg&) {
    std::visit([this]<class I>(const I&) {
      using Prev = mp_at<Transitions, I>;

      if constexpr (is_trait_same<Prev, mp_quote<msg>, TMsg>::value) {
        using is_src_same_prev_dst = mp_bind_back<is_trait_same, mp_quote<src>, dst<Prev>>;
        using J = mp_find_if_q<Transitions, is_src_same_prev_dst>;
        using Next = mp_bind_front<mp_at, Transitions, J>;

        if constexpr (mp_valid_q<Next>::value) {
          state = mp_find<Transitions, mp_invoke_q<Next>>{};
        }
      }
    }, state);

    return state.index();
  }

private:
  template<class T>
  using src = typename T::src;
  template<class T>
  using msg = typename T::msg;
  template<class T>
  using dst = typename T::dst;

  template<class T, class Trait, class V>
  using is_trait_same = mp_same<mp_eval_or_q<void, Trait, T>, V>;

  using State = mp_rename<mp_iota<mp_size<Transitions>>, std::variant>;
  State state{mp_size_t<0>{}};
};

https://godbolt.org/z/hTo3d6