Comment vérifier si un paramètre est une expression constante intégrale dans une macro de préprocesseur C?

Je nettoie actuellement une bibliothèque C existante pour la publier sans vergogne.

Une macro de préprocesseur, NPOT est utilisée pour calculer la puissance NPOT supérieure de deux pour une expression constante intégrale donnée au moment de la compilation . La macro est normalement utilisée dans les initialisations directes. Pour tous les autres cas (par exemple, en utilisant des parameters variables), il existe une fonction en ligne avec la même fonction.

Mais si l’utilisateur passe une variable, l’algorithme se développe en un énorme morceau de code machine. Ma question est la suivante: que puis-je faire pour empêcher un utilisateur de transmettre autre chose qu’une expression constante intégrale à ma macro?

 #define NPOT(x) complex_algorithm(x) const int c=10; int main(void) { int i=5; foo = NPOT(5); // works, and does everything it should foo = NPOT(c); // works also, but blows up the code extremely foo = NPOT(i); // blows up the code also } 

Ce que j’ai déjà essayé:

  1. Définissez la macro sur #define NPOT(x) complex_algorithm(x ## u) . Il fonctionne toujours et génère une erreur – même si cela n’est guère utile – du compilateur pour les parameters variables. À moins qu’il n’y ait pas de variable comme iu … Sale, dangereux, ne le veut pas.
  2. La documentation ne fonctionnait pas pour la plupart des utilisateurs.

Vous pouvez utiliser toute expression nécessitant une expression intégrale constante et optimisée.

 #define NPOT(X) \ (1 \ ? complex_algorithm(X) \ : sizeof(struct { int needs_constant[1 ? 1 : (X)]; }) \ ) 

éventuellement, vous devez convertir le résultat de sizeof en un type entier approprié, de sorte que l’expression de retour correspond à un type que vous attendez.

J’utilise une struct non struct ici pour

  • avoir un type si vraiment pas temporaire est produit
  • avoir un type unique tel que l’expression puisse être répétée n’importe où dans le code sans provoquer de conflits
  • déclencher l’utilisation d’un VLA, ce qui n’est pas autorisé dans une struct partir de C99:

Un membre d’une structure ou d’une union peut avoir n’importe quel type d’object autre qu’un type modifié de manière variable.

J’utilise le ternaire ?: Avec 1 comme expression de sélection pour s’assurer que le : est toujours évalué pour son type, mais jamais évalué en tant qu’expression.

Edit: Il semble que gcc accepte VLA dans struct comme une extension et ne le prévient même pas, même si je dis explicitement -std=c99 . C’est vraiment une mauvaise idée d’eux.

Pour un compilateur aussi étrange :), vous pouvez utiliser sizeof((int[X]){ 0 }) . Ceci est “aussi interdit” que la version ci-dessus, mais même gcc s’en plaint.

 #define INTEGRAL_CONST_EXPR(x) ((void) sizeof (struct {int a:(x);}), (x)) 

Cela donnera une erreur de compilation si x n’est pas une expression constante intégrale.

 my_function(INTEGRAL_CONST_EXPR(1 + 2 + 3)); // OK my_function(INTEGRAL_CONST_EXPR(1.0 + 2 + 3)); // comstack error 

Notez que cette solution ne fonctionne pas pour l’initialisation d’une variable statique:

 static int a = INTEGRAL_CONST_EXPR(2 + 3); 

déclenchera une erreur de compilation à cause d’une expression avec , n’est pas une expression constante.

Comme @JensGustedt l’a mis dans le commentaire, une expression constante intégrale résolue en un nombre entier négatif ne peut pas être utilisée dans cette solution, car la largeur du champ de bits ne peut pas être négative.