OVH Cloud OVH Cloud

ofstream : fichiers binaires

18 réponses
Avatar
Jeremie Fouche
Bonjour a tous

Dans mon projet, je souhaite sauvegarder mes données sous plusieurs formats.
Pour cela, j'ai décider de dériver plusieurs classes de ofstream (autant que
de format), et de surcharger l'operator<< pour chacune. Pour les format de
type texte, il n'y a pas de problemes, mais pour les formats binaires, la
seul solution que j'ai trouvé me semble <tres> lourde, voyez plutot :

#include <iostream>
#include <fstream>
using namespace std;

class A
{
public:
A():m_value(77) {}
virtual ~A() {}

public:
int m_value;
};

class oMadStream : public ofstream
{
public:
oMadStream(const char* sTitre)
{
open(sTitre, ios_base::out|ios_base::trunc|ios_base::binary);
}
};

oMadStream& operator<<(oMadStream& osMad, const A& rcA)
{
osMad << "mad";
osMad << static_cast<char> ((rcA.m_value )&0xFF);
osMad << static_cast<char> ((rcA.m_value>> 8)&0xFF);
osMad << static_cast<char> ((rcA.m_value>>16)&0xFF);
osMad << static_cast<char> ((rcA.m_value>>24)&0xFF);
return osMad;
}

ostream& operator<<(ostream& os, const A& rcA)
{
os << "m_value=" << rcA.m_value << endl;
return os;
}

int main(void)
{
A a;
cout << a;

oMadStream mad("test.mad");
if (mad.is_open())
{
mad << a;
mad.close();
}

return 0;
}

Rassurez moi, y a t il l'équivalent de la fonction C fwrite qui permettrait
de sauvegarder mes données au format binaires ?
Merci

--
Jérémie

10 réponses

1 2
Avatar
Jeremie Fouche
Jeremie Fouche a écrit dans le message
: cc13lc$mpq$
Bonjour a tous

Dans mon projet, je souhaite sauvegarder mes données sous plusieurs
formats.

Pour cela, j'ai décider de dériver plusieurs classes de ofstream (autant
que

de format), et de surcharger l'operator<< pour chacune. Pour les format de
type texte, il n'y a pas de problemes, mais pour les formats binaires, la
seul solution que j'ai trouvé me semble <tres> lourde, voyez plutot :


[SNIP]

osMad << static_cast<char> ((rcA.m_value )&0xFF);
osMad << static_cast<char> ((rcA.m_value>> 8)&0xFF);
osMad << static_cast<char> ((rcA.m_value>>16)&0xFF);
osMad << static_cast<char> ((rcA.m_value>>24)&0xFF);


osMad.write( (char*) &(rcA.m_value), sizeof rcA.m_value):

Rassurez moi, y a t il l'équivalent de la fonction C fwrite qui
permettrait

de sauvegarder mes données au format binaires ?


Et oui....

--
Jérémie

Avatar
Alexandre
il n'y aurait pas une méthode put() ou write() dans la classe ostream pour
faire ça ?

"Jeremie Fouche" a écrit dans le
message de news:cc13lc$mpq$
Bonjour a tous

Dans mon projet, je souhaite sauvegarder mes données sous plusieurs
formats.

Pour cela, j'ai décider de dériver plusieurs classes de ofstream (autant
que

de format), et de surcharger l'operator<< pour chacune. Pour les format de
type texte, il n'y a pas de problemes, mais pour les formats binaires, la
seul solution que j'ai trouvé me semble <tres> lourde, voyez plutot :

#include <iostream>
#include <fstream>
using namespace std;

class A
{
public:
A():m_value(77) {}
virtual ~A() {}

public:
int m_value;
};

class oMadStream : public ofstream
{
public:
oMadStream(const char* sTitre)
{
open(sTitre, ios_base::out|ios_base::trunc|ios_base::binary);
}
};

oMadStream& operator<<(oMadStream& osMad, const A& rcA)
{
osMad << "mad";
osMad << static_cast<char> ((rcA.m_value )&0xFF);
osMad << static_cast<char> ((rcA.m_value>> 8)&0xFF);
osMad << static_cast<char> ((rcA.m_value>>16)&0xFF);
osMad << static_cast<char> ((rcA.m_value>>24)&0xFF);
return osMad;
}

