3

背景の質問: boost.proto + 式ツリーを適切に変更する

value_typeこんにちは、 a からを抽出する次の変換を検討して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.TypeOf マジックを適用して取得される式ツリーに「最大」value_type を提供できます。上記は次のように使用されます

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が、アルゴリズムはネストされたfromdeduce_value_typeを抽出する方法を知りません。1 つの解決策は、新しい特性を定義し、次のように変更することです。value_typevector_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 または static 定数ごとにvector_expr、式をコンパイルするためだけに上記のすべてを実行する必要があることです。 IS-NOT vector-expression であり、変換されたツリーに対応するために vector_expr のインターフェイスを拡大しても意味がありません。

問題は、vector_exprツリーを変換し、ベクトル ノードをイテレータ ノードに変換すると同時に、ツリー自体からベクトル性を削除して、上記の問題が発生しないようにする方法があるということです。よろしくお願いします。

更新 申し訳ありませんが、何を達成すべきか(私が思うに)がより明確になったので、質問の最後の部分を変更しました。とりあえず自力で解決しようとして部分的に成功(?)したのですが、もっといい方法があるはず(だからまだ助けが必要です!)。

問題は、すべてのツリーノードがラップされている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;
}
4

1 に答える 1

3

あなたが直面している問題は、Proto のpass_through変換が元の式と同じドメインにある新しい式を作成することです。これは、句vector_begin_algoのアルゴリズムで発生します。otherwiseあなたはこれを望んでいませんが、それpass_throughはあなたに与えるものです. 2 つの戦略があります: を使用しないか、デフォルト ドメインで式を作成するように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::lazymake_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トランスフォームの const の奇妙さを回避することを忘れないでくださいpass_through(ブースト トランクで修正され、1.52 で修正されます)。

Proto 式が子の Fusion シーケンスであるという事実を利用する 1 つの最終的な解決策を考えることができます。transform_view式をラップし、各子を で変換する Fusion を作成しvector_begin_algoます。proto::functional::unpack_exprこれは、最初の例と同様に に渡されmake_exprます。proto::lazy同じ理由で、そこにも必要です。

Proto の組み込み変換に関するこの制限を指摘していただきありがとうございpass_throughます。これを行うためのより良い方法があるとよいでしょう。

于 2012-09-01T20:31:27.237 に答える