OVH Cloud OVH Cloud

ostreams et effacement

17 réponses
Avatar
Michaël Monerau
Bonjour,

J'aimerais faire une fonction où je mets des entiers dans une chaîne de
caractères. J'utilise donc un std::ostringstream avec les opérations de flux
qui vont bien. Tout marche impec.

Maintenant, dans la même fonction, je voudrais refaire la même opération...
Mais je ne sais pas comment effacer le strinstream dont je me suis servi
juste avant. Ca éviterait d'en créer un nouveau à chaque fois (ma solution
actuelle), et devoir trouver un nouveau nom de variables tout le temps
(fastidieux dans une fonction où je dois faire 10 ou 15 opération
semblables...).

J'ai cru voir dans une discussion récente entre James et Samuel que ce
n'était pas possible et qu'il fallait tout le temps créer un nouveau
ostream... Dites-moi que c'est faux ! ;-)
--
<=- Michaël "Cortex" Monerau -=>

10 réponses

1 2
Avatar
Laurent DELEPINE
Michaël Monerau wrote:
Bonjour,

J'aimerais faire une fonction où je mets des entiers dans une chaîne de
caractères. J'utilise donc un std::ostringstream avec les opérations de flux
qui vont bien. Tout marche impec.

Maintenant, dans la même fonction, je voudrais refaire la même opération...
Mais je ne sais pas comment effacer le strinstream dont je me suis servi
juste avant. Ca éviterait d'en créer un nouveau à chaque fois (ma solution
actuelle), et devoir trouver un nouveau nom de variables tout le temps
(fastidieux dans une fonction où je dois faire 10 ou 15 opération
semblables...).


Tu n'es pas obligé de trouver un nom de variable different a chaque
fois. Il te suffit de definir et d'utilise ta variable dans differents
bloc :

void MaFonction ()
{

....

{
std::ostringstream Message;
Message << ...
...
}
{
std::ostringstream Message;
Message << ...
...

}
...
} // Fin de la fonction

Chaque exemplaire de message n'est valide qu'entre les accolades qui le
declarent.Les choses ne doivent pas etre trop différentes de ce qui se
passerait si on pouvait vider les ostringstream.


A+

LD

Avatar
Samuel Krempp
--nextPart4223600.UEDxYjBxRb
Content-Type: text/plain; charset=iso-8859-1
Content-Transfer-Encoding: 8Bit

le Friday 19 September 2003 19:37, écrivit :

J'ai cru voir dans une discussion récente entre James et Samuel que ce
n'était pas possible et qu'il fallait tout le temps créer un nouveau
ostream... Dites-moi que c'est faux ! ;-)


et bien.. comment dire .. :-)
bon, pour te faire plaisir, à la limite, c'est faux, vu que tu peux extirper
de std::basic_stringbuf des fonctions membre 'protected' qui permettent
d'extraire du stringstream seulement un sous-morceau du buffer entier,
et qu'il t'est possible de remettre à zéro cette sous-séquence grace à la
fonction seekpos(..)


c'est exactement ce que j'ai fait pour mesurer par rapport à la création à
chaque fois d'un nouveau stringstream. (dans mon cas, à chaque itération 4
variables sont formattées avec diveres options de formattage, re-créer est
~ 2 fois plus lent, mais en fait pas plus lent que réutiliser en appelant à
chaque fois imbue(..) dessus. Enfin bref..)

je te colle les bouts concernés dans une pièce jointe (mon code est formatté
sans limiter très strictement les lignes à 80 colonnes. la limite dur est à
100 colonnes - ça passe largement dans ma fenêtre emacs :)


utilise outsstream::clear_buffer() pour repositionner à 0 le pointeur
courant.
après utilisation du stream, le pointeur cur() pointe sur la fin des
caractères nouvellement insérés, et cur_str() renvoie le morceau de la
chaîne qui t'intéresse. (qui est aussi [begin(), cur()[

remarque que cette classe est un ostream, on mets des caractères dedans, ils
ne sortent jamais. (enfin, on les copie avec str(), cur_str(), etc..)

