OVH Cloud OVH Cloud

ofstream et fonction write template

78 réponses
Avatar
Aurélien REGAT-BARREL
Bonjour à tous,
Pour mes besoins j'ai défini une fonction write ainsi :

template<typename T>
void write( std::ofstream & Stream, T Value )
{
Stream.write( reinterpret_cast<char*>( &Value ), sizeof T );
}

void main()
{
std::ofstream OutFileStream;
OutFileStream.open( "test" );
int c = 10;
write( OutFileStream, c );
}

Je m'étonne qu'elle n'existe pas déjà dans la STL, ou même carrement en tant
que fonction membre de ofstream. Je pense plutôt que j'ai mal cherché.
Y'a-t-il un équivalent et si non pourquoi ?
Merci.

--
Aurélien REGAT-BARREL

10 réponses

4 5 6 7 8
Avatar
Gabriel Dos Reis
"Michel Michaud" writes:

| Dans news:, Gabriel
| > Je soupçonnais bien compris que ta réponse initiale n'était
| > aussi qu'une conjecture. C'est pour cela que j'ai utilisé la
| > politesse
| > « Ah bon ? », mais la suite montre que tu prefères la rudesse.
|
| Mauvaise conjecture. Je préfère les explications que les
| insinuations. Comprenant maintenant que tout ce que tu
| avais à dire étant du deuxième, je ne vois pas d'intérêt
| à continuer. Je t'ai tout dit ce que j'avais à dire. Tu as
| le droit de ne pas être d'accord. Comme tu ne trouves rien
| à dire de précis pour expliquer ton désaccord,

Huh. Confert le reste du message auquel tu réponds.

| tu as gagné, je ne peux te convaincre.

Je ne savias pas que tu prenais cela comme une histoire de « gagner ·
De mieux en mieux.

| Ceci dit, je continue de penser que fwrite n'a pas été
| inventé pour écrire des void*.

Bah oui, ils ont certainement changé char* en void* en ne le faisant
pas exprès. Bouahahahaha.

[...]

| je vais carrément demander à Ritchie ce qu'il en pense...

Good luck!

-- Gaby
 
Avatar
Gabriel Dos Reis
"Michel Michaud" writes:

| Dans news:,
| > En ce qui me concerne, je n'ai pas compris la rélévance de la
| > phrase de Michel. Mais elle était strictement correcte. C'est
|
| La « relevance » (pertinence) dans mon esprit était simplement
| d'indiquer que fwrite n'ayant pas toujours eu void* dans sa
| signature, ce n'était peut-être pas par choix qu'il a cette
| signature aujourd'hui (c'est encore ce que je pense : pour ne
| pas casser le code existant, on a mis void* au lieu de char*
| quand on est passé à ISO C).

Bouhahaha. Puisque qu'avant il prenait char*, quelque code existant
aurait on cassé en continuant à prendre un char* ?

| (Merci quand même de me dire clairement que tu trouves mon
| affirmation strictement correcte...

Correcte et parfaitement non pertinente. Tu aurais tout aussi bien
dire que tu t'appelais Michel Michaud comme explication.

-- Gaby
Avatar
Gabriel Dos Reis
"Michel Michaud" writes:

| Dans news:, Gabriel
| > Je soupçonnais bien compris que ta réponse initiale n'était
| > aussi qu'une conjecture. C'est pour cela que j'ai utilisé la
| > politesse
| > « Ah bon ? », mais la suite montre que tu prefères la rudesse.
|
| Mauvaise conjecture.

Laquelle ?

-- Gaby
Avatar
Michel Michaud
Dans news:, Gabriel
"Michel Michaud" writes:
La « relevance » (pertinence) dans mon esprit était simplement
d'indiquer que fwrite n'ayant pas toujours eu void* dans sa
signature, ce n'était peut-être pas par choix qu'il a cette
signature aujourd'hui (c'est encore ce que je pense : pour ne
pas casser le code existant, on a mis void* au lieu de char*
quand on est passé à ISO C).


Bouhahaha. Puisque qu'avant il prenait char*, quelque code
existant aurait on cassé en continuant à prendre un char* ?


J'ai l'impression que tu me fais marcher, mais bon, allons-y.

Les paramètres n'étaient pas vérifiés en K&R C. Ni le nombre
ni le type. C'était « pratique »... Par exemple (avec FILE*) f :

/* En K&R C, ceci passe sans problème */
int MonFWrite(); /* On ne peut pas être plus précis */
struct S { int whatever; } var;
MonFWrite(&var, sizeof(var), 1, f); /* Appel très normal */


En ISO C, on a introduit les déclarations et vérifications des
paramètres... C'est « pratique », donc on veut en profiter :

/* En ISO C, ceci ne passe pas */
int MonFWrite(char*, int, int, FILE*); /* On aimerait ça */
struct S { int whatever; } var;
MonFWrite(&var, sizeof(var), 1, f); /* Erreur */

/* Par contre, ceci passe parfaitement */
int MonFWrite(void*, int, int, FILE*); /* On fait ce qu'on peut */
struct S { int whatever; } var;
MonFWrite(&var, sizeof(var), 1, f); /* OK */


