OVH Cloud OVH Cloud

sprintf avec la classe string (bis)

11 réponses
Avatar
Eric Bart
Juste une ptite info pour rajouter sprintf à la classe string :

//---------------------------------------------------------------------------
string BKsys::StrPrintf(const char *chFormat, ...)
{
va_list args;
va_start(args, chFormat);

// calcul de la taille du buffer
char * buff;
int iBufSize = vsnprintf(buff, 0, chFormat, args);

buff = new char[iBufSize +2];
vsnprintf(buff, iBufSize +1, chFormat, args);

va_end(args);
string sS = buff;
delete [] buff;
return sS;
}
//---------------------------------------------------------------------------

Ca s'utilise comme ça :

int i=11;
string sToto = BKsys::StrPrintf("La valeur hexa de %d est %#X\n",i,i):

Ca vous plait ?

10 réponses

1 2
Avatar
Ivan Vecerina
"Eric Bart" wrote in message
news:bp08o3$oqt$
|
//--------------------------------------------------------------------------
-
| string BKsys::StrPrintf(const char *chFormat, ...)
| {
| va_list args;
| va_start(args, chFormat);
|
| // calcul de la taille du buffer
| char * buff;
| int iBufSize = vsnprintf(buff, 0, chFormat, args);
|
| buff = new char[iBufSize +2];
| vsnprintf(buff, iBufSize +1, chFormat, args);
|
| va_end(args);
| string sS = buff;
| delete [] buff;
| return sS;
| }
...
| Ca vous plait ?

Très bien, à quelques détails près:
- buff n'est pas initialise avant le 1er appel a vsnprintf.
Bcp de compils en feront un 'warning'.
- je serais tente d'utiliser un buffer local lors du
premier appel, pour eviter l'alloc et le second
appel lorsque c'est possible.
- si le constructeur de sS lance une exception (eh oui,
c'est possible) il y aura un memory leak.
- si new[] ou le ctr lancent une exception, va_end
ne sera pas appelé (pas bien)

Donc, que penserais-tu de:

std::string strptrintf(const char *chFormat, ...)
{
va_list args;
va_start(args, chFormat);

// calcul de la taille du buffer
char localBuf[256];
int len = vsnprintf( localBuf, sizeof(localBuf)
, chFormat, args );
if( len<sizeof(localBuf) ) {
va_end(args);
return localBuf;
}

char* buf = 0;
try {
buf = new char[len+1];
vsnprintf( buf, len+1, chFormat, args);
std::string ans = buf;
va_end(args);
return ans;
}
catch(...) {
delete[] buf;
va_end(args);
throw;
}
}

Alternative: utiliser std::vector<char> plutot
que new[]. Mais try-catch resterait necessaire
pour appeler va_end(args), sauf tech speciales...


Amicalement,
Ivan
--
http://ivan.vecerina.com
Avatar
amerio
Ivan Vecerina wrote:
"Eric Bart" wrote in message
news:bp08o3$oqt$

//--------------------------------------------------------------------------

-
string BKsys::StrPrintf(const char *chFormat, ...)
...

Ca vous plait ?




boost::format, quelqu'un ?
(sauf si le but etait l'exercice de style).


Avatar
Fabien LE LEZ
[Note en passant : si jamais ce thread est un troll et que je suis
tombé dedans, toutes mes excuses...]

On Thu, 13 Nov 2003 19:54:25 +0100, "Ivan Vecerina"
wrote:

- si new[] ou le ctr lancent une exception, va_end
ne sera pas appelé (pas bien)


Plutôt que de s'emmerder avec des try/catch à ne plus finir, mets tout
le bazar dans une classe :

struct GestionVA
{
va_list args;
GestionVA (const char *chFormat) { va_start (args, chFormat); }
~GestionVA() { va_end (args); }
};

Alternative: utiliser std::vector<char> plutot
que new[].


Ce n'est AMHA pas une "alternative", mais la solution canonique. C'est
"new char[]" ou le buffer local qui sont les alternatives.


--
;-)

Avatar
Ivan Vecerina
"Fabien LE LEZ" wrote in message
news:
On Thu, 13 Nov 2003 19:54:25 +0100, "Ivan Vecerina"
wrote:

- si new[] ou le ctr lancent une exception, va_end
ne sera pas appelé (pas bien)


Plutôt que de s'emmerder avec des try/catch à ne plus finir, mets tout
le bazar dans une classe :
...

Alternative: utiliser std::vector<char> plutot
que new[].
Ce n'est AMHA pas une "alternative", mais la solution canonique. C'est

"new char[]" ou le buffer local qui sont les alternatives.


Ouaip. J'étais parti sur vector, puis me suis dit (a tort) que
ce serait trop long d'utiliser RAII pour le va_end, et que quitte
à utiliser un catch()...
Donc, pour clore l'exercice:

std::string strptrintf(const char *chFormat, ...)
{
struct va_Owner
{
va_list args;
va_Owner(const char *chFormat) { va_start(args, chFormat); }
~va_Owner() { va_end (args); }
//NB: copy-ctr, op= privés etc à ajouter si pas slmt pour ici
};
va_Owner va(chFormat);

char localBuf[256];
int const len = vsnprintf( localBuf, sizeof(localBuf)
, chFormat, va.args );
if( len<sizeof(localBuf) )
return localBuf;

std::vector<char> buf(len+1);
vsnprintf( &buf.front(), len+1, chFormat, va.args);
return &buf.front();
}


Ceci dit, oui:
- à réserver aux enracinés à printf.
- vsnprintf n'existe pas en standard C++ (n'est que ds C99)
- boost::format pour tout nouveau code:
http://www.boost.org/libs/format/doc/format.html

