2012-08-25 60 views
5

背景问题:boost.proto + detect invalid terminal before building the expression treeboost.proto +修改表达式树

嗨,我想要做到的,是

  1. 创建一个表达式树,所有的向量取代有 他们开始迭代器的副本(在我的情况是一个原始指针)
  2. 增加迭代器到树中,但该部分应该相对容易。

所以,对于1.我结束了这段代码

/////////////////////////////////////////////////////////////////////////////// 
// A transform that converts all vectors nodes in a tree to iterator nodes 
struct vector_begin : proto::transform <vector_begin> 
{ 
    template<typename Expr, typename Unused1, typename Unused2> 
    struct impl : boost::proto::transform_impl<Expr, Unused1, Unused2> 
    { 
     // must strip away the reference qualifier (&) 
     typedef typename proto::result_of::value< 
       typename boost::remove_reference<Expr>::type 
      >::type vector_type; 

     typedef typename proto::result_of::as_expr 
      <typename vector_type::const_iterator>::type result_type; 

     result_type operator()(
       typename impl::expr_param var 
      , typename impl::state_param 
      , typename impl::data_param) const 
     { 
      typename vector_type::const_iterator iter(proto::value(var).begin()); 
      return proto::as_expr(iter); // store iterator by value 
     } 
    }; 
}; 

struct vector_grammar_begin 
     : proto::or_ < 
      proto::when <vector_terminal, vector_begin> 
      // scalars want to be stored by value (proto stores them by const &), if not the code does not compile... 
      , proto::when <scalar_terminal, boost::proto::_make_terminal(boost::proto::_byval(boost::proto::_value))> 
      // descend the tree converting vectors to begin() iterators 
      , proto::when <proto::nary_expr<_, proto::vararg<vector_grammar_begin> > > 
     > 
{}; 

以上成功创建所有矢量由指针取代了树。到现在为止还挺好。现在,尝试增加 迭代器。我意识到提升迭代器会更好,所以只需一次转换,我就可以获得随机访问迭代器的大部分行为(取消引用是另一个缺失的部分)。对于2,所需的转换应该是

/////////////////////////////////////////////////////////////////////////////// 
// A transform that advances all iterators in a tree 
struct iter_advance : proto::transform <iter_advance> 
{ 
    template<typename Expr, typename Index, typename Dummy> 
    struct impl : boost::proto::transform_impl<Expr, Index, Dummy> 
    { 
     typedef void result_type; 
     result_type operator()(
       typename impl::expr_param var 
      , typename impl::state_param index // i'm using state to pass a data :(
      , typename impl::data_param) const 
     { 
      proto::value(var)+=index; // No good... compile error here :(
     } 
    }; 
}; 

// Ok, this is brittle, what if I decide the change vector<D,T>'s iterator type ? 
struct iter_terminal 
     : proto::and_< 
       proto::terminal<_> 
      , proto::if_<boost::is_pointer<proto::_value>()> 
      > 
{}; 


struct vector_grammar_advance 
     : proto::or_ < 
      proto::when <iter_terminal, iter_advance> 
      , proto::terminal<_> 
      , proto::when <proto::nary_expr<_, proto::vararg<vector_grammar_advance> > > 
     > 
{}; 

现在,在主要功能

template <class Expr> 
void check_advance (Expr const &e) 
{ 
    proto::display_expr (e); 

    typedef typename boost::result_of<vector_grammar_begin(Expr)>::type iterator_type; 
    iterator_type iter = vector_grammar_begin()(e); 
    proto::display_expr (iter); 

    vector_grammar_advance()(iter,1); 
    proto::display_expr (iter); 
} 

int main (int, char**) 
{ 
    vec<3, double> a(1), b(2), c(3); 
    check_advance(2*a+b/c); 
    return 0; 
} 

我收到以下错误消息(过滤掉垃圾):

array.cpp: 361:13:错误:只读位置的分配

'boost::proto::value<boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, 
boost::proto::argsns_::term<const double*>, 0l> >((* & var))' 

让我困扰的是“((* & var))'部分...不知道该怎么做才能解决这个问题。 在此先感谢,最好的问候

PS 无关的事:打一点与转换之后,我使用的一般模式是:

  1. 决定做什么树
  2. 编写一个执行操作的基本转换
  3. 编写一个语法,用于识别应该应用转换的位置,使用之前定义的转换

你认为这是否合理?我的意思是,执行一个简单的节点类型的代码很多。通过上下文,可以一次定义多个操作,区分节点类型。 也可以用变换来做到这一点?什么是一般的使用模式?

+0

错误消息意味着'var'(你试图通过'index'增加它)是不可变的。您是否尝试过使用更实用的样式,其中的变换会返回下一个迭代器? –

+0

@LucDanton尝试了,如果我在iter_advance中更改返回类型并返回一个修改过的指针(我已验证指针在变换中增加了),树不会更改。我正在关注proto的manual上的'increment_ints',但是现在我意识到它不同,在这种情况下,树存储对int变量的引用,现在我已经将ptrs按值存储在树中。替代方案:1.每增加一次,我就增加整个树的新副本(纯功能方法?)b)将指针存储在iterator_wrapper中,就像手册的“混合”示例一样。 – Giuliano

回答

4

