背景の質問: 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_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 または 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;
}