2017-02-24 8 views
1

J'écris une librairie de combinateur d'analyseur généralisée simple. Cela signifie que la bibliothèque contient de nombreux petits objets de fonction, appelée parseurs, qui (lorsqu'il est appelé) prennent une chaîne en entrée et renvoie une liste de ParseResults en sortie, où un ParseResult estC++: travailler avec plusieurs copies de std :: cin?

template <typename A> using ParseResult = std::pair<A, std::string> La liste est vide si l'analyseur a ne correspond pas, contient un seul résultat s'il correspond, et certains analyseurs qui peuvent correspondre de plusieurs façons (ambiguës) peuvent renvoyer plus de résultats. Cependant, cela signifie que tout un tas de copie de chaîne est actuellement en cours. En outre, au début, l'analyseur finalement construit doit être appelé avec une chaîne, ainsi tous les std::cin (ou le contenu coompété d'un fichier) sont copiés dans une chaîne. Ce qui semble être une meilleure idée (puisque les parseurs ne regardent que le ou les premiers caractères de l'avant de la chaîne), est de garder une trace de l'endroit où vous êtes en ce moment dans l'entrée standard courant. Et je crois que c'est exactement ce qu'est un std::istream. Cependant, les istreams ne sont pas copiables. Comment mon problème peut-il être résolu? Existe-t-il un moyen de renvoyer une copie d'un istream qui pointe vers quelques caractères vers où pointe l'original? Ou y a-t-il une autre façon plus propre de résoudre ce problème?

+3

Savez-vous quelles références sont en C++? – PiotrNycz

Répondre

1

La question peut être reformulée ainsi: Comment représentez-vous une partie non analysée de l'entrée d'une manière qui évite une copie excessive et permet le streaming d'entrée?

Le moyen le plus flexible est de le représenter avec un itérateur. Si vous parser faire un retour arrière, il devrait être un ForwardIterator, sinon, InputIterator est suffisant. Cela signifie que vous pouvez ensuite utiliser std::istream_iterator sur std::cin ou directement sur std::ifstream, ou analyser les baies std::strings ou char en mémoire. Streaming avec backtracking est un peu plus impliqué et vous obligerait à écrire un adaptateur itérateur tampon qui convertit InputIterator comme std::istream_iterator en ForwardIterator ou écrire un itérateur directement envelopper std::ifstream et en faisant .seekg() lorsque vous avez besoin de revenir en arrière.

Une autre option consiste à utiliser le std::string_view de C++ 17 qui ne copie pas et possède une interface conviviale, agréable à l'analyse. Cela ne résout cependant pas le streaming, vous devez tout d'abord lire le fichier entier en premier.

+0

Merci pour cette excellente réponse! D'autres recherches ont montré que '.seekg()' ne fonctionne pas correctement pour stdin, ce qui signifie que tout lire en mémoire (et ensuite utiliser 'std :: istringstream' ou' std :: string_view' pourrait très bien être le meilleur marche à suivre. – Qqwy