Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Comment ecrire une macro qui "mangent" un argument de type "..."

5 réponses
Avatar
mderie
En deux mots, j'ai un tout petit bout de code pour sortir des traces
via le port reseau (voici la seule fonction a utilisee :) :

Fichier udplog.h :

#define LOGGER udplog
void udplog(char *message, ...)

LOGGER etant un simple alias pour ne pas specifier le type de
"transport" pour les logs. Or donc je voudrais arriver a cette
situation :

Fichier logger.h :

// Warning : there can be only one !

// Serial Logger
//#include ...

// UDP Logger
//#include "udplog.h"

// NOP Logger
#define LOGGER noplog
inline void noplog(char *message, ...) {}

//TODO: How ?
// Ultra Quiet Logger
//#define LOGGER(?)
//#define LOGGER0(msg)
//#define LOGGER1(msg,x)
//#define LOGGER2(msg,x, y)
//...

Mon probleme est donc comment ecrire une MACRO qui puisse
avoir une impacte nulle pour du code en "release" ! Bien sur le NOP
Logger
est deja assez leger... Mais bon il y a surement moyens de faire mieux
non ?

Bien a vous

5 réponses

Avatar
Aurelien Regat-Barrel

Mon probleme est donc comment ecrire une MACRO qui puisse
avoir une impacte nulle pour du code en "release" ! Bien sur le NOP
Logger
est deja assez leger... Mais bon il y a surement moyens de faire mieux
non ?


C'est le nombre variable d'argument qui te pose problème si j'ai bien
compris. Y'a des compilos qui permettent de faire de telles macros, mais
c'est pas standard. Tu peux parenthéser l'appel à LOGGER:

#define LOGGER(x) printf x

LOGGER( ("hello %s", "world!" ) );

ou bricoler un truc à base de if(0) peut être...

#define LOGGER if ( 0 ) noplog

enfin, plutôt:

#define LOGGER if ( 1 ) {} else noplog

d'après mes souvenirs sur la bonne manière d'écrire des macros.

--
Aurélien Regat-Barrel

Avatar
Patrick 'Zener' Brunet
Bonjour.

Je réponds à mderie
En deux mots, j'ai un tout petit bout de code pour sortir des traces
via le port reseau (voici la seule fonction a utilisee :) :

Fichier udplog.h :

#define LOGGER udplog
void udplog(char *message, ...)

LOGGER etant un simple alias pour ne pas specifier le type de
"transport" pour les logs.


J'ai eu le problème dans une telle application.
A priori les macros à liste variable ça n'existe pas, sauf effets de bords
bizarres.

J'ai donc procédé ainsi:

#define COMMA ,

LOGGER( "Une %s à %u euros", "astuce" COMMA 2);

Ca ne couvre qu'un niveau d'imbrication bien sûr.

Cordialement,

--
/***************************************
* Patrick BRUNET
* E-mail: lien sur http://zener131.free.fr/ContactMe
***************************************/

Avatar
kanze
Aurelien Regat-Barrel wrote:

Mon probleme est donc comment ecrire une MACRO qui puisse
avoir une impacte nulle pour du code en "release" ! Bien sur
le NOP Logger est deja assez leger... Mais bon il y a
surement moyens de faire mieux non ?


C'est le nombre variable d'argument qui te pose problème si
j'ai bien compris. Y'a des compilos qui permettent de faire de
telles macros, mais c'est pas standard.


Ça dépend. Il faut d'abord savoir s'il fait du C ou du C++. Les
macros variadiques existent bien en C, voir __VA_ARGS__. Et en
C++, il n'y a aucun problème à écrire quelque chose du genre :

LOGGER( "x = " << x ) ;

avec :
#define LOGGER( msg ) log << msg
ou :
#define LOGGER( msg )

Tu peux parenthéser l'appel à LOGGER:

#define LOGGER(x) printf x

LOGGER( ("hello %s", "world!" ) );

ou bricoler un truc à base de if(0) peut être...

#define LOGGER if ( 0 ) noplog

enfin, plutôt:

#define LOGGER if ( 1 ) {} else noplog

d'après mes souvenirs sur la bonne manière d'écrire des macros.


En effet, Loïc a proposé quelque chose de ce genre dernièrement.
On a soit :
#define LOGGER getlog( __FILE__, __LINE__ )
soit
#define LOGGER if ( true ) ; else dummy
(Note bien que dans ce cas-ci, même avec les logs désactivés
lors de la compilation, il faut bien que ton programme contient
un object dummy.)

Ici aussi, on écrit ensuite :
LOGGER << "x = " << x ;

