Skip to content

Latest commit

 

History

History
361 lines (292 loc) · 10.7 KB

213.md

File metadata and controls

361 lines (292 loc) · 10.7 KB
Info

Example

template<class T, class... Ts>
constexpr auto find = ([] {
  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(0 == find<T0, std::pair<std::integral_constant<int, 1>, T1>, std::pair<std::integral_constant<int, 2>, T2>>);
static_assert(1 == find<T1, std::pair<std::integral_constant<int, 1>, T1>, std::pair<std::integral_constant<int, 2>, T2>>);
static_assert(2 == find<T2, std::pair<std::integral_constant<int, 1>, T1>, std::pair<std::integral_constant<int, 2>, T2>>);

https://godbolt.org/z/v4h3b8

Puzzle

  • Can you implement a simple State machine which returns the current state with the process call?
template<class Transitions>
struct sm {
  constexpr explicit(false) sm(const Transitions&) {}

  template<class TMsg>
  constexpr auto process(const TMsg&) {
    /*TODO*/return 0;
  }
};

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

struct msg0{};
struct msg1{};
struct msg2{};

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

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

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

    should("transition to destination state on expected message") = [&] {
      expect(1_i == sm.process(msg1{}));
    };

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

    should("transition to source state on expected message") = [&] {
      expect(0_i == sm.process(msg2{}));
    };
  };
}

https://godbolt.org/z/oEjrT8

Solutions

template<class Transitions>
struct sm {
    constexpr explicit(false) sm(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;
    }
    std::size_t  current_state = 0ul;
};

https://godbolt.org/z/rjvq6d

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_{};
};

https://godbolt.org/z/r85hqP

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

  template<class TMsg>
  constexpr auto 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/nGfW9j

namespace sm_impl {
template<class ...CS> struct set{};
template<class ...Cs> struct list{};

template<typename N, typename ... C>
struct is_in {
    static constexpr bool value {(std::is_same_v<N, C> || ...)};
};

template<class S, class L> struct add_to_set;
template<class ...Cs, class N>
struct add_to_set<set<Cs...>, list<N>> {
    using type = std::conditional_t< is_in<N,Cs...>::value, set<Cs...>, set<Cs...,N>>;
};

template<class... Cs, class N, class ...Ns>
struct add_to_set< set<Cs...>, list<N,Ns...> > {
    using type = std::conditional_t< is_in<N, Cs...>::value,
                                   typename add_to_set< set<Cs...>, list<Ns...>>::type,
                                   typename add_to_set< set<Cs...,N>, list<Ns...>>::type>;
};

template<class S, class T, class IC> struct index_of_impl;
template<class T, int N>
struct index_of_impl<set<>, T, std::integral_constant<int, N>> {
    using type = std::integral_constant<int, -1>;
};

template<class C, class ...Cs, class T, int N>
struct index_of_impl<set<C, Cs...>, T, std::integral_constant<int, N>> {
    using type = std::conditional_t< std::is_same_v<C,T>,
                                     std::integral_constant<int, N>,
                                     typename index_of_impl< set<Cs...>, T, std::integral_constant<int, N+1>>::type>;
};

template<class S, class T> struct index_of;
template<class ... Cs, class T> struct index_of<set<Cs...>, T> {
    using type = typename index_of_impl<set<Cs...>, T, std::integral_constant<int, 0> >::type;
};

template<class Src, class Msg, class Dst> struct instruction{};

template<typename Transitions> struct TransitionList;
template<template<typename> typename C, template<class, class, class> typename T,
         typename ... Src, typename ... Msg, typename ... Dst>
struct TransitionList< C<T<Src, Msg, Dst>...>> {
    using stateSet = add_to_set<set<>, list<Src..., Dst...>>::type;
    using type = std::tuple< instruction< typename index_of<stateSet, Src>::type,
                                          Msg,
                                          typename index_of<stateSet, Dst>::type> ...>;
};

template<int... S, class msg, class ...Msgs, int... D>
int process(int currState, msg const&, std::tuple< instruction<std::integral_constant<int, S>, Msgs, std::integral_constant<int, D>>...> const& ) {
    int newState = currState;
    ([&newState, currState= currState](){
        if ( currState == S && std::is_same_v<msg,Msgs>)
            newState = D;
    }() , ...);
    return newState;
}

} //namespace sm_impl

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

  template<class TMsg>
  constexpr auto process(const TMsg&) {
      using transitionList = typename sm_impl::TransitionList<Transitions>::type;
      state = sm_impl::process(state, TMsg{}, transitionList{});
      return state;
  }
  int state = 0;
};

https://godbolt.org/z/M5s16r

template<template <class...> class TList, class... Ts>
struct sm {
  constexpr explicit(false) sm(TList<Ts...>&&) {}

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

    return current_state;
  }

private:
  template <class TMsg>
  consteval static auto index_for() {
    return [] <class T, T... Is> (std::integer_sequence<T, Is...>) {
      return ((std::is_same_v<TMsg, typename Ts::src> ? Is : 0) + ... + 0);
    }(std::make_integer_sequence<int, sizeof...(Ts)>{});
  }

  int current_state{};
};

https://godbolt.org/z/aE799E

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

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

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

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

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

    template<class TMsg>
    constexpr auto process(const TMsg&) {
        constexpr auto transitions_size = std::tuple_size<Transitions>::value;
        using index_seq = std::make_index_sequence<transitions_size>;
        auto state_step = sum_matches<TMsg>(index_seq{});

        state += state_step;

        return state;
    }

    private:
        int state = 0;
};

https://godbolt.org/z/TGvhPo