De temps en temps, il est nécessaire d'avoir une instruction no-op en C++. Par exemple, lors de l'implémentation de assert()
qui est désactivée dans une configuration sans débogage (voir aussi cette question ):
#ifdef _DEBUG
#define assert(x) if( !x ) { \
ThrowExcepion(__FILE__, __LINE__);\
} else {\
//noop here \
}
#else
#define assert(x) //noop here
#endif
Jusqu'à présent, j'ai l'impression que la bonne façon est d'utiliser (void)0;
pour un no-op:
(void)0;
cependant je soupçonne que cela pourrait déclencher des avertissements sur certains compilateurs - quelque chose comme C4555: expression has no effect; expected expression with side-effect
Avertissement Visual C++ qui n'est pas émis pour ce cas particulier mais qui est émis lorsqu'il n'y a pas de transtypage en void
.
Est-il universellement portable? Y a-t-il une meilleure façon?
Je soupçonne que cela pourrait déclencher des avertissements sur certains compilateurs
Peu probable, car ((void)0)
est ce à quoi la macro standard assert
se développe lorsque NDEBUG
est définie. Ainsi, tout compilateur qui émet des avertissements émet des avertissements chaque fois que du code contenant des assertions est compilé pour publication. Je pense que cela serait considéré comme un bug par les utilisateurs.
Je suppose qu'un compilateur pourrait éviter ce problème en avertissant votre proposition (void)0
tout en ne traitant que ((void)0)
spécialement. Il vaut donc mieux utiliser ((void)0)
, mais j'en doute.
En général, jeter quelque chose à annuler, avec ou sans les parens enfermants supplémentaires, signifie idiomatiquement "ignorer cela". Par exemple, dans le code C qui convertit les paramètres de fonction en void
afin de supprimer les avertissements pour les variables inutilisées. Donc, sur ce point aussi, un compilateur qui avertirait serait plutôt impopulaire, car supprimer un avertissement vous en donnerait juste un autre.
Notez qu'en C++, les en-têtes standard sont autorisés à s'inclure mutuellement. Par conséquent, si vous utilisez tout en-tête standard, assert
peut avoir été défini par cela. Votre code n'est donc pas portable sur ce compte. Si vous parlez "universellement portable", vous devez normalement traiter toute macro définie dans n'importe quel en-tête standard comme un identifiant réservé. Vous pourriez le définir, mais utiliser un nom différent pour vos propres affirmations serait plus judicieux. Je sais que ce n'est qu'un exemple, mais je ne vois pas pourquoi vous voudriez jamais définir assert
de manière "universellement portable", car toutes les implémentations C++ l'ont déjà, et il ne fait pas ce que vous le définissez ici.
Le no-op le plus simple est simplement de ne pas avoir de code du tout:
#define noop
Le code utilisateur aura alors:
if (condition) noop; else do_something();
L'alternative que vous mentionnez est également un no-op: (void)0;
, mais si vous comptez l'utiliser dans une macro, vous devez laisser le ;
de côté pour que l'appelant ajoute:
#define noop (void)0
if (condition) noop; else do_something();
(Si ;
faisait partie de la macro, il y aurait alors un supplément ;
Là)
Que diriez-vous de do { } while(0)
? Oui, cela ajoute du code, mais je suis sûr que la plupart des compilateurs sont aujourd'hui capables de l'optimiser.
; est considéré comme un no-op standard. Notez qu'il est possible que le compilateur n'en génère aucun code.
Et à propos de:
#define NOP() ({(void)0;})
ou juste
#define NOP() ({;})
Je pense que l'objectif ici, et la raison de ne pas définir la macro à rien, est de exiger l'utilisateur d'ajouter un ;
. À cet effet, n'importe où une déclaration est légale, (void)0
(Ou ((void)0)
, Ou d'autres variantes) est acceptable.
J'ai trouvé cette question parce que je devais faire la même chose à global scope, où une vieille déclaration simple est illégale. Heureusement, C++ 11 nous offre une alternative: static_assert(true, "NO OP")
. Cela peut être utilisé n'importe où, et atteint mon objectif d'exiger un ;
Après la macro. (Dans mon cas, la macro est une balise pour un outil de génération de code qui analyse le fichier source, donc lors de la compilation du code en C++, il toujours sera un NO-OP.)
AFAIK, il est universellement portable.
#define MYDEFINE()
fera aussi bien.
Une autre option peut être quelque chose comme ceci:
void noop(...) {}
#define MYDEFINE() noop()
Cependant, je m'en tiendrai à (void)0
ou utilisez des éléments intrinsèques comme __ noop
inline void noop( ) {}
Auto-documentation
Je recommande d'utiliser:
static_cast<void> (0)
ce code ne sera pas omis par optimisation
static void nop_func() { }
typedef void (*nop_func_t)();
static nop_func_t nop = &nop_func;
for (...)
{
nop();
}