je ne me suis pas du tout penché sur l'équivalent in|out, mais c'est
possible qu'il n'y ait pas grand chose à changer pour que ça marche, au
delà de dériver de basic_iostream au lieu de basic_ostream.

aussi, il faut bien penser que clear_buffer n'agit sur rien d'autre que sur
le pointeur de la position courante dans le buffer,
c'est très différent de reconstruire un autre stringstream : tous ses
flags(), rdstate(), exceptions(), iword() et pword() restent inchangés.

tu peux essayer d'utiliser, ça compile et ça marche comme attendu sur
diverses plateformes, mais je ne suis pas sûr que le bon fonctionnement de
cette classe soit garanti par la norme (dans les grandes lignes oui, par
exple la norme impose que les chars soient stockés de manière contigüe et
donc utiliser [pbase(), pptr()[ est valide)

--
Sam
--nextPart4223600.UEDxYjBxRb
Content-Type: text/x-c++src; name="outsstream.hpp"
Content-Transfer-Encoding: 8Bit
Content-Disposition: attachment; filename="outsstream.hpp"

#include <sstream>
#include <cassert>

template<class Ch, class Tr = std::char_traits<Ch> >
class steal_basic_stringbuf : public std::basic_stringbuf<Ch, Tr>
{
typedef std::basic_stringbuf<Ch, Tr> buff_t;
public:
typedef std::basic_string<Ch,Tr> string_type;

// get [pbase, pptr[ from stringbuf::str(), which returns [pbase, epptr[ :
string_type cur_str() const { return string_type(str(), 0, pcount()); }

// publicize those functions (protected in stringbuf) :
using buff_t::pbase;
using buff_t::pptr;
using buff_t::epptr;
std::streamsize pcount() const { return pptr() - pbase(); }

// added convenience function :
void clear_buffer();
};


// --- boost's base_from_member<..> -----------------------------------------
// from <boost/utility/base_from_member.hpp>
namespace boost {
template < typename MemberType, int UniqueID=0 >
class base_from_member
{
protected:
MemberType member;

base_from_member()
: member()
{}

template< typename T1 >
explicit base_from_member( T1 x1 )
: member( x1 )
{}

template< typename T1, typename T2 >
base_from_member( T1 x1, T2 x2 )
: member( x1, x2 )
{}
};

} // namespace boost


// --- class basic_outsstream -----------------------------------------------
template<class Ch, class Tr = std:: char_traits<Ch> >
class basic_outsstream : boost::base_from_member<steal_basic_stringbuf<Ch, Tr> >,
public std:: basic_ostream<Ch, Tr>
// use stringbuf with its stolen protected members,
// publicly derived from basic_ostream to make this class a stream.
{
public:
typedef std::basic_string<Ch,Tr> string_type;
basic_outsstream() : pbase_type(),
std::basic_ostream<Ch,Tr>( & sb() ) {}
// buffer access via strings
string_type str() const { return sb().str(); } // [begin, end[
string_type cur_str() const { return sb().cur_str(); } // [begin, cur[

// copy-less access (note the pointers can be invalidated when modifying the stream)
const Ch * begin() const { return sb().pbase(); }
const Ch * cur() const { return sb().pptr(); }
const Ch * end() const { return sb().epptr(); }
std::streamsize cur_size() const { return sb().pcount(); } // cur-begin

void clear_buffer() { sb().clear_buffer(); }
private:
typedef boost::base_from_member<steal_basic_stringbuf<Ch, Tr> > pbase_type;
steal_basic_stringbuf<Ch, Tr> & sb() { return pbase_type::member; }
steal_basic_stringbuf<Ch, Tr> const& sb() const { return pbase_type::member; }
};

typedef basic_outsstream<char> outsstream;
typedef basic_outsstream<wchar_t> woutsstream;


// --- Implementation -----------------------------------------------------


template<class Ch, class Tr> inline
void steal_basic_stringbuf<Ch, Tr> :: clear_buffer() {
const Ch * p = pptr();
const Ch * b = pbase();
if(p != NULL && p != b) {
typedef typename Tr::pos_type pos_type;
pos_type pos = buff_t::seekpos(0, std::ios_base::out);
assert( pos != pos_type(std::streamoff(-1)) );
}
}


--nextPart4223600.UEDxYjBxRb--

Avatar
Michaël Monerau
Laurent DELEPINE wrote:
Michaël Monerau wrote:
Bonjour,

J'aimerais faire une fonction où je mets des entiers dans une chaîne
de caractères. J'utilise donc un std::ostringstream avec les
opérations de flux qui vont bien. Tout marche impec.

Maintenant, dans la même fonction, je voudrais refaire la même
opération... Mais je ne sais pas comment effacer le strinstream dont
je me suis servi juste avant. Ca éviterait d'en créer un nouveau à
chaque fois (ma solution actuelle), et devoir trouver un nouveau nom
de variables tout le temps (fastidieux dans une fonction où je dois
faire 10 ou 15 opération semblables...).


Tu n'es pas obligé de trouver un nom de variable different a chaque
fois. Il te suffit de definir et d'utilise ta variable dans differents
bloc :


<snip code>

Chaque exemplaire de message n'est valide qu'entre les accolades qui
le declarent.Les choses ne doivent pas etre trop différentes de ce
qui se passerait si on pouvait vider les ostringstream.


En effet, c'est une bonne idée à laquelle je n'avais pas pensé. Cependant,
ça reste toujours beaucoup plus long que de vider un stream (comme Samuel le
disait, ~2x plus de temps).

Merci.
--
<=- Michaël "Cortex" Monerau -=>


Avatar
Michaël Monerau
Samuel Krempp wrote:
le Friday 19 September 2003 19:37, écrivit :

J'ai cru voir dans une discussion récente entre James et Samuel que
ce n'était pas possible et qu'il fallait tout le temps créer un
nouveau ostream... Dites-moi que c'est faux ! ;-)


et bien.. comment dire .. :-)
bon, pour te faire plaisir, à la limite, c'est faux, vu que tu peux
extirper de std::basic_stringbuf des fonctions membre 'protected' qui
permettent d'extraire du stringstream seulement un sous-morceau du
buffer entier,
et qu'il t'est possible de remettre à zéro cette sous-séquence grace
à la fonction seekpos(..)


c'est exactement ce que j'ai fait pour mesurer par rapport à la
création à chaque fois d'un nouveau stringstream. (dans mon cas, à
chaque itération 4 variables sont formattées avec diveres options de
formattage, re-créer est ~ 2 fois plus lent, mais en fait pas plus
lent que réutiliser en appelant à chaque fois imbue(..) dessus. Enfin
bref..)