En mettant void*, on a accepté toutes les utilisations
préalables de fwrite. Avec char*, on aurait cassé toutes
celles écrivant autres choses que des char*, en particulier
les UDT : ça fait beaucoup de code brisé. Je dis bien toutes,
car je suis certain que personne n'écrivait
fwrite((char*)&var, sizeof(var), 1, f);...

--
Michel Michaud
http://www.gdzid.com
FAQ de fr.comp.lang.c++ :
http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ/


Avatar
Gabriel Dos Reis
"Michel Michaud" writes:

[...nice abstract theory elided...]

| les UDT : ça fait beaucoup de code brisé. Je dis bien toutes,
| car je suis certain que personne n'écrivait
| fwrite((char*)&var, sizeof(var), 1, f);...


C'est là où te trompes : dans ces temps reculés, les pointeurs sur
données n'étaient pas tous de la même taille et si tu ne fais pas le
cast explicit au site d'appel, tu as des problèmes graves -- au fond
tu as toujours ces problèmes là avec les fonctions variadics.
Donc, les gens compétents mettaient le cast -- parce qu'autrement il
n'y avait pas de garantie que ça marche.

-- Gaby
Avatar
kanze
"Michel Michaud" wrote in message
news:<D9vXc.30285$...
Dans news:,
En ce qui me concerne, je n'ai pas compris la rélévance de la phrase
de Michel. Mais elle était strictement correcte. C'est


La « relevance » (pertinence) dans mon esprit était simplement
d'indiquer que fwrite n'ayant pas toujours eu void* dans sa signature,
ce n'était peut-être pas par choix qu'il a cette signature aujourd'hui
(c'est encore ce que je pense : pour ne pas casser le code existant,
on a mis void* au lieu de char* quand on est passé à ISO C).


Quand fwrite a été pondu, le pointeur générique en C était char*. Alors,
c'est difficile à savoir si l'intention était d'avoir un pointeur
générique, ou un pointeur à des caractères. Mais il faut se mettre dans
la contexte de l'époque. Il s'agissait d'un langage expérimental sur un
OS expérimental sur des machines avec peu de mémoire et sans mémoire
virtuelle. Alors, d'une part, l'utilisation des fichiers temporaires
(ou dumper la mémoire sur disque ne pose aucun problème) était très
courant, et même dans les autres cas, on ne prenait pas forcement en
compte les problèmes que pourrait présenter la rélecture des données sur
une autre machine à un autre époque. (Et aussi, la philosophie de Unix,
c'était de faire prèsque tout ce qui n'était pas de fichier temporaire
en format texte.)

Dans ce contexte, je n'ai pas de mal à penser que l'intention était bien
que le pointeur soit générique.

Et évidemment, quelque soit l'intention à Bell Labs, les programmeurs
s'en sont servi comme pointeur générique. Une fois les conversions
implicites entre pointeurs supprimées, en faire autre chose qu'un void*
aurait effectivement cassé pas mal du code.

[...]

Je me souviens très bien d'avoir utilisé fwrite en 1983-84
et je l'ai probablement utilisé en 1982 pour la première fois.


Je ne sais pas quand je l'ai utilisé pour la première fois ; à vrai
dire, je ne suis pas sûr de l'avoir jamais utilisé. En revanche, je l'ai
bien implémenté en 1982-83, et la document qui nous servait de
spécification, c'était la doc de Unix version 7 (sortie en 1979).

Mais je ne crois pas qu'on l'a mis en doute.

--
James Kanze GABI Software http://www.gabi-soft.fr
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
Michel Michaud
Dans news:, Gabriel
"Michel Michaud" writes:

[...nice abstract theory elided...]

les UDT : ça fait beaucoup de code brisé. Je dis bien toutes,
car je suis certain que personne n'écrivait
fwrite((char*)&var, sizeof(var), 1, f);...



C'est là où te trompes : dans ces temps reculés, les pointeurs
sur données n'étaient pas tous de la même taille et si tu ne
fais pas le cast explicit au site d'appel, tu as des problèmes
graves -- au fond tu as toujours ces problèmes là avec les
fonctions variadics.


Tu as presque raison : tu « risquais » des problèmes graves.
Comme aujourd'hui quand on emploie un int pour recevoir une
valeur qui peut déborder, ou tout autre comportement qui dépend
des implémentations. Par conséquent, même si c'était
théoriquement risqué, c'était courant. Pour décider de ce qui
se fait et pourquoi, c'est la pratique qui compte, pas la
théorie (malheureusement dans bien des cas).

Donc, les gens compétents mettaient le cast -- parce
qu'autrement il n'y avait pas de garantie que ça marche.


D'accord, tu as raison. Je n'aurais pas dû généraliser. Il y
avait certainement des gens qui mettaient le cast : ceux qui
devaient porter les programmes et qui faisaient bien attention
à ce genre de choses et qui ceux avaient connu un problème avec
ça. Je ne saurais dire quel pourcentage ça donne. En disant
« personne » je pensais à « quasiment personne », mais c'est
peut-être exagéré de toute façon.

