如何在运行时查找boost::fusion::vector中的元素?

9

我这里有一个专有实现的通用状态机,它使用std::tr1::tuple作为转移表:

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的find_if只接受“一元的MPL Lambda表达式”,我想这只能在编译时工作。

所以,鉴于以上情况,我该如何实现find_next_state()

注意:

这是一个专有的嵌入式平台,只提供GCC 4.1.2,因此我们只能使用C++03+TR1。


作为一种替代方案,也许你可以将元组打包成元组? - Angew is no longer proud of SO
1
你可以自己编写一个 find_if 函数,使其能够在运行时与融合序列和函数一起使用,这个怎么样? - ForEveR
你为什么首选使用 tupletransition 有任何状态吗?如果没有,你可以使用 mpl::vectormpl::for_each 在运行时迭代它。 - Abyx
@Angew:看一下transition_table的typedef。如果我这样做,用户需要跳过哪些障碍? - sbi
根据我的理解,Boost.Fusion 是在您遇到“运行时问题”时使用的工具,而 find_next_state() 函数是只有在运行时才知道值的。今天早上我研究了一下,我猜想最好使用 Fusion。当然,我可能错了,这也是我来这里询问的原因。 - sbi
1个回答

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

现场示例。

I+1 == fusion_size<Seq>::value 时,intlong 参数以及 0 参数只是为了消除歧义,因为两个函数同样可行。由于 0int 类型,所以第一个重载(最后一个)是首选。


那似乎已经足够接近我所寻找的,我可以进行适当调整。关于返回值,你可能需要查看我的实际问题 :) 我总是需要返回一个 State 枚举变量,所以这很容易解决。(如果我没有找到匹配项,则有一些变体需要我抛出异常,还有一些需要返回预定义的值,但我可以使用布尔参数和更多重载来处理这些情况。)那个重载似乎有点过于聪明了,我不确定是否要将其放入此代码库中,但在我修改它以符合我的要求之后,我会看看是否需要它。 - sbi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接