你的直觉是正确的;你应该能够就地改变树。我需要调查Proto的pass_through变换似乎有一些常见的怪异现象,所以解决方案有点不明显。首先,我定义了一些我将在Proto算法中使用的可调参数。我更喜欢可调用的元素来进行原始转换,因为它们更简单,更易重用,并且导致易于阅读的Proto算法。

struct begin 
    : proto::callable 
{ 
    template<typename Sig> 
    struct result; 

    template<typename This, typename Rng> 
    struct result<This(Rng)> 
     : boost::range_iterator<Rng> 
    {}; 

    template<typename This, typename Rng> 
    struct result<This(Rng &)> 
     : boost::range_iterator<Rng> 
    {}; 

    template<typename Rng> 
    typename boost::range_iterator<Rng>::type 
    operator()(Rng &rng) const 
    { 
     return boost::begin(rng); 
    } 

    template<typename Rng> 
    typename boost::range_iterator<Rng const>::type 
    operator()(Rng const &rng) const 
    { 
     return boost::begin(rng); 
    } 
}; 

struct advance 
    : proto::callable 
{ 
    typedef void result_type; 

    template<typename Iter> 
    void operator()(Iter &it, unsigned d) const 
    { 
     it += d; 
    } 
}; 

现在,我解决您的脆性问题,一个简单的迭代器适配器:

template<typename Iter> 
struct vector_iterator 
    : boost::iterator_adaptor<vector_iterator<Iter>, Iter> 
{ 
    vector_iterator() 
     : boost::iterator_adaptor<vector_iterator<Iter>, Iter>() 
    {} 

    explicit vector_iterator(Iter iter) 
     : boost::iterator_adaptor<vector_iterator<Iter>, Iter>(iter) 
    {} 

    friend std::ostream &operator<<(std::ostream &sout, vector_iterator it) 
    { 
     return sout << "vector_iterator(value: " << *it << ")"; 
    } 
}; 

这里是把含有载体导入包含载体迭代器树一树的算法。

// Turn all vector terminals into vector iterator terminals 
struct vector_begin_algo 
    : proto::or_< 
     proto::when< 
      proto::terminal<std::vector<_, _> > 
      , proto::_make_terminal(
       vector_iterator<begin(proto::_value)>(begin(proto::_value)) 
      ) 
     > 
     , proto::when< 
      proto::terminal<_> 
      , proto::_make_terminal(proto::_byval(proto::_value)) 
     > 
     , proto::otherwise< 
      proto::_byval(proto::nary_expr<_, proto::vararg<vector_begin_algo> >) 
     > 
    > 
{}; 

应该不需要最后的proto::_byvalproto::nary_expr使用的pass_through转换不应该创建const临时节点。对于那个很抱歉。

这里是推进所有迭代器的算法。当你能够充分理解这一点时,你将成为一名原始大师。

// Mutate in-place by advancing all vector iterators the amount 
// in the state parameter 
struct vector_advance_algo 
    : proto::or_< 
     proto::when< 
      proto::terminal<vector_iterator<_> > 
      , advance(proto::_value, proto::_state) 
     > 
     , proto::when< 
      proto::terminal<_> 
      , proto::_void 
     > 
     , proto::otherwise< 
      proto::and_< 
       proto::fold< 
        _ 
        , proto::_state 
        , proto::and_< 
         vector_advance_algo 
         , proto::_state 
        > 
       > 
       , proto::_void 
      > 
     > 
    > 
{}; 

诀窍理解上面的是知道:

  1. proto::_void不执行任何操作,并返回void
  2. proto::and_,作为用于变换这样的情况下,执行所有的指定的变换,并返回结果最后一个。

毕竟,你现在可以做您设置了什么做出来的:将含载体到含有迭代器一树一树,然后推进所有的迭代器就地:

proto::literal<std::vector<int> > vec1; 
proto::value(vec1).assign(
    boost::make_counting_iterator(0) 
    , boost::make_counting_iterator(16) 
); 

auto beg = vector_begin_algo()(2 * vec1 + vec1); 
proto::display_expr(beg); 

vector_advance_algo()(beg, 1u); 
proto::display_expr(beg); 

vector_advance_algo()(beg, 1u); 
proto::display_expr(beg); 

我认为如果你没有碰到const奇怪的问题,你的代码就可以工作。另外,如果您编写普通的可调参数而不是原始转换,我认为您可能会有更轻松的时间。

希望这会有所帮助。

+0

嗨,Eric,在我接受你的答案之前,给我一些时间来检查这个解决方案(并且理解你的代码)(这有点忙不过来了)......这是正确的,毫无疑问!但是,现在让我感谢你的好意,你为我做了很多工作。顺便说一句,我发现原子上瘾...... :)(PS:我使用boost 1.50) – Giuliano

+2

仅供参考,'pass_through'变换中的常量奇怪在boost主干中是固定的。它应该是1.52的一部分。感谢您的反馈;我很高兴你喜欢proto! :) –

+0

好的,你的代码适合我,谢谢。顺便说一句,这也是使用其他boost库(boost.range,boost.iterator ...)“动态地”来提升日常任务的一个很好的例子。两个问题:1为什么你将专门化的结果和结果放在可调参数中。我认为它与树中E的常量有关,但是如何预测给定情况下需要哪些特化(不按尝试)? 2.最终变换:有几件事我不明白,但最重要的是:为什么要将最内层的vector_advance_algo包装到and_变换中?再次感谢! – Giuliano