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

8 réponses

4 5 6 7 8
Avatar
Gabriel Dos Reis
Loïc Joly writes:

| Gabriel Dos Reis wrote:
|
| > James Kanze writes:
| > | Malloc renvoyait un char*. Personne ne mettait un cast sur sa
| > valeur de
| > tu veux dire, personne dans les quatre murs qui font ta chambre ? Le
| > monde est plus grand que ça, James.
| > | rétour -- au contraire, déjà alors, les gens compétents évitaient
| > le
| > | cast parce qu'il masquait des erreurs potentielles.
| > Je ne sais pas qui t'a appris le C, ni où tu l'as appris.
| > Mais je n'ose pas imaginer que tu étais parmi ces careless programmers.
| > Dennis M. Ritchie -- l'inventeur du langage C -- dans son bouquin «
| > The C Programming Language », aussi connu comme le « White Book » © 1978,
| > est quand à lui très clair sur le cast de char* vers le type pointer idione.
|
| Ce nos jours, quand on regarde par exemple sur f.c.l.c, les gens
| déconseillent (assez unanimement) vivement le cast en sortie du malloc
| (ce qui pose des problèmes puisque le code en question ne peut pas
| alors être compilé par un compilateur C++).

Unanmement veut dire que tout le monde est d'accord et j'ai par le
passé, ainsi que d'autres, exprimé mon désaccord ; mais il est vrai
que je ne lis pas f.c.l.c très activement et très régulièrement -- par
manque de temps.

De ce que j'ai pu voir, la plupart de ces « déconseils » part du
principe que quelqu'un qui utilise malloc(), par exemple, n'a pas
forcément une déclaration visible. Ce qui relève, à mon sens,
simplement du careless programming.
Les autres, de ce que j'ai vu, partent simplement du fanatisme.

Dans la version 2 de K+R, Ritchie et Kernighan mentionnent que tous
leurs programmes ont été testés par le « Bjarne Stroustrup's C++
translator ». Cette mention a eu, malheureusement, le dégât collatéral
de nourrir certains fanatismes anti-C++ : il y a même des arguments
comme quoi ne pas mettre le cast est un message clair comme quoi, ce
n'est pas du C++. D'autres ont affirmé que l'opinion de Ritchie n'a
aucune espèce d'importance parce qu'il n'a pas participé à la
normalisation de ANSI C.
On trouvera peut-être des variantes aujourd'hui du style que c'est de
la belle théorie et qu'ils faisaient autre chose

| Quelqu'un a-t-il une idée
| de quand (et pourquoi) la ligne officielle du parti de ne pas mettre
| de cast est apparue ?

Je ne saurais te dire avec précision lorsque cette position est
apparue dans f.c.l.c.

-- Gaby
Avatar
kanze
Loïc Joly wrote in message
news:<cguqpu$9k$...
Gabriel Dos Reis wrote:

Dennis M. Ritchie -- l'inventeur du langage C -- dans son bouquin «
The C Programming Language », aussi connu comme le « White Book » ©
1978, est quand à lui très clair sur le cast de char* vers le type
pointer idione.


Ce nos jours, quand on regarde par exemple sur f.c.l.c, les gens
déconseillent (assez unanimement) vivement le cast en sortie du malloc
(ce qui pose des problèmes puisque le code en question ne peut pas
alors être compilé par un compilateur C++). Quelqu'un a-t-il une idée
de quand (et pourquoi) la ligne officielle du parti de ne pas mettre
de cast est apparue ?


Au plus tard le jour où quelqu'un a commencé à porter le C sur des
processeurs où sizeof(int) != sizeof(char*). C'était une règle
communement admise déjà en 1985, au moins. Y compris dans les sources de
Unix.

--
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:,
"Michel Michaud" writes:

Je suis d'accord sur le principe qu'il y avait certainement
un danger à ne pas forcer une conversion explicite des
pointeurs entre T* (T quelconque) et char*.



Que la conversion soit implicite ou explicite, le comportement
est le même.


Pas en K&R C : il n'y aurait tout simplement pas de conversion
implicite pour les paramètres (et c'est sur ce sujet que je
disais être d'accord sur le principe, c'était le sujet de
discussion avant que je change le titre...).

