boost-ext / sml

C++14 State Machine library
https://boost-ext.github.io/sml
Boost Software License 1.0
1.1k stars 173 forks source link

Problem with logging and member function actions/guards #435

Open pettni opened 3 years ago

pettni commented 3 years ago

Thanks for creating this library. I ran into an issue when trying to implement logging.

Expected Behavior

Logging should work when using member function guards and actions.

Actual Behavior

Compilation error:

boost/sml.hpp:1688:81:   required from ‘bool boost::ext::sml::v1_1_3::back::sm< <template-parameter-1-1> >::process_event(const TEvent&) [with TEvent = {anonymous}::e1; typename boost::ext::sml::v1_1_3::aux::enable_if<boost::ext::sml::v1_1_3::aux::integral_constant<bool, __is_base_of(TEvent, boost::ext::sml::v1_1_3::back::sm::events_ids)>::value, int>::type <anonymous> = 0; TSM = boost::ext::sml::v1_1_3::back::sm_policy<{anonymous}::logging, boost::ext::sml::v1_1_3::back::policies::logger<{anonymous}::my_logger> >]’
test.cpp:70:24:   required from here
sml.hpp:1192:94: error: ‘const struct boost::ext::sml::v1_1_3::aux::zero_wrapper<void ({anonymous}::logging::*)(), void>’ has no member named ‘get’
 1192 | turn static_cast<aux::pool_type<TLogger &> &>(deps).value.template log_action<SM>(action.get(), event);

Steps to Reproduce the Problem

This is a modified version of the logging.cpp example

#include <boost/sml.hpp>
#include <cassert>
#include <cstdio>
#include <iostream>

namespace sml = boost::sml;

namespace {
struct my_logger {
  template <class SM, class TEvent>
  void log_process_event(const TEvent&) {
    printf("[%s][process_event] %s\n", sml::aux::get_type_name<SM>(), sml::aux::get_type_name<TEvent>());
  }

  template <class SM, class TGuard, class TEvent>
  void log_guard(const TGuard&, const TEvent&, bool result) {
    printf("[%s][guard] %s %s %s\n", sml::aux::get_type_name<SM>(), sml::aux::get_type_name<TGuard>(),
           sml::aux::get_type_name<TEvent>(), (result ? "[OK]" : "[Reject]"));
  }

  template <class SM, class TAction, class TEvent>
  void log_action(const TAction&, const TEvent&) {
    printf("[%s][action] %s %s\n", sml::aux::get_type_name<SM>(), sml::aux::get_type_name<TAction>(),
           sml::aux::get_type_name<TEvent>());
  }

  template <class SM, class TSrcState, class TDstState>
  void log_state_change(const TSrcState& src, const TDstState& dst) {
    printf("[%s][transition] %s -> %s\n", sml::aux::get_type_name<SM>(), src.c_str(), dst.c_str());
  }
};

struct e1 {};
struct e2 {};

struct action {
  void operator()() {}
} action;

struct logging {

  void member_action()
  {
    return;
  }

  auto operator()() const noexcept {
    using namespace sml;
    return make_transition_table(
      // This works:
      // *"idle"_s + event<e1> / action = "s1"_s
      // This does not work:
       *"idle"_s + event<e1> / &logging::member_action = "s1"_s
    );
  }
};
}  // namespace

int main() {
  logging obj;
  my_logger logger;
  sml::sm<logging, sml::logger<my_logger>> sm{obj, logger};
  sm.process_event(e1{});
  sm.process_event(e2{});
}

Workaround

Wrap the member action or guard in a lambda instead of using the &self:: syntax.

Specifications

ceggers-arri commented 2 years ago

Same problem for me. I simply integrated struct my_logger from logging.cpp into the action_guards example:

//
// Copyright (c) 2016-2020 Kris Jusiak (kris at jusiak dot net)
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//
#include <boost/sml.hpp>
#include <cassert>
#include <iostream>
#include <typeinfo>

namespace sml = boost::sml;

namespace {

struct my_logger {
  template <class SM, class TEvent>
  void log_process_event(const TEvent&) {
    printf("[%s][process_event] %s\n", sml::aux::get_type_name<SM>(), sml::aux::get_type_name<TEvent>());
  }

