Skip to content

Latest commit

 

History

History
290 lines (236 loc) · 8.01 KB

215.md

File metadata and controls

290 lines (236 loc) · 8.01 KB
Info

Example

template<auto...> struct ids{};

template<auto N, auto... Ns>
auto dispatch(auto value, ids<N, Ns...>) -> decltype(value) {
  return inspect (value) {
    N  => value;
    _  => [] {
      if constexpr (sizeof...(Ns) > 0) {
        return dispatch(value, ids<Ns...>{});
      } else {
        return {};
      }
    }()
  };
}

int main() {
  std::cout << dispatch(0, ids<1, 2, 3>{}); // prints 0
  std::cout << dispatch(4, ids<1, 2, 3>{}); // prints 0

  std::cout << dispatch(1, ids<1, 2, 3>{}); // prints 1
  std::cout << dispatch(2, ids<1, 2, 3>{}); // prints 2
  std::cout << dispatch(3, ids<1, 2, 3>{}); // prints 3
}

https://godbolt.org/z/6349xe

Puzzle

  • Can you implement different run-time dispatching policies and apply them to process events?

    • Policies ideas

      • if/else
      • switch
      • inspect
      • fold expressions
      • jump table
      • goto table
      • ...
    • Double points for the most innovative policy

template <class TPolicy, class TMsg, class... Ts>
concept dispatch_policy = requires(TPolicy policy, const TMsg& msg,
                                   std::size_t& state) {
  policy.template operator()<Ts...>(msg, state);
};

constexpr auto example_policy = []<class... Ts>(const auto& msg, auto& state) {
  /*TODO*/
};

template <class... Transitions>
struct sm {
  template <class TMsg>
  constexpr auto process(const TMsg& msg,
                         dispatch_policy<TMsg, Transitions...> auto policy) {
    /*TODO*/
    return state_;
  }

  constexpr explicit(false) sm(const Transitions&...) { }

 private:
  std::size_t state_{};
};

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"_test =
      [](const auto& dispatch_policy) {
        sm sm{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{}, dispatch_policy));
          expect(0_i == sm.process(msg2{}, dispatch_policy));
        };

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

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

        should("transition to source state on expected message") = [&] {
          expect(0_i == sm.process(msg2{}, dispatch_policy));
        };
      }
      // policies
      | std::tuple{example_policy};
}

https://godbolt.org/z/obr4r3

Solutions

constexpr auto example_policy = []<class... Ts>(const auto& msg, auto& state) {
  using states_t = boost::mp11::mp_unique<
      boost::mp11::mp_list<typename Ts::src..., typename Ts::dst...>>;
  (
      [&state] {
        if constexpr (std::is_same_v<std::decay_t<decltype(msg)>,
                                     typename Ts::msg>) {
          if (state == boost::mp11::mp_find<states_t, typename Ts::src>{}) {
            state = boost::mp11::mp_find<states_t, typename Ts::dst>{};
          }
        }
      }(),
      ...);
};

template <class... Transitions>
struct sm {
  template <class TMsg>
  constexpr auto process(const TMsg& msg,
                         dispatch_policy<TMsg, Transitions...> auto policy) {
    policy.template operator()<Transitions...>(msg, state_);
    return state_;
  }

  constexpr explicit(false) sm(const Transitions&...) {}

 private:
  int state_{};
};

https://godbolt.org/z/v4Ked7

constexpr auto example_policy = []<class... Ts>(const auto& msg, auto& state){
    using states_t = boost::mp11::mp_unique<boost::mp11::mp_list<typename Ts::src..., typename Ts::dst...>>;
   ([&state] {
      if constexpr (std::is_same_v<std::decay_t<decltype(msg)>, typename Ts::msg>) {
        if (state == boost::mp11::mp_find<states_t, typename Ts::src>{}) {
          state = boost::mp11::mp_find<states_t, typename Ts::dst>{};
        }
      }
    }(), ...);
};

template <class... Transitions>
struct sm {
  template <class TMsg>
  constexpr auto process(const TMsg& msg,
                         dispatch_policy<TMsg, Transitions...> auto policy) {
    policy.template operator()<Transitions...>(msg, state_);
    return state_;
  }

  constexpr explicit(false) sm(const Transitions&...) {}

 private:
  std::size_t state_{};
};

https://godbolt.org/z/WG48Pv

constexpr auto only_thursdays_policy = []<class... TTransitions>(const auto& msg, auto& state){
    using msg_t = std::decay_t<decltype(msg)>;
    ([&] <typename TTransition> () {
      if constexpr (std::is_same_v<msg_t, typename TTransition::msg>) {
        if (boost::mp11::mp_find<boost::mp11::mp_list<TTransitions...>, TTransition>{} == state) {
          using namespace std::chrono;
          const auto d = floor<days>(system_clock::now());
          if (const year_month_weekday ymd{d}; ymd.weekday() == Thursday) {
            state = boost::mp11::mp_find<boost::mp11::mp_list<typename TTransitions::src...>,
                                         typename TTransition::dst>{};
            return true;
          }
        }
      }
      return false;
    }.template operator()<TTransitions>() or ...);
};

template <class... Transitions>
struct sm {
  template <class TMsg>
  constexpr auto process(const TMsg& msg,
                         dispatch_policy<TMsg, Transitions...> auto policy) {
    policy.template operator()<Transitions...>(msg, state_);
    return state_;
  }

  constexpr explicit(false) sm(const Transitions&...) {}

private:
  std::size_t state_{};
};

https://godbolt.org/z/h9PWfa

constexpr auto example_policy = []<class... Ts>(const auto& msg, auto& state){
  using Msgs = mp_list<typename Ts::msg...>;
  using MsgI = mp_find<Msgs, std::remove_cvref_t<decltype(msg)>>;

  if constexpr (mp_less<MsgI, mp_size<Msgs>>::value) {
    mp_with_index<sizeof...(Ts)>(state, [&]<class SrcI>(const SrcI&){
      if constexpr (mp_same<SrcI, MsgI>::value) {
        using Srcs = mp_list<typename Ts::src...>;
        using Dsts = mp_list<typename Ts::dst...>;
        using DstI = mp_at<Dsts, MsgI>;

        state = mp_find<Srcs, DstI>::value;
      }
    });
  }
};

template <class... Transitions>
struct sm {
  template <class TMsg>
  constexpr auto process(const TMsg& msg,
                         dispatch_policy<TMsg, Transitions...> auto policy) {
    policy.template operator()<Transitions...>(msg, state_);
    return state_;
  }

  constexpr explicit(false) sm(const Transitions&...) {}

 private:
  std::size_t state_{};
};

https://godbolt.org/z/exEYEE

constexpr auto example_policy = []<class... Ts, class T>(T const & msg, auto & state){
    return mp_with_index<sizeof...(Ts)>( state
                                       , [&]( auto I ) {
                                           using TMsg = mp_at_c<mp_list<typename Ts::msg...>,I>;
                                           using TDst = mp_at_c<mp_list<typename Ts::dst...>,I>;
                                           return std::is_same_v<T,TMsg>? mp_find<mp_list<typename Ts::src...>, TDst>{}: state;
                                       });
};

template <class... Transitions>
struct sm {
  template <class TMsg>
  constexpr auto process(const TMsg& msg,
                         dispatch_policy<TMsg, Transitions...> auto policy) {
    state_ = policy.template operator()<Transitions...> (msg,state_);
    return state_;
  }

  constexpr explicit(false) sm(const Transitions&...) {}

 private:
  std::size_t state_{};
};

https://godbolt.org/z/v4oE8o