[...]
Pour des raisons historiques, la norme exige que les
représentations des void* et de char* soient identiques. Selon
la norme, il y a conversion en void* ; dans la pratique, une
conversion en void* et une conversion en char* font faire
exactement la même chose.


Je ne saisis pas trop là. « la norme exige », mais c'est seulement
« dans la pratique » que ce sera le cas ?

[...]
Quelqu'un peut-il me donner des exemples précis pour que je
comprenne un peu mieux les conversions T* -> char* et T1* ->
void* -> T2* ?



C'est assez simple, en fait. Au niveau le plus bas, un void*
est un char*. Et un char* est capable d'adresser n'importe quel
octet. Quand on convertit un T* en char*, on met les bits
selecteurs d'octet du char* à zéro. Quand on convertit un char*
en T*, on ignore les bits selecteurs d'octet. (Au moins, c'est
comme ça que ça passait dans le seul cas que je connais.)


J'avoue que ton explication n'est pas suffisamment claire pour
que je comprenne vraiment, étant donné que je ne connais pas
cette architecture. Si tu te sens capable de faire mieux (en
montrant des bits peut-être...), j'apprécierais ! Là, je crois
comprendre que tu considères certains bits comme sélecteur de
« groupes d'octets » et certains bits pour dire quel octet dans
le groupe est désiré (un peu comme si c'était des segments).
Mais alors un T* qui aurait obligatoirement une adresse de la
base d'un groupe, aurait aussi ses bits déjà à 0... non ?

J'imagine qu'on peut vite devenir HS avec cette discussion,
mais pas si on revient toujours au principe de C++ et qu'on
ajoute ton idée que char* est équivalent à void*. En C++, on
doit pouvoir passer de T* à void* et puis pouvoir revenir à T*.
Si on met des bits à zéro quelque part dans ça, on ne pourra
pas les retrouver !

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



Avatar
kanze
Gabriel Dos Reis wrote in message
news:...

[...]
En réalité, je sais de source de première main ce qui se partiquait
dans l'entourage de Ricthie, mais au lieu de venir dire "le mec au
dessus de moi dit le contraire", j'ai pensé plutôt donner une source
que tout le monde pouvait vérifier, sur l'opinion et la pratique de
Ritchie.


J'ai vue des sources. Je ne parle pas de ce que certains pourraient
estîmer bon ou non. Je parle de ce qui se faisait réelement.

fait. Non, James, tu ne faisais pas de
la rhétorique : tu mentais simplement.


Ne juge pas les autres d'après toi-même.

--
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
Gabriel Dos Reis
writes:

| Gabriel Dos Reis wrote in message
| news:...
|
| [...]
| > En réalité, je sais de source de première main ce qui se partiquait
| > dans l'entourage de Ricthie, mais au lieu de venir dire "le mec au
| > dessus de moi dit le contraire", j'ai pensé plutôt donner une source
| > que tout le monde pouvait vérifier, sur l'opinion et la pratique de
| > Ritchie.
|
| J'ai vue des sources. Je ne parle pas de ce que certains pourraient
| estîmer bon ou non. Je parle de ce qui se faisait réelement.
|
| > fait. Non, James, tu ne faisais pas de
| > la rhétorique : tu mentais simplement.
|
| Ne juge pas les autres d'après toi-même.

Mais James, je ne juge pas les autres d'apres moi-meme. Ce n'est pas
moi qui avance des arguments d'anciens combattants. Regarde jhuste le
type d'arguments que tu avances depuis trois messages.

-- Gaby
Avatar
kanze
"Michel Michaud" wrote in message
news:<4dPYc.9219$...
Dans
news:,
"Michel Michaud" writes:

Je suis d'accord sur le principe qu'il y avait certainement
un danger à ne pas forcer une conversion explicite des
pointeurs entre T* (T quelconque) et char*.



Que la conversion soit implicite ou explicite, le comportement
est le même.


Pas en K&R C : il n'y aurait tout simplement pas de conversion
implicite pour les paramètres (et c'est sur ce sujet que je disais
être d'accord sur le principe, c'était le sujet de discussion avant
que je change le titre...).


S'il n'y a pas de conversion, il n'y a pas de conversion implicite. Si
le compilateur ne sait pas le type cible (ce qui est le cas en C K&R, ou
pour les varargs), il ne peut pas y avoir de conversion. Je croyais
l'avoir dit, d'ailleurs.

[...]
Pour des raisons historiques, la norme exige que les représentations
des void* et de char* soient identiques. Selon la norme, il y a
conversion en void* ; dans la pratique, une conversion en void* et
une conversion en char* font faire exactement la même chose.


Je ne saisis pas trop là. « la norme exige », mais c'est seulement «
dans la pratique » que ce sera le cas ?


Je ne suis pas 100% sûr sur « la norme exige » non plus. Ça fait un
moment que je ne prête plus beaucoup d'attention à la norme, simplement
parce qu'elle dit beaucoup de choses qui ne sont pas vraies pour mes
compilateurs. Et norme ou non, il faut que mes compilateurs compilent le
code que j'écris.

Dans la pratique, un compilateur ne va pas créer des formats artificiels
pour rien. Or, au niveau hardware, il y a tout juste deux formats qu'il
faut distinguer : les pointeurs vers les « bytes » (je ne dis pas
octets, parce qu'ils ne sont pas forcément 8 bits), et les pointeurs
vers les « mots ». Les pointeurs vers les bytes sonts potientiellement
plus grands, jamais plus petits. La norme exige bien que je puisse
convertir un char* (pointeur vers byte) en void* et de rétour sans perte
d'information. Du coup, un void* doit être au moins aussi grand qu'un
char*.

La seule exception possible, je crois, c'est dans le cas des
architectures « tagguées », où le pointeur contient des informations sur
le type, en plus de l'adresse. On pourrait alors concevoir que le void*
ne contient pas les informations sur le type, et donc, a un format
différent que celui de char*. Là, j'avoue que je n'ai aucune expérience.
Mais je ne sais pas si ça pose un problème quand on parle de la
portabilité réele.

Mais voilà. Je l'ai trouvé dans la norme C, §6.2.5/26 : « A pointer to
void shall have the same representation and alignment requirements as a
pointer to a character type. » (Je crois que l'intention, c'était de ne
pas casser des programmes qui passer des char* à des fonctions sans
déclaration qui prenait char* avant la norme, mais void* après.)

[...]
Quelqu'un peut-il me donner des exemples précis pour que je
comprenne un peu mieux les conversions T* -> char* et T1* ->
void* -> T2* ?



C'est assez simple, en fait. Au niveau le plus bas, un void* est un
char*. Et un char* est capable d'adresser n'importe quel octet.
Quand on convertit un T* en char*, on met les bits selecteurs
d'octet du char* à zéro. Quand on convertit un char* en T*, on
ignore les bits selecteurs d'octet. (Au moins, c'est comme ça que ça
passait dans le seul cas que je connais.)


J'avoue que ton explication n'est pas suffisamment claire pour que je
comprenne vraiment, étant donné que je ne connais pas cette
architecture. Si tu te sens capable de faire mieux (en montrant des
bits peut-être...), j'apprécierais !


J'essaie. J'avoue ne pas avoir été très clair.

Considérons une machine hypothétique de 16 bits, avec un adressage mot.
(En fait, elle n'est pas si hypothétique que ça, étant donné que j'ai
travaillé dessus. Mais c'était il y a longtemps ; je ne me rappelle plus
de tous les détails, et on -- pas toi -- me traiterait de menteur si je
me trompe du moindre détail, alors, je préfère parler de machine
hypothétique imaginaire.) Sur cette machine, on a un pointeur à un mot
qui comporte 16 bits (tous significatifs), et un pointeur à un octet qui
en comporte 32, dont les 16 premiers sont le pointeur au mot, le bit du
poids faible du deuxième 16 est le sélecteur d'octet, est les autres 15
sont ignorés. Sur une telle architecture, un int* comporte un mot, c-à-d
deux octets, un char* deux mot, ou quatre octets. Quand je convertis
int* en char*, le compilateur génère une valeur à deux mots, dont le
premier mot est le int*, tout simplement, et le deuxième est 0. Quand on
convertis un char* en int*, c'est l'inverse -- le compilateur ne prend
que le premier mot, en ignorant le deuxième.

En fait, quelque soit l'architecture (au moins, parmi celle que j'ai
connue -- ça ne serait peut-être pas vrai sur les architectures où
sizeof(int) == sizeof(char)), au niveau hardware, une adresse comporte
deux parties : une partie qui selection le mot, Sûr un Sparc moderen,
par exemple, une adresse comporte (toujours) 64 bits, dont les 3 bits de
poids faibles servent à sélectionner l'octet, et les autres à
sélectionner le mot dans lequel se trouve l'octet. Si on prend un
PDP-10, en revanche, on va trouver que les bits qui selectionnent
l'octet se trouvent aux poids forts -- sur certaines architectures, ils
se trouvent sur un mot à part. En fait, quand on parle d'un adressage à
octet, tout ce qu'on veut dire, c'est que les bits de sélection de
l'octet 1) se trouvent dans le même mot que la reste de l'adresse, 2)
qu'ils sont aux poids faibles, et 3) qu'il y a une puissance de deux
d'octets dans le mot, de façon à ce que si je traite l'adresse comme un
entier, et j'incrémente l'adresse du dernier octet dans le mot, je me
rétrouve avec l'adresse du premier octet dans le mot suivant. (Ce ne
serait pas le cas avec le PDP-10, par exemple, où, si je traite
l'adresse du dernier octet dans un mot comme un entier, et que je
l'incrémente, je me rétrouve avec l'adresse du dernier octet du mot
suivant. Et en passant, quand je dis qu'on traite l'adresse comme un
entier, et qu'on l'incrémente, je parle au niveau du hardware, et non ce
qui se passe en C ou en C++.)

