Boost.Proto и комплексное преобразование

Я экспериментирую с Прото построить DSEL который работает на геометрических векторах. Я пытаюсь написать преобразование, которое бы взяло выражение для присваивания и развернуло его по компонентам. Например, я хочу заменить

p = q + r;

от

p[0] = q[0] + r[0],
p[1] = q[1] + r[1],
...,
p[N] = q[N] + r[N],
p;

До сих пор я был в состоянии в основном заставить его работать, сделав шаблон преобразования unroll_vector_expr который рекурсивно разворачивает выражение для каждого компонента вектора, в сочетании с distribute_subscript преобразования.

Кажется, я не понимаю, в чем смысл «параметров», используемых с boost::result_of а также result_of::make_expr, Документация, примеры и внутренний код кажутся смешанными Expr, impl::expr а также impl::expr_param, Я не уверен, что я должен использовать и когда так, чтобы result_type соответствует фактическому типу результата. В настоящее время я заставил это работать пробной ошибкой, исследуя сообщения об ошибках и исправляя const а также & расхождения. Однако, как только я глубоко скопировал выражение, терминалы перестали удерживаться ссылкой, и мой код дал сбой.

struct distribute_subscript
: or_<
scalar_grammar
, when<
vector_literal
, _make_subscript( _, _state )
>
, plus< distribute_subscript, distribute_subscript >
, minus< distribute_subscript, distribute_subscript >
, multiplies< distribute_subscript, distribute_subscript >
, divides< distribute_subscript, distribute_subscript >
, assign< distribute_subscript, distribute_subscript >
>
{};

template< std::size_t I, std::size_t N >
struct unroll_vector_expr_c;

template< std::size_t I, std::size_t N >
struct unroll_vector_expr_c
: transform< unroll_vector_expr_c< I, N > >
{
template< typename Expr, typename State, typename Data >
struct impl
: transform_impl< Expr, State, Data >
{
typedef
typename result_of::make_expr<
tag::comma
, typename boost::result_of<
distribute_subscript(
Expr
, typename result_of::make_expr<
tag::terminal
, boost::mpl::size_t< I - 1 >
>::type
)
>::type
, typename boost::result_of<
unroll_vector_expr_c< I + 1, N >(
Expr
)
>::type
>::type
result_type;

result_type operator ()(
typename impl::expr_param expr
, typename impl::state_param state
, typename impl::data_param data
) const
{
return
make_expr< tag::comma >(
distribute_subscript()(
expr
, make_expr< tag::terminal >(
boost::mpl::size_t< I - 1 >()
)
)
, unroll_vector_expr_c< I + 1, N >() (
expr
)
);
}
};
};

template< std::size_t N >
struct unroll_vector_expr_c< N, N >
: transform< unroll_vector_expr_c< N, N > >
{
template< typename Expr, typename State, typename Data >
struct impl
: transform_impl< Expr, State, Data >
{
typedef
typename boost::result_of<
distribute_subscript(
Expr
, typename result_of::make_expr<
tag::terminal
, boost::mpl::size_t< N - 1 >
>::type
)
>::type
result_type;

result_type operator ()(
typename impl::expr_param expr
, typename impl::state_param state
, typename impl::data_param data
) const
{
return
distribute_subscript()(
expr
, make_expr< tag::terminal >(
boost::mpl::size_t< N - 1 >()
)
);
}
};
};

struct unroll_vector_expr
: transform< unroll_vector_expr >
{
template< typename Expr, typename State, typename Data >
struct impl
: transform_impl< Expr, State, Data >
{
typedef
typename dimension<
typename boost::remove_reference<
typename boost::result_of<
_value( State )
>::type
>::type
>::type
dimension;

typedef
typename result_of::make_expr<
tag::comma
, typename boost::result_of<
unroll_vector_expr_c< 1, dimension::value >(
Expr
)
>::type
, State
>::type
result_type;

result_type operator ()(
typename impl::expr_param expr
, typename impl::state_param state
, typename impl::data_param data
) const
{
return
make_expr< tag::comma >(
unroll_vector_expr_c< 1, dimension::value >()(
expr
)
, boost::ref( state )
);
}
};
};

Как мне написать свое преобразование, чтобы result_type соответствует результат из operator ()и работает на terminalОба держат по значению и по ссылке?

Обновить: Код обновлен после Эрикответ. Единственное оставшееся несоответствие между result_type а также make_expr для первый создание unroll_vector_expr_c, где terminal< mpl::size_t< 0 > >::type проводится постоянная ссылка вместо значение. Любопытно, что последующие экземпляры одного и того же шаблона с более высокими индексами не приводят к этой проблеме.

Обновить: Мне удалось заставить код полностью работать после изменения distribue_subscript transform, чтобы заставить взять индекс индекса по значению:

struct distribute_subscript
: or_<
scalar_grammar
, when<
vector_literal
, _make_subscript( _, _byval( _state ) ) // <-- HERE
>
, plus< distribute_subscript, distribute_subscript >
, minus< distribute_subscript, distribute_subscript >
, multiplies< distribute_subscript, distribute_subscript >
, divides< distribute_subscript, distribute_subscript >
, assign< distribute_subscript, distribute_subscript >
>
{};

2

Решение

Быстрый ответ на данный момент. Я постараюсь более внимательно посмотреть на ваше преобразование завтра.

Значение impl::expr а также impl::expr_param можно понять, посмотрев на документацию для transform_impl. Короче говоря, используйте impl::expr_param в подписи вашего преобразования operator(), поскольку он добавляет const & чтобы выражения не копировались при передаче вашей функции. использование Expr в ваших вычислениях типа, как в boost::result_of, impl::expr не очень полезен и может быть проигнорирован.

Что касается deep_copy всегда заставляя ваши терминалы удерживаться на значении, это почти то, что deep_copy для. Но, может быть, вы имеете в виду что-то еще, так как я не вижу deep_copy в вашем коде где угодно.

Слово о make_expr: заставляет задуматься очень осторожно о том, какие узлы должны храниться по ссылке, а какие по значению. В вычислении типа (result_of::make_expr), ссылочные типы — это то, что должно храниться по ссылке, но при фактическом вызове функции (proto::make_expr), вы должны обернуть вещи с boost::ref если хотите то держите по ссылке. Это странно Проверьте пользовательские документы на make_expr.

3

Другие решения

Других решений пока нет …