Supprimer la fonte de la constante dans le préprocesseur

Contexte

Dans un code de microcontrôleur, j’utilise une bibliothèque fournie par le producteur où plusieurs constantes sont définies. J’essaie de donner une erreur s’il y a une incompatibilité entre certaines de mes constantes (partagées avec des composants extérieurs au microcontrôleur, avec git-subtree ) et les constantes du microcontrôleur.

Par exemple, la bibliothèque définit:

 #ifdef SOME_PARTICULAR_MODEL #define FLASH_BLOCK_SIZE ((uint8_t)64) /* else, other models */ #endif 

Et quelque part, dans un en-tête partagé entre le code du microcontrôleur et du code compilé sur le PC, j’ai par exemple:

 #define MYPROG_BLOCK_SIZE 64 

Et pour être sûr que ces constantes correspondent, dans le code du microcontrôleur, là où les deux constantes existent, j’ai:

 #if MYPROG_BLOCK_SIZE != FLASH_BLOCK_SIZE #error "mismatch between actual block size and defined block size" #endif 

Cela permet de s’assurer que si le code est jamais porté sur un microcontrôleur plus grand, l’en-tête partagé sera également mis à jour.

Le problème

Le problème est que cela se réduit à:

 #if 64 != ((uint8_t)64) 

ce que je ne suis pas sûr si est valide C , mais néanmoins fait le starter du compilateur. En testant, j’ai découvert que le problème n’est pas que uint8_t est un typedef et qu’il est toujours étouffé par un cast vers int par exemple.

La question

Existe-t-il un moyen de supprimer la partie (uint8_t) d’une valeur définie comme ((uint8_t)64) ? Sinon, y a-t-il un moyen de le changer pour que l’expression devienne une sans casting?

J’ai pensé à définir uint8_t comme quelque chose et à le définir après le #if , mais je ne vois pas comment je peux éviter la nature du casting de (Y)X et le transformer en une expression arithmétique.

Voici une version améliorée (la première version est ci-dessous). Celui-ci ne dépend pas de la dissortingbution étant uint8_t ; cela fonctionnera avec n’importe FLASH_BLOCK_SIZE liste de remplacement FLASH_BLOCK_SIZE de la forme ((some type) number) .

 #define MYPROG_BLOCK_SIZE 64 #define FLASH_BLOCK_SIZE ((uint8_t)64) #define B(x) #define C(x) B x #define D(x) C x #if MYPROG_BLOCK_SIZE != D(FLASH_BLOCK_SIZE) #error "mismatch between actual block size and defined block size" #endif 

Voici la version originale:

 #define MYPROG_BLOCK_SIZE 64 #define FLASH_BLOCK_SIZE ((uint8_t)64) #define uint8_t #define Helper(x) x #define Deparenthesize(x) Helper x #if MYPROG_BLOCK_SIZE != Deparenthesize(Deparenthesize(FLASH_BLOCK_SIZE)) #error "mismatch between actual block size and defined block size" #endif #undef uint8_t 

Lors de l’écriture de code, je préférerais une assertion statique, mais ce qui précède correspond à ce que vous avez demandé dans le préprocesseur.

La solution consiste à utiliser l’assert statique. Avec une bonne macro STATIC_ASSERT , vous pouvez placer une STATIC_ASSERT statique dans file-scope dans votre fichier d’entête:

 STATIC_ASSERT(FLASH_BLOCK_SIZE == MYPROG_BLOCK_SIZE); 

Voici un exemple de définition de la macro STATIC_ASSERT :

 #define CAT(x, y) CAT_(x, y) #define CAT_(x, y) x ## y #define STATIC_ASSERT(expr) \ extern int CAT(static_assert_failed_, __LINE__)[(expr) ? 1 : -1] 

Avec certains compilateurs (par exemple, IAR), vous avez static_assert comme compilateur intégré. static_assert est également présent dans C11 mais malheureusement, le compilateur C intégré ne supporte pas beaucoup C11.

Pour tout type de texte, vous pouvez obtenir ce montant en passant à

 #define FLASH_BLOCK_SIZE ((uint8_t)+64) 

remarquez le petit plus là-dedans?

Le préprocesseur remplace le nom de type sur lequel il ne connaît rien par 0. Cela fonctionne donc dans les deux contextes. (Le pré-processeur est supposé faire toute l’arithmétique dans [u]intmax_t , de toute façon)

Pour le type réel que vous utilisez, tout cela n’a pas beaucoup de sens. Il n’existe pas de constante uint8_t . Dès qu’ils sont évalués, toutes les expressions d’un type plus étroit que int sont converties en int . Donc, cela ne fait probablement aucune différence.

Après le remplacement de la macro, une expression de préprocesseur peut avoir des sous-expressions du type defined identifier defined ( identifier ) ou defined ( identifier ) . En dehors de cela, les identificateurs et les mots-clés n’ont aucune signification. chaque identifiant ou mot-clé est remplacé par 0 . Donc ça:

 #if 64 != ((uint8_t)64) 

est équivalent à ceci:

 #if 64 != ((0)64) 

qui est une erreur de syntaxe. Le fait que ce soit un typedef n’est pas le problème; un mot clé comme int reçoit le même traitement.

Donc, étant donné:

 #define FLASH_BLOCK_SIZE ((uint8_t)64) 

vous ne pouvez pas utiliser FLASH_BLOCK_SIZE dans une expression de préprocesseur.

La meilleure solution à laquelle je puisse penser est de changer votre définition:

 #define FLASH_BLOCK_SIZE_NUM 64 #define FLASH_BLOCK_SIZE ((uint8_t)FLASH_BLOCK_SIZE_NUM) 

Vous pouvez ensuite utiliser FLASH_BLOCK_SIZE_NUM dans les expressions de préprocesseur et FLASH_BLOCK_SIZE ailleurs.

D’autre part, avez-vous vraiment besoin de la (int8_t) en premier lieu? Les expressions arithmétiques sont implicitement converties dans de nombreux contextes, généralement en un type approprié. Dans de nombreux contextes, (uint8_t) sera quand même promu vers int . Il est très probable que vous puissiez simplement laisser tomber le casting et utiliser:

 #define FLASH_BLOCK_SIZE 64 

Pour être sûr, vous devrez examiner tout le code faisant référence à FLASH_BLOCK_SIZE . (Évaluer la sizeof FLASH_BLOCK_SIZE poserait un problème, mais je parie que vous ne le ferez jamais.)

Je sais que le fil de discussion est ancien, mais je l’ai rencontré aujourd’hui … Si vous ne voulez pas ou ne pouvez pas changer le #define, les autres réponses sont géniales, mais si vous avez access au #define, je vous recommande d’utiliser for des opérations de traitement (#if – #else – #endif) au lieu de:

 #define FLASH_BLOCK_SIZE ((uint8_t)64) 

utiliser ce définir:

 #define FLASH_BLOCK_SIZE (64U) 

Ainsi, le casting est toujours là et le compilateur ne sera pas “confondu”.