boost.proto +修改表达式树到位 [英] boost.proto + modify expression tree in place

查看:195
本文介绍了boost.proto +修改表达式树到位的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

背景问题: boost.proto +在构建表达式树之前检测无效终端



您好,我想实现的是


  1. 创建一个表达式树的副本,其中all向量用
    替换它们的开始迭代器(在我的例子中是一个原始指针)

  2. 将迭代器增加到



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

      /////////////////////////////// //////////////////////////////////////////////// b $ b //将树中的所有向量节点转换为迭代器节点的变换
    struct vector_begin:proto :: transform< vector_begin>
    {
    template< typename Expr,typename Unused1,typename Unused2>
    struct impl:boost :: proto :: transform_impl< Expr,Unused1,Unused2>
    {
    //必须剥离引用限定符(&)
    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>
    //标量想要通过值存储(proto存储它们通过const&),如果没有代码不编译...
    ,proto ::当< scalar_terminal,boost :: proto :: _ make_terminal(boost :: proto :: _ byval(boost :: proto :: _ value))>
    //下降树转换向量到begin()迭代器
    ,proto ::当< proto :: nary_expr< _,proto :: vararg< vector_grammar_begin> > >
    >
    {};

    上面成功创建了一个树,其中所有的向量都被指针替换。到现在为止还挺好。现在,尝试增加
    迭代器。我意识到这将是更好的提前迭代器,所以只需一个变换,我可以得到大多数
    行为的随机访问迭代器(解引用是另一个缺失的部分)。对于2.,所需的转换应为

      ///////////////// ////////////////////////////////////////////////// //////////// 
    //改进树中所有迭代器的变换
    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 //我使用状态传递数据:(
    ,typename impl :: data_param)const
    {
    proto :: value(var)+ = index; //没有好...编译错误这里:(
    }
    };
    };

    //好,这是脆弱的,如果我决定改变向量< D,T>的迭代器类型?
    struct iter_terminal
    : :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))'part ...无法理解如何解决这个问题。
    提前感谢,最好的问候



    PS
    无关的事:在使用transforms后,我使用的是:


    1. 决定要对树做些什么

    2. 执行操作

    3. 编写一个语法,识别应该应用变换的位置,使用先前定义的变换

    你认为这是合理吗?我的意思是,它是一个很多的代码执行只是一个基本的操作对单个
    种节点。使用上下文,可以同时定义多个ops,区分节点类型。
    也可以用transforms做到这一点吗?

    解决方案

    你的直觉是正确的;你应该能够在现场改变树。似乎有一些常见的古怪与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>()
    {}

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

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

    这里是将包含向量的树转换为包含向量迭代器的树的算法。

      //将所有向量终端转换成向量迭代器终端
    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 :: _ byval 需要。 proto :: nary_expr 使用的 pass_through 转换不应创建const临时节点。对不起,



    这里是算法,以推进所有的迭代器到位。

      //通过推进所有的向量迭代器来就地变换在状态参数中的金额
    //
    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 ::和_ 当用作这样的变换时,执行所有指定的变换并返回最后一个的结果。

    毕竟,你现在可以做你所做的:将包含向量的树转换为包含迭代器的树,然后将所有迭代器in-place:

      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怪异。如果你写普通的callables,而不是原始的转换,我想你可能会有一个更容易的时间。



    希望这有助于。


    Background question: boost.proto + detect invalid terminal before building the expression tree.

    Hi, what i'm trying to achieve is

    1. create a copy of an expression tree, where all vectors are substituted with their begin iterators (in my case is a raw pointer)
    2. increment the iterators in place
    3. dereference iterators in the tree, but that part should be relatively easy.

    So, for 1. I ended up with this code

    ///////////////////////////////////////////////////////////////////////////////
    // 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> > >
            >
    {};
    

    The above succeeds to create a tree where all vectors are replaced by pointers. So far, so good. Now, try to increment iterators. I realized that is would be better to advance iterators, so with just one transform, i could get most of the behavior of a random access iterator (dereference is the other missing piece). For 2., the required transform should be

    ///////////////////////////////////////////////////////////////////////////////
    // 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> > >
            >
    {};
    

    Now, in the main function

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

    I get the following error message (filtered out the junk):

    array.cpp:361:13: error: assignment of read-only location

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

    What bothers me is the '((* & var))' part... cannot understand what to do to fix this. Thanks in advance, best regards

    PS Unrelated thing: after playing a little with transforms, the general pattern i'm using is:

    1. Decide what to do to the tree
    2. Write a primitive transform that performs the operation
    3. Write a grammar that recognizes where the transform should be applied, use the previously defined transform

    Do you think this is reasonable? I mean, it is a lot of code to perform just an elementary op to a single kind of node. With contexts, it is possible to define several ops at once, discriminating on the node type. It is possible to do this with transforms also ? What is the general pattern to be used?

    解决方案

    Your intuition is correct; you should be able to mutate the tree in-place. There seems to be some const weirdness with Proto's pass_through transform that I need to investigate, so the solution is a little non-obvious. First, I define some callables that I will use in the Proto algorithms. I prefer callables to primitive transforms because they are simpler to grok, more reusable, and result in easier-to-read Proto algorithms.

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

    Now, I solve your brittleness problem with a simple iterator adaptor:

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

    Here's the algorithm to turn a tree containing vectors into a tree containing vector iterators.

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

    The last proto::_byval shouldn't be needed. The pass_through transform used by proto::nary_expr shouldn't be creating const temporary nodes. Sorry about that.

    And here is the algorithm to advance all the iterators in-place. When you can fully grok this, you will truly be a Proto master.

    // 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
                >
            >
        >
    {};
    

    The trick to understanding the above is knowing:

    1. proto::_void does nothing and returns void
    2. proto::and_, when used as a transform like this, executes all the specified transforms and returns the result of the last.

    After all that, you can now do what you had set out to do: Turn a tree containing vectors into a tree containing iterators, and then advance all the iterators in-place:

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

    I think your code would have worked had you not run into the const weirdness. Also, I think you might have an easier time of it if you write ordinary callables instead of primitive transforms.

    Hope this helps.

    这篇关于boost.proto +修改表达式树到位的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