Alors disons plutôt qu'il y avait bien des gens « incompétents »
et qui devaient penser que leur programme était correct puisqu'il
compilait et fonctionnait. Les messages d'erreur qu'ils auraient
vus si ANSI avait maintenu char* (bon, en mettant const char*
probablement, pour fwrite) les auraient surpris beaucoup...

Alors ça ne change pas ma conjecture, ou plutôt ça la renforce
peut-être : puisque char* fonctionnait et qu'en le gardant on
aurait simplement obligé les incompétents à corriger leur
programme, pourquoi être passé à (const) void*, si ce n'est
pour ne pas briser leur code ?

--
Michel Michaud
http://www.gdzid.com
FAQ de fr.comp.lang.c++ :
http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ/


Avatar
Gabriel Dos Reis
"Michel Michaud" writes:

| Dans news:, Gabriel
| > "Michel Michaud" writes:
| >
| > [...nice abstract theory elided...]
| >
| >> les UDT : ça fait beaucoup de code brisé. Je dis bien toutes,
| >> car je suis certain que personne n'écrivait
| >> fwrite((char*)&var, sizeof(var), 1, f);...
| >
| >
| > C'est là où te trompes : dans ces temps reculés, les pointeurs
| > sur données n'étaient pas tous de la même taille et si tu ne
| > fais pas le cast explicit au site d'appel, tu as des problèmes
| > graves -- au fond tu as toujours ces problèmes là avec les
| > fonctions variadics.
|
| Tu as presque raison : tu « risquais » des problèmes graves.

Ce n'est pas seulement qu'on risquait.

| Comme aujourd'hui quand on emploie un int pour recevoir une
| valeur qui peut déborder, ou tout autre comportement qui dépend
| des implémentations.

Le problème don't il est question ici, c'est plus proche de ce qui se
passe quand tu appelles une fonction variadic qui attend un pointeur
nul à la fin (comme les fonctions execl*) et que tu utilises 0 pour
marquer la fin au prétexte que 0 agit comme pointeur nul. Même
aujourd'hui ça donne des maux de têtes.

| Par conséquent, même si c'était
| théoriquement risqué, c'était courant. Pour décider de ce qui
| se fait et pourquoi, c'est la pratique qui compte, pas la
| théorie (malheureusement dans bien des cas).

Mais je ne te parle pas de la théorie, Michel. C'est toi qui dans
cette discussion empiles théories siur théories.

[...]

| Alors ça ne change pas ma conjecture, ou plutôt ça la renforce
| peut-être : puisque char* fonctionnait et qu'en le gardant on
| aurait simplement obligé les incompétents à corriger leur
| programme, pourquoi être passé à (const) void*, si ce n'est
| pour ne pas briser leur code ?

Le comportement du comité ANSI C -- et non d'ISO C -- vis à vis de
void et void* suit, de mon point de vue, des cheminement moins
linéaires que tu ne sembles le croire.
Le comité C n'hésite pas à casser du code écrit par des « careless
people » pour ne pas dire incompétents. C99 en contient d'autres
exemples.

-- Gaby
Avatar
Gabriel Dos Reis
writes:

[...]

| Et évidemment, quelque soit l'intention à Bell Labs, les programmeurs
| s'en sont servi comme pointeur générique. Une fois les conversions
| implicites entre pointeurs supprimées, en faire autre chose qu'un void*
| aurait effectivement cassé pas mal du code.

Pour que l'appel soit « portable », le cast vers char* était
nécessaire. Les gens compétents mettaient le cast.

-- Gaby
Avatar
Michel Michaud
Dans news:, Gabriel
"Michel Michaud" writes:

Dans news:,
C'est là où te trompes : dans ces temps reculés, les pointeurs
sur données n'étaient pas tous de la même taille et si tu ne
fais pas le cast explicit au site d'appel, tu as des problèmes
graves -- au fond tu as toujours ces problèmes là avec les
fonctions variadics.


Tu as presque raison : tu « risquais » des problèmes graves.


Ce n'est pas seulement qu'on risquait.


Il y avait des environnements où ça fonctionnait. Alors oui,
c'était un risque, pas un fait. Dans un environnement où le cast
était essentiel, oui, c'est pas seulement qu'on risquait.

En fait, fwrite est un problème mineur je crois. On courait
bien plus de danger avec memset ou memcpy...

[...]
Le comportement du comité ANSI C -- et non d'ISO C -- vis à vis
de void et void* suit, de mon point de vue, des cheminement
moins linéaires que tu ne sembles le croire.


C'est ton point de vue, merci enfin de l'exprimer un peu plus
clairement.

Le comité C n'hésite pas à casser du code écrit par des «
careless people » pour ne pas dire incompétents. C99 en
contient d'autres exemples.


Raison de plus pour se demander pour ils ne l'ont pas fait
ici en mettant void* alors char* était la tradition...

Est-ce que C99 a remis (const) char* ? :-)

--
Michel Michaud
http://www.gdzid.com
FAQ de fr.comp.lang.c++ :
http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ/



4 5 6 7 8