5

背景の質問: boost.proto + 式ツリーを構築する前に無効な端末を検出します

こんにちは、私が達成しようとしているのは

  1. 式ツリーのコピーを作成します。ここで、すべてのベクトルが開始反復子に置き換えられます (私の場合は生のポインターです)。
  2. イテレータをその場でインクリメントする
  3. ツリーのイテレータを逆参照しますが、その部分は比較的簡単なはずです。

だから、1.私はこのコードで終わった

///////////////////////////////////////////////////////////////////////////////
// A transform that converts all vectors nodes in a tree to iterator nodes
struct vector_begin : proto::transform <vector_begin>
{
    template<typename Expr, typename Unused1, typename Unused2>
    struct impl : boost::proto::transform_impl<Expr, Unused1, Unused2>
    {
        // must strip away the reference qualifier (&)
        typedef typename proto::result_of::value<
                typename boost::remove_reference<Expr>::type
            >::type vector_type;

        typedef typename proto::result_of::as_expr
            <typename vector_type::const_iterator>::type result_type;

        result_type operator ()(
              typename impl::expr_param var
            , typename impl::state_param
            , typename impl::data_param) const
        {
            typename vector_type::const_iterator iter(proto::value(var).begin());
            return proto::as_expr(iter); // store iterator by value
        }
    };
};

struct vector_grammar_begin
        : proto::or_ <
            proto::when <vector_terminal, vector_begin>
            // scalars want to be stored by value (proto stores them by const &), if not the code does not compile... 
          , proto::when <scalar_terminal, boost::proto::_make_terminal(boost::proto::_byval(boost::proto::_value))>
            // descend the tree converting vectors to begin() iterators
          , proto::when <proto::nary_expr<_, proto::vararg<vector_grammar_begin> > >
        >
{};

上記は、すべてのベクトルがポインターに置き換えられたツリーの作成に成功しました。ここまでは順調ですね。ここで、反復子をインクリメントしてみます。イテレータを進めるほうがよいことに気付いたので、1 つの変換だけで、ランダム アクセス イテレータのほとんどの動作を得ることができました (逆参照は、もう 1 つの欠落している部分です)。2. の場合、必要な変換は

///////////////////////////////////////////////////////////////////////////////
// A transform that advances all iterators in a tree
struct iter_advance : proto::transform <iter_advance>
{
    template<typename Expr, typename Index, typename Dummy>
    struct impl : boost::proto::transform_impl<Expr, Index, Dummy>
    {
        typedef void result_type;
        result_type operator ()(
              typename impl::expr_param var
            , typename impl::state_param index // i'm using state to pass a data :(
            , typename impl::data_param) const
        {
            proto::value(var)+=index; // No good... compile error here :(
        }
    };
};

// Ok, this is brittle, what if I decide the change vector<D,T>'s iterator type ?
struct iter_terminal
        :   proto::and_<
                proto::terminal<_>
             ,  proto::if_<boost::is_pointer<proto::_value>()> 
            >
{};


struct vector_grammar_advance
        : proto::or_ <
            proto::when <iter_terminal, iter_advance>
          , proto::terminal<_>
          , proto::when <proto::nary_expr<_, proto::vararg<vector_grammar_advance> > >
        >
{};

さて、メイン関数で

template <class Expr>
void check_advance (Expr const &e)
{
    proto::display_expr (e);

    typedef typename boost::result_of<vector_grammar_begin(Expr)>::type iterator_type;
    iterator_type iter = vector_grammar_begin()(e);
    proto::display_expr (iter);

    vector_grammar_advance ()(iter,1);
    proto::display_expr (iter);
 }

 int main (int, char**)
 {
    vec<3, double> a(1), b(2), c(3);
    check_advance(2*a+b/c);
    return 0;
 }

次のエラー メッセージが表示されます (ジャンクは除外されます)。

array.cpp:361:13: エラー: 読み取り専用の場所の割り当て

'boost::proto::value<boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal,
 boost::proto::argsns_::term<const double*>, 0l> >((* & var))'

気になるのは '((* & var))' の部分です...これを修正するために何をすべきか理解できません。よろしくお願いします。

PS 無関係なこと:変換で少し遊んだ後、私が使用している一般的なパターンは次のとおりです。

  1. 木をどうするか決める
  2. 操作を実行するプリミティブ変換を作成する
  3. 変換が適用されるべき場所を認識する文法を書き、以前に定義された変換を使用します