ostream& operator<<(ostream& os, const A& rcA)
{
os << "m_value=" << rcA.m_value << endl;
return os;
}

int main(void)
{
A a;
cout << a;

oMadStream mad("test.mad");
if (mad.is_open())
{
mad << a;
mad.close();
}

return 0;
}

Rassurez moi, y a t il l'équivalent de la fonction C fwrite qui
permettrait

de sauvegarder mes données au format binaires ?
Merci

--
Jérémie



Avatar
Jean-Marc Bourguet
"Jeremie Fouche" writes:

[...]
oMadStream& operator<<(oMadStream& osMad, const A& rcA)
{
osMad << "mad";
osMad << static_cast<char> ((rcA.m_value )&0xFF);
osMad << static_cast<char> ((rcA.m_value>> 8)&0xFF);
osMad << static_cast<char> ((rcA.m_value>>16)&0xFF);
osMad << static_cast<char> ((rcA.m_value>>24)&0xFF);
return osMad;
}


[...]
Rassurez moi, y a t il l'équivalent de la fonction C fwrite qui permettrait
de sauvegarder mes données au format binaires ?
Merci


Il y a un membre write de ostream qui fait la meme chose que fwrite.

Mais ca ne fait pas la meme chose que ce que tu as ecrit (tu sors les
bytes dans un ordre fixe tandis que write le fait dans un ordre
dependant de la machine) qui est presque la bonne solution.

Pour faire les choses vraiment bien, il ne faudrait pas derive de
std::ofstream mais de std::basic_ios (ce qui te permets d'utiliser les
streambuf et en particulier std::basic_filebuf).

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
"Jeremie Fouche" wrote in message
news:<cc13lc$mpq$...

Dans mon projet, je souhaite sauvegarder mes données sous plusieurs
formats. Pour cela, j'ai décider de dériver plusieurs classes de
ofstream (autant que de format), et de surcharger l'operator<< pour
chacune.


Pourquoi l'héritage de ofstream ? Les operator<< ne sont pas virtuels.
Ils ont une sémantique bien définie dans ostream, définition qui fait
partie du contrat de la classe.

Pour les format de type texte, il n'y a pas de problemes, mais pour
les formats binaires, la seul solution que j'ai trouvé me semble
<tres> lourde, voyez plutot :

#include <iostream>
#include <fstream>
using namespace std;

class A
{
public:
A():m_value(77) {}
virtual ~A() {}

public:
int m_value;
};

class oMadStream : public ofstream
{
public:
oMadStream(const char* sTitre)
{
open(sTitre, ios_base::out|ios_base::trunc|ios_base::binary);
}
};

oMadStream& operator<<(oMadStream& osMad, const A& rcA)
{
osMad << "mad";
osMad << static_cast<char> ((rcA.m_value )&0xFF);
osMad << static_cast<char> ((rcA.m_value>> 8)&0xFF);
osMad << static_cast<char> ((rcA.m_value>>16)&0xFF);
osMad << static_cast<char> ((rcA.m_value>>24)&0xFF);
return osMad;
}


En général, on aurait plutôt écrit un oMadStream& operartor<<(
oMadStream&, int ) d'abord. Mais sinon, c'est la technique consacrée
pour sortir un int en binaire. (À la rigueur, on préfère l'affecter
d'abord à un unsigned, puis faire les manips sur l'unsigned. Mais la
différence n'est detectable que si la machine n'est pas à complément à
deux.)

En général, aussi, il n'y a pas besoin d'hériter de ofstream pour ça.
C'est même contre-indiqué -- si tu en hérites, et tu écris :

osMad << premierElem << deuxiemeElem ;

et quelqu'un a oublié de fournir un operator<<( oMadStream& ) pour le
type du premier élément, tu n'aurais pas d'erreur de compilation, mais
plutôt des sorties textuelles.

ostream& operator<<(ostream& os, const A& rcA)
{
os << "m_value=" << rcA.m_value << endl;
return os;
}

