2

I'm experimenting with Proto to build a DSEL that operates on geometric vectors. I'm trying to write a transform that would take an assign expression and unroll it component wise. For instance, I want to replace

p = q + r;

by

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

So far I have been able to mostly make it work by making a transform template unroll_vector_expr that recursively unrolls the expression for each vector component, in conjunction with a distribute_subscript transform.

I don't seem to understand whats the rationale for the 'parameters' used with boost::result_of and result_of::make_expr. The documentation, examples and internal code seem to mix Expr, impl::expr and impl::expr_param. I'm not sure what should I be using and when so that the result_type matches the actual result type. Currently I made it work by trial an error, by examining the error messages and fixing the const and & discrepancies. However, as soon as I deep copy the expression the terminals are no longer hold by reference and my code fails.

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 )
                );
        }
    };
};

How should I write my transform so that the result_type matches the result from operator (), and works for terminals both hold by value and by reference?

Update: Code updated after Eric's answer. The only remaining mismatch between result_type and make_expr is for the first instantiation of unroll_vector_expr_c, where the terminal< mpl::size_t< 0 > >::type is held by const reference instead of by value. Curiously enough, subsequent instantiations of the same template with higher indices do not result in this issue.

Update: I managed to get the code to fully work, after modifying the distribue_subscript transform to force to take the subscript index by value:

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 >
    >
{};
4

1 回答 1

3

现在快速回答。明天我会试着更仔细地看看你的变换。

impl::expr和的含义impl::expr_param可以通过查看文档来理解transform_impl。简而言之,impl::expr_param在你的 transform 的签名中使用operator(),因为它添加了 aconst &以确保表达式在传递给你的函数时不会被复制。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.

于 2012-10-31T05:27:17.963 回答