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

Я экспериментирую с 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 >
    >
{};

1 ответ

Решение

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

Значение 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,

Другие вопросы по тегам