int main(void)
{
A a;
cout << a;

oMadStream mad("test.mad");
if (mad.is_open())
{
mad << a;
mad.close();
}

return 0;
}

Rassurez moi, y a t il l'équivalent de la fonction C fwrite qui
permettrait de sauvegarder mes données au format binaires ?


Il y a un équivalent de la fonction C fwrite, mais il ne marche pas
mieux que fwrite. La solution que tu as écris est nettement mieux.

--
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
Jeremie Fouche
Jean-Marc Bourguet a écrit dans le message :

"Jeremie Fouche" writes:


[...]
Pour faire les choses vraiment bien, il ne faudrait pas derive de
std::ofstream mais de std::basic_ios (ce qui te permets d'utiliser les
streambuf et en particulier std::basic_filebuf).


ok, merci.
J'ai bien lu aussi la réponse de James Kanze.
J'ai donc essayé ca, est ce plus propre ?

#include <iostream>
#include <fstream>
using namespace std;

class CMad
{
public:
CMad():m_value(15) {}
virtual ~CMad() {}

public:
int m_value;
};

class madStream : public ios
{
public:
explicit madStream(filebuf *flux)
{
init(flux);
}
};

madStream& operator<<(madStream& oms, const CMad& rcMad)
{
oms.rdbuf()->sputc( (char) rcMad.m_value );
return oms;
}

madStream& operator>>(madStream& ims, CMad& rMad)
{
rMad.m_value = ims.rdbuf()->sgetc();
return ims;
}

ostream& operator<<(ostream& os, const CMad& rcMad)
{
os << "value=" << rcMad.m_value << endl;
return os;
}

int main(void)
{
CMad mad;
filebuf fb;
fb.open("mad", ios::out|ios::binary|ios::trunc);
if ( fb.is_open() )
{
madStream oms(&fb);
oms << mad;
fb.close();
}

mad.m_value = 10;
cout << mad;
filebuf fb2;
fb2.open("mad", ios::in|ios::binary);
if (fb2.is_open())
{
madStream ims(&fb2);
ims >> mad;
cout << mad;
fb2.close();
}
return 0;
}

La chose qui me gene est que j'aurai souhaité cacher le fait que le filebuf
doive etre ouvert en mode binaire. Peut etre mettre l'initialisation du
filebuf dans le constructeur de madStream ie:

madStream(const char* sNom, madStream::openmode mode)
{
filebuf fb;
fb.open(mode|ios_base::mode_binary);
init(&fb);
}

Merci encore pour les précisions que vous m'avez apporté.
--
Jérémie

Avatar
Jeremie Fouche
a écrit dans le message :

"Jeremie Fouche" wrote in message
news:<cc13lc$mpq$...

Dans mon projet, je souhaite sauvegarder mes données sous plusieurs
formats. Pour cela, j'ai décider de dériver plusieurs classes de
ofstream (autant que de format), et de surcharger l'operator<< pour
chacune.


Pourquoi l'héritage de ofstream ? Les operator<< ne sont pas virtuels.
Ils ont une sémantique bien définie dans ostream, définition qui fait
partie du contrat de la classe.


oui.

oMadStream& operator<<(oMadStream& osMad, const A& rcA)
{
osMad << "mad";
osMad << static_cast<char> ((rcA.m_value )&0xFF);
osMad << static_cast<char> ((rcA.m_value>> 8)&0xFF);
osMad << static_cast<char> ((rcA.m_value>>16)&0xFF);
osMad << static_cast<char> ((rcA.m_value>>24)&0xFF);
return osMad;
}


En général, on aurait plutôt écrit un oMadStream& operartor<<(
oMadStream&, int ) d'abord. Mais sinon, c'est la technique consacrée
pour sortir un int en binaire. (À la rigueur, on préfère l'affecter
d'abord à un unsigned, puis faire les manips sur l'unsigned. Mais la
différence n'est detectable que si la machine n'est pas à complément à
deux.)


Donc, il faudrait mieux que je surcharge l'opérator<< pour chaque type de
base contenu dans ma classe A ?

En général, aussi, il n'y a pas besoin d'hériter de ofstream pour ça.
C'est même contre-indiqué -- si tu en hérites, et tu écris :

