boost.proto +修改表达式树到位

Giu*_*ano 5 c++ boost template-meta-programming boost-proto

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

嗨,我想要实现的是

  1. 创建一个表达式树的副本,其中所有向量都用它们的开始迭代器代替(在我的例子中是一个原始指针)
  2. 将迭代器增加到位
  3. 在树中取消引用迭代器,但该部分应该相对容易.

所以,对于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> > >
        >
{};
Run Code Online (Sandbox Code Playgroud)

以上成功创建了一个树,其中所有向量都被指针替换.到现在为止还挺好.现在,尝试增加迭代器.我意识到推进迭代器会更好,所以只需一次转换,我就可以获得随机访问迭代器的大部分行为(取消引用是另一个缺失的部分).对于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> > >
        >
{};
Run Code Online (Sandbox Code Playgroud)

现在,在主要功能

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;
 }
Run Code Online (Sandbox Code Playgroud)

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

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

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

困扰我的是'((*&var))'部分...无法理解如何解决这个问题.在此先感谢,最好的问候

PS 无关的事情:在玩转换后,我使用的一般模式是:

  1. 决定如何对树做什么
  2. 写一个执行操作的原始变换
  3. 编写一个识别应该应用变换的语法,使用先前定义的变换

你认为这是合理的吗?我的意思是,对于单一节点执行一个基本操作是很多代码.使用上下文,可以同时定义多个操作,区分节点类型.也可以通过变换来实现这一点吗?使用的一般模式是什么?

Eri*_*ler 4

你的直觉是正确的;你应该能够就地改变树。Proto 的转换似乎有一些 const 奇怪之处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;
    }
};
Run Code Online (Sandbox Code Playgroud)

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

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 << " )";
    }
};
Run Code Online (Sandbox Code Playgroud)

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

// 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> >)
        >
    >
{};
Run Code Online (Sandbox Code Playgroud)

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

这是就地推进所有迭代器的算法。当你能完全领会这一点时,你将成为真正的 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
            >
        >
    >
{};
Run Code Online (Sandbox Code Playgroud)

理解上述内容的技巧是知道:

  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);
Run Code Online (Sandbox Code Playgroud)

我认为如果你没有遇到 const 怪异的话你的代码会起作用。另外,我认为如果您编写普通的可调用对象而不是原始转换,您可能会更轻松。

希望这可以帮助。

  • 仅供参考,“pass_through”变换中的常量怪异已在 boost trunk 中修复。它应该是 boost 1.52 的一部分。感谢您的反馈; 我很高兴你喜欢原型!:) (2认同)
  • 1. 我希望我的可调用对象能够与 boost::result_of 一起使用,无论它是如何调用的。这些都有效:“boost::result_of&lt;begin(T)&gt;”、“boost::result_of&lt;begin(T &amp;)&gt;”和“boost::result_of&lt;begin(T const &amp;)&gt;”。我很彻底。2. 如果您尝试单独使用“vector_advance_algo”作为“proto::fold”的第三个模板参数,您会发现它不起作用。为什么?第三个参数应该是一个转换,对值和状态执行某些操作并返回新状态。但是“vector_advance_algo”返回“void”。我使用“and_”返回新状态(与旧状态相同)。 (2认同)