OVH Cloud OVH Cloud

cout ou un fichier...

14 réponses
Avatar
Marc Boyer
Bonjour,

je redécouvre peut-être la roue, mais j'ai un problème pour
faire en C++ un truc que je savais bien faire en C.

Sur ma ligne de commande, soit l'utilisateur passe un nom
de fichier, et il faut lire dans ce fichier, soit il ne
dit rien, et on lit dans cin.

Mais comment faire ?

Je connais cin, fistream, mais entre le fait qu'on ne peut
pas copier de ostream, le RAII, la seule solution que
je vois, c'est de déclarer un pointeur
istream *in;
ce qui va être lourd à utiliser.

Il y a un truc ?

Merci,
Marc Boyer
--
Entre le fort et le faible, c'est la liberte qui opprime et le droit
qui libere. Henri Lacordaire, Dominicain

4 réponses

1 2
Avatar
Marc Boyer
Le 12-02-2006, James Kanze a écrit :
Marc Boyer wrote:

je redécouvre peut-être la roue, mais j'ai un problème pour
faire en C++ un truc que je savais bien faire en C.

Sur ma ligne de commande, soit l'utilisateur passe un nom
de fichier, et il faut lire dans ce fichier, soit il ne
dit rien, et on lit dans cin.

Mais comment faire ?

Je connais cin, fistream, mais entre le fait qu'on ne peut pas
copier de ostream, le RAII, la seule solution que je vois,
c'est de déclarer un pointeur
istream *in;
ce qui va être lourd à utiliser.


La solution habituelle, c'est quelque chose du genre :

if ( argc == 1 ) {
process( std::cin ) ;
} else {
for ( int i = 1 ; i < argc ; ++ i ) {
if ( strcmp( argv[ i ], "-" ) ) {
process( std::cin ) ;
} else {
std::ifstream src( argv[ i ] ) ;
if ( ! src ) {
std::cerr << "Cannot open " << argv[ i ] << std::endl ;
} else {
process( src ) ;
}
}
}
}

Si (comme moi), tu utilises une classe pour parser la ligne de
commande, la plus facile, c'est qu'elle se présente comme un
std::vector avec les noms de fichiers, après avoir enlevé
les options. Ensuite, tu fais comme ci-dessus, sauf avec ce
vecteur, à la place de argv.


Sauf qu'il y a un problème de combinatoire: là tu présentes
deux appels valeurs possibles pour la liste de paramètres
de process, imbriqués dans 3 branches de if.
Si on prend cin *et* cout, ça fait 4, et aver cerr, 8.

Bon, c'est vrai qu'on peut jouer avec ?:.
process(option_cin?cin:file_in, option_cout?cout:file_out);

Enfin, si tu t'y connais avec les flux, ce n'est pas trop
difficile d'écrire un flux qui fait le tous.


Heuh, non, pour le moment, on ne peut pas dire que je m'y
connaisse...

Marc Boyer
--
Entre le fort et le faible, c'est la liberte qui opprime et le droit
qui libere. Henri Lacordaire, Dominicain


Avatar
Jean-Marc Bourguet
"kanze" writes:

Jean-Marc Bourguet wrote:
Marc Boyer writes:

Sur ma ligne de commande, soit l'utilisateur passe un nom de
fichier, et il faut lire dans ce fichier, soit il ne dit rien, et on
lit dans cin.


[...]
Il y a un truc ?


rdbuf


C'est curieux, mais je ne me suis jamais servi de rdbuf dans ce
cas-là.


Je suis a peu pres certain que cette utilisation a deja ete evoquee
ici (ou sur comp.lang.c++.moderated) pour cet usage. Je ne l'ai pas
inventee.

Si on veut s'amuser à changer le streambuf de std::cin,


Je ne pensais pas a ca (et apparemment Marc ne l'a pas compris comme
ca, l'utilisation qu'il a donnee en exemple etait celle que
j'envisageais).

A+

--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org



Avatar
kanze
Marc Boyer wrote:
Marc Boyer writes:



[...]
Le rdbuf renvoyé par un fstream, c'est un pointeur vers un
membre.


Mais le dit membre pourrait avoir un champs interne qui lui
dit qu'il est partagé, incrémenté à chaque appel à rdbuf(
streambuf* ).


Quand Jean-Marc a dit membre, il voulait dire membre. Tout
court. Et le destructeur d'un objet appel les destructeurs de
tous ses membres, sans faille.

Ceci dit, quand deux fstream partagent le même streambuf,
s'il n'y a pas de notion de partage, c'est le premier
destructeur appelé qui ferme le flux.


C'est un peu plus complexe. Il y a le filebuf (le dérivé de
streambuf) qui est membre, et il y a le streambuf qu'il utilise
à l'instant. Chaque fstream detruit le filebuf qui est membre
dans son déstructeur. Indépendamment du streambuf qu'il utilise
à l'instant.

Note aussi que la fonction rdbuf() n'est pas virtuelle. Il y a
donc std::ios_base<>::rdbuf(), qui renvoie le streambuf dont
l'objet se sert actuellement, et std::ifstream::rdbuf(), qui
renvoie le std::filebuf membre. Qui ne sont pas forcément le
même objet.