osMad << premierElem << deuxiemeElem ;

et quelqu'un a oublié de fournir un operator<<( oMadStream& ) pour le
type du premier élément, tu n'aurais pas d'erreur de compilation, mais
plutôt des sorties textuelles.


ok, voir ma réponse à Jean-Marc Bourguet pour une autre méthode utilisée.
Merci pour tout

--
Jérémie


Avatar
Jean-Marc Bourguet
"Jeremie Fouche" writes:

Jean-Marc Bourguet a écrit dans le message :

"Jeremie Fouche" writes:


[...]
Pour faire les choses vraiment bien, il ne faudrait pas derive de
std::ofstream mais de std::basic_ios (ce qui te permets d'utiliser les
streambuf et en particulier std::basic_filebuf).


ok, merci.
J'ai bien lu aussi la réponse de James Kanze.
J'ai donc essayé ca, est ce plus propre ?


Ca m'a l'air d'etre dans la voie que je suggerais. Mais je n'ai pas
regarde en detail.

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
James Kanze
"Jeremie Fouche" writes:

|> Jean-Marc Bourguet a écrit dans le message :
|>
|> > "Jeremie Fouche" writes:

|> [...]

|> > Pour faire les choses vraiment bien, il ne faudrait pas derive de
|> > std::ofstream mais de std::basic_ios (ce qui te permets d'utiliser
|> > les streambuf et en particulier std::basic_filebuf).

|> ok, merci.

|> J'ai bien lu aussi la réponse de James Kanze. J'ai donc essayé ca,
|> est ce plus propre ?

Nettement.

|> #include <iostream>
|> #include <fstream>
|> using namespace std;

|> class CMad
|> {
|> public:
|> CMad():m_value(15) {}
|> virtual ~CMad() {}

|> public:
|> int m_value;
|> };

|> class madStream : public ios
|> {
|> public:
|> explicit madStream(filebuf *flux)
^^^^^^^

Ici, j'utiliserais streambuf.

|> {
|> init(flux);
|> }
|> };

|> madStream& operator<<(madStream& oms, const CMad& rcMad)
|> {
|> oms.rdbuf()->sputc( (char) rcMad.m_value );

La conversion explicite n'est pas nécessaire, mais... Est-ce que ça fait
réelement ce que tu veux ?

Plus généralement, les operator<< de base doivent probablement se
modéler sur les ostream -- jusqu'au point de définir ta propre classe de
sentinelle. En gros : si on a déjà vu une erreur quelconque, on n'essaie
plus, et si on voit une erreur, il faut en positionner le bit
correspondant dans ios. Donc, en gros, pour sortir un entier, j'écrirai
quelque chose du genre (en supposant des int de 32 bits) :

madStream&
operator<( madStream& dest, int value )
{
madStream::sentry sentry( dest ) ;
if ( sentry ) {
streambuf* sb = dest.rdbuf() ;
unsigned tmp = value ;
int shift = 32 ;
while ( dest && shift > 0 ) {
if ( sb->sputc( (tmp >> shift) & 0xff ) == EOF ) {
dest.clear( std::ios::badbit ) ;
}
shift -= 8 ;
}
}
return dest ;
}

Ça, c'est pour écrire des int comme 4 octets, poids fort d'abord, format
complément à deux. C-à-d format Internet.

Dans un premier temps au moins, madStream::sentry serait assez simple ;
quelque chose du genre :

class madStream::sentry
{
public:
sentry( madStream& dest )
: myDest( dest )
{
}
operator bool() const
{
return ! myDest.fail() ;
}

private:
madStream& myDest ;
}

|> return oms;
|> }

|> madStream& operator>>(madStream& ims, CMad& rMad)

Comme dans les iostream, je définirais d'abord un istream et un
ostream ; si j'ai besoin des flux bidirectionnels, j'ajouterais un
iostream dérivé des deux (mais dans l'ensemble, je préfèrerais éviter la
bidirectionnalité). Et si on prévoit la bidirectionnalité, il faut
dériver virtuellement de std::ios.

|> {
|> rMad.m_value = ims.rdbuf()->sgetc();
|> return ims;
|> }

