boost.proto +从特定于域的表达式包装器中解压缩表达式

时间:2012-08-31 19:02:53

标签: c++ boost dsl template-meta-programming boost-proto

背景问题:boost.proto + modify expression tree in place

嗨,请考虑以下转换从value_type中提取vector_expr(请参阅前面的问题)

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 + modify expression tree in place和接受的答案中的代码)被破坏了(通常的大量模板实例化错误回溯,为了我的乐趣)

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算法不知道如何从{{1}中提取嵌套的value_type }}。一种解决方案是定义新特征并修改vector_iterator<double*>如下

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>() > > {}; 结构中找到方便定义的每个typedef或静态常量,我需要执行以上所有操作才能获得表达式编译,即使是迭代器表达式IS-NOT vector-expression,放大vector_expr的接口以适应转换树也没有意义。

问题是:有一种方法可以转换vector_expr树,将矢量节点转换为迭代器节点,同时从树本身中删除 vector-ness ,我不会遇到上述问题? 在此先感谢,最诚挚的问候!

更新 对不起,我现在改变了问题的最后一部分,我的想法更清楚了(我认为)应该实现的目标。与此同时,我试图通过自己的部分成功解决问题(?),但我觉得应该有更好的方法(所以我仍然需要帮助!)。

在我看来,问题来自于所有树节点都包含在vector_expr中,这具有将要求放在终端上的副作用(主要是用于成功编译的静态内容)。 OTOH,一旦构造了有效的vector_expr(即:服从vector_exp),那么我可以将其转换为有效的iterator_tree而无需进一步检查。

我尝试创建一个转换,将树中的所有vector_grammar个节点更改为“proto :: expr”。代码如下:

vector_expr

1 个答案:

答案 0 :(得分:3)

您遇到的问题是Proto的pass_through转换会创建与原始域名在同一域中的新表达式。这种情况发生在vector_begin_algo算法的otherwise子句中。你不想要这个,但这是pass_through给你的。您有两种策略:不要使用pass_through或欺骗pass_through来构建默认域中的表达式。

如果您使用的是最新版本的Proto(1.51),则可以使用make_exprunpacking expression代替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,从而获得相同的效果。首先,我编写一个callable来剥离特定于域的包装器:

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转换中的const怪异问题(固定的是升级干线,将在1.52,顺便说一下)。

我可以想到一个最终的解决方案,它利用了Proto表达式是他们孩子的Fusion序列的事实。您创建了一个包含表达式的Fusion transform_view,并使用vector_begin_algo转换每个子项。这会传递给proto::functional::unpack_expr,就像第一个带有make_expr的示例一样。出于同样的原因,你也需要proto::lazy

感谢您指出Proto内置pass_through转换的这一限制。有一个更好的方法可以做到这一点。