9

я здесь фирменную реализацию общей государственной машины, которая использует std::tr1::tuple как таблицу переходов:Как найти элемент в boost :: fusion :: vector во время выполнения?

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; 

Там же есть функция

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

найти следующее состояние в таблице переходов по заданному текущее состояние и событие.

Все это прекрасно работает, за исключением реализации этой платформы tuple, не поддерживающей более 10 элементов. То же самое верно для boost::tuple, поэтому вместо этого я пытаюсь использовать boost::fusion::vector. Но, похоже, fusion's find_if принимает только «унарный MPL Lambda Expression» - который, я полагаю, работает только во время компиляции.

Итак, как я могу реализовать find_next_state()?

Примечание:

Это запатентованный встраиваемых платформ, которая поставляет только GCC 4.1.2, так что мы застряли с C++ 03 + TR1.

+0

Как альтернативный ресурс, может быть, вы могли бы поместить кортежи в кортежи? – Angew

+1

Как насчет написать собственный 'find_if', который будет работать с последовательностью fusion и функцией во время выполнения? – ForEveR

+0

Почему вы используете 'tuple' в первую очередь? Есть ли у «перехода» какое-либо состояние? Если это не так, вы можете использовать 'mpl :: vector' и' mpl :: for_each', чтобы перебирать его во время выполнения. – Abyx

ответ

10

Написание собственных find_if довольно тривиально, за исключением части «вернуть найденное значение». Поскольку boost::fusion::vector является гетерогенным контейнером, для возврата не требуется ни одного правильного типа. Одно из возможных решений, которое приходит на ум принимает функцию продолжения, который вызывается с найденным значением:

#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 и long параметров, а также 0 аргумент только для устранения неоднозначности при I+1 == fusion_size<Seq>::value, поскольку обе функции будут одинаково жизнеспособны. 0, являющийся типом int, делает первую перегрузку (окончательную) предпочтительной.

+0

Это похоже на то, что я ищу, что я могу приспособить его. Повторите возвращаемое значение, которое вы, возможно, захотите посмотреть на мою фактическую проблему. ':)' Мне всегда нужно возвращать переменную enum State, поэтому это кусок торта. (В случае, если я не нахожу соответствия, есть варианты, где мне нужно бросить, и те, где мне нужно вернуть заранее определенное значение, но те, с которыми я могу справиться с использованием логического параметра и еще большей перегрузкой.) Эта перегружающая вещь кажется немного слишком умный, и я не уверен, что хочу поместить его в эту базу кода, но я посмотрю, не понадобится ли это после того, как я массировал это, чтобы выполнить мои ставки. – sbi