Ici aussi, tu ne lis qu'un seul caractère. Pire, tu ne signales aucune
erreur. Comme ci-dessus :

iMadStream&
operator>>( IMadStream& source, int& value )
{
IMadStream::sentry sentry( source ) ;
if ( sentry ) {
streambuf* sb = source.rdbuf() ;
unsigned result = 0 ;
int shift = 32 ;
int tmp = sb->sgetc() ;
while ( tmp != EOF && shift > 0 ) {
shift -= 8 ;
result = result | (tmp << shift) ;
tmp = sb->sgetc() ;
}
if ( shift != 0 ) {
if ( shift == 32 ) {
source.clear( std::ios::eofbit | std::ios::failbit ) ;
} else {
source.clear( std::ios::eofbit | std::ios::badbit ) ;
}
} else {
value = tmp ;
}
}
return source ;

Ici, j'avoue ne pas suivre les conventions des istream standard, mais je
ne sais pas comment faire autrement pour permettre à l'utilisateur de
distinguer entre une vraie fin de fichier, et une erreur de formattage.
(Mais beaucoup dépend du format binaire qu'on implémente, et comment on
sait s'il y a un entier qui suit ou non.)

Aussi, l'affectation de value à la fin n'est pas garantie par la norme.
Mais ça fonctionne avec toutes les implémentations modernes, et je ne
sais pas de tête comment l'écrire d'une façon portable.

|> ostream& operator<<(ostream& os, const CMad& rcMad)
|> {
|> os << "value=" << rcMad.m_value << endl;
|> return os;
|> }

|> int main(void)
|> {
|> CMad mad;
|> filebuf fb;
|> fb.open("mad", ios::out|ios::binary|ios::trunc);
|> if ( fb.is_open() )
|> {
|> madStream oms(&fb);
|> oms << mad;
|> fb.close();
|> }

|> mad.m_value = 10;

Il faudrait tester avec d'autres valeurs. Qu'est-ce que ça donne avec
1000, par exemple ?

|> cout << mad;
|> filebuf fb2;
|> fb2.open("mad", ios::in|ios::binary);
|> if (fb2.is_open())
|> {
|> madStream ims(&fb2);
|> ims >> mad;
|> cout << mad;
|> fb2.close();
|> }
|> return 0;
|> }

|> La chose qui me gene est que j'aurai souhaité cacher le fait que le
|> filebuf doive etre ouvert en mode binaire. Peut etre mettre
|> l'initialisation du filebuf dans le constructeur de madStream ie:

|> madStream(const char* sNom, madStream::openmode mode)
|> {
|> filebuf fb;
|> fb.open(mode|ios_base::mode_binary);
|> init(&fb);

Alors, ios va stocker un pointeur au fb qui va disparaître du moment
qu'on qui le constructeur. Pas une bonne idée de tout.

La solution « consacrée », encore, c'est de se modéler sur les iostream.
Tu crées une classe dérivée, fMadStream (ou plutôt ifMadStream et
ofMadStream) qui s'occupe de la gestion du filebuf. Quelque chose du
genre :

class ifMadStream : private virtual std::filebuf, public iMadStream
...

L'astuce avec l'héritage virtuel, c'est un truc que je dois à Dietmar
Kühl -- il s'assure que le filebuf est bien initialisé avant de passer
son adresse au constructeur de iMadStream. Y compris dans le cas où on
dérive un fMadStream bidirectionnel par la suite.

|> }

--
James Kanze
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
James Kanze
"Jeremie Fouche" writes:

[...]
|> > En général, on aurait plutôt écrit un oMadStream& operartor<<(
|> > oMadStream&, int ) d'abord. Mais sinon, c'est la technique
|> > consacrée pour sortir un int en binaire. (À la rigueur, on préfère
|> > l'affecter d'abord à un unsigned, puis faire les manips sur
|> > l'unsigned. Mais la différence n'est detectable que si la machine
|> > n'est pas à complément à deux.)

|> Donc, il faudrait mieux que je surcharge l'opérator<< pour chaque
|> type de base contenu dans ma classe A ?

Je surchargerais l'opérateur << pour char, int, unsigned, long, unsigned
long et double avant même de me poser la question sur ce qui pourrait se
trouver dans la classe A. Quitte à ce que dans un premier temps, le
surcharge pour double soit une fonction membre privée, sans
implémentation.

--
James Kanze
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
Jeremie Fouche
James Kanze a écrit dans le message :

"Jeremie Fouche" writes:

|> Jean-Marc Bourguet a écrit dans le message :
|>
|> > "Jeremie Fouche" writes:

|> [...]

|> > Pour faire les choses vraiment bien, il ne faudrait pas derive de
|> > std::ofstream mais de std::basic_ios (ce qui te permets d'utiliser
|> > les streambuf et en particulier std::basic_filebuf).

|> ok, merci.

|> J'ai bien lu aussi la réponse de James Kanze. J'ai donc essayé ca,
|> est ce plus propre ?

Nettement.


Bien, j'avance ...

|> #include <iostream>
|> #include <fstream>
|> using namespace std;

|> class CMad
|> {
|> public:
|> CMad():m_value(15) {}
|> virtual ~CMad() {}

|> public:
|> int m_value;
|> };

|> class madStream : public ios
|> {
|> public:
|> explicit madStream(filebuf *flux)
^^^^^^^

Ici, j'utiliserais streambuf.


Pourquoi pas, mais en fait, le flux (dans le cas présent) est forcément
orienté fichier. Dans mon projet, j'ai 3 classes de flux différents, 2
orientés fichiers, et 1 quelquonque. Pour info, je travail sur des fichier
musicaux : le format MID, le format MAD (mon format particulier), et un
format txt.

|> {
|> init(flux);
|> }
|> };

|> madStream& operator<<(madStream& oms, const CMad& rcMad)
|> {
|> oms.rdbuf()->sputc( (char) rcMad.m_value );

La conversion explicite n'est pas nécessaire, mais... Est-ce que ça fait
réelement ce que tu veux ?


oui pour le moment, car m_value est inferieur à 127 dans mon projet. Mais la
réponse que j'attend de ta part est tout simplement de déclarer m_value en
char je pense ! cf: une autre remarque plus bas.

Plus généralement, les operator<< de base doivent probablement se
modéler sur les ostream -- jusqu'au point de définir ta propre classe de
sentinelle. En gros : si on a déjà vu une erreur quelconque, on n'essaie
plus, et si on voit une erreur, il faut en positionner le bit
correspondant dans ios. Donc, en gros, pour sortir un entier, j'écrirai
quelque chose du genre (en supposant des int de 32 bits) :

madStream&
operator<<( madStream& dest, int value )
{
madStream::sentry sentry( dest ) ;
if ( sentry ) {
streambuf* sb = dest.rdbuf() ;
unsigned tmp = value ;
int shift = 32 ;
while ( dest && shift > 0 ) {
if ( sb->sputc( (tmp >> shift) & 0xff ) == EOF ) {
dest.clear( std::ios::badbit ) ;
}
shift -= 8 ;
}
}
return dest ;
}

Ça, c'est pour écrire des int comme 4 octets, poids fort d'abord, format
complément à deux. C-à-d format Internet.


Ah oui, ok, je note. C'est <un peu> plus complet que ce que j'ai fait ;)

Dans un premier temps au moins, madStream::sentry serait assez simple ;
quelque chose du genre :

class madStream::sentry
{
public:
sentry( madStream& dest )
: myDest( dest )
{
}
operator bool() const
{
return ! myDest.fail() ;
}

private:
madStream& myDest ;
}


Je n'avais pas trop compris le rome de sentry. Je vais faire des essais pour
comprendre un peu.


Comme dans les iostream, je définirais d'abord un istream et un
ostream ; si j'ai besoin des flux bidirectionnels, j'ajouterais un
iostream dérivé des deux (mais dans l'ensemble, je préfèrerais éviter la
bidirectionnalité). Et si on prévoit la bidirectionnalité, il faut
dériver virtuellement de std::ios.


Bien, je suis d'accord.


|> {
|> rMad.m_value = ims.rdbuf()->sgetc();
|> return ims;
|> }

Ici aussi, tu ne lis qu'un seul caractère. Pire, tu ne signales aucune
erreur. Comme ci-dessus :

iMadStream&
operator>>( IMadStream& source, int& value )
{
IMadStream::sentry sentry( source ) ;
if ( sentry ) {
streambuf* sb = source.rdbuf() ;
unsigned result = 0 ;
int shift = 32 ;
int tmp = sb->sgetc() ;
while ( tmp != EOF && shift > 0 ) {
shift -= 8 ;
result = result | (tmp << shift) ;
tmp = sb->sgetc() ;
}
if ( shift != 0 ) {
if ( shift == 32 ) {
source.clear( std::ios::eofbit | std::ios::failbit ) ;
} else {
source.clear( std::ios::eofbit | std::ios::badbit ) ;
}
} else {
value = tmp ;
}
}
return source ;


heu, je copie, je colle, je vais faire quelque tests, et je pense que je
comprendrais un peu plus tard dans la journée ;)

Ici, j'avoue ne pas suivre les conventions des istream standard, mais je
ne sais pas comment faire autrement pour permettre à l'utilisateur de
distinguer entre une vraie fin de fichier, et une erreur de formattage.
(Mais beaucoup dépend du format binaire qu'on implémente, et comment on
sait s'il y a un entier qui suit ou non.)

Aussi, l'affectation de value à la fin n'est pas garantie par la norme.
Mais ça fonctionne avec toutes les implémentations modernes, et je ne
sais pas de tête comment l'écrire d'une façon portable.

|> ostream& operator<<(ostream& os, const CMad& rcMad)
|> {
|> os << "value=" << rcMad.m_value << endl;
|> return os;
|> }

|> int main(void)
|> {
|> CMad mad;
|> filebuf fb;
|> fb.open("mad", ios::out|ios::binary|ios::trunc);
|> if ( fb.is_open() )
|> {
|> madStream oms(&fb);
|> oms << mad;
|> fb.close();
|> }

|> mad.m_value = 10;

Il faudrait tester avec d'autres valeurs. Qu'est-ce que ça donne avec
1000, par exemple ?


Toujours le meme probleme que précédement, j'utilise en réalité des
accesseurs qui me premettent de controller la valeur passée a m_value.

|> cout << mad;
|> filebuf fb2;
|> fb2.open("mad", ios::in|ios::binary);
|> if (fb2.is_open())
|> {
|> madStream ims(&fb2);
|> ims >> mad;
|> cout << mad;
|> fb2.close();
|> }
|> return 0;
|> }

|> La chose qui me gene est que j'aurai souhaité cacher le fait que le
|> filebuf doive etre ouvert en mode binaire. Peut etre mettre
|> l'initialisation du filebuf dans le constructeur de madStream ie:

|> madStream(const char* sNom, madStream::openmode mode)
|> {
|> filebuf fb;
|> fb.open(mode|ios_base::mode_binary);
|> init(&fb);

Alors, ios va stocker un pointeur au fb qui va disparaître du moment
qu'on qui le constructeur. Pas une bonne idée de tout.


Non, effectivement, mais dans mes tests, le filebuf etait variable membre de
ma classe (on ne peut pas dire que c'était clair). Peut etre n'est ce pas
une bonne idée.

La solution « consacrée », encore, c'est de se modéler sur les iostream.
Tu crées une classe dérivée, fMadStream (ou plutôt ifMadStream et
ofMadStream) qui s'occupe de la gestion du filebuf. Quelque chose du
genre :

class ifMadStream : private virtual std::filebuf, public iMadStream
...


Oula ! Je vais faire quelques essais, car je ne connais pas du tout
l'héritage virtuel.

L'astuce avec l'héritage virtuel, c'est un truc que je dois à Dietmar
Kühl -- il s'assure que le filebuf est bien initialisé avant de passer
son adresse au constructeur de iMadStream. Y compris dans le cas où on
dérive un fMadStream bidirectionnel par la suite.


Merci pour le temps que tu m'accordes. Le passage d'un developpeur C à un
developpeur C++ n'est pas si aisé contrairement a ce que je pensais au
départ ;(
Cordialement

--
Jérémie

1 2