Personnellement, et ça n'engage que moi, je trouve que
l'utilisation de rdbuf() non const est un peu l'abus, surtout
sur un flux dérivé, du genre ofstream. On pourrait être amené à
le faire, surtout sur std::cout, quand on a affaire à un
ensemble de code déjà écrit et auquel on ne peut pas (ou ne veut
pas) toucher, et qui sort vers std::cout, mais dans
l'ensemble... Si je vais jouer avec les streambuf, je joue avec
les streambuf, et je ne crée des ostream ou des istream au
besoin.

(C'est peut-être aussi une partie de la raison pourquoi tu ne
peux pas appeler la version non-const de rdbuf() sur un lvalue
dont le type statique est ofstream ou ifstream.)

Est-ce que cela signifie que le code suivant est correct,
malgrès le non appel du destructeur de *ptr_file ?

ostream out;
ofstream* ptr_file ; // <<<=== ne manquait-il pas quelque chose ?
if (*ptr_file) {
out.rdbuf( ptr_file->rdbuf() );
}


Correct, pour quelle définition de correct ? Si tu fermes le
fichier correctement, il ne doit pas y avoir de problème ; il y
aurait une fuite de mémoire, peut-être, et c'est tout.

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34


Avatar
kanze
Marc Boyer wrote:
Marc Boyer wrote:

je redécouvre peut-être la roue, mais j'ai un problème
pour faire en C++ un truc que je savais bien faire en C.

Sur ma ligne de commande, soit l'utilisateur passe un nom
de fichier, et il faut lire dans ce fichier, soit il ne
dit rien, et on lit dans cin.

Mais comment faire ?

Je connais cin, fistream, mais entre le fait qu'on ne peut
pas copier de ostream, le RAII, la seule solution que je
vois, c'est de déclarer un pointeur
istream *in;
ce qui va être lourd à utiliser.


La solution habituelle, c'est quelque chose du genre :

if ( argc = 1 ) {
process( std::cin ) ;
} else {
for ( int i > if ( strcmp( argv[ i ], "-" ) ) {
process( std::cin ) ;
} else {
std::ifstream src( argv[ i ] ) ;
if ( ! src ) {
std::cerr << "Cannot open " << argv[ i ] << std::e ndl ;
} else {
process( src ) ;
}
}
}
}

Si (comme moi), tu utilises une classe pour parser la ligne
de commande, la plus facile, c'est qu'elle se présente comme
un std::vector avec les noms de fichiers, après avoir enlevé
les options. Ensuite, tu fais comme ci-dessus, sauf avec ce
vecteur, à la place de argv.


Sauf qu'il y a un problème de combinatoire: là tu présentes
deux appels valeurs possibles pour la liste de paramètres de
process, imbriqués dans 3 branches de if. Si on prend cin
*et* cout, ça fait 4, et aver cerr, 8.


C'est que j'ai tout mis ensemble:-). En fait :

-- cin et cout, ce n'est pas la même direction. Où est-ce que
tu vas avoir un flux qui peut être connecté soit à un, soit
à l'autre.

-- Pour les autres, je n'ai fait que présentait la solution
classique, dans le cas des programmes qui utilisent les
conventions des filtres Unix. Il a l'avantage d'être facile
à comprendre, même si on ne connaît pas la fonctionnement
interne des flux.

-- Et finalement, ou bien, tu imbriques, ou bien, tu dupliques
les if, parce qu'il faut aussi de la logique pour ne pas
fermer les cin, etc.

En suivant les idées de Jean-Marc, on pourrait effectivement
écrire quelque chose du genre :

std::filebuf file ;
std::streambuf* toUse == NULL ;
if ( useStandardIn ) {
toUse = std::cin.rdbuf() ;
} else {
if ( file.open( filename, std::ios::in ) ) {
toUse = &file ;
} else {
std::cerr << "Cannot open " << filename << std::endl ;
}
}
std::istream source( toUse ) ;
// ...

Dans la pratique, j'ai souvent vu du code semblable qui se
servait des istream et des pointeurs, plutôt que des streambuf.
Quelque chose du genre :

std::istream* source = NULL ;
if ( useStandardIn ) {
source = &std::cin ;
} else {
source = new std::ifstream( filename ) ;
if ( ! *source ) {
std::cerr << ...
delete source ;
source = NULL ;
}
}

if ( source != NULL ) {
process( *source ) ;
}

if ( source != &std::cin ) {
delete source ;
}

Évidemment, aujourd'hui, on se servira d'une classe RAII à la
place du pointeur. (Et personnellement, je mettrais le premier
if ci-dessus dans une fonction à part. Qui renvoie une instance
de la classe RAII. Mais alors, il faudrait du comptage des
références, etc.)

Les alternatifs ne manquent pas.

Bon, c'est vrai qu'on peut jouer avec ?:.
process(option_cin?cin:file_in, option_cout?cout:file_out);

Enfin, si tu t'y connais avec les flux, ce n'est pas trop
difficile d'écrire un flux qui fait le tous.


Heuh, non, pour le moment, on ne peut pas dire que je m'y
connaisse...


Dans ce cas-là, le dernier exemple est probablement le plus
facile à suivre, même s'il est un peu verbeux, par rapport aux
solutions qui utilise les streambuf directement.

(En passant, note aussi qu'il faut faire attention à la gestion
de l'erreur quand tu utilises un streambuf dans plusiers flux.
Si tu utilises mon premier exemple, ci-dessus, dans une boucle,
par exemple, une fin de fichier sur std::cin peut se perdre.)

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34



1 2