OVH Cloud OVH Cloud

passer une liste d'argument variable lors d'un appel

36 réponses
Avatar
superbabar51
Bonjour,

Comment est t-il possible de passer une liste d'argument variable =E0
une autre fonction?

Par exemple, considerons la fonction handle_error suivante:

void handle_error (int errorcode, std::string format, ...)
{
printf(format.c_str(), ...)
// o=F9 ... repr=E9sente les arguments pass=E9s =E0 handle_error
}

Est-ce possible (proprement)?

Merci

10 réponses

1 2 3 4
Avatar
Sylvain
kanze wrote on 12/04/2006 09:59:

Tu penses que Sylvain est un troll ? Je n'irais pas jusqu'à là.

c'est vraiment l'école du rire ce ng !...


Sylvain.

Avatar
Sylvain
Arnaud Meurgues wrote on 12/04/2006 08:40:

ami troller, bonsoir,

Je suppose que là, vous n'êtes pas méprisant....


belle inflation, c'est quoi après ?

Non. Mais vous n'avez visiblement pas compris ce que voulait dire James,
étant donné votre réponse.


ben visiblement, tu comprends pas ce que je dis; je te proposerais bien
d'arréter d'essayer de deviner ce que j'ai pu comprendre ou ce que
j'aurais ............ on peut s'en tenir aux écrits, non ?

Ce n'était pas hors sujet. Ou du moins, si ça l'était, c'est vous qui
l'avez introduit en disant : « vouloir mettre l'usage de l'un dans
l'autre est source de duplication de code et ne parait pas vraiment
utile ni efficace. »

En quoi la duplication de code (généré, dans votre propos) n'est-elle
pas efficace ?


pour la Nième fois, le fil est:

Sylvain
vouloir mettre l'usage de l'un dans l'autre est source de


duplication de code et ne parait pas vraiment utile ni
efficace.



James
Je ne sais pas en ce qui concerne l'efficacité -- il a été assez

efficace pour les applications où je m'en suis servi. En
revanche, je ne vois pas ton point en ce qui concerne la
duplication du code -- les templates sont là pour ça.


donc "mon point" sur la duplication n'a jamais existé - je laisse à
James ce qui est dit par James.

sur l'utilité:

un /gestionnaire d'erreur/ sert (notamment) à
- faire le ménage (là des tempo avec dstrc cleaner sont efficaces)
- enregistrer le contexte (pile d'appel, parametres, état interne)
- stabiliser le code (reprendre normalement le traitement) ou répercuter
l'erreur ou terminer en sauvant ce qui peut l'être.

un /stream avec opérateurs d'injection/ (je parlais de ça, pas d'un
mystérieux template, même si le truc utilisé peut être template based)
sert à:
- accumuler / collecter / formater de l'information.

ces 2 fonctionnalités me paraissent disjointes; lorsque je maintiens mon
stream pour lui apprendre à traiter de caractères spéciaux (c'est un
exemple) je me fous un peu de savoir quel code d'erreur parmi n je
retournerais au shell qui lancera le run qui plante; inversement si je
veux raffiner le log des call stacks (c'est un autre exemple) je me fous
de savoir s'il existe pour mon streamer texte un opérateur << supportant
le type qu'un collège inventera demain.

donc oui on peut faire un:

CAppli< CLoger<CStreamer>, CEventHdlr< ...>, < < > > >.run();

mais on peut aussi définir des objets qui soient autonomes *et* spécialisés.

En revanche, je ne saisis pas le problème d'avoir du code binaire
dupliqué ? C'est par soucis de la taille de l'exécutable ?
En quoi cette solution ne vous semble-t-elle pas satisfaisante ?




lire plus haut.

Un template ne duplique pas le code source, ce qui, pour la maintenance
d'une application est essentiel. Donc « un template, ça duplique »,
soit, mais ça dépend de quoi l'on parle.


lire plus haut.

Par ailleurs, je comprends très bien le problème que pose la duplication
de code source, mais j'ai du mal à saisir le problème de la duplication
du code généré. Mais vos propos laissent entendre que la duplication de
code généré induit par les templates n'est pas une solution
satisfaisante. On peut donc en déduire que, pour vous, cette duplication
pose un problème. Lequel ?


saisir, laisse entendre, induit, déduire, ....

quel est le problème ? s'il existe il est surement de communication.

Pourquoi n'avoir pas répondu quelque chose du genre : « Certes, le code
source n'est pas dupliqué, mais il reste de la duplication dans le code
généré » ? C'est certainement plus utile et plus efficace (critères dont
vous avez le soucis) que « excellente celle-là !! merci ! je la note en
tête des histoires droles. ».