Selon le hardware, il est parfois possible d'adresses d'autres tailles
inférieur à un mot. Sur le Sparc, par exemple, on peut adresses des
sous-mots de 1, 2 ou 4 octets. Sur un PDP-10, en fait, à la place d'un
sélecteur d'octets, on a deux champs, un qui donne le numéro de bit, et
l'autre qui spécifie combien de bits. On pourait donc décomposer le mot
(de 24 bits) en 4 bytes de 6 bits, ou en 3 bytes de 8 bits. (Les normes
C/C++ exigent le deuxième. Et on aurait sizeof( int ) == 3.)

Note bien que selon la norme, si le char* n'est pas correctement aligné
pour un int*, le comportement est indéfini. Sur des machines où la
sélection de l'octet architecture, on va réaligner sur le mot inférieur.
Pas exprès, mais simplement parce que c'est ce que donne
l'implémentation la plus simple. Sur mon Sparc, en revanche, le nombre
de bits dans un int* est le même que dans un char*. On a simplement
l'exigeance que dans un int*, les deux bits de poids faibles soient 0.
Or, lors d'une conversion char* en int*, le compilateur ne fait rien
pour s'en assurer. S'ils ne sont pas 0, c'est un comportement indéfini ;
en fait, j'aurais une erreur de bus si j'essaie de me servir du
pointeur.

