C++Talk.NET Forum Index C++Talk.NET
C++ language newsgroups
 
Archives   FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

string::clear() vs string::resize( 0 ) vs string::erase( beg

 
Post new topic   Reply to topic    C++Talk.NET Forum Index -> C++ (French)
View previous topic :: View next topic  
Author Message
Aurelien Regat-Barrel
Guest





PostPosted: Mon Aug 22, 2005 1:49 pm    Post subject: string::clear() vs string::resize( 0 ) vs string::erase( beg Reply with quote



Bonjour,
Je cherche un moyen de réinitialiser la longueur d'une string à 0 sans
toucher à sa capacity().
Que dit la norme à ce propos ? Quel sont les influences de:
- clear()
- resize( 0 )
- erase( begin(), end() )
sur la valeur de capacity() ?

Merci.

--
Aurélien Regat-Barrel
Back to top
James Kanze
Guest





PostPosted: Mon Aug 22, 2005 5:15 pm    Post subject: Re: string::clear() vs string::resize( 0 ) vs string::erase( Reply with quote



Aurelien Regat-Barrel wrote:

Quote:
Je cherche un moyen de réinitialiser la longueur d'une string
à 0 sans toucher à sa capacity(). Que dit la norme à ce
propos ? Quel sont les influences de:
- clear()
- resize( 0 )
- erase( begin(), end() )
sur la valeur de capacity() ?

Dans le cas de std::string, la norme ne dit pas grand chose.
Pratiquement, toute fonction non const, ainsi que data() et
c_str(), peuvent modifier la capacité. (Techniquement, je crois
même que les fonctions const peuvent la modifier. Mais elles ne
peuvent pas invalider les itérateurs, ni les les pointeurs dans
la chaîne, et c'est difficile à concevoir une implémentation qui
modifie la capacité sans invalider certains itérateurs ou
pointeurs.)

Dans la pratique, je dirais que si tu as des concernes où la
capacité a de l'importance, std::string n'est pas le type qui
convient. Peut-être std::vector<char> ?

--
James Kanze mailto: [email]james.kanze (AT) free (DOT) fr[/email]
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 pl. Pierre Sémard, 78210 St.-Cyr-l'École, France +33 (0)1 30 23 00 34

Back to top
Loïc Joly
Guest





PostPosted: Mon Aug 22, 2005 11:47 pm    Post subject: Re: string::clear() vs string::resize( 0 ) vs string::erase( Reply with quote



Aurelien Regat-Barrel a écrit :
Quote:
Bonjour,
Je cherche un moyen de réinitialiser la longueur d'une string à 0 sans
toucher à sa capacity().
Que dit la norme à ce propos ? Quel sont les influences de:
- clear()
- resize( 0 )
- erase( begin(), end() )
sur la valeur de capacity() ?

Bien que rien ne soit défini à ce sujet, je pense qu'une solution à base
de copie + swap, comme pour les vecteurs, a une chance de marcher.

--
Loïc

Back to top
Aurelien Regat-Barrel
Guest





PostPosted: Tue Aug 23, 2005 7:58 am    Post subject: Re: string::clear() vs string::resize( 0 ) vs string::erase( Reply with quote

Merci à vous deux.
Je vais ajouter quelques précisions. C'est bien std::string qu'il me
faut, utilisé comme paramètre out :

void DoSomething( std::string & Result )
{
Result.clear();
Result.reserve( /* ce qu'il faut */ );
for ( ... )
{
Result.append( ... );
}
}


std::string res;
while ( /* pas mal de fois */ )
{
DoSomething( res );
// ...
}

Vu que le cas typique d'utilisation, c'est le même std::string qui sert
de multiples fois comme paramètre out, et que :
- la string est contruite au moyen de pas mal de concaténations
- elle a régulièrement la même longueur une fois construite

l'idée est de profiter de la place allouée lors des traitements
précédents. Ma crainte est que le clear() désalloue la mémoire, et donc
d'avoir une allocation en suivant avec reserve().

Mon programme marche, et j'ai même envie de dire que cette optimisation
ne se verra pas. C'est donc essentiellement par curiosité. Car je peux
simplement faire un resize() suivi de copies, mais les append() rend mon
code plus simple.
Je sais que le clear() de ma STL (VC++ 7.1) fait un erase( begin(),
end() ), et que la prochaine version (VC++ Cool fera un resize( 0 ).
Je me demandais du point de vue de la norme quelle était la nuance...
Merci.

--
Aurélien Regat-Barrel
Back to top
Fabien LE LEZ
Guest





PostPosted: Tue Aug 23, 2005 8:57 am    Post subject: Re: string::clear() vs string::resize( 0 ) vs string::erase( Reply with quote

On Tue, 23 Aug 2005 09:58:27 +0200, Aurelien Regat-Barrel
<nospam.aregatba (AT) yahoo (DOT) fr>:

Quote:
- la string est contruite au moyen de pas mal de concaténations

Utiliser un ostringstream ou un ostrstream comme intermédiaire dans ce
cas (et donc "operator<<" à la place de "append"), serait-ce une
optimisation ? une pessimisation ? aucun des deux ?


Back to top
kanze@gabi-soft.fr
Guest





PostPosted: Tue Aug 23, 2005 9:40 am    Post subject: Re: string::clear() vs string::resize( 0 ) vs string::erase( Reply with quote

Aurelien Regat-Barrel wrote:

Quote:
Je vais ajouter quelques précisions. C'est bien std::string
qu'il me faut, utilisé comme paramètre out :

void DoSomething( std::string & Result )
{
Result.clear();
Result.reserve( /* ce qu'il faut */ );
for ( ... )
{
Result.append( ... );
}
}

std::string res;
while ( /* pas mal de fois */ )
{
DoSomething( res );
// ...
}

Vu que le cas typique d'utilisation, c'est le même std::string
qui sert de multiples fois comme paramètre out, et que :
- la string est contruite au moyen de pas mal de concaténations
- elle a régulièrement la même longueur une fois construite
l'idée est de profiter de la place allouée lors des
traitements précédents. Ma crainte est que le clear()
désalloue la mémoire, et donc d'avoir une allocation en
suivant avec reserve().

Le problème, c'est que les implémentations de std::string
varient beaucoup. Avec g++, au moins,

std::string
DoSomething()
{
std::string result ;
result.reserve( /* ce qu'il faut */ ) ;
for ( ... ) {
result.append( ... ) ;
}
return result ;
}

while ( /* pas mal de fois */ ) {
std::string res( DoSomething() ) ;
// ...
}

serait probablement plus rapide de ta version (encore que ce
n'est pas sûr -- tout dépend de beaucoup de choses qu'on ne
maîtrise pas).

Quote:
Mon programme marche, et j'ai même envie de dire que cette
optimisation ne se verra pas. C'est donc essentiellement par
curiosité. Car je peux simplement faire un resize() suivi de
copies, mais les append() rend mon code plus simple.

Je sais que le clear() de ma STL (VC++ 7.1) fait un erase(
begin(), end() ), et que la prochaine version (VC++ Cool fera un
resize( 0 ). Je me demandais du point de vue de la norme
quelle était la nuance...

Aucune, je crois. Du point de vue de la norme ; dans une
implémentation donnée, évidemment, ça peut en faire une.

En gros, la seule chose, c'est d'implémenter un benchmark, et
comparer des valeurs réeles. En particulier :

-- Si l'implémentation se sert du comptage des références et de
copie à l'écriture, la solution optimale pourrait bien
dépendre de ce que tu fais avec la chaîne res dans ta
boucle. En règle générale (mais avec beaucoup d'exceptions),
tu as intérêt à limiter la portée de chaque instance la plus
possible, pour éviter des partages réels, tandis que les
copies en soi ne sont pas chères. (Si, par exemple, dans la
boucle, tu as affecté res à une autre variable qui existe
encore, ou clear() ou resize() va forcément réalloue de la
mémoire.

-- Si l'implémentation utilise ce qu'on appelle l'optimisation
pour les petites chaînes (c'est le cas des implémentations
Microsoft à partir de la version 7, je crois), il est
prèsque sur que clear() ou resize( 0 ) libère la mémoire
allouer, pour ramener la capacité à sa valeur minimum (qui
sera 8, 16, 32, ou quelque chose du genre).

S'il faut vraiment éviter l'allocation, la solution que je vois
serait de déclarer la chaîne en dehors de la boucle, en
l'initialisant avec une chaîne de la bonne longueur, et utiliser
les itérateurs dans DoSomething :

void
DoSomething( std::string::iterator dest )
{
dest = std::copy( s.begin(), s.end(), dest ) ;
// à la place de « append( s ) »...
}

std::string res( longueurVoulue, ' ' ) ;
while ( ... ) {
DoSomething( res.begin() ) ;
// ...
}

Mais même ici, dans le cas d'une implémentation copie à
l'écriture, il faudrait s'assurer que l'implémentation n'est
jamais partagée.

Mais je n'y irais que si le profiler me disait qu'il fallait (au
moins que DoSomething soit assez général et assez simple qu'il y
a un intérêt à en faire un template, sur le type d'itérateur).

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34


Back to top
kanze@gabi-soft.fr
Guest





PostPosted: Tue Aug 23, 2005 10:23 am    Post subject: Re: string::clear() vs string::resize( 0 ) vs string::erase( Reply with quote

Fabien LE LEZ wrote:
Quote:
On Tue, 23 Aug 2005 09:58:27 +0200, Aurelien Regat-Barrel
[email]nospam.aregatba (AT) yahoo (DOT) fr[/email]>:

- la string est contruite au moyen de pas mal de concaténations

Utiliser un ostringstream ou un ostrstream comme intermédiaire
dans ce cas (et donc "operator<<" à la place de "append"),
serait-ce une optimisation ? une pessimisation ? aucun des
deux ?

Ça dépend de l'implémentation, évidemment, mais pour toutes les
implémentations que je connais, ça serait une pessimismation,
souvent même de beaucoup.

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34


Back to top
Aurelien Regat-Barrel
Guest





PostPosted: Tue Aug 23, 2005 12:29 pm    Post subject: Re: string::clear() vs string::resize( 0 ) vs string::erase( Reply with quote

Quote:
Utiliser un ostringstream ou un ostrstream comme intermédiaire
dans ce cas (et donc "operator<<" à la place de "append"),
serait-ce une optimisation ? une pessimisation ? aucun des
deux ?


Ça dépend de l'implémentation, évidemment, mais pour toutes les
implémentations que je connais, ça serait une pessimismation,
souvent même de beaucoup.

Je n'ai plus les valeurs en tête, mais j'avais testé la différence sur
des concaténations de chaînes. J'avais été très déçu par ostringstream
et surpris par std::string.

--
Aurélien Regat-Barrel

Back to top
Aurelien Regat-Barrel
Guest





PostPosted: Tue Aug 23, 2005 12:39 pm    Post subject: Re: string::clear() vs string::resize( 0 ) vs string::erase( Reply with quote

Quote:
Le problème, c'est que les implémentations de std::string
varient beaucoup. Avec g++, au moins,

std::string
DoSomething()
{
std::string result ;
result.reserve( /* ce qu'il faut */ ) ;
for ( ... ) {
result.append( ... ) ;
}
return result ;
}

while ( /* pas mal de fois */ ) {
std::string res( DoSomething() ) ;
// ...
}

serait probablement plus rapide de ta version (encore que ce
n'est pas sûr -- tout dépend de beaucoup de choses qu'on ne
maîtrise pas).

Mais dans mon cas DoSomething() renvoie un booléen = erreur ou pas.

Quote:
Je sais que le clear() de ma STL (VC++ 7.1) fait un erase(
begin(), end() ), et que la prochaine version (VC++ Cool fera un
resize( 0 ). Je me demandais du point de vue de la norme
quelle était la nuance...


Aucune, je crois. Du point de vue de la norme ; dans une
implémentation donnée, évidemment, ça peut en faire une.

En gros, la seule chose, c'est d'implémenter un benchmark, et
comparer des valeurs réeles. En particulier :

-- Si l'implémentation se sert du comptage des références et de
copie à l'écriture, la solution optimale pourrait bien
dépendre de ce que tu fais avec la chaîne res dans ta
boucle. En règle générale (mais avec beaucoup d'exceptions),
tu as intérêt à limiter la portée de chaque instance la plus
possible, pour éviter des partages réels, tandis que les
copies en soi ne sont pas chères. (Si, par exemple, dans la
boucle, tu as affecté res à une autre variable qui existe
encore, ou clear() ou resize() va forcément réalloue de la
mémoire.

-- Si l'implémentation utilise ce qu'on appelle l'optimisation
pour les petites chaînes (c'est le cas des implémentations
Microsoft à partir de la version 7, je crois), il est
prèsque sur que clear() ou resize( 0 ) libère la mémoire
allouer, pour ramener la capacité à sa valeur minimum (qui
sera 8, 16, 32, ou quelque chose du genre).

Oui c'est ça (16 max par défaut, sauf si sizeof( T ) > 16 dans ce cas
c'est sizeof( T )).

Quote:
S'il faut vraiment éviter l'allocation, la solution que je vois
serait de déclarer la chaîne en dehors de la boucle, en
l'initialisant avec une chaîne de la bonne longueur, et utiliser
les itérateurs dans DoSomething :

void
DoSomething( std::string::iterator dest )
{
dest = std::copy( s.begin(), s.end(), dest ) ;
// à la place de « append( s ) »...
}

std::string res( longueurVoulue, ' ' ) ;
while ( ... ) {
DoSomething( res.begin() ) ;
// ...
}

Mais même ici, dans le cas d'une implémentation copie à
l'écriture, il faudrait s'assurer que l'implémentation n'est
jamais partagée.

Mais je n'y irais que si le profiler me disait qu'il fallait (au
moins que DoSomething soit assez général et assez simple qu'il y
a un intérêt à en faire un template, sur le type d'itérateur).

Justement je ne souhaite pas modifier mon code, qui est plus simple avec
les append(). Surtout que je n'ai aucun besoin d'optimiser cette portion
de code. Le but était juste d'éventuellement bénificier d'une
optimisation gratuite en appelant une fonction plutôt qu'une autre, et
de se triturer les neurones aussi Smile
Merci à vous.

--
Aurélien Regat-Barrel

Back to top
kanze@gabi-soft.fr
Guest





PostPosted: Tue Aug 23, 2005 4:21 pm    Post subject: Re: string::clear() vs string::resize( 0 ) vs string::erase( Reply with quote

Aurelien Regat-Barrel wrote:
Quote:
Le problème, c'est que les implémentations de std::string
varient beaucoup. Avec g++, au moins,

std::string
DoSomething()
{
std::string result ;
result.reserve( /* ce qu'il faut */ ) ;
for ( ... ) {
result.append( ... ) ;
}
return result ;
}

while ( /* pas mal de fois */ ) {
std::string res( DoSomething() ) ;
// ...
}

serait probablement plus rapide de ta version (encore que ce
n'est pas sûr -- tout dépend de beaucoup de choses qu'on ne
maîtrise pas).

Mais dans mon cas DoSomething() renvoie un booléen = erreur ou pas.

Tiens, ça fait longtemps que je n'en ai pas parlé. C'est pour ça
qu'il existe Fallible<T> ; on renvoie Fallible< std::string >,
et le tour est joué. C'est même plus robuste, puisque tout essai
à accéder à la chaîne si la variable de contrôle est fausse
donne une violation d'une assertion.

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34


Back to top
Aurelien Regat-Barrel
Guest





PostPosted: Wed Aug 24, 2005 7:44 am    Post subject: Re: string::clear() vs string::resize( 0 ) vs string::erase( Reply with quote

Quote:
Tiens, ça fait longtemps que je n'en ai pas parlé. C'est pour ça
qu'il existe Fallible<T> ; on renvoie Fallible< std::string >,
et le tour est joué. C'est même plus robuste, puisque tout essai
à accéder à la chaîne si la variable de contrôle est fausse
donne une violation d'une assertion.

Ca a l'air intéressant J'ai trouvé ce lien:
http://cpptips.hyperformix.com/cpptips/fallible
y'aurait-t il mieux ?

--
Aurélien Regat-Barrel

Back to top
kanze@gabi-soft.fr
Guest





PostPosted: Wed Aug 24, 2005 8:48 am    Post subject: Re: string::clear() vs string::resize( 0 ) vs string::erase( Reply with quote

Aurelien Regat-Barrel wrote:
Quote:
Tiens, ça fait longtemps que je n'en ai pas parlé. C'est
pour ça qu'il existe Fallible<T> ; on renvoie Fallible
std::string >, et le tour est joué. C'est même plus robuste,
puisque tout essai à accéder à la chaîne si la variable de
contrôle est fausse donne une violation d'une assertion.

Ca a l'air intéressant J'ai trouvé ce lien:
http://cpptips.hyperformix.com/cpptips/fallible y'aurait-t il
mieux ?

Il y avait une implémentation complète dans ma bibliothèque,
mais pour des raisons diverses, elle n'est pas en ligne
actuellement. Sinon, je crois que le concepte est assez simple,
et il y a même une implémentation dans la page que tu cites.

La seule chose à ajouter, réelement, c'est que depuis, j'ai
ajouté une fonction membre :

template< typename T >
T
Fallible< T >::elseDefaultTo( T const& defaultValue )
{
return isValid() ? value : defaultValue ;
}

Aussi, une ou deux fois il m'est arrivé d'avoir besoin de plus
d'informations dans le cas d'erreur. Alors, j'ai réécrit mon
propre « fallible » à la main, plutôt que d'utiliser le
template, avec une enum à la place du booléen, etc. Je n'y avais
pas reflechi à l'époque, mais j'imagine qu'on pourrait faire la
même chose de façon générique, avec des traits. Quelque chose du
genre :

class DefaultFallibleTraits
{
public:
typedef bool StatusType ;
static bool isOk( StatusType validator )
{
return validator ;
}
StatusType defaultInvalid()
{
return false ;
}
StatusType defaultValid()
{
return true ;
}
} ;

//
---------------------------------------------------------------------------
// Contraints :
//
// Sur Value :
// Supporte la copie, l'affectation, et la construction
par
// défaut.
//
// L'affectation d'une Value doit donner la garantie forte
// par rapport aux exceptions.
//
// Sur Traits :
// Définit tous les éléments définis dans
// DefaultFallibleTraits.
//
// Sur Traits::StatusType :
// Supporte la copie et l'affectation, sans risque
// d'exception.
//
// Value et Traits::StatusType ne doivent pas être le même
// type.
//
---------------------------------------------------------------------------
template< typename Value, typename Traits = DefaultFallibleTraits >
class Fallible
{
typedef Traits::StatusType
StatusType ;

public:
explicit Fallible( StatusType validator
=
Traits::defaultInvalid() ) ;
explicit Fallible( T const& value,
StatusType validator
=
Traits::defaultValid() ) ;
Fallible& operator=( T const& value ) ;
Fallible& operator=( StatusType validator ) ;
// Constructeur de copie, affectation par copie et
// destructeur fournis par le compilateur...

bool isValid() const ;
StatusType status() const ;
T const& value() const ;
T elseDefaultTo( T const& defaultValue )
const ;

void invalidate( StatusType newStatus
=
Traits::defaultInvalid() ) ;
void validate( T const& newValue,
StatusType newStatus
=
Traits::defaultValid() ) ;

private:
// Attention :
// L'ordre des déclarations ici est critique pour la
// bonne gestion des exceptions. Lors d'une
affectation,
// il faut toujours que myValue soit affectée avant
// myStatus ; sinon, si l'affectation de myValue lève
une
// exception, on risque d'avoir un état valid sans
avoir
// de valeur valide. (L'ordre des déclarations
détermine
// l'ordre des affectations dans l'opérateur
// d'affectation générée par le compilateur.)
//
-----------------------------------------------------------------------
T myValue ;
StatusType myStatus ;
} ;

template< typename Value, typename Traits >
Fallible< Value, Traits >::Fallible(
StatusType validator )
: myStatus( validator )
{
assert( ! isValid() ) ;
}

template< typename Value, typename Traits >
Fallible< Value, Traits >::Fallible(
T const& value,
StatusType validator )
: myValue( value )
, myStatus( validator )
{
assert( isValid() ) ;
}

template< typename Value, typename Traits >
Fallible< Value, Traits >::operator=(
T const& value )
{
myValue = value ;
myStatus = Traits::defaultValid() ;
return *this ;
}

template< typename Value, typename Traits >
Fallible< Value, Traits >::operator=(
StatusType validator )
{
bool wasOk = isOk() ;
myStatus = validator ;
assert( ! isOk() || wasOK() ) ;
return *this ;
}

template< typename Value, typename Traits >
bool
Fallible< Value, Traits >::isValid() const
{
return Traits::isOk( myStatus ) ;
}

template< typename Value, typename Traits >
StatusType
Fallible< Value, Traits >::validator() const
{
return myStatus ;
}

template< typename Value, typename Traits >
T const&
Fallible< Value, Traits >::value() const
{
assert( isValid() ) ;
return myValue ;
}

template< typename Value, typename Traits >
T
Fallible< Value, Traits >::elseDefaultTo(
T const& defaultValue ) const
{
return isValid() ? myValue : defaultValue ;
}

template< typename Value, typename Traits >
void
Fallible< Value, Traits >::invalidate(
StatusType newStatus )
{
assert( ! Traits::isOk( newStatus ) ) ;
myStatus = newStatus ;
}

template< typename Value, typename Traits >
void
Fallible< Value, Traits >::validate(
T const& newValue,
StatusType newStatus )
{
assert( Traits::isOk( newStatus ) ) ;
myValue = newValue ;
myStatus = newStatus ;
}

(Attention : je viens juste d'avoir l'idée ; le code ci-dessus
n'est qu'une première esquisse, écrit dans une démi-heure, et
jamais compilé. Il y a donc de fortes chances qu'il contient des
erreurs. Mais ça doit donner une idée de ce qu'il faudrait.)

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34


Back to top
Aurelien Regat-Barrel
Guest





PostPosted: Thu Aug 25, 2005 3:11 pm    Post subject: Re: string::clear() vs string::resize( 0 ) vs string::erase( Reply with quote

Ok merci, je garde ça sous le coude.

--
Aurélien Regat-Barrel
Back to top
Display posts from previous:   
Post new topic   Reply to topic    C++Talk.NET Forum Index -> C++ (French) All times are GMT
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2006 phpBB Group
SEO toolkit © 2004-2006 webmedic.