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.
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.
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.
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:
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 ?
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:
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 ?
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:
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 ?
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:
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
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:
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 kanze.james@neuf.fr
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
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:
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
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.
In news:e1ns7k$vs5$1@emma.aioe.org,
James Kanze <kanze.james@neuf.fr> 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.
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.
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
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
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
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
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
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