2012-08-25 9 views
5

Hintergrund frage: boost.proto + detect invalid terminal before building the expression tree.boost.proto + modify ausdruck baum in ort

Hallo, was ich versuche zu erreichen, ist

  1. eine Kopie eines Ausdrucksbaum erstellen, in dem alle Vektoren mit substituiert sind ihre beginnen Iteratoren (in meinem Fall ist ein Rohzeiger)
  2. inkrementieren die Iteratoren
  3. deriteration Iteratoren im Baum, aber dieser Teil sollte relativ einfach sein.

So zum 1. endete ich mit diesem Code auf

/////////////////////////////////////////////////////////////////////////////// 
// 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> > > 
     > 
{}; 

Die oben gelingt es, einen Baum zu erstellen, in dem alle Vektoren durch Zeiger ersetzt werden. So weit, ist es gut. Versuchen Sie nun, Iteratoren zu inkrementieren. Ich erkannte, dass es besser wäre, Iteratoren weiterzuentwickeln, also konnte ich mit nur einer Transformation das meiste des Verhaltens eines Direktzugriffs-Iterators erhalten (Dereferenz ist das andere fehlende Stück). Für 2. Transformation der erforderlich ist, sollte

/////////////////////////////////////////////////////////////////////////////// 
// 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> > > 
     > 
{}; 

nun in der Hauptfunktion

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; 
} 

Ich erhalte die folgende Fehlermeldung (filtert Junk) sein:

array.cpp: 361: 13: Fehler: Zuweisung von schreibgeschützten Standort

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

Was mich stört, ist die ‚((* & var)) 'Teil ... kann nicht verstehen, was zu tun ist, um dies zu beheben. Vielen Dank im Voraus, mit besten Grüßen

PS Unrelated Sache: Nach einem wenig mit Transformationen zu spielen, das allgemeine Muster ich benutze ist:

  1. Entscheiden Sie, was
  2. , um den Baum zu tun
  3. Schreiben Sie eine primitive Transformation, die die Operation ausführt
  4. Schreiben Sie eine Grammatik, die erkennt, wo die Transformation angewendet werden soll, verwenden Sie die zuvor definierte Transformation

Glaubst du, das ist vernünftig? Ich meine, es ist eine Menge Code, um nur eine elementare Operation zu einem einzigen Knoten durchzuführen. Mit Kontexten ist es möglich, mehrere Ops auf einmal zu definieren und dabei den Knotentyp zu unterscheiden. Es ist möglich, dies auch mit Transformationen zu tun? Wie ist das allgemeine Muster zu verwenden?

+0

Die Fehlermeldung bedeutet, dass 'var' (wo Sie versuchen, es um 'index' zu erhöhen) unveränderlich ist. Haben Sie versucht, einen funktionaleren Stil zu verwenden, bei dem die Transformation stattdessen den nächsten Iterator zurückgibt? –

+0

@LucDanton Versucht, wenn ich den Rückgabetyp in Iter_Advance ändern und einen geänderten Zeiger zurückgeben (ich habe überprüft, dass der Zeiger in der Transformation erhöht wird), wird der Baum nicht geändert. Ich habe die 'increment_ints' auf dem Manual von proto verfolgt, aber jetzt merke ich, dass es anders ist, in diesem Fall speichert der Baum Verweise auf int vars, jetzt habe ich die ptrs nach Wert im Baum gespeichert. Alternativen: 1. mache jedes Mal eine neue Kopie des gesamten Baums, wenn ich inkrementiere (rein funktionaler Ansatz?) B) speichere die Zeiger in einem iterator_wrapper wie im "gemischten" Beispiel des Handbuchs. – Giuliano

Antwort

4

Ihre Intuition ist korrekt; Sie sollten in der Lage sein, den Baum an Ort und Stelle zu verändern. Es scheint etwas const Seltsamkeit mit Proto pass_through Transformation, die ich untersuchen muss, so dass die Lösung ein wenig nicht offensichtlich ist. Zuerst definiere ich einige Callables, die ich in den Proto-Algorithmen verwenden werde. Ich bevorzuge Callables gegenüber primitiven Transformationen, weil sie einfacher zu ranken, wiederverwendbarer sind und zu leichter zu lesenden Proto-Algorithmen führen.

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; 
    } 
}; 

Jetzt löse ich Ihre Sprödigkeit Problem mit einem einfachen Iterator-Adapter:

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 << ")"; 
    } 
}; 

Hier wird der Algorithmus ist ein Baum enthält Vektoren in einen Baum mit Vektor Iteratoren zu drehen.

// 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> >) 
     > 
    > 
{}; 

Die letzte proto::_byval sollte nicht benötigt werden. Die pass_through Transformation, die von proto::nary_expr verwendet wird, sollte nicht konstante temporäre Knoten erstellen. Das tut mir leid.

Und hier ist der Algorithmus, um alle Iteratoren in-Place. Wenn du das vollständig erreichen kannst, wirst du wahrhaftig ein Proto-Meister sein.

// 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 
      > 
     > 
    > 
{}; 

Der Trick zu wissen, die oben zum Verständnis ist:

  1. proto::_void nichts tut und gibt void
  2. proto::and_, wenn sie als eine wie diese Transformation verwendet, führt alle angegebenen Transformationen und gibt das Ergebnis vom letzten.

Nach allem, was können Sie nun tun, was Sie sich vorgenommen hatte: Drehen Sie einen Baum Vektoren in einen Baum enthält Iteratoren enthält, und dann voran alle Iteratoren in-place:

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); 

Ich denke, dein Code hätte funktioniert, hättest du nicht in die komische Seltsamkeit geraten. Außerdem denke ich, dass Sie es leichter haben könnten, wenn Sie gewöhnliche Callables anstelle primitiver Transformationen schreiben.

Hoffe, das hilft.

+0

Hey Eric, gib mir etwas Zeit, um diese Lösung zu überprüfen (und Code zu verstehen), bevor ich deine Antwort akzeptiere (ein bisschen beschäftigt, morgendlich) ... das ist sicher richtig, keine Zweifel daran! Aber lass mich dir jetzt für deine Freundlichkeit danken, du hast viel Arbeit für mich getan. BTW Ich finde Proto süchtig ... :) (PS: Ich benutze Boost 1.50) – Giuliano

+2

FYI, die const Seltsamkeit in der 'pass_through' Transformation ist in Boost Stamm behoben. Es sollte Teil von Boost sein 1.52. Danke für die Rückmeldung; Ich bin froh, dass du Proto magst! :) –

+0

Ok, du Code funktioniert für mich, danke. Übrigens ist es auch ein schönes Beispiel für die Verwendung anderer Boost-Bibliotheken (boost.range, boost.iterator ...), um alltägliche Aufgaben zu "beschleunigen". Zwei Fragen: 1 Warum spezialisieren Sie sowohl Ergebnis und Ergebnis in callables. Ich sehe, dass es mit der Konstanz von E im Baum zusammenhängt, aber wie kann ich vorhersagen, welche Spezialisierungen in einem bestimmten Fall erforderlich sind (nein durch Versuch)? 2. Die letzte Transformation: Es gibt ein paar Dinge, die ich nicht verstehe, aber das Wichtigste ist: Warum wickelst du das innerste vector_advance_algo in die and_ transform?Danke noch einmal! – Giuliano