背景问题: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>()
            >
    >
{};

上面的代码可用于为表达式树提供“最大”值类型,这是通过应用通常的 C++ boost 规则和 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 算法不知道如何从 value_type 中提取嵌套的 vector_iterator<double*> 。一种解决方案是定义一个新的traits并修改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 或静态常量,我将需要执行上述所有操作才能编译表达式,即使是迭代器 - expression IS-NOT vector-expression 并且扩大 vector_expr 的接口(interface)以适应变换的树是没有意义的。

问题是:有一种方法可以转换 vector_expr 树,将 vector 节点转换为迭代器节点,同时从树本身中删除 vector ,这样我就不会遇到上述问题?
提前致谢,最好的问候!

更新
抱歉,我更改了问题的最后一部分,因为我对(我认为)应该实现的目标更加清楚。与此同时,我试图自己解决这个问题,并取得了部分成功(?),但我觉得应该有更好的方法(所以我仍然需要帮助!)。

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

我尝试创建一个转换,将树中的所有 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_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 来获得相同的效果。首先,我编写了一个可调用来剥离特定于域的包装器:
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 奇怪问题(这是固定的,是 boost 主干,将在 1.52 中,顺便说一句)。

我可以想到一个最终解决方案,它利用 Proto 表达式是其子项的 Fusion 序列这一事实。您创建一个 Fusion transform_view 来包装表达式并使用 vector_begin_algo 转换每个子项。这被传递给 proto::functional::unpack_expr ,就像第一个例子中的 make_expr 一样。出于同样的原因,您在那里也需要 proto::lazy

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

关于c++ - boost.proto + 从特定于域的表达式包装器中解开表达式,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/12221193/

10-12 17:50