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

6 réponses

1 2 3 4
Avatar
Michel Decima

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();



Je vois le principe. Je suppose aussi que si la valeur de test est
connue a la compilation, un compilateur va pouvoir optimiser le tout et
supprimer completement l'injection.
Mais ca limite un peu l'usage, on ne peut pas passer le stream en
parametre (mais a quoi ca pourrait bien servir...) Bref, c'est une macro.

Merci pour l'astuce.


Avatar
Michel Decima
Michel Decima wrote:

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.


Je suis bien d'accord sur le but du log, et la nature des parametres qui
y seront injectes. Mais ce qui m'interesse, c'est convaincre des
utilisateurs qu'un tel systeme de log ne coute rien, ou pas grand chose,
quand il n'est pas actif.

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


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


Pourquoi pas, si c'est moi qui implemente cette classe ? Plus
serieusement, il y a bien un operateur de conversion implicite vers
void* dans std::basic_ios, non ?


Avatar
James Kanze
Michel Decima wrote:
Michel Decima wrote:



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.



Je suis bien d'accord sur le but du log, et la nature des
parametres qui y seront injectes. Mais ce qui m'interesse,
c'est convaincre des utilisateurs qu'un tel systeme de log ne
coute rien, ou pas grand chose, quand il n'est pas actif.


Je comprends. Prèsque toujours, j'emballe l'appel dans un macro,

LOG( Log::serious, "result = " << whatever ) ;

Il n'y a pas de raison technique, mais quand on discute au début
du projet, l'argument qu'on peut le supprimer complétement s'il
faut est parfois important. Bien que depuis quinze ans, on n'en
a jamais eu besoin de le supprimer.

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




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



Pourquoi pas, si c'est moi qui implemente cette classe ?


Ahhh, mais c'est moi qui l'a implémentée:-). (Au fond, c'est une
bonne idée -- je crois que je vais l'adopter.)

Plus serieusement, il y a bien un operateur de conversion
implicite vers void* dans std::basic_ios, non ?


Oui, mais d'où vois-tu un std::basic_ios. Il y a bien un
ostream* dans LogStream, comme membre, mais c'est tout.

--
James Kanze
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:e1ns7k$vs5$,
James Kanze typed:

Je comprends. Prèsque toujours, j'emballe l'appel dans un macro,

LOG( Log::serious, "result = " << whatever ) ;

Il n'y a pas de raison technique, mais quand on discute au début
du projet, l'argument qu'on peut le supprimer complétement s'il
faut est parfois important. Bien que depuis quinze ans, on n'en
a jamais eu besoin de le supprimer.


C'est exactement le cas dans lequel je me trouve ;)

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


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


Pourquoi pas, si c'est moi qui implemente cette classe ?


Ahhh, mais c'est moi qui l'a implémentée:-). (Au fond, c'est une
bonne idée -- je crois que je vais l'adopter.)


Je t'en prie ;)

Plus serieusement, il y a bien un operateur de conversion
implicite vers void* dans std::basic_ios, non ?


Oui, mais d'où vois-tu un std::basic_ios. Il y a bien un
ostream* dans LogStream, comme membre, mais c'est tout.


C'etait par analogie: puisque le LogStream reprends une partie du
comportement d'un std::ostream (au moins l'operateur d'injection),
on peut de maniere naturelle fournir la conversion vers bool, en
prenant l'etat du log (actif ou pas) et l'etat du stream sous-jacent.

M.




Avatar
kanze
Loïc Joly wrote:
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 g enre :

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


Plutôt :
#define LOG(x) if ( !(x) ) ; else log( __FILE__, __LINE__ )
non ? (Profitons du fait que c'est un macro.) Aussi, typiquement, la
condition dans l'if serait plutôt du genre :
if ( ! log.isActive( x ) )
. Où isActive est une fonction inline plus ou moins du genre :
return myIsActive[ param ] ;
ou
return myStreams[ param ] != NULL ;
Si le compilateur n'est pas trop bête, et que le paramètre est une
constante, on finit avec un conditionnel on ne peut plus simple... ni
plus rapide.

LOG(test) << maGrosseFonctionCouteuse();


Tiens. C'est assez chouette ; si je remplace la définition avec :
#define LOG(x) if ( true ) ; else log( __FILE__, __LINE__ )
un bon compilateur ne génère rien. Ce qui est un argument parfois
essentiel pour faire passer la pillule, même si par la suite, je n'ai
jamais eu besoin d'en profiter.

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

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();


Je vois le principe. Je suppose aussi que si la valeur de test est
connue a la compilation, un compilateur va pouvoir optimiser le tout
et supprimer completement l'injection.
Mais ca limite un peu l'usage, on ne peut pas passer le stream en
parametre (mais a quoi ca pourrait bien servir...) Bref, c'est une
macro.


Dans la pratique, il faut un macro (ou plutôt plusieurs), parce que si
tu démandes aux gens d'écrire :
log.stream( gravity, __FILE__, __LINE__ ) << ...
tu vas te rétrouver avec bien peu de messages dans le log, même
configuré pour une verbosité maximum. C'est un cas où à mon avis,
tout
est permis pour réduire au maximum ce que le programmeur a à écrire.
Et
je veux dire vraiment tout.

Je dis en fait plusieurs macros, parce qu'en plus que le cas évident
ici, tu en veux un pour les fonctions, qui déclare un objet local,
dont
le constructeur ET le destructeur émet un message. Et une pour en
obtenir l'objet de flux, de façon à l'utiliser quand il n'est pas
commode de sortir le message dans une seule expression. Dans la
pratique, je définis en général plusieurs macros « de haut niveau
», du
genre :
#define LOG_FN( name ) FunctionLogger log ## __LINE__ ( # name )
#define LOG_VAR( name )
LOG( Log::defaultVarGravity ) << #name " = " << name
etc. et un ou deux qui donne accès à ce qui se passe réelement, pour
le
cas où les macros de haut niveau ne conviennent pas.

D'après mes expériences, c'est beaucoup pls facile à convaincre un
programmeur à écrire :
LOG_VAR( toto ) ;
que d'écrire :
log.stream( Log::defaultVarGravity, __FILE__, __LINE )
<< "toto = " << toto ;

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



1 2 3 4