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.
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);
}
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.