背景问题:boost.proto+修改中的表达式树
嗨,考虑以下转换,从vector_expr
中提取value_type
(参见前面的问题)
template <class T> struct value_type_trait;
template <std::size_t D, class T>
struct value_type_trait<vector<D, T> >
{
typedef typename vector<D, T>::value_type type;
};
struct deduce_value_type
: proto::or_<
proto::when <vector_terminal, value_type_trait<proto::_value>() >
, proto::when <scalar_terminal, proto::_value>
, proto::otherwise <
proto::_default<deduce_value_type>()
>
>
{};
上述代码可用于向表达式树提供"maximal"value_type,该表达式树是应用常用的C++提升规则和Boost.TypeOf魔术获得的。以上内容如下
template <class Expr>
struct vector_expr : proto::extends <Expr, vector_expr <Expr>, vector_domain>
{
typedef proto::extends <Expr, vector_expr <Expr>, vector_domain> base_type;
// OK! now my expression has a 'value_type'
typedef typename boost::result_of<deduce_value_type(Expr)>::type value_type;
vector_expr (Expr const &e) : base_type (e) {}
};
但现在,以下代码(检查上一个问题:boost.proto+修改表达式树,并在接受的答案中修改代码)被破坏了(为了我的高兴,通常会有巨大的模板实例化错误回溯)
int main ()
{
double data[] = {1, 2, 3};
vector<3, double> a(data, data+3), b(data,data+3), c(data,data+3);
auto iter = vector_begin_algo()(a + b);
return 0;
}
原因很简单。typename boost::result_of<vector_begin_algo(a+b)>::type
的类型为:
vector_expr<
basic_expr<
tag::plus
, list2< expr<tag::terminal, term<vector_iterator<double*> >, 0l>
, expr<tag::terminal, term<vector_iterator<double*> >, 0l>
>
,
2l>
>
因此,外部vector_expr<...>
触发了对嵌套value_type
的评估,但deduce_value_type
算法不知道如何从vector_iterator<double*>
中提取嵌套value_type
。一种解决方案是定义一个新的特征并将deduce_value_type
修改为如下
// A further trait
template <class Iter>
struct value_type_trait<vector_iterator<Iter> >
{
typedef typename std::iterator_traits<Iter>::value_type type;
};
// Algorithm to deduce the value type of an expression.
struct deduce_value_type
: proto::or_<
proto::when <vector_terminal, value_type_trait<proto::_value>() >
, proto::when <scalar_terminal, proto::_value>
, proto::when <proto::terminal<vector_iterator<proto::_> > , value_type_trait<proto::_value>()> // <- need this now
, proto::otherwise <
proto::_default<deduce_value_type>()
>
>
{};
这种方法有几个问题,但最重要的是:对于我在vector_expr
结构中发现方便定义的每个typedef或静态常量,我只需要执行以上所有操作就可以编译表达式,即使是迭代器表达式is-NOT-向量表达式,并且扩大vector_expr的接口以容纳转换后的树是没有意义的。
问题是:有没有一种方法可以转换vector_expr
树,将向量节点转换为迭代器节点,同时从树本身删除向量ness,这样我就不会出现上述问题?提前感谢,致以最良好的问候!
更新对不起,我改了问题的最后一部分,因为我对应该实现什么(我认为)更加清楚了。与此同时,我试图自己解决这个问题,但取得了部分成功(?),但我觉得应该有更好的方法(所以我仍然需要帮助!)。
在我看来,问题来自于将所有树节点都封装在vector_expr
中,这会产生对终端提出要求的副作用(主要是成功编译的静态内容)。OTOH,一旦构造了有效的vector_exp
(即:服从vector_grammar
),那么我就可以将其转换为有效的迭代器树,而无需进一步检查。
我尝试创建一个转换,将树中的所有vector_expr
节点改回"proto::expr"。代码如下:
template <class Expr, long Arity = Expr::proto_arity_c>
struct deep_copy_unwrap_impl;
template <class Expr>
struct deep_copy_unwrap_impl <Expr,0>
{
typedef typename proto::tag_of <Expr>::type Tag;
typedef typename proto::result_of::value<Expr>::type A0;
typedef typename proto::result_of::make_expr<Tag, proto::default_domain, A0>::type result_type;
template<typename Expr2, typename S, typename D>
result_type operator()(Expr2 const &e, S const &, D const &) const
{
return proto::make_expr <Tag, proto::default_domain> (e.proto_base().child0);
}
};
template <class Expr>
struct deep_copy_unwrap_impl <Expr,1>
{
typedef typename proto::tag_of <Expr>::type Tag;
typedef typename proto::result_of::child_c<Expr, 0>::type A0;
typedef typename proto::result_of::make_expr<Tag, proto::default_domain, A0>::type result_type;
template<typename Expr2, typename S, typename D>
result_type operator()(Expr2 const &e, S const &, D const &) const
{
return proto::make_expr <Tag, proto::default_domain> (e.proto_base().child0);
}
};
template <class Expr>
struct deep_copy_unwrap_impl <Expr,2>
{
typedef typename proto::tag_of <Expr>::type Tag;
typedef typename proto::result_of::child_c<Expr, 0>::type A0;
typedef typename proto::result_of::child_c<Expr, 1>::type A1;
typedef typename proto::result_of::make_expr<Tag, proto::default_domain, A0, A1>::type result_type;
template<typename Expr2, typename S, typename D>
result_type operator()(Expr2 const &e, S const &, D const &) const
{
return proto::make_expr <Tag, proto::default_domain> (e.proto_base().child0, e.proto_base().child1);
}
};
struct unwrap : proto::callable
{
template <class Sig> struct result;
template <class This, class Expr>
struct result <This(Expr)>
{
typedef typename
deep_copy_unwrap_impl <Expr>
::result_type type;
};
template <class This, class Expr>
struct result <This(Expr&)>
: result<This(Expr)> {};
template <class This, class Expr>
struct result <This(Expr const&)>
: result<This(Expr)> {};
template <class Expr>
typename result <unwrap(Expr)>::type
operator () (Expr const &e) const
{
return deep_copy_unwrap_impl<Expr>()(e, 0, 0);
}
};
struct retarget
: proto::otherwise <
unwrap(proto::nary_expr<proto::_, proto::vararg<retarget> >)
>
{};
int main ()
{
int data[] = {1, 2, 3};
vector<3, int> a(data, data+3), b(data,data+3), c(data,data+3);
auto x=a+b+c; // <- x is an expression tree made up of vector_expr<...> nodes
auto y=retarget()(x); // <- y is an expression tree made up of proto::expr<...> nodes
return 0;
}
您遇到的问题是Proto的pass_through
转换会创建与原始表达式在同一域中的新表达式。这种情况发生在vector_begin_algo
算法的otherwise
子句中。你不想要这个,但这就是pass_through
给你的。有两种策略:不要使用pass_through
,或者欺骗pass_through
在默认域中构建表达式。
如果您使用的是最新版本的Proto(1.51),则可以使用make_expr
和拆包表达式,而不是pass_through
:
// 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::lazy<
proto::functional::make_expr<proto::tag_of<_>()>(
vector_begin_algo(proto::pack(_))...
)
>
>
>
{};
这里需要proto::lazy
,因为在调用make_expr
函数对象之前,首先需要构建它。
如果您使用的是Proto的旧版本,则可以通过先从表达式中删除特定于域的包装器来欺骗pass_through
,从而获得相同的效果。首先,我编写了一个可调用的剥离域特定包装器:
struct get_base_expr
: proto::callable
{
template<typename Expr>
struct result;
template<typename This, typename Expr>
struct result<This(Expr)>
{
typedef
typename boost::remove_reference<Expr>::type::proto_base_expr
type;
};
template<typename Expr>
typename Expr::proto_base_expr operator()(Expr const &expr) const
{
return expr.proto_base();
}
};
然后,vector_begin_algo
将更改如下:
// 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::pass_through<
proto::nary_expr<_, proto::vararg<vector_begin_algo> >
>(get_base_expr(_)))
>
>
{};
这也不是一件艺术品,但它完成了任务。不要忘记proto::_byval
来处理pass_through
变换中的常量怪异(它是固定的boost trunk,将在1.52中,顺便说一句)。
我可以想出一个最终的解决方案,利用Proto表达是其子代的融合序列这一事实。您可以创建一个Fusion transform_view
,它包装表达式并使用vector_begin_algo
变换每个子级。它被传递给proto::functional::unpack_expr
,与make_expr
的第一个示例非常相似。出于同样的原因,你也需要proto::lazy
。
感谢您指出Proto内置pass_through
变换的限制。如果能有更好的方法来做这件事,那就太好了。