Du coup, qu'est-ce qui est le plus rapide ? Faire une nouvelle variable à
chaque fois, ou utiliser outstream ?

je te colle les bouts concernés dans une pièce jointe (mon code est
formatté sans limiter très strictement les lignes à 80 colonnes. la
limite dur est à 100 colonnes - ça passe largement dans ma fenêtre
emacs :)


Merci. Pas de problème pour les lignes, je suis en 1600x1200 sous Windows et
tout passe très bien ;-)

utilise outsstream::clear_buffer() pour repositionner à 0 le pointeur
courant.
après utilisation du stream, le pointeur cur() pointe sur la fin des
caractères nouvellement insérés, et cur_str() renvoie le morceau de la
chaîne qui t'intéresse. (qui est aussi [begin(), cur()[


OK.

remarque que cette classe est un ostream, on mets des caractères
dedans, ils ne sortent jamais. (enfin, on les copie avec str(),
cur_str(), etc..)


OK. Mais à la destruction, comment ça se passe ? Il détruit bien [begin,
end[ ou alors il ne détruit que [begin, cur[ ?

je ne me suis pas du tout penché sur l'équivalent in|out, mais c'est
possible qu'il n'y ait pas grand chose à changer pour que ça marche,
au delà de dériver de basic_iostream au lieu de basic_ostream.


Pour l'instant je n'ai pas non plus besoin de l'équivalent en istream... Je
te tiendrai au courant si je me penche dessus.

aussi, il faut bien penser que clear_buffer n'agit sur rien d'autre
que sur le pointeur de la position courante dans le buffer,
c'est très différent de reconstruire un autre stringstream : tous ses
flags(), rdstate(), exceptions(), iword() et pword() restent
inchangés.


OK. Ce qui est très embêtant... Mais bon, ça peut se modifier simplement ;-)

tu peux essayer d'utiliser, ça compile et ça marche comme attendu sur
diverses plateformes, mais je ne suis pas sûr que le bon
fonctionnement de cette classe soit garanti par la norme (dans les
grandes lignes oui, par exple la norme impose que les chars soient
stockés de manière contigüe et donc utiliser [pbase(), pptr()[ est
valide)


Je vais essayer sur mon VC 7.1 et je te tiens au courant.
Au fait, danns clear_buffer, pourquoi ne pas désallouer le [begin, end[ ?
C'est un choix pour les perfs, ou alors c'est qu'on ne peut pas le faire ?
Ou alors, lui assigner un streambuf vide ? Ca aurait alors le réel
comportement attendu...
--
<=- Michaël "Cortex" Monerau -=>


Avatar
Michaël Monerau
Michaël Monerau wrote:
Je vais essayer sur mon VC 7.1 et je te tiens au courant.


OK, ça fonctionne bien sous VC 7.1. Il y a juste un warning (en Warning
level 4) de "conversion de 'int __w64' en 'std::streamsize' : possible loss
of data". Pour l'enlever, il suffit de remplacer la ligne :

std::streamsize pcount() const { return pptr() - pbase(); }

par

std::streamsize pcount() const { return
static_cast<std::streamsize>(pptr() - pbase()); }



et c'est bon.

Voilà, donc ça fonctionne bien, mais est-ce que la désallocation se fait
bien ? Et est-ce que c'est bien plus rapide que d'en créer un autre ?



Merci.
--
<=- Michaël "Cortex" Monerau -=>

Avatar
Samuel Krempp
le Saturday 20 September 2003 10:55, écrivit :

std::streamsize pcount() const { return
static_cast<std::streamsize>(pptr() - pbase()); }


ah tiens, j'avais pas fait gaffe à ça.

Voilà, donc ça fonctionne bien, mais est-ce que la désallocation se fait
bien ? Et est-ce que c'est bien plus rapide que d'en créer un autre ?


oui, le buffer reste géré par le stringbuf, qui le désalloue à la
destruction.

ah j'avais oublié de le préciser : il y a en fait bcp plus simple pour
réutiliser le stringstream, en désallouant le buffer :
stringstream oss;
oss.str("");

si ça se trouve ça te convient mieux. Là il y a réallocation, ça prend plus
de temps que de simplement faire un seek.

exemple sur la boucle :
N = 10*1000*1000;
outsstream oss;
for(int i=0; i < N; ++i) {
oss << i;
oss.clear_buffer();
}

comparée à la même avec un stringstream et .str("") au lieu de clear_buffer,
et aussi à un stringstream créé à chaque itération, avec g++ 3.3 -O :
outsstream.clear_buffer() : 4.51s.
stringstream.str("") : 15.57s. =3.45233 * outsstream
stringstream : 31.77s. =7.04435 * outsstream

on voit que la réallocation est responsable de la moitié du temps pris par
la re-création du stringstream. (la moitié restante, je sais pas exactement
comment elle est perdue, je suspecte une histoire de locale)

--
Sam

Avatar
James Kanze
Laurent DELEPINE writes:

|> Michaël Monerau wrote:

|> > J'aimerais faire une fonction où je mets des entiers dans une
|> > chaîne de caractères. J'utilise donc un std::ostringstream
|> > avec les opérations de flux qui vont bien. Tout marche impec.
|> > Maintenant, dans la même fonction, je voudrais refaire la
|> > même opération...

|> > Mais je ne sais pas comment effacer le strinstream dont je me suis
|> > servi juste avant. Ca éviterait d'en créer un nouveau à
|> > chaque fois (ma solution actuelle), et devoir trouver un nouveau
|> > nom de variables tout le temps (fastidieux dans une fonction où
|> > je dois faire 10 ou 15 opération semblables...).

|> Tu n'es pas obligé de trouver un nom de variable different a
|> chaque fois. Il te suffit de definir et d'utilise ta variable dans
|> differents bloc :

|> void MaFonction ()
|> {

|> ....

|> {
|> std::ostringstream Message;
|> Message << ...
|> ...
|> }
|> {
|> std::ostringstream Message;
|> Message << ...
|> ...
|>
|> }
|> ...
|> } // Fin de la fonction

|> Chaque exemplaire de message n'est valide qu'entre les accolades qui
|> le declarent.Les choses ne doivent pas etre trop différentes de
|> ce qui se passerait si on pouvait vider les ostringstream.

Même... Pourquoi mettre autant de bordel dans une seule fonction ? Je
commencerai par créer une fonction à part pour la conversion en
chaîne :

template< typename T >
std::string
asString( T const& value )
{
std::ostringstream result ;
result << value ;
return result.str() ;
}

Avec évidemment des spécialisations, le cas échéant, pour
positionner des paramètres de formattage.

Ensuite, s'il s'agit de créer des messages avec plusieurs valeurs,
alors, le opérateur + de std::string existe bien.

Enfin, si l'internationalisation serait jamais une question, je te
conseille très fort de régarder du côté GB_Format (à ma
site) ou boost::format (chez Boost, évidemment).

--
James Kanze mailto:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
11 rue de Rambouillet, 78460 Chevreuse, France +33 1 41 89 80 93
Avatar
Samuel Krempp
le Saturday 20 September 2003 14:56, écrivit :
OK. Dans un sens, c'est pas énorme... si mes calculs sont bons, ça donne
du 4.51e-5 ms pour un clear_buffer et 3.18e-4 ms pour une recréation de
strinstream. Mais c'est vrai que pour commencer un projet, ça m'embête
vraiment d'utiliser une méthode lente. Je vais donc utiliser ta classe (si
tu m'en autorises ;-) ).

Sur quelle genre de machine as-tu testé cela ? C'était un Pentium 4 3GHz
ou un P2 300 ? :p


c'est une machine plutôt rapide, un P4 1.8 GHz.
effectivement, ce sont de toute façon des actions plutôt rapides.
la différence ne serait vraiment importante que dans un programme qui passe
son temps à formatter des variables. (j'imagine que ça peut exister)
je me suis penché dessus parceque pour boost::format, je voudrais fournir
une classe aussi efficace que possible, qui ne souffre pas excessivement de
la comparaison avec printf, alors j'ai expérimenté tout ce qui touche au
stringstream.
(au final j'utilise un seul buffer, en recréant à chaque fois un stream sur
ce buffer. ça économise une bonne proportion du temps - un formattage de 4
variables réordonnées tourne dans les 5x printf- , tout en ne subissant
aucun effet 'sticky' quoique l'utilisateur modifie dans le stream lors du
formattage)

Oui, c'est assez effrayant. Je n'arrive pas à comprendre comment la classe
de ostream a pu être pensée, sans qu'on puisse l'effacer. C'est un choix
de design que je trouve profondément stupide, mais d'un autre côté, je
n'ai pas toutes les cartes en main pour juger...


moi aussi, je trouve ça dommage qu'il n'aie été décidé de forcer
l'utilisation kleenex des stringstream, même dans les cas où il serait
avantageux de réutiliser au moins le buffer d'un formattage à l'autre,
alors que ça ne demande très peu de chose (soit rendre public pptr() et
pbase(), soit fournir une fonction cur_str() en plus de str() ).
Et aussi le fait même que dans un stringbuf le pointeur de fin doive
correspondre à la fin de la séquence des caractères entrés plutôt qu'à la
fin du tableau alloué (ça fait qu'overflow est appelé à chaque appel à
sputc) est idiot. D'ailleurs la stdlib du compilo intel-7.1 n'est pas
conforme à la norme sur ce point, et c'est mieux comme ça (cependant pour
exhiber le comportement non-conforme en pratique faut vraiment le faire
exprès, dériver de leur stringbuf et obtenir l'accès à pptr() / pend())

En général le gâchis de temps ne gêne pas trop, mais pour des programmes
spécifiques ça peut obliger à refaire un stringbuf plus efficace, alors que
celui de la stdlib pourrait être modifié pour être aussi efficace que
voulu. c'est dommage..

enfin, comme de toute façon j'essaie que boost::format compile sur des
compilos qui n'ont pas de stringstream, finalement ça donne une raison de
plus pour faire mon stringbuf custom.

Autrement, pour la class outsstream, je verrais bien un type de retour
'outsstream&' pour un appel à 'clear_buffer', pour permettre d'écrire
quelque chose du genre :

oss << 12345;
oss.clear_buffer () << 23;
// oss.cur_str() == "23"


oui, pour l'utilisateur final c'est vrai que ça peut être pratique.

--
Sam

Avatar
Michaël Monerau
Samuel Krempp wrote:
le Saturday 20 September 2003 14:56, écrivit :
OK. Dans un sens, c'est pas énorme... si mes calculs sont bons, ça
donne du 4.51e-5 ms pour un clear_buffer et 3.18e-4 ms pour une
recréation de strinstream. Mais c'est vrai que pour commencer un
projet, ça m'embête vraiment d'utiliser une méthode lente. Je vais
donc utiliser ta classe (si tu m'en autorises ;-) ).

Sur quelle genre de machine as-tu testé cela ? C'était un Pentium 4
3GHz ou un P2 300 ? :p


c'est une machine plutôt rapide, un P4 1.8 GHz.
effectivement, ce sont de toute façon des actions plutôt rapides.


Effectivement, c'est plutôt rapide. Et je me demande si je ne vais pas
utiliser la solution 'oss.str ("")' qui reste assez rapide pour mes besoins.
Enfin, je verrai en temps voulus.

la différence ne serait vraiment importante que dans un programme qui
passe son temps à formatter des variables. (j'imagine que ça peut
exister)
je me suis penché dessus parceque pour boost::format, je voudrais
fournir une classe aussi efficace que possible, qui ne souffre pas
excessivement de la comparaison avec printf, alors j'ai expérimenté
tout ce qui touche au stringstream.
(au final j'utilise un seul buffer, en recréant à chaque fois un
stream sur ce buffer. ça économise une bonne proportion du temps - un
formattage de 4 variables réordonnées tourne dans les 5x printf- ,
tout en ne subissant aucun effet 'sticky' quoique l'utilisateur
modifie dans le stream lors du formattage)


C'est assez sympa, en effet ;-)

Oui, c'est assez effrayant. Je n'arrive pas à comprendre comment la
classe de ostream a pu être pensée, sans qu'on puisse l'effacer.
C'est un choix de design que je trouve profondément stupide, mais
d'un autre côté, je n'ai pas toutes les cartes en main pour juger...


moi aussi, je trouve ça dommage qu'il n'aie été décidé de forcer
l'utilisation kleenex des stringstream, même dans les cas où il serait
avantageux de réutiliser au moins le buffer d'un formattage à l'autre,
alors que ça ne demande très peu de chose (soit rendre public pptr()
et pbase(), soit fournir une fonction cur_str() en plus de str() ).


Pourquoi ne pas le proposer pour le futur de la STL ? (peut-être repenser
carrément le fonctionnement des ostreams pour permettre une utilisation plus
intuitive)

Et aussi le fait même que dans un stringbuf le pointeur de fin doive
correspondre à la fin de la séquence des caractères entrés plutôt
qu'à la fin du tableau alloué (ça fait qu'overflow est appelé à
chaque appel à sputc) est idiot. D'ailleurs la stdlib du compilo
intel-7.1 n'est pas conforme à la norme sur ce point, et c'est mieux
comme ça (cependant pour exhiber le comportement non-conforme en
pratique faut vraiment le faire exprès, dériver de leur stringbuf et
obtenir l'accès à pptr() / pend())


Oui, mais c'est toujours du non-portable... Donc pas terrible terrible :(

En général le gâchis de temps ne gêne pas trop, mais pour des
programmes spécifiques ça peut obliger à refaire un stringbuf plus
efficace, alors que celui de la stdlib pourrait être modifié pour
être aussi efficace que voulu. c'est dommage..


Encore une proposition à faire ?

enfin, comme de toute façon j'essaie que boost::format compile sur des
compilos qui n'ont pas de stringstream, finalement ça donne une
raison de plus pour faire mon stringbuf custom.


;-)
--
<=- Michaël "Cortex" Monerau -=>


Avatar
James Kanze
"Michaël Monerau" writes:

|> James Kanze wrote:
|> > "Michaël Monerau" writes:
|> >>> > Chaque exemplaire de message n'est valide qu'entre les
|> >>> > accolades qui le declarent.Les choses ne doivent pas etre
|> >>> > trop différentes de ce qui se passerait si on pouvait
|> >>> > vider les ostringstream.

|> >>> En effet, c'est une bonne idée à laquelle je n'avais pas
|> >>> pensé. Cependant, ça reste toujours beaucoup plus long
|> >>> que de vider un stream (comme Samuel le disait, ~2x plus de
|> >>> temps).

|> > Et qu'est-ce que tu fais avec des messages ? Est-ce que la
|> > différence d'une facteur 2 est signifiant à côté de ce
|> > qui se passe après ?

|> De quoi veux-tu parler ?

Que moi, j'ai utilisé des instruments de formattage surtout pour
préparer à une écriture physique. Or, si l'écriture physique
prend dix millisecondes (ce qui est fréquemment le cas), qu'importe
que le formattage prend dix microsécondes, ou cent.

|> > Samuel se place dans un cas particulier -- il développe un
|> > composant d'une bibliothèque très générale. Il n'a
|> > aucun accès au code utilisateur, et il ne peut pas exclure la
|> > possibilité que chez certains utilisateurs, la performance soit
|> > un problème. En revanche, à un époque, j'ai mésuré
|> > la performance de GB_Format -- c'était bien 10 fois moins
|> > rapide que sprintf. N'empèche que ça n'a jamais été un
|> > problème dans mes applications. (Et il se servait bien au
|> > formattage des fichiers de trace et de log -- c-à-d qu'il
|> > était appelé parfois dans des boucles assez serrées.)

|> En effet, je suis sûr que ça ne ralentirait pas beaucoup le
|> code d'utilise oss.str (""), mais par principe (et aussi parce que
|> je commence une librairie qui pourra être utilisée par
|> d'autres gens qui auront peut-être besoin de vitess), je
|> préfère faire une solution à la fois propre et efficace.

Et moi, en principe, je préfère faire propre, sans trop m'occuper
des performances, jusqu'à ce qu'il y a bien ou moins une indication
que les performances ont une certaine importance. J'ai reflechi sur
GB_Format à l'époque où je l'ai écrit. J'ai même fait des
mésures, au cas où. Mais bien que je sais comment améliorer la
performance de façon notable, je ne me suis embêté à le
faire, parce que jusqu'ici, je n'en ai pas eu besoin.

|> Je suis contre l'utilisation de sprintf pour toutes les failles de
|> sécurités que cela représente, et pour le "style C" que
|> j'essaye d'éviter en C++. Mais pour "lutter" contre ce sprintf,
|> il faudrait peut-être que C++ présente une solution convenable
|> en face...

Le problème est réel. En fait, sprintf n'est simplement pas
utilisable, parce que je vois mal une application où on n'est pas
amené à sortir des types utilisateur. Il ne vient donc pas en
question. En revanche, les flux C++ sont à peu près inutilisable
dans un contexte international. Du coup, il n'y a pas de solution
standard. D'autant plus qu'en général, ni filebuf ni FILE*
n'offrent des garanties nécessaires pour les utiliser pour des
écritures sur disque, et ni l'un ni l'autre ne supporte les sockets
pour l'interface Tivoli, ni syslog sous Unix, ni des composants de
l'interface graphique. (C'est d'ailleurs pourquoi on se sert si souvent
de sprintf ou ostringstream.) Du coup, on est obligé à se rabattre
sur des solutions non standard comme GB_Format ou boost::format.

--
James Kanze mailto:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
11 rue de Rambouillet, 78460 Chevreuse, France +33 1 41 89 80 93
1 2