OVH Cloud OVH Cloud

Du C au C++...

13 réponses
Avatar
bruckner.olivier
Bonsoir !

Je convertie en ce moment du code C en C++ (tout du moins j'essais).

Je rencontre un probleme et je suis sur que ce n'es pas très compliqué a
comprendre.

voici le code en C:

FILE *f;
int c;
unsigned char *buffer;
char checkname[MAX_OSPATH];

f = fopen (checkname, "wb");
fwrite (buffer, 1, c, f);
fclose (f);


je désire convertir ceci en C++, voici ce que je pense etre correct:

ofstream file_out;
char checkname[MAX_OSPATH];
int c;
unsigned char *buffer;


file_out.open (checkname, ios::out | ios::binary);
fwrite (buffer, 1, c, f); // je ne sais pas comment remplacer ceci
file_out.close();


le fwrite me dérange donc ici, éxiste t'il un équivalent en C++ svp ?

Merci de vos réponses.

10 réponses

1 2
Avatar
Jean-Marc Bourguet
"" writes:

le fwrite me dérange donc ici, éxiste t'il un équivalent en C++ svp ?


La fonction membre write de ostream est vraissemblablement
ce que tu cherches.

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
Marc
"" a écrit :

file_out.open (checkname, ios::out | ios::binary);


Tu devrais rajouter ios::trunc (c'est sans doute le défaut, mais autant
être explicite).

fwrite (buffer, 1, c, f); // je ne sais pas comment remplacer ceci


file_out.write(buffer,c);

file_out.close();


Avatar
bruckner.olivier
Marc wrote:


file_out.open (checkname, ios::out | ios::binary);



Tu devrais rajouter ios::trunc (c'est sans doute le défaut, mais autant
être explicite).


fwrite (buffer, 1, c, f); // je ne sais pas comment remplacer ceci



file_out.write(buffer,c);


file_out.close();



Marc, j'obtiens le message d'erreur suivant:


error C2664: 'std::basic_ostream<_Elem,_Traits>::write' : cannot
convert parameter 1 from 'unsigned char *' to 'const char *'
with
[
_Elem=char,
_Traits=std::char_traits<char>
]
Types pointed to are unrelated; conversion requires
reinterpret_cast, C-style cast or function-style cast

mais je comprends pas pourquoi il me parle de 'const char *'
en effet dans mon code buffer est loin d'etre constant.

unsigned char *buffer = new unsigned char[vid.width * vid.height * 3 + 18];

voilà je me creuse les méninges, mais je me vois encore dans
l'obligation de vous demander de l'aide et un éclairssissement.

Merci de vos réponses.


Avatar
Marc
"" wrote:

error C2664: 'std::basic_ostream<_Elem,_Traits>::write' : cannot
convert parameter 1 from 'unsigned char *' to 'const char *'
with
[
_Elem=char,
_Traits=std::char_traits<char>
]
Types pointed to are unrelated; conversion requires
reinterpret_cast, C-style cast or function-style cast

mais je comprends pas pourquoi il me parle de 'const char *'
en effet dans mon code buffer est loin d'etre constant.


En fait c'est ma faute, je n'ai fait attention que ton buffer contient
des unsigned char et pas des char. Le const en question signifie que la
fonction write ne modifie pas le buffer.

Une solution sale est de suivre l'indication du compilateur :
file_out.write(reinterpret_cast<const char*>(buffer),c);

Plus proprement :
std::copy_n(buffer,c,ostream_iterator(file_out));

Je dis tout ça de tête sans vérifier, donc sans garantie...

Avatar
Marc
Marc wrote:

std::copy_n(buffer,c,ostream_iterator(file_out));


Hum, copy_n ne semble pas standard en fait, donc il faut peut-être
remplacer cette ligne par :

std::copy(buffer,buffer+c,ostream_iterator(file_out));

(plus ça va, plus il faut croiser les orteils que le compilateur sache
optimiser tout ça en un write unique correspondant à ton code C de
départ...)

Avatar
Marc
Marc wrote:

std::copy_n(buffer,c,ostream_iterator(file_out));
Je dis tout ça de tête sans vérifier, donc sans garantie...


Eh oui, j'ai oublié un détail :
std::copy_n(buffer,c,std::ostream_iterator<unsigned char>(file_out));

Si ce n'est toujours pas ça, je pense que tu arriveras à corriger...

Avatar
bruckner.olivier
Marc wrote:
Marc wrote:


std::copy_n(buffer,c,ostream_iterator(file_out));
Je dis tout ça de tête sans vérifier, donc sans garantie...



Eh oui, j'ai oublié un détail :
std::copy_n(buffer,c,std::ostream_iterator<unsigned char>(file_out));

Si ce n'est toujours pas ça, je pense que tu arriveras à corriger...


Marc, ios::trunc fait tout foirer. en effet j'enregistre des captures
d'écran par un appui sur une touche, et ca fait 100 copies d'un coup et
de fichiers vide.

En revanche celà fonctionne parfaitement juste avec ios::out | ios::binary.

bref c'est impecc, une simple 'file_out.write ((const char *)buffer,
c);' et c'est dans la poche, encore merci.


Avatar
kanze
"" wrote in
message news:<40aaa0ec$0$2516$...
Marc wrote:
Marc wrote:

std::copy_n(buffer,c,ostream_iterator(file_out));
Je dis tout ça de tête sans vérifier, donc sans garantie...


Eh oui, j'ai oublié un détail :
std::copy_n(buffer,c,std::ostream_iterator<unsigned char>(file_out));



Je ne suis pas sûr que ce soit la bonne solution. L'ostream_iterator
utilise l'opérateur <<, ce qui « formatte ». C'est vrai que dans le cas
de « unsigned char », le formattage ne fait pas grand chose (à condition
que width() n'a pas été positionné auparavant), mais en principe, c'est
faut. ostreambuf_iterator serait plus correct, conceptuellement.

Aussi, pour être 100% sûr, il faudrait faire imbue( std::locale( "C" )
sur le flux avant de s'en servir.

Si ce n'est toujours pas ça, je pense que tu arriveras à corriger...


Marc, ios::trunc fait tout foirer. en effet j'enregistre des captures
d'écran par un appui sur une touche, et ca fait 100 copies d'un coup
et de fichiers vide.


Dans ce cas-ci, avec une implémentation conforme de C++, le trunc ne
doit pas faire de différence. Avec ou sans le trunc, c'est l'équivalent
d'ouvrir le fichier avec "wb" en C. Et "wb" vide le fichier. (C'est un
des problèmes à définir des modes d'ouverture en termes de C ; on ne
peut offrir que des fonctionnalités de C, bien que la façon qu'on la
spécifie en C++ offrira d'autres possibilités.)

En tout cas, ça correspond au C que tu as posté. Tu a appelé fopen avec
"wb" comme deuxième paramètre. L'équivalent en C++, c'est bien ios::out
| ios::binary [| ios::trunc].

De la norme C (99, mais ça n'a pas changé de C90), concernant le
paramètre de mode :
wb truncate to zero length or create binary file for writing

Je ne comprends pas trop ton histoire de « 100 copies d'un coup et des
ficheiers vides ». La séquence est bien :

void
write_out( std::string const& filename,
std::vector< unsigned char > const& data )

{
std::ofstream file_out(
filename.c_str(),
std::ios::out | std::ios::binary | std::ios::trunc ) ;
if ( ! file_out ) {
// Erreur...
} else {
if ( data.size() > 0 ) {
file_out.write(
reinterpret_cast< char const* >( &data[0] ),
data.size() ) ;
}
file_out.close() ;
if ( ! file_out ) {
// Erreur d'écriture...
}
}
}

(J'ai pris la liberté de rétranscrire tout en C++. C'est possible que tu
ne veux pas aller aussi loin d'un coup, mais ça ne change rien dans les
principes.)

Si tu donnes le même nom de fichier à chaque appel, évidemment, tu
écrases le précédant à chaque fois ; à la fin, tu as un fichier avec le
résultat du dernière écriture. Si tu donnes des noms différents, tu as
autant de fichiers que d'appels à la fonction.

Si tu veux mettre plusieurs buffers dans le même fichier, il faut
l'ouvrir qu'une fois, et ne le fermer que quand tu auras fini. Ou
l'ouvrir sans le std::ios::trunc, mais avec std::ios::app. Si tu veux
pouvoir positionner de façon arbitraire dans le fichier, il faut
l'ouvrir ios::in | ios::out, même si tu ne compte que d'y écrire.

En revanche celà fonctionne parfaitement juste avec ios::out |
ios::binary.


Si l'ajonction de ios::trunc fait une différence, ton implémentation C++
n'est pas conforme.

C'est possible que tu es en train d'utiliser <iostream.h>, plutôt que
<ostream>. Malheureusement, je ne connais pas ce que faisait les
iostream classiques dans ce cas-là. Je ne me suis jamais servi des
iostream pour les entrées/sorties binaires. (Chaque fois que j'ai eu
besoins des entrées/sorties binaire, j'ai eu besoin aussi des
fonctionnalités non disponible dans les iostream. Et que c'était des
programmes pour une plateforme bien précise -- Solaris ou HP/UX. Je me
suis donc rétourné vers les fonctions de l'API de la plateforme.)

bref c'est impecc, une simple 'file_out.write ((const char *)buffer,
c);' et c'est dans la poche, encore merci.


Pourvu que ça marche. J'aimerais bien savoir quand même pourquoi ça
marchait avec "wb" en C, mais non avec le ios::trunc en C++.

--
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
Marc
wrote:

std::copy_n(buffer,c,std::ostream_iterator<unsigned char>(file_out));



Je ne suis pas sûr que ce soit la bonne solution. L'ostream_iterator
utilise l'opérateur <<, ce qui « formatte ». C'est vrai que dans le cas
de « unsigned char », le formattage ne fait pas grand chose (à condition
que width() n'a pas été positionné auparavant), mais en principe, c'est
faux. ostreambuf_iterator serait plus correct, conceptuellement.


zut, ostreambuf_iterator n'est pas documenté dans mes sources
habituelles. Et msdn semble dire qu'il utilise l'opérateur >>, ce
qui est un peu absurde...

Sinon, le fait que le fichier soit ouvert en ios::binary ne change rien
aux histoires de formattage ?

Aussi, pour être 100% sûr, il faudrait faire imbue( std::locale( "C" )
sur le flux avant de s'en servir.


Ah oui, effectivement.

(Chaque fois que j'ai eu
besoins des entrées/sorties binaire, j'ai eu besoin aussi des
fonctionnalités non disponible dans les iostream. Et que c'était des
programmes pour une plateforme bien précise -- Solaris ou HP/UX. Je me
suis donc rétourné vers les fonctions de l'API de la plateforme.)


Tiens, moi non plus je n'utilise jamais les iostream pour les fichiers
binaires. read et write conviennent parfaitement à ce genre de travail.

PS: C'est bien ici, on apprend des choses en répondant aux questions des
autres :-)



Avatar
kanze
Marc wrote in message
news:<c8fa2a$8i1$...
wrote:

std::copy_n(buffer,c,std::ostream_iterator<unsigned char>(file_out));



Je ne suis pas sûr que ce soit la bonne solution. L'ostream_iterator
utilise l'opérateur <<, ce qui « formatte ». C'est vrai que dans le
cas de « unsigned char », le formattage ne fait pas grand chose (à
condition que width() n'a pas été positionné auparavant), mais en
principe, c'est faux. ostreambuf_iterator serait plus correct,
conceptuellement.


zut, ostreambuf_iterator n'est pas documenté dans mes sources
habituelles. Et msdn semble dire qu'il utilise l'opérateur <<, ce qui
est un peu absurde...


Un peu absurde, oui, étant donné que les streambuf n'ont pas d'opérateur
<<.

Sinon, le fait que le fichier soit ouvert en ios::binary ne change
rien aux histoires de formattage ?


Non. Ce devient vraiment une FAQ :

Quand tu sors quelque chose par un std::ofstream, il y a trois
transformations qui ont lieu :

le formattage :
C'est le rôle, ou au moins, un des rôles princiaux des ostream.
C'est ce qui se fait dans l'opérateur <<. Pour l'éviter, on ne se
sert pas des opérateurs <<. Certains diront même, on ne se sert pas
des ostream, mais s'attaque directement aux streambuf. Je n'en suis
pas tout à fait convaincu ; les ostream ont aussi un rôle à jouer
dans la gestion de l'erreur.

À cet égard, il vaut la peine de dire qu'on ne veut pour ainsi dire
jamais des sorties non formattées. On peut, dans bien de cas,
vouloir des formattages binaires (XDR, BER, etc., ou quelque chose
qu'on a défini soi-même), mais c'est toujours formatté. Or, les
ostream, eux, ne savent qu'un type de formattage. Alors, si on peut
bricoler des buffers, puis les sortir au moyen de ostream::write,
c'est ce que j'appelerais une solution « quick and dirty ». La
solution propre consiste à faire ces propres ostream, avec les
opérateurs << qui convient. Ces ostream-là doivent dériver de ios,
de façon à hériter la gestion des erreurs et du streambuf*, mais ils
n'ont rien à faire directement avec ostream.

le transcodage :
Pour je ne sais pas quelle raison, le transcodage a lieu dans
filebuf. (Logiquement, on s'attendrait à une classe flitrante
intermédiaire, comme c'est le cas en Java.) Si tu veux l'éviter, la
solution consiste à s'assurer que le codecvt du locale imbué est
celle du locale "C", c-à-d à faire quelque chose du genre :

dest.imbue(
std::locale(
dest.getloc(),
std::use_facet< std::codecvt< char, char, mbstate_t >(
std::locale( "C" ) ) ) ) ;

Dans la pratique, la plupart des codecvt< char, char > ont un
transcodage dégénéré. C'est donc que l'erreur peut passer inaperçue
pendant longtemps.

l'adaptation aux conventions du SE :
C'est la conversion d'un 'n' ou d'une fin de fichier en ce qui sert
dans le système d'exploitation. Cette transformation s'effectue en
tout dernier, après le transcodage. Pour la supprimer (ou plutôt,
pour utiliser d'autres conventions), on ouvre le fichier en mode
binaire.

[...]
Tiens, moi non plus je n'utilise jamais les iostream pour les fichiers
binaires. read et write conviennent parfaitement à ce genre de
travail.


À part éventuellement des questions de portabilité.

En fait, je me suis servi des entrées/sorties binaire que dans deux
cas@: la communication entre processeurs, et la persistance. Dans le
premier cas, il me faut en général lire et écrire aux sockets, qui ont
leurs propres protocols, et qui ne sont pas supportés par les iostream.
Et dans le seconde, il m'a prèsque toujours fallu des écritures
synchronisées et plus ou moins atomiques. Qui ne sont pas supportées non
plus.

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