これは合理的だと思いますか?つまり、単一の種類のノードに対して基本的な操作を実行するだけでも、大量のコードになります。コンテキストを使用すると、ノード タイプを区別して、一度に複数の op を定義できます。変換でもこれを行うことは可能ですか? 使用される一般的なパターンは何ですか?

4

1 に答える 1

4

あなたの直感は正しいです。ツリーをインプレースで変更できるはずです。Protoの変換には、調査する必要のある一定の奇妙な点があるようpass_throughです。そのため、解決策は少しわかりにくいです。まず、Protoアルゴリズムで使用するいくつかの呼び出し可能オブジェクトを定義します。プリミティブ変換よりも呼び出し可能オブジェクトの方が好きです。呼び出し可能オブジェクトは、簡単に作成でき、再利用可能であり、Protoアルゴリズムが読みやすくなるためです。

struct begin
  : proto::callable
{
    template<typename Sig>
    struct result;

    template<typename This, typename Rng>
    struct result<This(Rng)>
      : boost::range_iterator<Rng>
    {};

    template<typename This, typename Rng>
    struct result<This(Rng &)>
      : boost::range_iterator<Rng>
    {};

    template<typename Rng>
    typename boost::range_iterator<Rng>::type
    operator()(Rng &rng) const
    {
        return boost::begin(rng);
    }

    template<typename Rng>
    typename boost::range_iterator<Rng const>::type 
    operator()(Rng const &rng) const
    {
        return boost::begin(rng);
    }
};

struct advance
  : proto::callable
{
    typedef void result_type;

    template<typename Iter>
    void operator()(Iter &it, unsigned d) const
    {
        it += d;
    }
};

ここで、単純なイテレータアダプタを使用して脆弱性の問題を解決します。

template<typename Iter>
struct vector_iterator
  : boost::iterator_adaptor<vector_iterator<Iter>, Iter>
{
    vector_iterator()
      : boost::iterator_adaptor<vector_iterator<Iter>, Iter>()
    {}

    explicit vector_iterator(Iter iter)
      : boost::iterator_adaptor<vector_iterator<Iter>, Iter>(iter)
    {}

    friend std::ostream &operator<<(std::ostream &sout, vector_iterator it)
    {
        return sout << "vector_iterator(value: " << *it << " )";
    }
};

ベクトルを含むツリーをベクトルイテレータを含むツリーに変換するアルゴリズムは次のとおりです。

// 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::nary_expr<_, proto::vararg<vector_begin_algo> >)
        >
    >
{};

最後proto::_byvalは必要ありません。によって使用されるpass_through変換は、proto::nary_exprconst一時ノードを作成するべきではありません。申し訳ありません。

そして、これがすべてのイテレータをインプレースで進めるためのアルゴリズムです。あなたがこれを完全に理解することができるとき、あなたは本当にプロトマスターになるでしょう。

// Mutate in-place by advancing all vector iterators the amount
// in the state parameter
struct vector_advance_algo
  : proto::or_<
        proto::when<
            proto::terminal<vector_iterator<_> >
          , advance(proto::_value, proto::_state)
        >
      , proto::when<
            proto::terminal<_>
          , proto::_void
        >
      , proto::otherwise<
            proto::and_<
                proto::fold<
                    _
                  , proto::_state
                  , proto::and_<
                        vector_advance_algo
                      , proto::_state
                    >
                >
              , proto::_void
            >
        >
    >
{};

上記を理解する秘訣は、次のことを知っていることです。

  1. proto::_void何もせずに戻るvoid
  2. proto::and_、このような変換として使用すると、指定されたすべての変換を実行し、最後の結果を返します。

これで、設定したことを実行できます。ベクトルを含むツリーをイテレータを含むツリーに変換し、すべてのイテレータをインプレースで進めます。

proto::literal<std::vector<int> > vec1;
proto::value(vec1).assign(
    boost::make_counting_iterator(0)
  , boost::make_counting_iterator(16)
);

auto beg = vector_begin_algo()(2 * vec1 + vec1);
proto::display_expr(beg);

vector_advance_algo()(beg, 1u);
proto::display_expr(beg);

vector_advance_algo()(beg, 1u);
proto::display_expr(beg);

constの奇妙さに遭遇しなければ、コードは機能していたと思います。また、プリミティブ変換の代わりに通常の呼び出し可能オブジェクトを作成すると、より簡単に処理できるようになると思います。

お役に立てれば。

于 2012-08-26T20:38:03.823 に答える