2016-09-27 6 views
1

Betrachten Sie einen Java-ähnlichen Streaming Iterator:Einfacher Stream-Iterator in C++

template<class V> 
struct Iterator 
{ 
    V& next(); 
    bool hasNext(); 
} 

template<class V> 
struct Iterable 
{ 
    Iterator<V> iterator(); 
} 

Wie würden Sie diese in eine std :: iterator gegossen, so dass Sie es in Entdeckung, for-Schleifen verwenden können usw.

ich glaube, ich mit einem InputIterator beginnen müssen, aber ich mit den folgenden Dingen bin kämpfen hier:

  • sinnvoll end()?
  • Wann sind sie gleich()?
  • was ist mit Entfernung?
+3

Sie werden dies nicht tun können. Ein C++ - Iterator und ein Java-Iterator sind unterschiedliche Konzepte. – NathanOliver

+3

Versuchen Sie nicht, Java-Code in C++ zu schreiben. Java-Code hat genug Probleme, wenn er in Java geschrieben ist. –

+1

Wenn man fragt, wie man einen Java-Iterator zum Erzeugen eines C++ - Iterators "umsetzt", fragt man sich, wie viel Eis man braucht, um ein Feuer zu starten. –

Antwort

1

Es ist durchaus machbar, aber ein Schmerz.

Sie wären besser dran, einen generatorbasierten Iterator zu erstellen, bei dem die Backing-Operation eine std::function< optional<T> > ist. Hier ist ein Beispiel für einen solchen Generator-Iterator:

template<class T> 
struct generator_iterator { 
    using difference_type=std::ptrdiff_t; 
    using value_type=T; 
    using pointer=T*; 
    using reference=T; 
    using iterator_category=std::input_iterator_tag; 
    std::optional<T> state; 
    std::function< std::optional<T>() > operation; 
    // we store the current element in "state" if we have one: 
    T operator*() const { 
    return *state; 
    } 
    // to advance, we invoke our operation. If it returns a nullopt 
    // we have reached the end: 
    generator_iterator& operator++() { 
    state = operation(); 
    return *this;   
    } 
    generator_iterator operator++(int) { 
    auto r = *this; 
    ++(*this); 
    return r; 
    } 
    // generator iterators are only equal if they are both in the "end" state: 
    friend bool operator==(generator_iterator const& lhs, generator_iterator const& rhs) { 
    if (!lhs.state && !rhs.state) return true; 
    return false; 
    } 
    friend bool operator!=(generator_iterator const& lhs, generator_iterator const& rhs) { 
    return !(lhs==rhs); 
    } 
    // We implicitly construct from a std::function with the right signature: 
    generator_iterator(std::function< std::optional<T>() > f):operation(std::move(f)) 
    { 
    if (operation) 
     state = operation(); 
    } 
    // default all special member functions: 
    generator_iterator(generator_iterator &&) =default; 
    generator_iterator(generator_iterator const&) =default; 
    generator_iterator& operator=(generator_iterator &&) =default; 
    generator_iterator& operator=(generator_iterator const&) =default; 
    generator_iterator() =default; 
}; 

tun so mit Ihrem Java-like iterface noch getan werden kann.

Es gibt Lösungen in boost, die dies erleichtern. Aber ich werde es "in rohen" schreiben, basierend auf C++ 17 Konzepten, die nach C++ 11 zurückportiert werden können, wenn Sie sie benötigen (oder aus Boost oder anderen Quellen extrahiert werden).

Sie würden einen Eingabe-Iterator generieren, da dies die Java-ähnliche Schnittstelle unterstützt.

Zuerst würde ich einen Helfer schreiben. Der Helfer würde eine optional< Iterator<V> > und eine optional<V> halten.

Es würde ++ unterstützen. ++ würde den Iterator weiterleiten und den Wert in optional<V> lesen.

Es würde unäre * unterstützen. * würde den Wert im optionalen zurückgeben.

bool is_end() gibt True zurück, wenn optional<Iterator<V>> leer ist, oder !.hasNext().

== gibt true wenn und nur wenn beide Argumente .is_end(). != wäre einfach ! auf == angewendet.

Dieser Helper ist noch kein Iterator, hat aber die meisten Schlüsseloperationen.

Dann verwenden wir this poly_iterator welcher Typ löscht alles Iterator-like. Die Operationen, die wir oben auf dem Helfer geschrieben haben, sind ausreichend.

Dann schreiben wir eine Funktion, die eine Iterable<V> und produziert eine range<poly_iterator<T>> des Typs Löschklasse oben, mit dem Helper Pseudo-Iterator oben. Ein range<It> ist eine Klasse, die wie folgt aussieht:

template<class It> 
struct range { 
    It b; It e; 
    It begin() const { return b; } 
    It end() const { return e; } 
}; 

und vielleicht andere Dienstprogramm Helfer.

Der Typ löschen Schritt könnte übersprungen werden, wenn Sie wählen. Erweitern Sie einfach den oben beschriebenen Helfer zu einem vollwertigen Eingabe-Iterator. Boost hat Helfer, die das leichter machen. Lassen Sie den Bereichsrückkehrer diesen vollwertigen Eingabe-Iterator zurückgeben. Ich hatte zufällig die poly_iterator<T> in einer anderen Antwort herumliegen, und wenn Sie einen Hammer haben, sieht dieses Problem sicher wie ein Nagel aus.

Beachten Sie, dass der Code wirklich (Java) -ähnlich sein soll, wenn wir tatsächlich (intelligente) Zeiger auf die Objekte haben wollen, die nicht kopiert werden.

+0

Ich begann bereits und ging genau diesen Weg. – user3612643

+0

Okay, ich bin gerade über den Operator == gestolpert. Sie sagen also, dass zwei Iteratoren nur dann gleich sind, wenn beide am Ende sind. Das macht Sinn, da es sonst schwer ist, hier Gleichheit zu definieren, weil das Iterable zwischen zwei Aufrufen mutieren könnte. – user3612643

+0

@ user3612643 Ja, zwei Iteratoren sind nur dann gleich, wenn sie beide am Ende sind. Dies ist ein Modell der Eingabeiteration. Und der End-Iterator ist entweder ein Iterator, der kein 'hasNext' hat oder ein Sentinal ist. Beim Konstruieren des Helpers aus Ihrem Java-Iterator wird zunächst 'hasNext' abgefragt, andernfalls ist es ein End-Iterator, andernfalls wird das' next' Element sofort gespeichert. '++' prüft 'hasNext', wenn nicht, beendet es sich selbst, andernfalls speichert es' next'. Du könntest den Aufruf von "next" wahrscheinlich verschieben, bis er tatsächlich gelesen wurde, aber ich denke, das macht es etwas schwieriger zu schreiben, also werde ich es bei der Konstruktion und bei '++' machen. – Yakk