c - systèmes - système embarqué transformationnel




Quand dois-je utiliser l'abstraction de type dans les systèmes embarqués (6)

J'ai travaillé sur un certain nombre de systèmes embarqués différents. Ils ont tous utilisé typedef s (ou #defines ) pour des types tels que UINT32 .

C'est une bonne technique car elle ramène la taille du type au programmeur et vous rend plus conscient des risques de débordement, etc.

Mais sur certains systèmes, vous savez que le compilateur et le processeur ne changeront pas pendant toute la durée du projet.

Alors qu'est-ce qui devrait influencer votre décision de créer et d'appliquer des types spécifiques à un projet?

EDIT Je pense que j'ai réussi à perdre l'essentiel de ma question, et peut-être que c'est vraiment deux.

Avec la programmation embarquée, vous pouvez avoir besoin de types de taille spécifiques pour les interfaces et aussi pour faire face à des ressources restreintes telles que la RAM. Cela ne peut pas être évité, mais vous pouvez choisir d'utiliser les types de base du compilateur.

Pour tout le reste, les types ont moins d'importance.
Vous devez faire attention de ne pas causer de débordement et devrez peut-être faire attention à l'utilisation des registres et des piles. Ce qui peut vous mener à UINT16 , UCHAR . L'utilisation de types tels que UCHAR peut cependant ajouter un 'fluff' au compilateur. Parce que les registres sont généralement plus grands, certains compilateurs peuvent ajouter du code pour forcer le résultat dans le type.

i++;
peut devenir
ADD REG,1
AND REG, 0xFF
ce qui est inutile.

Donc je pense que ma question aurait dû être:

Compte tenu des contraintes inhérentes aux logiciels embarqués, quelle est la meilleure politique à mettre en place pour un projet auquel beaucoup de personnes travailleront, et qui ne seront pas tous du même niveau d'expérience.


Cohérence, commodité et lisibilité. "UINT32" est beaucoup plus lisible et inscriptible que "unsigned long long", ce qui est l'équivalent pour certains systèmes.

En outre, le compilateur et le processeur peuvent être corrigés pour la durée de vie d'un projet, mais le code de ce projet peut trouver une nouvelle vie dans un autre projet. Dans ce cas, avoir des types de données cohérents est très pratique.


La norme C99 comporte un certain nombre de types d'entiers de taille standard. Si vous pouvez utiliser un compilateur qui supporte C99 (gcc fait), vous les trouverez dans <stdint.h> et vous pouvez simplement les utiliser dans vos projets.

En outre, il peut être particulièrement important dans les projets intégrés d'utiliser les types comme une sorte de «filet de sécurité» pour des choses comme les conversions d'unités. Si vous pouvez utiliser C ++, je comprends qu'il existe des bibliothèques «unitaires» qui vous permettent de travailler dans des unités physiques définies par le système de type C ++ (via des modèles) compilées en tant qu'opérations sur les types scalaires sous-jacents. Par exemple, ces bibliothèques ne vous permettent pas d'ajouter une distance_t à un mass_t car les unités ne s'alignent pas; vous obtiendrez une erreur de compilation.

Même si vous ne pouvez pas travailler en C ++ ou dans un autre langage qui vous permet d'écrire du code de cette façon, vous pouvez au moins utiliser le système de type C pour vous aider à repérer les erreurs comme celles-là. (C'était en fait l'intention originale de la notation hongroise de Simonyi.) Juste parce que le compilateur ne vous crierait pas d'ajouter un meter_t à un gram_t ne veut pas dire que vous ne devriez pas utiliser des types comme ça. Les revues de code seront beaucoup plus productives à la découverte d'erreurs unitaires alors.


