boost.proto + modify expression tree in place

牧云@^-^@ 提交于 2019-12-04 10:04:41

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.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!