9

I have here a proprietary implementation of a generic state machine that uses a std::tr1::tuple as a transition table:

template<State StartState, Event TriggerEvent, State TargetState>
struct transition {...};

typedef std::tr1::tuple< transition< ready      , run      , running     >
                       , transition< running    , terminate, terminating >
                       , transition< terminating, finish   , terminated  >
                       > transition_table;

There's a function

template<typename Transitions>
State find_next_state( State current
                     , Event event
                     , const Transitions& transition_table );

to find the next state in the transition table given a current state and an event.

This all works fine except for this platform's tuple implementation not supporting more than 10 items. The same seems to be true for boost::tuple, so I am trying to employ boost::fusion::vector instead. But it seems fusion's find_if only takes "a unary MPL Lambda Expression" — which, I suppose, only works at compile time.

So given the above, how can I implement find_next_state()?

Note:

This a proprietary embedded platform that supplies only GCC 4.1.2, so we're stuck with C++03+TR1.

Xeo
  • 129,499
  • 52
  • 291
  • 397
sbi
  • 219,715
  • 46
  • 258
  • 445
  • As an alternative workarond, maybe you could pack tuples into tuples? – Angew is no longer proud of SO Nov 22 '13 at 11:43
  • 1
    How about write your own `find_if`, that will work with fusion-sequence and function at runtime? – ForEveR Nov 22 '13 at 11:43
  • Why do you use `tuple` in the first place? Does `transition` have any state? If it doesn't you can use the `mpl::vector` and `mpl::for_each` to iterate over it in run-time. – Abyx Nov 22 '13 at 12:21
  • @Angew: Look at the typedef for `transition_table`. What hoops would I require users to jump through if I did that? – sbi Nov 22 '13 at 12:23
  • @Abyx: From what I understood, Boost.Fusion is what you use when you have "runtime issues", and `find_next_state()` is called with values only known at runtime. When I looked into it this morning, I guessed I might therefore be better off using Fusion. Of course, I could be wrong, but that's exactly why I came here asking. – sbi Nov 22 '13 at 12:54

1 Answers1

10

Writing your own find_if is rather trivial, except for the "return the found value" part. Since a boost::fusion::vector is a heterogenous container, there is no single right type to return. One possible solution that comes to mind is accepting a continuation function that is invoked with the found value:

#include <boost/fusion/include/size.hpp>
#include <boost/fusion/include/at_c.hpp>

// private implementation details
namespace detail{
// shorthand ...
template<class S>
struct fusion_size{
  static const unsigned value =
    boost::fusion::result_of::size<S>::type::value;
};

// classic compile-time counter
template<unsigned> struct uint_{};

template<class Seq, class Pred, class F>
void find_if(Seq&, Pred const&, F, uint_<fusion_size<Seq>::value>, int)
{ /* reached the end, do nothing */ }

template<class Seq, class Pred, class F, unsigned I>
void find_if(Seq& s, Pred const& pred, F f, uint_<I>, long){
    if(pred(boost::fusion::at_c<I>(s)))
    {
        f(boost::fusion::at_c<I>(s));
        return; // bail as soon as we find it
    }
    find_if(s, pred, f, uint_<I+1>(), 0);
}
} // detail::

template<class Seq, class Pred, class F>
void find_if(Seq& s, Pred const& pred, F f){
    detail::find_if(s, pred, f, detail::uint_<0>(), 0);
}

Live example.

The int and long parameters, as well as the 0 argument are just for disambiguation when I+1 == fusion_size<Seq>::value, as both functions would be equally viable. 0 being of type int makes the first overload (the final one) preferred.

Xeo
  • 129,499
  • 52
  • 291
  • 397
  • That seems to be close enough to what I am looking for that I can adapt it. Re the return value you might want to look at my actual problem. `:)` I always need to return a `State` enum variable, so this is a piece of cake. (In case I don't find a matching there's variants where I need to throw and those where I need to return a pre-defined value, but those I can deal with using a boolean parameter and even more overloading.) That overloading thing seems a bit overly clever and I am not sure I want to put it into this code base, but I'll see if I need it after I massaged this to do my bidding. – sbi Nov 22 '13 at 13:00