parce que je vais faire mes réponses et non pas copier/coller ce que tu
crois devoir choisir pour moi, enfin si tu veux bien, hein, faut pas
lacher les chiens que pour ça.

Sylvain.




Avatar
Sylvain
kanze wrote on 12/04/2006 10:10:

ah les temporaires, un bon flush masqué dans un destructeur,
j'ai bon Monsieur devinette ?


Il n'y a pas de devinette. C'est devenu une technique quasiment
standard pour la gestion des fichiers de log.


"standard" mais aucune ref. prête à copier/coller, je ne suis pas en vaine.

C'est l'idiome du wrapper d'un flux. Je l'ai déjà présenté
maintes fois. (En fait, ce n'est qu'une incarnation particulière
du modèle de Proxy. Je l'ai trouvé ici dans certain code écrit


maintes fois mais aucune ref. prête à copier/coller ...

dans le même temps, peux-tu nous expliquer comment loguer
*une* fois la même erreur survenue dans X (64, 128, ...)
threads lancés simultanément mais échouant tous pour la même
raison (extérieure aux threads).


C'est simple -- le temporaire detient un lock. Pour toute sa
durée de vie.


je suis pas sur de comprendre ou alors mon cas était mal exprimé, je
reformule: un process principal (mettons un parser d'url) lance 92
threads sniffers en simultané; 12 threads échouent pour URL invalide, 32
finissent correctement et le restant se suicide suite à un timeout
serveur. je souhaite loger une fois "32 pages lues, 60 echecs".

je m'attendais à devoir synchroniser des trucs, définir des pools de
réception d'erreurs, balancer des évenements diffus, ...

je veux bien mettre un seul lock à la place, tu penses, mais où.

peux-tu également indiquer où placer ton temporaire dans une
chaine d'appels de méthodes (le C++ est procédurale, non?)
d'où on sort par des catch / throw en cascade ?


Il n'y a pas le moindre catch ni throw. C'est le temporaire qui
fait tout le boulot.


"il n'y a pas" ?? jamais ? a p'us ? disparu ? interdit ???

dans mes applis il y en a, je t'assure (sacrée "pas compétence en C++"
!!), je reformule: s'il y en avait, quel modèle serait indiqué ?

Sylvain.


Avatar
kanze
Sylvain wrote:
Arnaud Meurgues wrote on 12/04/2006 08:40:


[...]
Sylvain
vouloir mettre l'usage de l'un dans l'autre est source de


duplication de code et ne parait pas vraiment utile ni
efficace.

James
Je ne sais pas en ce qui concerne l'efficacité -- il a été assez

efficace pour les applications où je m'en suis servi. En
revanche, je ne vois pas ton point en ce qui concerne la
duplication du code -- les templates sont là pour ça.


donc "mon point" sur la duplication n'a jamais existé - je
laisse à James ce qui est dit par James.


Juste pour clarifier, j'avoue qu'il ne m'est jamais venu à
l'esprit que la « duplication du code » puisse s'appliquer à
d'autre chose que les sources -- c'est le seul « code » qui me
concerne, en général.

sur l'utilité:

un /gestionnaire d'erreur/ sert (notamment) à
- faire le ménage (là des tempo avec dstrc cleaner sont efficaces)
- enregistrer le contexte (pile d'appel, parametres, état interne)
- stabiliser le code (reprendre normalement le traitement) ou répercuter
l'erreur ou terminer en sauvant ce qui peut l'être.

un /stream avec opérateurs d'injection/ (je parlais de ça, pas d'un
mystérieux template, même si le truc utilisé peut être template b ased)
sert à:
- accumuler / collecter / formater de l'information.

ces 2 fonctionnalités me paraissent disjointes;


Jusqu'à là, on est, en fait, à peu près d'accord.

lorsque je maintiens mon stream pour lui apprendre à traiter
de caractères spéciaux (c'est un exemple) je me fous un peu de
savoir quel code d'erreur parmi n je retournerais au shell qui
lancera le run qui plante; inversement si je veux raffiner le
log des call stacks (c'est un autre exemple) je me fous de
savoir s'il existe pour mon streamer texte un opérateur <<
supportant le type qu'un collège inventera demain.


Le problème est pragmatique. Logiquement, je suis à peu près
d'accord : formatter le message est une fonctionnalité distincte
que de le passer au loggeur, qui lui, selon la gravité et la
façon qu'il est configuré, ferait ce qu'il en faut. Séparer ces
deux fonctions, en revanche, pose deux problèmes pratiques :

-- On veut que le programmeur utilise le log le plus possible.
Pour ce faire, il faut lui en rendre l'utilisation la plus
simple possible. Exiger qu'il crée un ostringstream local ne
va pas dans ce sens.

-- Selon la configuration du log, la plupart des logs sont
typiquement désactivés. Et si les logs sont utilisés autant
qu'on veut, formatter le message du log qui n'apparaîtront
pas consumera beaucoup trop de temps CPU. En répoussant le
formattage dans la classe de log, on pourrait ne le faire
que si c'est utile.

Note bien que ce n'est pas le code appelant qui doit faire la
décision sur la termination. Il décide la gravité, et c'est le
sous-système de log/gestion d'erreur qui s'occupe de la reste.
Le programmeur écrit donc quelque chose du genre :

log.error( Log::serious ) << "x = " << x ;

(Sauf que la plupart du temps, à la place de log.error(), il y a
un macro, qui y ajoute le __FILE__ et le __LINE__
automatiquement.)

La fonction log.error renvoie un objet temporaire, qui contient
un pointeur au flux -- pointeur qui serait null si le log n'est
pas actif ici, pour la gravité donnée. Dans une application
multithread, log.error prendrait aussi un lock si le log est
actif. Le destructeur de l'objet temporaire rendrait le main de
nouveau au log, qui flushera le flux (ajoutant un 'n', s'il
faut), libèrera le lock s'il faut, et terminera le programme, si
c'est ça qui a été démandé.

Dans de grosses applications, évidemment, on ajoute un paramètre
avec le sous-système à l'appel de log.error, afin de pouvoir
n'activer le log que pour certains sous-systèmes.

--
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





Avatar
kanze
Michel Decima wrote:
In news:,
kanze typed:

C'est l'idiome du wrapper d'un flux. Je l'ai déjà présenté
maintes fois. (En fait, ce n'est qu'une incarnation
particulière du modèle de Proxy. Je l'ai trouvé ici dans
certain code écrit il y a plus de dix ans -- avec des {...}
en plus, parce que les compilateurs d'alors ne respectaient
pas la durée de vie telle qu'elle est définie par la norme
aujourd'hui.)


A propos de duree de vie, je sais que le code suivant est
correct:

void print(string const& s);
print(string("foo").append("bar"));

mais que se passe-t il pour celui ci:

void print(char const*s);
print(string("foo").append("bar").c_str());


Les deux cas sont identiques. C-à-d que s'il sont correct ou non
dépend de quand tu as écrit le code. Avant la norme, la durée de
vie dépendait de l'implémentation, et pourrait être n'importe
quoi entre « première utilisation », et le '}' suivant. Et dans
tes exemples, l'appel d'une fonction membre compte comme
utilisation -- le compilateur avait le droit d'appeler le
destructeur dès le rétour de append. (Dans la pratique, c'est
ce que faisait g++.)

La norme a spécifié beaucoup plus précisement : c'est à la fin
de l'expression complète. Ici, grosso modo, au ;. Dans la
pratique, tous les compilateurs qui avaient une durée de vie
plus courte l'ont étendue pour être conforme. Ceux qui avaient
une durée de vie plus longue, en revanche, ont souvent des
options pour avoir l'ancienne durée -- options qui sont, au
moins chez Sun, actives par défaut.

Les deux compilateurs que j'ai sous la main (gcc4 et xlC6)
appellent le destructeur du temporaire quand il faut (cad
apres l'appel a print), mais je voudrait savoir si c'est un
comportement garanti ou bien juste un hasard
d'implementation...


C'est garanti par la norme.

--
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


Avatar
Michel Decima
In news:,
kanze typed:
Michel Decima wrote:

A propos de duree de vie, je sais que le code suivant est
correct:

void print(string const& s);
print(string("foo").append("bar"));

mais que se passe-t il pour celui ci:

void print(char const*s);
print(string("foo").append("bar").c_str());


Les deux cas sont identiques. C-à-d que s'il sont correct ou non
dépend de quand tu as écrit le code. Avant la norme, la durée de
vie dépendait de l'implémentation, et pourrait être n'importe
quoi entre « première utilisation », et le '}' suivant. Et dans
tes exemples, l'appel d'une fonction membre compte comme
utilisation -- le compilateur avait le droit d'appeler le
destructeur dès le rétour de append. (Dans la pratique, c'est
ce que faisait g++.)

La norme a spécifié beaucoup plus précisement : c'est à la fin
de l'expression complète. Ici, grosso modo, au ;. Dans la
pratique, tous les compilateurs qui avaient une durée de vie
plus courte l'ont étendue pour être conforme. Ceux qui avaient
une durée de vie plus longue, en revanche, ont souvent des
options pour avoir l'ancienne durée -- options qui sont, au
moins chez Sun, actives par défaut.


Merci pour l'explication (en particulier l'aspect historique). Je vais
essayer de noter le comportement du compilateur Sun quelque
part...

Les deux compilateurs que j'ai sous la main (gcc4 et xlC6)
appellent le destructeur du temporaire quand il faut (cad
apres l'appel a print), mais je voudrait savoir si c'est un
comportement garanti ou bien juste un hasard
d'implementation...


C'est garanti par la norme.


Je remercie aussi la norme, qui simplifie mon code (en supprimant le
couplage entre deux classes...)


Avatar
Michel Decima
In news:,
kanze typed:

-- Selon la configuration du log, la plupart des logs sont
typiquement désactivés. Et si les logs sont utilisés autant
qu'on veut, formatter le message du log qui n'apparaîtront
pas consumera beaucoup trop de temps CPU. En répoussant le
formattage dans la classe de log, on pourrait ne le faire
que si c'est utile.

Note bien que ce n'est pas le code appelant qui doit faire la
décision sur la termination. Il décide la gravité, et c'est le
sous-système de log/gestion d'erreur qui s'occupe de la reste.
Le programmeur écrit donc quelque chose du genre :

log.error( Log::serious ) << "x = " << x ;



Si j'ai bien tout compris, le temporaire ne va faire le formattage que
si le log est active. Ca elimine le cout du formattage, mais on a
toujours celui des parametres injectes:

log.error( Log::serious ) << "result=" << maGrosseFonctionCouteuse();

Evidemment, il serait souhaitable d'injecter dans le log uniquement
des parametres "qui ne coutent rien a obtenir", comme des variables du
site appelant, mais des fois on ne peut pas faire autrement...
J'ai du mal a voir une solution qui puisse fonctionner avec l'operation
d'injection, et des log qu'on peut activer/desactiver au runtime, a
par ceci:

if (LogStream& slog = log.error( Log::serious )) {
slog << << "result=" << maGrosseFonctionCouteuse();
}

Existe-t il d'autres methodes ?

Avatar
kanze
Michel Decima wrote:
In news:,
kanze typed:

-- Selon la configuration du log, la plupart des logs sont
typiquement désactivés. Et si les logs sont utilisés autant
qu'on veut, formatter le message du log qui n'apparaîtront
pas consumera beaucoup trop de temps CPU. En répoussant le
formattage dans la classe de log, on pourrait ne le faire
que si c'est utile.

Note bien que ce n'est pas le code appelant qui doit faire la
décision sur la termination. Il décide la gravité, et c'est le
sous-système de log/gestion d'erreur qui s'occupe de la reste.
Le programmeur écrit donc quelque chose du genre :

log.error( Log::serious ) << "x = " << x ;


Si j'ai bien tout compris, le temporaire ne va faire le
formattage que si le log est active. Ca elimine le cout du
formattage, mais on a toujours celui des parametres injectes:

log.error( Log::serious ) << "result=" << maGrosseFonctionCouteuse();


Si tu utilises des expressions importantes, oui. Je dirais en
revanche que c'est déjà un problème. Le but du log, c'est de
voir l'état immédiat, non ce qu'on peut en faire. Dans la
pratique, les paramètres sont prèsque toujours des variables,
locales ou membres, qui sont passées par référence. Le coût, en
fait, c'est un test par paramètre.

Evidemment, il serait souhaitable d'injecter dans le log
uniquement des parametres "qui ne coutent rien a obtenir",
comme des variables du site appelant, mais des fois on ne peut
pas faire autrement... J'ai du mal a voir une solution qui
puisse fonctionner avec l'operation d'injection, et des log
qu'on peut activer/desactiver au runtime, a par ceci:

if (LogStream& slog = log.error( Log::serious )) {
slog << << "result=" << maGrosseFonctionCouteuse();
}


Sauf qu'il n'y a pas de conversion d'un LogStream en bool:-).

Existe-t il d'autres methodes ?


Pas à ma connaissance, mais j'avoue que je n'ai jamais cherché.
Peut-être quelque chose à base de boost::function -- le
paramètre, c'est un objet fonctionnel, qu'on n'appelle que si.

J'avoue que j'ai du mal à concevoir le cas où ça serait un
problème. Le pire que j'ai eu, une fois, c'est où on sortait un
tableau dans le log. Alors, quelque chose comme :

{
LogStream log( log.error( Log::info ) ) ;
log << "Entete..." << std::endl ;
for ( int i = 0 ; i < tbl.size() ; ++ i ) {
log << std::setw( 2 ) << i << tbl[ i ] ...
}
}

Même là, c'était assez rapide sans test préalable.

--
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


Avatar
kanze
Michel Decima wrote:

Je vais
essayer de noter le comportement du compilateur Sun quelque
part...


Au cas où : l'option pour forcer un comportement standard chez
Sun est : -features=tmplife

--
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

Avatar
Loïc Joly
log.error( Log::serious ) << "result=" << maGrosseFonctionCouteuse();

Evidemment, il serait souhaitable d'injecter dans le log uniquement
des parametres "qui ne coutent rien a obtenir", comme des variables du
site appelant, mais des fois on ne peut pas faire autrement...
J'ai du mal a voir une solution qui puisse fonctionner avec l'operation
d'injection, et des log qu'on peut activer/desactiver au runtime, a
par ceci:

if (LogStream& slog = log.error( Log::serious )) {
slog << << "result=" << maGrosseFonctionCouteuse();
}

Existe-t il d'autres methodes ?


Il y a une astuce à base de macro. Après, on aime ou pas... Un truc genre :


#define LOG(x) if (!x) ; else log

LOG(test) << maGrosseFonctionCouteuse();


--
Loïc

1 2 3 4