mardi 5 avril 2011

stream iterators

C'est un équilibre, il me faut ma dose de code. Et vu que j'en ai en quantités industrielles au boulot, j'ai plutôt tendance à me rattraper sur mes lectures une fois réintégré mon trou. Cependant, ce n'est pas une raison pour se laisser totalement abattre, et je me suis dit que j'allais mentionner une fonctionnalité que je me suis mis à utiliser récemment: les stream iterators. À ma grande honte, je dois admettre que j'en ai compris l'utilité en notant un exercice d'un candidat lors d'une de nos sessions de recrutement. L'ensemble était moyen, mais sa manière de lire les fichiers de données était tout à fait remarquable.

Exercice classique: étant donné un fichier contenant une liste d'entiers, les mettre dans un vecteur, et les afficher à l'écran.

Les solutions habituelles vont du plus manuel à coup de buffers et de recherche d'espaces, ou de caractères non numériques, à des choses plus propres de type boucles avec des ifstream. Sans parler des soucis à la fin du fichier, pour savoir quand s'arrêter, comment supporter un retour chariot final ou pas...

Avec les istream_iterator, c'est un plaisir: comme leur nom l'indique, on leur donne un istream, et on itère.


#include <iostream>
#include <fstream>
#include <vector>
#include <algorithm>
#include <iterator>

int main()
{
std::vector<int> myNumbers;

std::ifstream f("myfile.txt");
std::copy(std::istream_iterator<int>(f),
std::istream_iterator<int>(),
std::back_inserter(myNumbers));

std::for_each(myNumbers.begin(),
myNumbers.end(),
[](int i){std::cout << i << "\n";});
std::cout.flush();

return 0;
}

Et le résultat:

$ cat myfile.txt
1 123 33 2222 1425
78
11

$ ./test
1
123
33
2222
1425
78
11


J'avoue, je n'ai pas pu m'empêcher de coller une lambda. On ne se refait pas.

Alors certes, ce n'est que de l'encapsulation de fonctionnalités de base. Mais c'est pour moi le bon idiome pour ce genre d'activités, et j'aimerais voir plus de code se baser dessus, et moins sur des solutions à la mimine et trop souvent buggées.

La beauté de la chose apparaît complétement lorsque l'on se rend compte qu'on peut passer n'importe quel objet streamable en paramètre template. Créez votre objet, son opérateur de stream, et basta, lisez-en des listes et des listes.

Dernière remarque, un poil hors sujet: je crois que la beauté des stream et de la hiérarchie de classe associée n'apparaît vraiment que le jour où vous devez faire un test unitaire d'une horreur prenant un FILE *. Le triptyque istream pour la classe générique, istringstream pour les tests unitaires et ifstream pour la production resplendit au firmament de l'ingénierie logicielle.

2 commentaires:

Socrate a dit…

Alors la, mon ami, je dis non !

FILE * => interface
fmemopen() => pour les test unitaires
fopen() => pour la prod

Le triptyque, il est bien la!

M87 a dit…

Je note le fmemopen :)