Là, je crois comprendre que tu considères certains bits comme
sélecteur de « groupes d'octets » et certains bits pour dire quel
octet dans le groupe est désiré (un peu comme si c'était des
segments).


C'est toujours comme ça, si on désend assez bas vers le hardware.

En fait, sur la plupart des architectures modernes (dites à adressage à
octet), l'« adresse » que le hardware envoie vers la mémoire comporte
trois informations : l'adresse du mot (ton groupe d'octets) à accéder,
une indication d'offset dans le mot, et une indication du nombre
d'octets concernés. Prèsque toujours, aussi, si l'offset plus le nombre
d'octets concernés est supérieur à la taille d'un mot, ça ne marche pas.
(Chez Intel, ça marche parce que le hardware dans la CPU reconnaît le
cas, et découpe en plusieurs accès.)

Ça n'est pas toujours été le cas, et sur l'architecture hypothétique que
j'ai présentée d'abord, l'accès par octet était implémenté par le
logiciel (et c'était fort cher en temps d'exécution, par rapport à un
accès mot). Même sur les TMS-9000, l'accès physique à la mémoire était
toujours par mot. Même si l'architecture présentée à l'utilisateur était
bien un adressage par octet, dans le cas d'une écriture d'un octet, le
hardware faisait d'abord la lecture d'un mot, puis remplaçait l'octet
dan le mot et réécrivait le mot. Et *p = 1 prenait à peu près deux fois
plus long si p était un char* que s'il était un int*.

