2017-02-24 1 views
1

Ich schreibe eine einfache verallgemeinerte Parser-Kombinator-Bibliothek. Dies bedeutet, dass die Bibliothek viele kleine Funktionsobjekte, die so genannte Parser enthält, die einen String als Eingabe (wenn genannt) nehmen und eine Liste der ParseResults als Ausgabe zurück, wo ein parseResult istC++: Arbeiten mit mehreren Kopien von Std :: Cin?

template <typename A> using ParseResult = std::pair<A, std::string> Die Liste ist leer, wenn der Parser tut stimmt nicht überein, enthält ein einzelnes Ergebnis, wenn es übereinstimmt, und bestimmte Parser, die mehrere (mehrdeutige) Übereinstimmungen aufweisen, geben möglicherweise mehr Ergebnisse zurück.

Dies bedeutet jedoch, dass gerade jetzt eine ganze Reihe von String-Kopieren läuft. Zu Beginn muss der schließlich konstruierte Parser mit einer Zeichenkette aufgerufen werden, so dass alle std::cin (oder der Coompete-Inhalt einer Datei) in eine Zeichenkette kopiert werden.

Was scheint eine bessere Idee zu sein (da die Parser immer nur die ersten (wenigen) Zeichen an der aktuellen Vorderseite der Zeichenkette betrachten), ist es wichtig zu wissen, wo Sie sich gerade in der Standardeingabe befinden Strom. Und ich glaube das ist genau das, was ein std::istream ist. Ireams sind jedoch nicht kopierbar. Wie kann mein Problem gelöst werden? Gibt es eine Möglichkeit, eine Kopie eines iStreams zurückzugeben, der auf ein paar Zeichen hinweist, wo das Original liegt? Oder gibt es einen anderen, saubereren Weg, dieses Problem zu lösen?

+3

Wissen Sie, welche Referenzen in C++ sind? – PiotrNycz

Antwort

1

Die Frage kann so umformuliert werden: Wie stellen Sie den ungeparsten Teil der Eingabe in einer Weise dar, die übermäßiges Kopieren vermeidet und Eingabe-Streaming ermöglicht?

Der flexibelste Weg ist, es mit einem Iterator darzustellen. Wenn Parser Backtracking ausführen, müsste es ein ForwardIterator sein, wenn nicht, InputIterator ist ausreichend. Dies bedeutet, dass Sie dann std::istream_iterator über std::cin oder std::ifstream s direkt verwenden oder von in-memory std::strings oder char Arrays analysieren können. Das Streaming mit Backtracking ist ein wenig komplizierter und würde erfordern, dass Sie einen Puffer-Iterator-Adapter schreiben, der InputIterator wie std::istream_iterator in einen ForwardIterator konvertiert oder einen Iterator schreibt, der direkt std::ifstream und tut .seekg(), wenn Sie zurückverfolgen müssen.

Eine andere Option ist die Verwendung von C++ 17's std::string_view, die nicht kopiert und hat eine schöne, Parsing-freundliche Schnittstelle. Dies löst das Streaming jedoch nicht, Sie müssten immer zuerst die gesamte Datei lesen.

+0

Vielen Dank für diese großartige Antwort! Weitere Untersuchungen haben ergeben, dass '.seekg()' für stdin nicht richtig funktioniert, was bedeutet, dass das Lesen von allem in den Speicher (und dann entweder mit einem 'std :: istringstream' oder einem' std :: string_view') sehr gut sein könnte Weg zu gehen. – Qqwy