Comment mon code pourrait-il dire une constante de compilation par rapport à une variable?

Voici mon problème. J’ai une macro BINARY_FLAG :

 #define BINARY_FLAG( n ) ( static_cast( 1 << ( n ) ) ) 

Ce qui peut être utilisé soit comme ça (scénario “constant”):

 static const SomeConstant = BINARY_FLAG( 5 ); 

ou comme ceci (scénario “variable”):

 for( int i = 0; i < 10; i++ ) { DWORD flag = BINARY_FLAG( i ); // do something with the value } 

Cette macro n’est pas infaillible du tout – on peut passer -1 ou 34 là-bas et il y aura tout au plus un avertissement, mais le comportement ne sera pas défini. Je voudrais le rendre plus infaillible.

Pour le scénario constant, je pourrais utiliser un modèle:

 template class BinaryFlag { staticAssert( 0 <= Shift && Shift < sizeof( DWORD) * CHAR_BIT ); public: static const DWORD FlagValue = static_cast( 1 << Shift ); }; #define BINARY_FLAG( n ) CBinaryFlag::FlagValue 

mais cela ne va pas pour le scénario “variable” – J’aurais besoin d’une assertion d’exécution ici:

 inline DWORD ProduceBinaryFlag( int shift ) { assert( 0 <= shift && shift < sizeof( DWORD) * CHAR_BIT ); return static_cast( 1 << shift ); } #define BINARY_FLAG( n ) ProduceBinaryFlag(n) 

Ce dernier est bon, mais n’a pas de vérification au moment de la compilation. Bien sûr, j’aimerais une vérification à la compilation si possible et une vérification à l’exécution sinon. À tout moment, je veux le moins de temps système possible afin d’éviter un appel de fonction (qui ne sera peut-être pas en ligne) lorsqu’une vérification à la compilation est possible.

J’ai vu cette question , mais il ne semble pas qu’il s’agisse du même problème.

Existe-t-il une structure permettant d’alterner entre les deux selon que l’expression passée en tant que numéro d’indicateur est une constante de compilation ou une variable?

Il n’est pas possible de passer un argument à une macro ou une fonction et de déterminer s’il s’agit d’une constante de temps de compilation ou d’une variable.

Le meilleur moyen consiste à #define BINARY_FLAG(n) avec comstackr le code temporel et à placer cette macro partout, puis à la comstackr. Vous recevrez des erreurs de compilation aux endroits où n sera l’exécution. Vous pouvez désormais remplacer ces macros par votre macro d’exécution BINARY_FLAG_RUNTIME(n) . C’est le seul moyen possible.

C’est plus simple que vous ne le pensez 🙂

Regardons:

 #include  static inline int FLAG(int n) { assert(n>=0 && n<32); return 1< 

Je n'utilise pas MSVC, mais j'ai compilé avec Mingw GCC 4.5:

g ++ -c -S -O3 08042.cpp

Le code résultant de la première méthode ressemble à ceci:

 __Z5test1i: pushl %ebp movl %esp, %ebp subl $24, %esp movl 8(%ebp), %ecx cmpl $31, %ecx ja L4 movl $1, %eax sall %cl, %eax leave ret L4: movl $4, 8(%esp) movl $LC0, 4(%esp) movl $LC1, (%esp) call __assert .p2align 2,,3 

Et le deuxième:

 __Z5test2v: pushl %ebp movl %esp, %ebp movl $32, %eax leave ret 

Voir? Le compilateur est assez intelligent pour le faire pour vous. Pas besoin de macros, pas besoin de métaprogrammation, pas besoin de C ++ 0x. Aussi simple que cela.

Vérifiez si MSVC fait la même chose ... Mais regardez - il est très facile pour le compilateur d'évaluer une expression constante et de supprimer la twig conditionnelle inutilisée. Cochez-la si vous voulez être sûr ... Mais en général, faites confiance à vos outils.

Je vous suggère d’utiliser deux macros. BINARY_FLAG CONST_BINARY_FLAG Cela facilitera la compréhension de votre code pour les autres. Vous savez, au moment de la rédaction, s’il s’agit d’un const ou non. Et je ne voudrais en aucun cas m’inquiéter des frais généraux d’exécution. Votre optimiseur, du moins dans VS, vous aidera à résoudre ce problème.