背景问题:boost.proto + detect invalid terminal before building the expression tree

嗨,我想达到的目标是

  • 创建一个表达式树的拷贝,其中所有 vector 都替换为
    他们的开始迭代器(在我的情况下是原始指针)
  • 增加迭代器的位置
  • 在树中取消引用迭代器,但是该部分应该相对容易。

  • 因此,对于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> > >
            >
    {};
    

    上面成功创建了一个树,其中所有 vector 都被指针替换。到现在为止还挺好。现在,尝试增加
    迭代器。我意识到推进迭代器会更好,因此只需进行一次转换,我就可以得到大部分
    随机访问迭代器的行为(取消引用是另一个缺失的部分)。对于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
    不相关的事情:在进行了一些转换之后,我使用的一般模式是:
  • 决定对树进行操作
  • 编写执行
  • 操作的原始转换
  • 编写一个语法,该语法可识别应在何处应用转换,请使用先前定义的转换

  • 您认为这合理吗?我的意思是,对单个代码执行基本操作需要很多代码
    一种节点。使用上下文,可以一次定义多个操作,以区分节点类型。
    是否可以通过转换来做到这一点?一般使用什么模式?

    最佳答案

    您的直觉是正确的;您应该能够就地变异树。我需要研究Proto的pass_through转换似乎有一些const怪异之处,因此解决方案有点不太明显。首先,我定义一些将在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 << " )";
        }
    };
    

    这是将包含 vector 的树转换为包含 vector 迭代器的树的算法。
    // 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::_byvalpass_through使用的proto::nary_expr转换不应创建const临时节点。对于那个很抱歉。

    这是就地推进所有迭代器的算法。当您完全掌握了这一点时,您将真正成为Proto大师。
    // 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
                >
            >
        >
    {};
    

    了解以上内容的技巧是:
  • proto::_void不执行任何操作并返回void
  • proto::and_用作这样的转换时,将执行所有指定的转换并返回最后一个的结果。

  • 毕竟,您现在可以按照自己的意愿去做:将包含 vector 的树变成包含迭代器的树,然后就地推进所有迭代器:
    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怪异的话,您的代码就会奏效。另外,我认为如果您编写普通的可调用对象而不是原始转换,则可能会更轻松一些。

    希望这可以帮助。

    关于c++ - boost.proto +修改表达式树,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/12122191/

    10-11 11:24