[Note en passant : si jamais ce thread est un troll et que je suis
tombé dedans, toutes mes excuses...]
Si c'en était un, je suis aussi tombé dedans... n'en avait pas l'air.


Merci,
Ivan
--
http://ivan.vecerina.com


Avatar
kanze
"Ivan Vecerina" wrote in message
news:<bp0k0v$iab$...

"Eric Bart" wrote in message
news:bp08o3$oqt$

//--------------------------------------------------------------------------
-
| string BKsys::StrPrintf(const char *chFormat, ...)
| {
| va_list args;
| va_start(args, chFormat);

| // calcul de la taille du buffer
| char * buff;
| int iBufSize = vsnprintf(buff, 0, chFormat, args);

| buff = new char[iBufSize +2];
| vsnprintf(buff, iBufSize +1, chFormat, args);

| va_end(args);
| string sS = buff;
| delete [] buff;
| return sS;
| }
...
| Ca vous plait ?


Non. Je n'en vois pas l'intérêt.

Très bien, à quelques détails près:
- buff n'est pas initialise avant le 1er appel a vsnprintf.
Bcp de compils en feront un 'warning'.


En effet, il aurait pû en passer (char*)NULL. (Si on tient à faire du C,
autant le faire correctement.)

- je serais tente d'utiliser un buffer local lors du
premier appel, pour eviter l'alloc et le second
appel lorsque c'est possible.
- si le constructeur de sS lance une exception (eh oui,
c'est possible) il y aura un memory leak.


J'ai une classe Buffer qui gère les deux. Elle commence avec un char[]
membre, puis passe à malloc/realloc quand ça ne suffit pas.

- si new[] ou le ctr lancent une exception, va_end
ne sera pas appelé (pas bien)


Encore une raison d'éviter les vararg's en C++.

En fait, la solution est assez simple. Voir GB_Format à ma site, par
exemple. (Et l'« assez simple » est un peu ironique. Bien que la plupart
de la complexité est due au fait de vouloir supporter d'une part
l'extension X/Open des paramètres positionnels, et de l'autre, à vouloir
supporter absolument toutes les options de printf, y compris celles qui
n'ont pas d'équivalent dans les options de formattage des ostream.)

--
James Kanze GABI Software mailto:
Conseils en informatique orientée objet/ http://www.gabi-soft.fr
Beratung in objektorientierter Datenverarbeitung
11 rue de Rambouillet, 78460 Chevreuse, France, +33 (0)1 30 23 45 16

Avatar
Eric Bart
Merci pour la leçon ! Je vois que je suis loin d'être 100% C++

Ces flux y m'embêtent. Cette classe string me parait mal équippée
en standard, surtout après avoir connu la classe AnsiString de Borland.

Si la technologie objet me parait incontournable, je me demande naivement
si le C++ est sa meilleure implémentation ...


Donc, pour clore l'exercice:

std::string strptrintf(const char *chFormat, ...)
{
struct va_Owner
{
va_list args;
va_Owner(const char *chFormat) { va_start(args, chFormat); }
~va_Owner() { va_end (args); }
//NB: copy-ctr, op= privés etc à ajouter si pas slmt pour ici
};
va_Owner va(chFormat);

char localBuf[256];
int const len = vsnprintf( localBuf, sizeof(localBuf)
, chFormat, va.args );
if( len<sizeof(localBuf) )
return localBuf;

std::vector<char> buf(len+1);
vsnprintf( &buf.front(), len+1, chFormat, va.args);
return &buf.front();
}


Ceci dit, oui:
- à réserver aux enracinés à printf.
- vsnprintf n'existe pas en standard C++ (n'est que ds C99)
- boost::format pour tout nouveau code:
http://www.boost.org/libs/format/doc/format.html