  template <class SM, class TGuard, class TEvent>
  void log_guard(const TGuard&, const TEvent&, bool result) {
    printf("[%s][guard] %s %s %s\n", sml::aux::get_type_name<SM>(), sml::aux::get_type_name<TGuard>(),
           sml::aux::get_type_name<TEvent>(), (result ? "[OK]" : "[Reject]"));
  }

  template <class SM, class TAction, class TEvent>
  void log_action(const TAction&, const TEvent&) {
    printf("[%s][action] %s %s\n", sml::aux::get_type_name<SM>(), sml::aux::get_type_name<TAction>(),
           sml::aux::get_type_name<TEvent>());
  }

  template <class SM, class TSrcState, class TDstState>
  void log_state_change(const TSrcState& src, const TDstState& dst) {
    printf("[%s][transition] %s -> %s\n", sml::aux::get_type_name<SM>(), src.c_str(), dst.c_str());
  }
};

struct e1 {};
struct e2 {};
struct e3 {};
struct e4 {};
struct e5 {};

// Allow out of header implementation
bool guard2_impl(int i);

struct actions_guards {
  using self = actions_guards;

  auto operator()() {
    using namespace sml;

    auto guard1 = [] {
      std::cout << "guard1" << std::endl;
      return true;
    };

    auto guard2 = wrap(&guard2_impl);

    auto action1 = [](auto e) { std::cout << "action1: " << typeid(e).name() << std::endl; };

    struct action2 {
      void operator()(int i) {
        assert(42 == i);
        std::cout << "action2" << std::endl;
      }
    };

    // clang-format off
    return make_transition_table(
       *"idle"_s + event<e1> = "s1"_s
      , "s1"_s + event<e2> [ guard1 ] / action1 = "s2"_s
      , "s2"_s + event<e3> [ guard1 && ![] { return false;} ] / (action1, action2{}) = "s3"_s
      , "s3"_s + event<e4> [ !guard1 || guard2 ] / (action1, [] { std::cout << "action3" << std::endl; }) = "s4"_s
      , "s3"_s + event<e4> [ guard1 ] / ([] { std::cout << "action4" << std::endl; }, [this] { action4(); } ) = "s5"_s
      , "s5"_s + event<e5> [ &self::guard3 ] / &self::action5 = X
    );
    // clang-format on
  }

  bool guard3(int i) const noexcept {
    assert(42 == i);
    std::cout << "guard3" << std::endl;
    return true;
  }

  void action4() const { std::cout << "action4" << std::endl; }

  void action5(int i, const e5&) {
    assert(42 == i);
    std::cout << "action5" << std::endl;
  }
};
bool guard2_impl(int i) {
  assert(42 == i);
  std::cout << "guard2" << std::endl;
  return false;
}
}  // namespace

int main() {
  my_logger logger;
  actions_guards ag{};
  sml::sm<actions_guards, sml::logger<my_logger>> sm{ag, 42, logger};
  sm.process_event(e1{});
  sm.process_event(e2{});
  sm.process_event(e3{});
  sm.process_event(e4{});
  sm.process_event(e5{});
  assert(sm.is(sml::X));
}

Compiler output:

