James Kanze a écrit dans le message :"Jeremie Fouche" writes:
|> #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.
James Kanze <kanze@gabi-soft.fr> a écrit dans le message :
m2llhzpvkc.fsf@lns-p19-16-82-65-105-167.adsl.proxad.net...
"Jeremie Fouche" <jeremie.fouche.tonmasque@tiscali.fr> writes:
|> #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.
James Kanze a écrit dans le message :"Jeremie Fouche" writes:
|> #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.
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 ;
}
|> 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.
|> La chose qui me gene est que j'aurai souhaité cacher le fait que le
|> filebuf doive etre ouvert en mode binaire.
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.
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 ;
}
|> 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.
|> La chose qui me gene est que j'aurai souhaité cacher le fait que le
|> filebuf doive etre ouvert en mode binaire.
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.
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 ;
}
|> 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.
|> La chose qui me gene est que j'aurai souhaité cacher le fait que le
|> filebuf doive etre ouvert en mode binaire.
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 a écrit dans le message :
[...]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 ) {
plutot : while (sentry && shilft>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 ;
}
Ce serait cette classe qui specialiserait le type d'erreur, et (par
exemple) les messages a afficher en consequence ?
Je ne vois pas vraiment son utilité par rapport à un méthode
madStream::is_valid() ou quelque chose dans le genre qui me parrait
plus simple.
|> 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() ;
En fait, il me semble que sbumpc est plus adapté a ce que l'on veut faire.
}
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.
Je ne comprend pas. En quoi n'est-ce pas spécifié par la norme.
[...]|> La chose qui me gene est que j'aurai souhaité cacher le fait que
|> le filebuf doive etre ouvert en mode binaire.
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.
Si j'ai bien compris, le mot clef virtuel que tu utilises n'a qu'un
unique but, appeller le constructeur par defaut de la classe
std::filebuf avant le constructeur de iMadStream ?
Est-ce une astuce qui fonctionne car c'était prévu dans la norme ou
bien est-ce un effet de bord du comportement des compilateurs.
Dans la doc que j'ai, je vois que l'utilisation de l'héritage virtuel
est utilisé pour assurer l'unicité de la construction d'un objet dans
le cas d'un multi-héritage avec plusieurs classes peres
identiques. Ai-je mal compris cette partie ?
James Kanze <kanze@gabi-soft.fr> a écrit dans le message :
m2llhzpvkc.fsf@lns-p19-16-82-65-105-167.adsl.proxad.net...
[...]
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 ) {
plutot : while (sentry && shilft>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 ;
}
Ce serait cette classe qui specialiserait le type d'erreur, et (par
exemple) les messages a afficher en consequence ?
Je ne vois pas vraiment son utilité par rapport à un méthode
madStream::is_valid() ou quelque chose dans le genre qui me parrait
plus simple.
|> 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() ;
En fait, il me semble que sbumpc est plus adapté a ce que l'on veut faire.
}
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.
Je ne comprend pas. En quoi n'est-ce pas spécifié par la norme.
[...]
|> La chose qui me gene est que j'aurai souhaité cacher le fait que
|> le filebuf doive etre ouvert en mode binaire.
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.
Si j'ai bien compris, le mot clef virtuel que tu utilises n'a qu'un
unique but, appeller le constructeur par defaut de la classe
std::filebuf avant le constructeur de iMadStream ?
Est-ce une astuce qui fonctionne car c'était prévu dans la norme ou
bien est-ce un effet de bord du comportement des compilateurs.
Dans la doc que j'ai, je vois que l'utilisation de l'héritage virtuel
est utilisé pour assurer l'unicité de la construction d'un objet dans
le cas d'un multi-héritage avec plusieurs classes peres
identiques. Ai-je mal compris cette partie ?
James Kanze a écrit dans le message :
[...]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 ) {
plutot : while (sentry && shilft>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 ;
}
Ce serait cette classe qui specialiserait le type d'erreur, et (par
exemple) les messages a afficher en consequence ?
Je ne vois pas vraiment son utilité par rapport à un méthode
madStream::is_valid() ou quelque chose dans le genre qui me parrait
plus simple.
|> 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() ;
En fait, il me semble que sbumpc est plus adapté a ce que l'on veut faire.
}
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.
Je ne comprend pas. En quoi n'est-ce pas spécifié par la norme.
[...]|> La chose qui me gene est que j'aurai souhaité cacher le fait que
|> le filebuf doive etre ouvert en mode binaire.
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.
Si j'ai bien compris, le mot clef virtuel que tu utilises n'a qu'un
unique but, appeller le constructeur par defaut de la classe
std::filebuf avant le constructeur de iMadStream ?
Est-ce une astuce qui fonctionne car c'était prévu dans la norme ou
bien est-ce un effet de bord du comportement des compilateurs.
Dans la doc que j'ai, je vois que l'utilisation de l'héritage virtuel
est utilisé pour assurer l'unicité de la construction d'un objet dans
le cas d'un multi-héritage avec plusieurs classes peres
identiques. Ai-je mal compris cette partie ?
James Kanze a écrit dans le message :
[...]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 ) {
plutot : while (sentry && shilft>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 ;
}
Ce serait cette classe qui specialiserait le type d'erreur, et (par
exemple) les messages a afficher en consequence ?
Je ne vois pas vraiment son utilité par rapport à un méthode
madStream::is_valid() ou quelque chose dans le genre qui me parrait
plus simple.
|> 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() ;
En fait, il me semble que sbumpc est plus adapté a ce que l'on veut faire.
}
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.
Je ne comprend pas. En quoi n'est-ce pas spécifié par la norme.
[...]|> La chose qui me gene est que j'aurai souhaité cacher le fait que
|> le filebuf doive etre ouvert en mode binaire.
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.
Si j'ai bien compris, le mot clef virtuel que tu utilises n'a qu'un
unique but, appeller le constructeur par defaut de la classe
std::filebuf avant le constructeur de iMadStream ?
Est-ce une astuce qui fonctionne car c'était prévu dans la norme ou
bien est-ce un effet de bord du comportement des compilateurs.
Dans la doc que j'ai, je vois que l'utilisation de l'héritage virtuel
est utilisé pour assurer l'unicité de la construction d'un objet dans
le cas d'un multi-héritage avec plusieurs classes peres
identiques. Ai-je mal compris cette partie ?
James Kanze <kanze@gabi-soft.fr> a écrit dans le message :
m2llhzpvkc.fsf@lns-p19-16-82-65-105-167.adsl.proxad.net...
[...]
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 ) {
plutot : while (sentry && shilft>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 ;
}
Ce serait cette classe qui specialiserait le type d'erreur, et (par
exemple) les messages a afficher en consequence ?
Je ne vois pas vraiment son utilité par rapport à un méthode
madStream::is_valid() ou quelque chose dans le genre qui me parrait
plus simple.
|> 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() ;
En fait, il me semble que sbumpc est plus adapté a ce que l'on veut faire.
}
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.
Je ne comprend pas. En quoi n'est-ce pas spécifié par la norme.
[...]
|> La chose qui me gene est que j'aurai souhaité cacher le fait que
|> le filebuf doive etre ouvert en mode binaire.
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.
Si j'ai bien compris, le mot clef virtuel que tu utilises n'a qu'un
unique but, appeller le constructeur par defaut de la classe
std::filebuf avant le constructeur de iMadStream ?
Est-ce une astuce qui fonctionne car c'était prévu dans la norme ou
bien est-ce un effet de bord du comportement des compilateurs.
Dans la doc que j'ai, je vois que l'utilisation de l'héritage virtuel
est utilisé pour assurer l'unicité de la construction d'un objet dans
le cas d'un multi-héritage avec plusieurs classes peres
identiques. Ai-je mal compris cette partie ?
James Kanze a écrit dans le message :
[...]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 ) {
plutot : while (sentry && shilft>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 ;
}
Ce serait cette classe qui specialiserait le type d'erreur, et (par
exemple) les messages a afficher en consequence ?
Je ne vois pas vraiment son utilité par rapport à un méthode
madStream::is_valid() ou quelque chose dans le genre qui me parrait
plus simple.
|> 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() ;
En fait, il me semble que sbumpc est plus adapté a ce que l'on veut faire.
}
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.
Je ne comprend pas. En quoi n'est-ce pas spécifié par la norme.
[...]|> La chose qui me gene est que j'aurai souhaité cacher le fait que
|> le filebuf doive etre ouvert en mode binaire.
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.
Si j'ai bien compris, le mot clef virtuel que tu utilises n'a qu'un
unique but, appeller le constructeur par defaut de la classe
std::filebuf avant le constructeur de iMadStream ?
Est-ce une astuce qui fonctionne car c'était prévu dans la norme ou
bien est-ce un effet de bord du comportement des compilateurs.
Dans la doc que j'ai, je vois que l'utilisation de l'héritage virtuel
est utilisé pour assurer l'unicité de la construction d'un objet dans
le cas d'un multi-héritage avec plusieurs classes peres
identiques. Ai-je mal compris cette partie ?
"Jeremie Fouche" wrote in message
news:<ccjau9$8vo$...James Kanze a écrit dans le message :
tmp = sb->sgetc() ;
En fait, il me semble que sbumpc est plus adapté a ce que l'on veut
faire.
Ooops. Tout à fait. Même après tant de temps, je me laisse berner
par le nom.
Je ne comprend pas. En quoi n'est-ce pas spécifié par la norme.
L'état de la variable qu'on lit en cas d'erreur. Je crois que c'est
spécifié pour certains types, que la variable n'est pas modifiée. Mais
je ne suis pas sûr que ce soit le cas pour tous les types. Si je lis
vers un std::string, par exemple ? Est-ce que l'implémentation est
obligée à stocker dans une chaîne locale jusqu'à ce qu'elle a fini, pour
être sûr qu'il n'y a pas d'erreur, et puis affecter la chaîne à sa
destination finale, ou est-ce que l'implémentation peut stocker
directement dans la destination finale ?
"Jeremie Fouche" <jeremie.fouche.tonmasque@tiscali.fr> wrote in message
news:<ccjau9$8vo$1@news.tiscali.fr>...
James Kanze <kanze@gabi-soft.fr> a écrit dans le message :
m2llhzpvkc.fsf@lns-p19-16-82-65-105-167.adsl.proxad.net...
tmp = sb->sgetc() ;
En fait, il me semble que sbumpc est plus adapté a ce que l'on veut
faire.
Ooops. Tout à fait. Même après tant de temps, je me laisse berner
par le nom.
Je ne comprend pas. En quoi n'est-ce pas spécifié par la norme.
L'état de la variable qu'on lit en cas d'erreur. Je crois que c'est
spécifié pour certains types, que la variable n'est pas modifiée. Mais
je ne suis pas sûr que ce soit le cas pour tous les types. Si je lis
vers un std::string, par exemple ? Est-ce que l'implémentation est
obligée à stocker dans une chaîne locale jusqu'à ce qu'elle a fini, pour
être sûr qu'il n'y a pas d'erreur, et puis affecter la chaîne à sa
destination finale, ou est-ce que l'implémentation peut stocker
directement dans la destination finale ?
"Jeremie Fouche" wrote in message
news:<ccjau9$8vo$...James Kanze a écrit dans le message :
tmp = sb->sgetc() ;
En fait, il me semble que sbumpc est plus adapté a ce que l'on veut
faire.
Ooops. Tout à fait. Même après tant de temps, je me laisse berner
par le nom.
Je ne comprend pas. En quoi n'est-ce pas spécifié par la norme.
L'état de la variable qu'on lit en cas d'erreur. Je crois que c'est
spécifié pour certains types, que la variable n'est pas modifiée. Mais
je ne suis pas sûr que ce soit le cas pour tous les types. Si je lis
vers un std::string, par exemple ? Est-ce que l'implémentation est
obligée à stocker dans une chaîne locale jusqu'à ce qu'elle a fini, pour
être sûr qu'il n'y a pas d'erreur, et puis affecter la chaîne à sa
destination finale, ou est-ce que l'implémentation peut stocker
directement dans la destination finale ?
Je vais peut-être jeter un coup
d'oeil dans la norme, à la recherche de cette (absence de ?) garantie.
Je vais peut-être jeter un coup
d'oeil dans la norme, à la recherche de cette (absence de ?) garantie.
Je vais peut-être jeter un coup
d'oeil dans la norme, à la recherche de cette (absence de ?) garantie.
writes:"Jeremie Fouche" wrote in message
news:<ccjau9$8vo$...James Kanze a écrit dans le message :
[...]tmp = sb->sgetc() ;
En fait, il me semble que sbumpc est plus adapté a ce que l'on veut
faire.
Ooops. Tout à fait. Même après tant de temps, je me laisse berner
par le nom.
Si je comprend bien, l'unique différence est que sgetc() fait un
read ahead, il lit le prochain caractère sans incrémenter la position
courante ?
[...]Je ne comprend pas. En quoi n'est-ce pas spécifié par la norme.
L'état de la variable qu'on lit en cas d'erreur. Je crois que c'est
spécifié pour certains types, que la variable n'est pas
modifiée. Mais je ne suis pas sûr que ce soit le cas pour tous les
types. Si je lis vers un std::string, par exemple ? Est-ce que
l'implémentation est obligée à stocker dans une chaîne locale
jusqu'à ce qu'elle a fini, pour être sûr qu'il n'y a pas d'erreur,
et puis affecter la chaîne à sa destination finale, ou est-ce que
l'implémentation peut stocker directement dans la destination
finale ?
Je ne sais pas si la norme le spécifie, mais je n'y vois pas
d'opposition. Je ne vois pas vraiment d'intérêt de modifier
directement la chaîne cible par rapport à
std::string result ;
// remplissage de result ...
target.swap( result ) ;
Je vois par contre des inconvénients. Je vais peut-être jeter un coup
d'oeil dans la norme, à la recherche de cette (absence de ?) garantie.
Sinon, quelqu'un voit-il une raison de ne pas garantir l'atomicité
de la lecture d'une chaîne ? Le code ci-dessus n'est-il pas assez bon
(pour une définition ad-hoc de bon) pour garantir cette atomicité ?
kanze@gabi-soft.fr writes:
"Jeremie Fouche" <jeremie.fouche.tonmasque@tiscali.fr> wrote in message
news:<ccjau9$8vo$1@news.tiscali.fr>...
James Kanze <kanze@gabi-soft.fr> a écrit dans le message :
m2llhzpvkc.fsf@lns-p19-16-82-65-105-167.adsl.proxad.net...
[...]
tmp = sb->sgetc() ;
En fait, il me semble que sbumpc est plus adapté a ce que l'on veut
faire.
Ooops. Tout à fait. Même après tant de temps, je me laisse berner
par le nom.
Si je comprend bien, l'unique différence est que sgetc() fait un
read ahead, il lit le prochain caractère sans incrémenter la position
courante ?
[...]
Je ne comprend pas. En quoi n'est-ce pas spécifié par la norme.
L'état de la variable qu'on lit en cas d'erreur. Je crois que c'est
spécifié pour certains types, que la variable n'est pas
modifiée. Mais je ne suis pas sûr que ce soit le cas pour tous les
types. Si je lis vers un std::string, par exemple ? Est-ce que
l'implémentation est obligée à stocker dans une chaîne locale
jusqu'à ce qu'elle a fini, pour être sûr qu'il n'y a pas d'erreur,
et puis affecter la chaîne à sa destination finale, ou est-ce que
l'implémentation peut stocker directement dans la destination
finale ?
Je ne sais pas si la norme le spécifie, mais je n'y vois pas
d'opposition. Je ne vois pas vraiment d'intérêt de modifier
directement la chaîne cible par rapport à
std::string result ;
// remplissage de result ...
target.swap( result ) ;
Je vois par contre des inconvénients. Je vais peut-être jeter un coup
d'oeil dans la norme, à la recherche de cette (absence de ?) garantie.
Sinon, quelqu'un voit-il une raison de ne pas garantir l'atomicité
de la lecture d'une chaîne ? Le code ci-dessus n'est-il pas assez bon
(pour une définition ad-hoc de bon) pour garantir cette atomicité ?
writes:"Jeremie Fouche" wrote in message
news:<ccjau9$8vo$...James Kanze a écrit dans le message :
[...]tmp = sb->sgetc() ;
En fait, il me semble que sbumpc est plus adapté a ce que l'on veut
faire.
Ooops. Tout à fait. Même après tant de temps, je me laisse berner
par le nom.
Si je comprend bien, l'unique différence est que sgetc() fait un
read ahead, il lit le prochain caractère sans incrémenter la position
courante ?
[...]Je ne comprend pas. En quoi n'est-ce pas spécifié par la norme.
L'état de la variable qu'on lit en cas d'erreur. Je crois que c'est
spécifié pour certains types, que la variable n'est pas
modifiée. Mais je ne suis pas sûr que ce soit le cas pour tous les
types. Si je lis vers un std::string, par exemple ? Est-ce que
l'implémentation est obligée à stocker dans une chaîne locale
jusqu'à ce qu'elle a fini, pour être sûr qu'il n'y a pas d'erreur,
et puis affecter la chaîne à sa destination finale, ou est-ce que
l'implémentation peut stocker directement dans la destination
finale ?
Je ne sais pas si la norme le spécifie, mais je n'y vois pas
d'opposition. Je ne vois pas vraiment d'intérêt de modifier
directement la chaîne cible par rapport à
std::string result ;
// remplissage de result ...
target.swap( result ) ;
Je vois par contre des inconvénients. Je vais peut-être jeter un coup
d'oeil dans la norme, à la recherche de cette (absence de ?) garantie.
Sinon, quelqu'un voit-il une raison de ne pas garantir l'atomicité
de la lecture d'une chaîne ? Le code ci-dessus n'est-il pas assez bon
(pour une définition ad-hoc de bon) pour garantir cette atomicité ?
drkm writes:Je vais peut-être jeter un
coup d'oeil dans la norme, à la recherche de cette (absence de ?)
garantie.
21.3.7.9 Inserters and extractors [lib.string.io]
template<class charT, class traits, class Allocator>
basic_istream<charT,traits>&
operator>>(basic_istream<charT,traits>& is,
basic_string<charT,traits,Allocator>& str);
1. Effects: Begins by constructing a sentry object k as if k were
constructed by typename basic_istream<charT,traits>::sentry
k(is). If bool(k) is true, it calls str.erase() and then
extracts characters from is and appends them to str as if by
calling str.append(1,c). If is.width() [...]
L'opérateur d'entrée commence donc par appeler erase() sur la
chaîne.
Pourquoi une telle contrainte ?
Pourquoi ne pas imposer plutôt l'atomicité de la leture ?
Quelque chose comme :
template< class charT , class traits , class Allocator >
basic_istream< charT , traits > &
operator>>(
basic_istream< charT , traits > & is ,
basic_string< charT , traits , Allocator > & str
)
{
typename basic_istream< charT , traits >::sentry sentry( is ) ;
if ( sentry ) {
basic_string< charT , traits , Allocator > result ;
// ... remplissage de result ...
str.swap( result ) ;
}
return is ;
}
n'est-il pas plus satisfaisant ?
J'imagine que j'ai loupé quelque
chose, mais je n'arrive pas à voir la motivation derrière « If bool(k)
is true, _it calls str.erase()_ and then [...] ».
drkm <usenet.fclcxx@fgeorges.org> writes:
Je vais peut-être jeter un
coup d'oeil dans la norme, à la recherche de cette (absence de ?)
garantie.
21.3.7.9 Inserters and extractors [lib.string.io]
template<class charT, class traits, class Allocator>
basic_istream<charT,traits>&
operator>>(basic_istream<charT,traits>& is,
basic_string<charT,traits,Allocator>& str);
1. Effects: Begins by constructing a sentry object k as if k were
constructed by typename basic_istream<charT,traits>::sentry
k(is). If bool(k) is true, it calls str.erase() and then
extracts characters from is and appends them to str as if by
calling str.append(1,c). If is.width() [...]
L'opérateur d'entrée commence donc par appeler erase() sur la
chaîne.
Pourquoi une telle contrainte ?
Pourquoi ne pas imposer plutôt l'atomicité de la leture ?
Quelque chose comme :
template< class charT , class traits , class Allocator >
basic_istream< charT , traits > &
operator>>(
basic_istream< charT , traits > & is ,
basic_string< charT , traits , Allocator > & str
)
{
typename basic_istream< charT , traits >::sentry sentry( is ) ;
if ( sentry ) {
basic_string< charT , traits , Allocator > result ;
// ... remplissage de result ...
str.swap( result ) ;
}
return is ;
}
n'est-il pas plus satisfaisant ?
J'imagine que j'ai loupé quelque
chose, mais je n'arrive pas à voir la motivation derrière « If bool(k)
is true, _it calls str.erase()_ and then [...] ».
drkm writes:Je vais peut-être jeter un
coup d'oeil dans la norme, à la recherche de cette (absence de ?)
garantie.
21.3.7.9 Inserters and extractors [lib.string.io]
template<class charT, class traits, class Allocator>
basic_istream<charT,traits>&
operator>>(basic_istream<charT,traits>& is,
basic_string<charT,traits,Allocator>& str);
1. Effects: Begins by constructing a sentry object k as if k were
constructed by typename basic_istream<charT,traits>::sentry
k(is). If bool(k) is true, it calls str.erase() and then
extracts characters from is and appends them to str as if by
calling str.append(1,c). If is.width() [...]
L'opérateur d'entrée commence donc par appeler erase() sur la
chaîne.
Pourquoi une telle contrainte ?
Pourquoi ne pas imposer plutôt l'atomicité de la leture ?
Quelque chose comme :
template< class charT , class traits , class Allocator >
basic_istream< charT , traits > &
operator>>(
basic_istream< charT , traits > & is ,
basic_string< charT , traits , Allocator > & str
)
{
typename basic_istream< charT , traits >::sentry sentry( is ) ;
if ( sentry ) {
basic_string< charT , traits , Allocator > result ;
// ... remplissage de result ...
str.swap( result ) ;
}
return is ;
}
n'est-il pas plus satisfaisant ?
J'imagine que j'ai loupé quelque
chose, mais je n'arrive pas à voir la motivation derrière « If bool(k)
is true, _it calls str.erase()_ and then [...] ».