[Note en passant : si jamais ce thread est un troll et que je suis
tombé dedans, toutes mes excuses...]
Si c'en était un, je suis aussi tombé dedans... n'en avait pas l'air.


Merci,
Ivan
--
http://ivan.vecerina.com





Avatar
Christophe Lephay
Eric Bart wrote:
Merci pour la leçon ! Je vois que je suis loin d'être 100% C++

Ces flux y m'embêtent. Cette classe string me parait mal équippée
en standard, surtout après avoir connu la classe AnsiString de
Borland.

Si la technologie objet me parait incontournable, je me demande
naivement si le C++ est sa meilleure implémentation ...


La question est légitime. Attention seulement à ne pas confondre POO et
librairie (les qualités et défauts de std::string n'ont pas grand chose à
voir avec le modèle objet du C++)...

Chris

Avatar
Fabien LE LEZ
On Fri, 14 Nov 2003 21:09:57 +0100, "Eric Bart"
wrote:

Cette classe string me parait mal équippée
en standard


Tu peux toujours prendre une autre classe de chaînes, en essayant tout
de même de garder la portabilité. Il me semble que boost en propose.

--
;-)

Avatar
James Kanze
Fabien LE LEZ writes:

|> On Fri, 14 Nov 2003 21:09:57 +0100, "Eric Bart"
|> wrote:

|> >Cette classe string me parait mal équippée
|> >en standard

|> Tu peux toujours prendre une autre classe de chaînes, en essayant
|> tout de même de garder la portabilité. Il me semble que boost
|> en propose.

Il faudrait aussi comprendre ce qu'il entend par « mal
équipée ». Je ne crois pas qu'il soit interdit d'écrire des
fonctions libres qui fait ce qu'on veut.

--
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
Eric Bart
Il faudrait aussi comprendre ce qu'il entend par « mal
équipée ». Je ne crois pas qu'il soit interdit d'écrire des
fonctions libres qui fait ce qu'on veut.


Est-ce qu'il est possible de modifier la classe string sans changer
les sources systèmes ? Si pour rajouter une fonction à la classe string
je suis obligé d'éditer les sources standards, alors je ne pourrai pas
par exemple, distribuer mes sources. Il faudra que je prévienne de
modifier la classe string. Pas très pratique.

Je peux bien sûr rajouter les sources de boost à mon projet. M'enfin
tout dépend de la confiance que j'accorde à boost. Est-ce que boost
se compile sur tout ? durera t-il ? Est-ce qu'une librairie standard
ne remplacera boost d'ici peu, rendant mon programme illisible dans
quelques années.

1 2