助推.原型和复杂变换

Boost.Proto and Complex Transform

本文关键字:变换 复杂 原型 助推      更新时间:2023-10-16

我正在试验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_ofresult_of::make_expr使用"参数"的基本原理是什么。文档、示例和内部代码似乎混合了Exprimpl::exprimpl::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_typeoperator ()的结果相匹配,并同时适用于按值和按引用保存的terminal

更新:代码在Eric回答后更新。result_typemake_expr之间唯一剩下的不匹配是unroll_vector_expr_c第一个实例化,其中terminal< mpl::size_t< 0 > >::type常量引用而不是由持有。奇怪的是,具有更高索引的同一模板的后续实例化并没有导致这个问题。

更新:在修改distribue_subscript转换以强制按值获取下标索引后,我设法使代码完全工作:

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

现在是一个快速答案。明天我会更仔细地观察你的转变。

impl::exprimpl::expr_param的含义可以通过查看transform_impl的文档来理解。简而言之,在转换的operator()的签名中使用impl::expr_param,因为它添加了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的用户文档。