Kopie eines istream objects
-
Hallo Leuts,
Folgendes Problem. Ich habe hier einen Satz von Klassen die alle eine parse-Methode aufweisen. Diese methoden nehmen ein istream object als parameter entgegen. Die klassen beschreiben bestimmte token die im stream vorzufinden sein sollen. Eine der klassen "selection" enspricht einem oder. Sie Spaltet den Pfad in mehrere mögliche alternativen auf.
+--- Peter | Hallo --+ | +--- WeltZuerst wird der obere Pfad abgearbeitet. Stimmt er nicht mit den im Stream vorhandenen Zeichen überein geht der zum nächten Pfad über. Und hier liegt dann das Problem vor. Die Zeichen wurden bereits im Ersten Pfad aus dem "istream" gelesen. Ich muss also an die Anfangsposition zurückkehren.
Eine meiner Ideen ist es, eine Kopie des Streams für jeden zweig zu erstellen. Das ist aber leider nicht so einfach möglich.
Die andere auf iteratoren umzusteigen. Das führt aber zu problemen mit meinen virtuellen methoden. Dei leider keine templates sein dürfen. Ich weiß, dass es unget gibt. Dies scheint mir aber nicht gerade eine gute methode zu sein. Besonders, wenn der Pfad etwas länger war. Außerdem arbeite ich mit Ausnahmen. stimmt ein Token nicht überrein. Wird eine Ausnahme geworfen. die parse methode von "selection" fängt die Ausnahme auf. Es ist also nicht klar. Wieviel schritte zurückgegangen werden müssen. Ich müsste einen Zähler mitgeben.Wie würdet ihr vorgehen?
Martin
void selection::parse(std::istream &is) const{ int c = is.get(); const_iterator end = branches.end(); if(branches.size() > 0){ const_iterator end = --branches.end(); for(const_iterator it = branches.begin(); it != end; it++) try{ (*it)->parse(is); return; }catch(parsing_exception&){ } (*end)->parse(is); } }
-
Wie weit müsstest du denn zurückgehen? Bis zu einem gewissen Punkt kann der Stream auch per ungetc() rückwärts lesen (mindestens ein Zeichen ist vorgeschrieben, je nach Puffer-Strategie geht es auch weiter). Ansonsten bleibt noch die Möglichkeit, mit tellg() und seekg() zu arbeiten.
PS: Hast du schonmal von Boost::Spirit gehört?
-
Ist das deutlich schneller als die relevanten Teile in nen String einzulesen und dann jeweils mit istringstreams zu arbeiten?
-
Wie weit ist unbekannt. Es hängt von der zu parsenden Grammatik ab. Mit den Klassen. "atom", "selection" und "sequence" lässt sich ein Baum aufbauen. Was genau der Baum dann darstellt hängt vom Benutzer ab. Es könnte also zumindest theoretisch ne ganze menge Schritte sein.
Spirit sagt mir jetzt so nichts. Wenn's geht möchte ich boost so wie andere externe Bibliotheken vermeiden. C++0x stell aber kein Problem dar. Trotzdem werde ich's mir mal anschauen.
seekg und tellg sehen aber stark nach der Gesuchten Lösung aus. Merci.
-
Würde seekg auch mit std::cin funktionieren?
-
Da bin ich mir nicht sicher, aber ich glaube es nicht. Aber es gäbe noch einen alternativen Ansatz: Du liest den Dateiinhalt in einen String ein und jagst den Parser dann dort drüber.
(Spirit hat für sowas einen Multipass-Iterator definiert, um mehrfach über die selbe Eingabesequenz laufen zu können, aber ich bin mir auch nicht sicher, wie dieser implementiert war - ist schon eine Weile her)
-
@Martin Kalbfuß:
Vermutlich wäre es sinnvoll das Interface der Parse() Methode anzupassen.
z.B. einfach so:bool Parse(char const*& inout_begin, char const* end);Ein Zeiger ist leicht kopierbar, und leicht wiederherstellbar.
Wenn die Parse-Methoden wirklich mit
std::istreamarbeiten wollen, dann können sie das intern gerne machen. Dazu könnte man einen eigenen Stream-Buffer + Stream implementieren.
Diese Streams könnte man dann mit dem Zeigerpaar initialisieren, und wenn etwas erfolgreich geparsed werden konnte den aktuellen "Read-Pointer" auslesen, damit man deninout_beginParameter entsprechend anpassen kann.Die Verwendung so eines Streams bleibt dabei streng optional, jede Parse Implementierung kann sich aussuchen ob sie den Helfer-Stream verwenden möchte, oder den Input auf andere Art und Weise parsen.