boost.proto + изменить дерево выражений на месте
Фоновый вопрос: boost.proto + обнаруживает недействительный терминал перед построением дерева выражений.
Привет, чего я пытаюсь добиться, так это
- создать копию дерева выражений, где все векторы заменены их начальными итераторами (в моем случае это необработанный указатель)
- увеличить итераторы на месте
- Итераторы разыменования в дереве, но эта часть должна быть относительно простой.
Итак, для 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 ответ
Ваша интуиция верна; Вы должны быть в состоянии изменить дерево на месте. Кажется, что-то странное с Прото pass_through
преобразовать, что мне нужно исследовать, поэтому решение немного неочевидно. Во-первых, я определяю некоторые вызываемые объекты, которые я буду использовать в алгоритмах 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::_byval
не должно быть нужно pass_through
преобразование используется proto::nary_expr
не должен создавать постоянные временные узлы. Извини за это.
И вот алгоритм для продвижения всех итераторов на месте. Когда вы сможете полностью справиться с этим, вы действительно станете Прото-мастером.
// 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_
при использовании в качестве такого преобразования выполняет все указанные преобразования и возвращает результат последнего.
После всего этого вы теперь можете делать то, что намеревались сделать: превратить дерево, содержащее векторы, в дерево, содержащее итераторы, а затем продвинуть все итераторы на месте:
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);
Я думаю, что ваш код сработал бы, если бы вы не столкнулись со странностью констант. Кроме того, я думаю, вам будет легче, если вы будете писать обычные функции, а не примитивные преобразования.
Надеюсь это поможет.