In file included from sml/example/actions_guards.cpp:8:
sml/include/boost/sml.hpp: In instantiation of ‘void boost::ext::sml::v1_1_5::back::policies::log_guard(const boost::ext::sml::v1_1_5::aux::type<TLogger>&, TDeps&, const boost::ext::sml::v1_1_5::aux::zero_wrapper<TAction>&, const TEvent&, bool) [with SM = {anonymous}::actions_guards; TLogger = {anonymous}::my_logger; TDeps = boost::ext::sml::v1_1_5::aux::pool<int, {anonymous}::actions_guards&, {anonymous}::my_logger&>; TGuard = bool ({anonymous}::actions_guards::*)(int) const noexcept; TEvent = {anonymous}::e5]’:
sml/include/boost/sml.hpp:1939:35:   required from ‘static auto boost::ext::sml::v1_1_5::front::call<TEvent, boost::ext::sml::v1_1_5::aux::type_list<TArgs ...>, TLogger>::execute_impl(const boost::ext::sml::v1_1_5::aux::type<bool>&, T, const TEvent&, SM&, TDeps&) [with TSM = {anonymous}::actions_guards; T = boost::ext::sml::v1_1_5::aux::zero_wrapper<bool ({anonymous}::actions_guards::*)(int) const noexcept, void>; SM = boost::ext::sml::v1_1_5::back::sm_impl<boost::ext::sml::v1_1_5::back::sm_policy<{anonymous}::actions_guards, boost::ext::sml::v1_1_5::back::policies::logger<{anonymous}::my_logger> > >; TDeps = boost::ext::sml::v1_1_5::aux::pool<int, {anonymous}::actions_guards&, {anonymous}::my_logger&>; TEvent = {anonymous}::e5; Ts = {{anonymous}::actions_guards&, int}; TLogger = {anonymous}::my_logger]’
sml/include/boost/sml.hpp:1934:44:   required from ‘static auto boost::ext::sml::v1_1_5::front::call<TEvent, boost::ext::sml::v1_1_5::aux::type_list<TArgs ...>, TLogger>::execute(T, const TEvent&, TSM&, TDeps&, TSubs&) [with T = boost::ext::sml::v1_1_5::aux::zero_wrapper<bool ({anonymous}::actions_guards::*)(int) const noexcept, void>; TSM = boost::ext::sml::v1_1_5::back::sm_impl<boost::ext::sml::v1_1_5::back::sm_policy<{anonymous}::actions_guards, boost::ext::sml::v1_1_5::back::policies::logger<{anonymous}::my_logger> > >; TDeps = boost::ext::sml::v1_1_5::aux::pool<int, {anonymous}::actions_guards&, {anonymous}::my_logger&>; TSubs = boost::ext::sml::v1_1_5::aux::pool<boost::ext::sml::v1_1_5::back::sm_impl<boost::ext::sml::v1_1_5::back::sm_policy<{anonymous}::actions_guards, boost::ext::sml::v1_1_5::back::policies::logger<{anonymous}::my_logger> > > >; TEvent = {anonymous}::e5; Ts = {{anonymous}::actions_guards&, int}; TLogger = {anonymous}::my_logger]’
sml/include/boost/sml.hpp:2489:72:   required from ‘bool boost::ext::sml::v1_1_5::front::transition<boost::ext::sml::v1_1_5::front::state<T>, boost::ext::sml::v1_1_5::front::state<S2>, boost::ext::sml::v1_1_5::front::event<E>, G, A>::execute(const TEvent&, SM&, TDeps&, TSubs&, typename SM::state_t&, boost::ext::sml::v1_1_5::aux::false_type) [with TEvent = {anonymous}::e5; SM = boost::ext::sml::v1_1_5::back::sm_impl<boost::ext::sml::v1_1_5::back::sm_policy<{anonymous}::actions_guards, boost::ext::sml::v1_1_5::back::policies::logger<{anonymous}::my_logger> > >; TDeps = boost::ext::sml::v1_1_5::aux::pool<int, {anonymous}::actions_guards&, {anonymous}::my_logger&>; TSubs = boost::ext::sml::v1_1_5::aux::pool<boost::ext::sml::v1_1_5::back::sm_impl<boost::ext::sml::v1_1_5::back::sm_policy<{anonymous}::actions_guards, boost::ext::sml::v1_1_5::back::policies::logger<{anonymous}::my_logger> > > >; S1 = boost::ext::sml::v1_1_5::back::terminate_state; S2 = boost::ext::sml::v1_1_5::aux::string<char, 's', '5'>; E = {anonymous}::e5; G = boost::ext::sml::v1_1_5::aux::zero_wrapper<bool ({anonymous}::actions_guards::*)(int) const noexcept, void>; A = boost::ext::sml::v1_1_5::aux::zero_wrapper<void ({anonymous}::actions_guards::*)(int, const {anonymous}::e5&), void>; typename SM::state_t = unsigned char; boost::ext::sml::v1_1_5::aux::false_type = boost::ext::sml::v1_1_5::aux::integral_constant<bool, false>]’
sml/include/boost/sml.hpp:917:48:   required from ‘static bool boost::ext::sml::v1_1_5::back::transitions<T>::execute(const TEvent&, SM&, TDeps&, TSubs&, typename SM::state_t&) [with TEvent = {anonymous}::e5; SM = boost::ext::sml::v1_1_5::back::sm_impl<boost::ext::sml::v1_1_5::back::sm_policy<{anonymous}::actions_guards, boost::ext::sml::v1_1_5::back::policies::logger<{anonymous}::my_logger> > >; TDeps = boost::ext::sml::v1_1_5::aux::pool<int, {anonymous}::actions_guards&, {anonymous}::my_logger&>; TSubs = boost::ext::sml::v1_1_5::aux::pool<boost::ext::sml::v1_1_5::back::sm_impl<boost::ext::sml::v1_1_5::back::sm_policy<{anonymous}::actions_guards, boost::ext::sml::v1_1_5::back::policies::logger<{anonymous}::my_logger> > > >; T = boost::ext::sml::v1_1_5::front::transition<boost::ext::sml::v1_1_5::front::state<boost::ext::sml::v1_1_5::back::terminate_state>, boost::ext::sml::v1_1_5::front::state<boost::ext::sml::v1_1_5::aux::string<char, 's', '5'> >, boost::ext::sml::v1_1_5::front::event<{anonymous}::e5>, boost::ext::sml::v1_1_5::aux::zero_wrapper<bool ({anonymous}::actions_guards::*)(int) const noexcept, void>, boost::ext::sml::v1_1_5::aux::zero_wrapper<void ({anonymous}::actions_guards::*)(int, const {anonymous}::e5&), void> >; typename SM::state_t = unsigned char]’
sml/include/boost/sml.hpp:1115:39:   required from ‘static bool boost::ext::sml::v1_1_5::back::policies::jump_table::dispatch(sm_impl&, State&, const TEvent&, TDeps&, TSubs&, const boost::ext::sml::v1_1_5::aux::type_list<T7s ...>&) [with int <anonymous> = 0; TMappings = boost::ext::sml::v1_1_5::back::get_event_mapping_impl_helper<{anonymous}::e5, boost::ext::sml::v1_1_5::back::sm_impl<boost::ext::sml::v1_1_5::back::sm_policy<{anonymous}::actions_guards, boost::ext::sml::v1_1_5::back::policies::logger<{anonymous}::my_logger> > >::mappings>; sm_impl = boost::ext::sml::v1_1_5::back::sm_impl<boost::ext::sml::v1_1_5::back::sm_policy<{anonymous}::actions_guards, boost::ext::sml::v1_1_5::back::policies::logger<{anonymous}::my_logger> > >; State = unsigned char; TEvent = {anonymous}::e5; TDeps = boost::ext::sml::v1_1_5::aux::pool<int, {anonymous}::actions_guards&, {anonymous}::my_logger&>; TSubs = boost::ext::sml::v1_1_5::aux::pool<boost::ext::sml::v1_1_5::back::sm_impl<boost::ext::sml::v1_1_5::back::sm_policy<{anonymous}::actions_guards, boost::ext::sml::v1_1_5::back::policies::logger<{anonymous}::my_logger> > > >; TStates = {boost::ext::sml::v1_1_5::aux::string<char, 'i', 'd', 'l', 'e'>, boost::ext::sml::v1_1_5::aux::string<char, 's', '1'>, boost::ext::sml::v1_1_5::aux::string<char, 's', '2'>, boost::ext::sml::v1_1_5::aux::string<char, 's', '3'>, boost::ext::sml::v1_1_5::aux::string<char, 's', '4'>, boost::ext::sml::v1_1_5::aux::string<char, 's', '5'>, boost::ext::sml::v1_1_5::back::terminate_state}]’
sml/include/boost/sml.hpp:1500:55:   required from ‘bool boost::ext::sml::v1_1_5::back::sm_impl< <template-parameter-1-1> >::process_event_impl(const TEvent&, TDeps&, TSubs&, const boost::ext::sml::v1_1_5::aux::type_list<TStates ...>&, boost::ext::sml::v1_1_5::aux::index_sequence<0>) [with TMappings = boost::ext::sml::v1_1_5::back::get_event_mapping_impl_helper<{anonymous}::e5, boost::ext::sml::v1_1_5::back::sm_impl<boost::ext::sml::v1_1_5::back::sm_policy<{anonymous}::actions_guards, boost::ext::sml::v1_1_5::back::policies::logger<{anonymous}::my_logger> > >::mappings>; TEvent = {anonymous}::e5; TDeps = boost::ext::sml::v1_1_5::aux::pool<int, {anonymous}::actions_guards&, {anonymous}::my_logger&>; TSubs = boost::ext::sml::v1_1_5::aux::pool<boost::ext::sml::v1_1_5::back::sm_impl<boost::ext::sml::v1_1_5::back::sm_policy<{anonymous}::actions_guards, boost::ext::sml::v1_1_5::back::policies::logger<{anonymous}::my_logger> > > >; TStates = {boost::ext::sml::v1_1_5::aux::string<char, 'i', 'd', 'l', 'e'>, boost::ext::sml::v1_1_5::aux::string<char, 's', '1'>, boost::ext::sml::v1_1_5::aux::string<char, 's', '2'>, boost::ext::sml::v1_1_5::aux::string<char, 's', '3'>, boost::ext::sml::v1_1_5::aux::string<char, 's', '4'>, boost::ext::sml::v1_1_5::aux::string<char, 's', '5'>, boost::ext::sml::v1_1_5::back::terminate_state}; TSM = boost::ext::sml::v1_1_5::back::sm_policy<{anonymous}::actions_guards, boost::ext::sml::v1_1_5::back::policies::logger<{anonymous}::my_logger> >]’
sml/include/boost/sml.hpp:1527:41:   required from ‘bool boost::ext::sml::v1_1_5::back::sm_impl< <template-parameter-1-1> >::process_event_except_imp(const TEvent&, TDeps&, TSubs&, boost::ext::sml::v1_1_5::aux::false_type) [with TMappings = boost::ext::sml::v1_1_5::back::get_event_mapping_impl_helper<{anonymous}::e5, boost::ext::sml::v1_1_5::back::sm_impl<boost::ext::sml::v1_1_5::back::sm_policy<{anonymous}::actions_guards, boost::ext::sml::v1_1_5::back::policies::logger<{anonymous}::my_logger> > >::mappings>; TEvent = {anonymous}::e5; TDeps = boost::ext::sml::v1_1_5::aux::pool<int, {anonymous}::actions_guards&, {anonymous}::my_logger&>; TSubs = boost::ext::sml::v1_1_5::aux::pool<boost::ext::sml::v1_1_5::back::sm_impl<boost::ext::sml::v1_1_5::back::sm_policy<{anonymous}::actions_guards, boost::ext::sml::v1_1_5::back::policies::logger<{anonymous}::my_logger> > > >; TSM = boost::ext::sml::v1_1_5::back::sm_policy<{anonymous}::actions_guards, boost::ext::sml::v1_1_5::back::policies::logger<{anonymous}::my_logger> >; boost::ext::sml::v1_1_5::aux::false_type = boost::ext::sml::v1_1_5::aux::integral_constant<bool, false>]’
sml/include/boost/sml.hpp:1440:90:   required from ‘bool boost::ext::sml::v1_1_5::back::sm_impl< <template-parameter-1-1> >::process_internal_events(const TEvent&, TDeps&, TSubs&) [with TEvent = {anonymous}::e5; TDeps = boost::ext::sml::v1_1_5::aux::pool<int, {anonymous}::actions_guards&, {anonymous}::my_logger&>; TSubs = boost::ext::sml::v1_1_5::aux::pool<boost::ext::sml::v1_1_5::back::sm_impl<boost::ext::sml::v1_1_5::back::sm_policy<{anonymous}::actions_guards, boost::ext::sml::v1_1_5::back::policies::logger<{anonymous}::my_logger> > > >; typename boost::ext::sml::v1_1_5::aux::enable_if<(boost::ext::sml::v1_1_5::aux::integral_constant<bool, __is_base_of(typename boost::ext::sml::v1_1_5::back::event_type<TEvent>::generic_t, typename boost::ext::sml::v1_1_5::aux::apply<boost::ext::sml::v1_1_5::aux::inherit, typename boost::ext::sml::v1_1_5::aux::apply<boost::ext::sml::v1_1_5::aux::unique_t, typename boost::ext::sml::v1_1_5::aux::join<typename boost::ext::sml::v1_1_5::aux::apply<boost::ext::sml::v1_1_5::aux::unique_t, typename boost::ext::sml::v1_1_5::aux::apply<boost::ext::sml::v1_1_5::back::get_sub_internal_events, decltype((declval<typename TSM::sm>)().operator()())>::type>::type, typename boost::ext::sml::v1_1_5::aux::apply<boost::ext::sml::v1_1_5::back::get_all_events, decltype((declval<typename TSM::sm>)().operator()())>::type>::type>::type>::type)>::value && (! boost::ext::sml::v1_1_5::aux::integral_constant<bool, __is_base_of(typename boost::ext::sml::v1_1_5::back::event_type<TEvent>::mapped_t, typename boost::ext::sml::v1_1_5::aux::apply<boost::ext::sml::v1_1_5::aux::inherit, typename boost::ext::sml::v1_1_5::aux::apply<boost::ext::sml::v1_1_5::aux::unique_t, typename boost::ext::sml::v1_1_5::aux::join<typename boost::ext::sml::v1_1_5::aux::apply<boost::ext::sml::v1_1_5::aux::unique_t, typename boost::ext::sml::v1_1_5::aux::apply<boost::ext::sml::v1_1_5::back::get_sub_internal_events, decltype((declval<typename TSM::sm>)().operator()())>::type>::type, typename boost::ext::sml::v1_1_5::aux::apply<boost::ext::sml::v1_1_5::back::get_all_events, decltype((declval<typename TSM::sm>)().operator()())>::type>::type>::type>::type)>::value)), int>::type <anonymous> = 0; TSM = boost::ext::sml::v1_1_5::back::sm_policy<{anonymous}::actions_guards, boost::ext::sml::v1_1_5::back::policies::logger<{anonymous}::my_logger> >]’
sml/include/boost/sml.hpp:1398:43:   required from ‘bool boost::ext::sml::v1_1_5::back::sm_impl< <template-parameter-1-1> >::process_event(const TEvent&, TDeps&, TSubs&) [with TEvent = {anonymous}::e5; TDeps = boost::ext::sml::v1_1_5::aux::pool<int, {anonymous}::actions_guards&, {anonymous}::my_logger&>; TSubs = boost::ext::sml::v1_1_5::aux::pool<boost::ext::sml::v1_1_5::back::sm_impl<boost::ext::sml::v1_1_5::back::sm_policy<{anonymous}::actions_guards, boost::ext::sml::v1_1_5::back::policies::logger<{anonymous}::my_logger> > > >; TSM = boost::ext::sml::v1_1_5::back::sm_policy<{anonymous}::actions_guards, boost::ext::sml::v1_1_5::back::policies::logger<{anonymous}::my_logger> >]’
sml/include/boost/sml.hpp:1719:58:   required from ‘bool boost::ext::sml::v1_1_5::back::sm< <template-parameter-1-1> >::process_event(const TEvent&) [with TEvent = {anonymous}::e5; typename boost::ext::sml::v1_1_5::aux::enable_if<boost::ext::sml::v1_1_5::aux::integral_constant<bool, __is_base_of(TEvent, boost::ext::sml::v1_1_5::back::sm< <template-parameter-1-1> >::events_ids)>::value, int>::type <anonymous> = 0; TSM = boost::ext::sml::v1_1_5::back::sm_policy<{anonymous}::actions_guards, boost::ext::sml::v1_1_5::back::policies::logger<{anonymous}::my_logger> >]’
sml/example/actions_guards.cpp:112:19:   required from here
sml/include/boost/sml.hpp:1239:92: error: ‘const struct boost::ext::sml::v1_1_5::aux::zero_wrapper<bool ({anonymous}::actions_guards::*)(int) const noexcept, void>’ has no member named ‘get’
compilation terminated due to -Wfatal-errors.

Specifications

martinerk0 commented 1 year ago

At least it should be written in documentation that it doesn't work?

Travis-Spidertracks commented 10 months ago

Also suffering from this issue.. any tips?

ceggers-arri commented 10 months ago

Also suffering from this issue.. any tips?

I don't use member functions anymore. For guards and actions I use functors now:

struct MyFsm {
    /*
     *  guards
     */
    struct error {
        bool operator()(auto const &event)
        {
            return ...;
        }
    };

    /*
     *  actions
     */
    struct updateStatus {
        void operator()(Dependencies &d)
        {
            d.myHandler.updateStatus();
        }
    };

    auto operator()()
    {
        using namespace sml;

        return make_transition_table(
...
    }
};