Mais alors un T* qui aurait obligatoirement une adresse de la base
d'un groupe, aurait aussi ses bits déjà à 0... non ?


Normalement. Je parlais du cas où on convertissait un char* (ou un
void*, ce qui revient au même) en int*.

J'imagine qu'on peut vite devenir HS avec cette discussion, mais pas
si on revient toujours au principe de C++ et qu'on ajoute ton idée que
char* est équivalent à void*.


Je crois que ça ne fait pas de mal à comprendre ce qui se passe en
dessous, même si ce n'est pas le genre de chose que je présenterais à un
débutant. L'histoire n'est pas sans intérêt non plus ; ça aide à
comprendre le pourquoi de certaines règles.

En C++, on doit pouvoir passer de T* à void* et puis pouvoir revenir à
T*. Si on met des bits à zéro quelque part dans ça, on ne pourra pas
les retrouver !


Et voilà où je me suis mal fait comprendre. Si le void* a plus de bits,
il faut bien parler de la valeur de ces bits supplémentaires. C'est les
bits qu'a le void*, mais non le T*, qu'on met à zéro. Évidemment, à la
réconversion en T*, on les jette à la poubelle.

--
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:,
"Michel Michaud" wrote in message
news:<4dPYc.9219$...
Dans
news:,
Que la conversion soit implicite ou explicite, le comportement
est le même.


Pas en K&R C : il n'y aurait tout simplement pas de conversion
implicite pour les paramètres (et c'est sur ce sujet que je
disais être d'accord sur le principe, c'était le sujet de
discussion avant que je change le titre...).


S'il n'y a pas de conversion, il n'y a pas de conversion
implicite. Si le compilateur ne sait pas le type cible (ce qui
est le cas en C K&R, ou pour les varargs), il ne peut pas y
avoir de conversion. Je croyais l'avoir dit, d'ailleurs.


Oui, oui, je précisais simplement la différence K&R C vs ANSI C,
parce que f(p) peut faire une conversion implicite en ANSI C,
mais pas en K&R C (si p est un pointeur).


Considérons une machine hypothétique de 16 bits, avec un
[... explication sur une architecture bien spéciale et encore

d'autres ...]

D'accord, d'accord, d'accord : il y a eu des architectures
vraiment tordues. Je ne suis pas sûr qu'il y en a beaucoup
d'aussi tordues aujourd'hui... Merci pour les explications !

N.B. Ça fait vraiment « vieux » de parler du PDP-10 comme si
tu y avais travaillé. Moi je n'ai connu que le PDP-11 :-)

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



Avatar
kanze
"Michel Michaud" wrote in message
news:<is8Zc.16121$...

N.B. Ça fait vraiment « vieux » de parler du PDP-10 comme si
tu y avais travaillé. Moi je n'ai connu que le PDP-11 :-)


Et pourtant... Mes connaissances du PDP-10 sont assez récentes. C'est
une machine qui a eu beaucoup d'affectionados, qui continue d'entretenir
des simulateurs, etc. C'est probable, aussi, qu'il existe un compilateur
C pour la bête, et peut-être même un compilateur C++. Aujourd'hui.
(Alors que je n'ai pas vu de PDP-11 depuis 1978, environ.)

En ce qui concerne la machine « hypothétique » à adressage mot :
l'adressage mot était plutôt la règle jusqu'à la réussite phénomianle du
PDP-11. Quand j'ai réelement commencé l'informatique, c'était encore
très répandu -- parmi les architectures répandues, il n'y avait que
l'IBM 360 et le PDP-11 à adressage octet (et les microprocesseurs 8
bits, évidemment).

Maintenant, si je voulais vraiment paraître vieux, je parlerais du IBM
1401 (que j'ai eu l'occasion de pouvoir programmer quand j'étais au
lycée). Mais je suis assez sûr qu'il n'a jamais eu de compilateur C pour
cette machine (dont l'arithmétique était base 10 -- il faudrait émuler
la représentation binaire qu'exige la norme).

--
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

4 5 6 7 8