J'utilise très rarement l'abstraction de type. Voici mes arguments, classés par ordre croissant de subjectivité:

  1. Les variables locales sont différentes des membres de la structure et des tableaux dans le sens où vous souhaitez les insérer dans un registre. Sur une cible 32b / 64b, un int16_t local peut rendre le code plus lent par rapport à un int local puisque le compilateur devra ajouter des opérations à / force / overflow selon la sémantique de int16_t . Alors que C99 définit un typedef intfast_t , AFAIK un plain plain s'intégrera aussi bien dans un registre, et c'est sûrement un nom plus court.

  2. Les organisations qui aiment ces typedefs se retrouvent presque invariablement avec plusieurs d'entre eux ( INT32, int32_t, INT32_T , ad infinitum). Les organisations utilisant des types intégrés sont donc mieux loties, d'une certaine manière, n'ayant qu'un seul ensemble de noms. Je souhaite que les gens utilisent le typedefs de stdint.h ou windows.h ou n'importe quoi existant; et quand une cible n'a pas ce fichier .h, à quel point est-il difficile d'en ajouter un?

  3. Les typedefs peuvent théoriquement faciliter la portabilité, mais pour ma part, je n'ai jamais rien obtenu d'eux. Existe-t-il un système utile que vous pouvez porter d'une cible 32b vers une cible 16b? Y a-t-il un système 16b qui n'est pas trivial à mettre en communication avec une cible 32b? De plus, si la plupart des vars sont des ints, vous gagnerez quelque chose sur les 32 bits de la nouvelle cible, mais s'ils sont int16_t , vous ne le ferez pas. Et les endroits qui sont difficiles à porter ont tendance à nécessiter une inspection manuelle de toute façon; Avant d'essayer un port, vous ne savez pas où ils sont. Maintenant, si quelqu'un pense qu'il est si facile de porter les choses si vous avez des typedefs partout - quand le temps passe au port, ce qui arrive à quelques systèmes, écrivez un script convertissant tous les noms dans la base de code. Cela devrait fonctionner selon la logique «pas d'inspection manuelle requise», et cela reporte l'effort à un moment où cela donne un avantage.

  4. Maintenant, si la portabilité peut être un avantage théorique des typedefs, la lisibilité va sûrement dans le drain. Regardez simplement stdint.h: {int,uint}{max,fast,least}{8,16,32,64}_t . Beaucoup de types. Un programme a beaucoup de variables; est-ce vraiment facile de comprendre ce qui doit être int_fast16_t et qui doit être uint_least32_t ? Combien de fois convertissons-nous silencieusement entre eux, les rendant complètement inutiles? (J'aime particulièrement les conversions BOOL / Bool / eBool / boolean / bool / int.Tous les programmes écrits par une organisation ordonnée imposant typedefs est jonché de cela).

  5. Bien sûr, en C ++, nous pourrions rendre le système de type plus strict, en enveloppant des nombres dans des instanciations de classes de modèles avec des opérateurs surchargés et d'autres choses. Cela signifie que vous allez maintenant recevoir des messages d'erreur de la forme "class Number <int, Least, 32> n'a aucun opérateur + surcharge pour l'argument de type de classe Number <unsigned long, Fast, 64>, les candidats sont ..." I ne l'appelle pas "lisibilité" non plus. Vos chances d'implémenter correctement ces classes wrapper sont microscopiques, et la plupart du temps, vous attendez que les innombrables instanciations de modèles soient compilées.


J'aime utiliser les types stdint.h pour définir les API système spécifiquement car ils indiquent explicitement la taille des éléments. De retour dans l'ancien temps de Palm OS, les API du système étaient définies en utilisant un tas de types "wishy-washy" comme "Word" et "SWord" hérités de Mac OS très classiques. Ils ont fait un nettoyage pour dire Int16 à la place et cela a rendu l'API plus facile à comprendre pour les nouveaux arrivants, en particulier avec les problèmes bizarres de pointeur 16 bits sur ce système. Lors de la conception de Palm OS Cobalt, ils ont de nouveau changé ces noms pour correspondre aux noms de stdint.h, le rendant encore plus clair et réduisant la quantité de typedefs qu'ils devaient gérer.


Peut-être que je suis bizarre, mais j'utilise ub, ui, ul, sb, si, et sl pour mes types entiers. Peut-être le "i" pour 16 bits semble un peu daté, mais j'aime le look de ui / si mieux que uw / sw.


Si vos systèmes embarqués sont en quelque sorte un système critique pour la sécurité (ou similaire), il est fortement conseillé (si nécessaire) d'utiliser typedefs plutôt que des types simples.

Comme TK. a déjà dit, MISRA-C a une règle (consultative) de le faire:

Règle 6.3 (avis): les typedefs qui indiquent la taille et la signature doivent être utilisés à la place des types numériques de base.

(tirée de MISRA-C 2004, Règle # 13 (adv) de MISRA-C 1998)

La même chose s'applique à C ++ dans cette zone; par exemple. Normes de codage JSF C ++ :

Règle AV 209 Un fichier UniversalTypes sera créé pour définir tous les types de standards à utiliser par les développeurs. Les types comprennent: [uint16, int16, uint32_t etc.]





embedded