(Au fond, je me démande si:
#define LOGGER false && dummy
ne serait pas mieux pour supprimer le log. Ça permettrait
d'introduire des logs dans un initialisateur dans un
constructeur, par exemple :
Toto::Toto(
int titi )
: myTiti( ((LOGGER << "titi = " << titi), titi) )
// ...
.)

--
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
Aurelien Regat-Barrel

En effet, Loïc a proposé quelque chose de ce genre dernièrement.
On a soit :
#define LOGGER getlog( __FILE__, __LINE__ )
soit
#define LOGGER if ( true ) ; else dummy
(Note bien que dans ce cas-ci, même avec les logs désactivés
lors de la compilation, il faut bien que ton programme contient
un object dummy.)


C'est à peu près ce que j'ai dans mon appli, sauf que getlog() renvoie
un stream qui wrapp std::cout/ofstream/... ou qui ne fait rien du tout.
J'ai pas cherché à évaluer si le compilateur supprimait son utilisation,
vu que jusque là je n'en n'ai pas éprouvé le besoin.

Ici aussi, on écrit ensuite :
LOGGER << "x = " << x ;

(Au fond, je me démande si:
#define LOGGER false && dummy
ne serait pas mieux pour supprimer le log. Ça permettrait
d'introduire des logs dans un initialisateur dans un
constructeur, par exemple :
Toto::Toto(
int titi )
: myTiti( ((LOGGER << "titi = " << titi), titi) )
// ...
..)


Actuellement, dans mon cas, les logs sont supprimés en recompilant
(uniquement) getlog.cpp pour que getlog() renvoie un wrapper "nul". Je
réalise qu'on pourrait aisément avoir la même chose avec un :

#define LOGGER if ( !log_are_enabled ) ; else dummy

et ainsi avoir un comportement dynamique, pour le coût d'un if(). Je ne
sais pas vraiment pourquoi, mais utiliser

#define LOGGER log_are_enabled && dummy

me parrait plus risqué.

--
Aurélien Regat-Barrel

Avatar
kanze
Aurelien Regat-Barrel wrote:

En effet, Loïc a proposé quelque chose de ce genre dernièrement.
On a soit :
#define LOGGER getlog( __FILE__, __LINE__ )
soit
#define LOGGER if ( true ) ; else dummy
(Note bien que dans ce cas-ci, même avec les logs désactivés
lors de la compilation, il faut bien que ton programme contient
un object dummy.)


C'est à peu près ce que j'ai dans mon appli, sauf que getlog()
renvoie un stream qui wrapp std::cout/ofstream/... ou qui ne
fait rien du tout. J'ai pas cherché à évaluer si le
compilateur supprimait son utilisation, vu que jusque là je
n'en n'ai pas éprouvé le besoin.


Tout à fait. Je n'ai jamais eu besoin de supprimer la
configuration dynamique du log non plus -- correctement écrit et
utilisé, les tests consument vraiment peu de temps. Mais la
possibilité de pouvoir le faire a souvent été essentiel pour
faire accepter politiquement le logging.

Ici aussi, on écrit ensuite :
LOGGER << "x = " << x ;

(Au fond, je me démande si:
#define LOGGER false && dummy
ne serait pas mieux pour supprimer le log. Ça permettrait
d'introduire des logs dans un initialisateur dans un
constructeur, par exemple :
Toto::Toto(
int titi )
: myTiti( ((LOGGER << "titi = " << titi), titi) )
// ...
..)


Actuellement, dans mon cas, les logs sont supprimés en
recompilant (uniquement) getlog.cpp pour que getlog() renvoie
un wrapper "nul". Je réalise qu'on pourrait aisément avoir la
même chose avec un :

#define LOGGER if ( !log_are_enabled ) ; else dummy

et ainsi avoir un comportement dynamique, pour le coût d'un
if(). Je ne sais pas vraiment pourquoi, mais utiliser

#define LOGGER log_are_enabled && dummy

me parrait plus risqué.


C'est cependant plus ou moins ce qui se trouve dans la plupart
des macros pour assert. L'intérêt, comme j'ai dit, c'est que
c'est une expression, et qu'il peut servir (avec l'opérateur
virgule) dans des contextes où seulement une expression est
permise. La risque, évidemment, c'est aussi que c'est une
expression, ce qui donne davantage de possibilités à le faire
foirer : true || LOGGER << ..., par exemple,

Dans la pratique, je ne travaille pas avec des programmeurs qui
cherchent à détourner ce que j'ai fait à ce point-là, et je n'ai
pas vu des « accidents » avec l'expression qui n'ont pas
provoqué des erreurs de compilation.

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