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

StackOverflow https://stackoverflow.com/questions/7084

  •  08-06-2019
  •  | 
  •  

Question

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

C'est une bonne technique car elle indique 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 ?

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

Avec la programmation intégrée, vous aurez peut-être besoin de types de taille spécifique pour les interfaces et également pour faire face à des ressources restreintes telles que la RAM.Cela ne peut ê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 à ne pas provoquer de débordement et devrez peut-être faire attention à l'utilisation des registres et de la pile.Ce qui peut vous amener à UINT16, UCHAR.Utiliser des types tels que UCHAR peut cependant ajouter du « fluff » au compilateur.Étant donné 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.

Je pense donc que ma question aurait dû être :-

étant donné les contraintes des logiciels embarqués, quelle est la meilleure politique à définir pour un projet sur lequel de nombreuses personnes travailleront - qui n'auront pas toutes le même niveau d'expérience.

Était-ce utile?

La solution

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

  1. Les variables locales sont différentes des membres de structure et des tableaux dans le sens où vous souhaitez qu'elles tiennent dans un registre.Sur une cible 32b/64b, un local int16_t 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 intfast_t typedef, AFAIK, un simple int rentrera tout aussi bien dans un registre, et c'est certainement un nom plus court.

  2. Les organisations qui aiment ces typedefs se retrouvent presque invariablement avec plusieurs d'entre elles (INT32, int32_t, INT32_T, À l'infini).Les organisations utilisant des types intégrés ont donc intérêt, d'une certaine manière, à n'avoir qu'un seul ensemble de noms.J'aimerais que les gens utilisent les typedefs de stdint.h ou windows.h ou tout ce qui existe ;et lorsqu'une cible n'a pas ce fichier .h, est-il difficile d'en ajouter un ?

  3. Les typedefs peuvent théoriquement faciliter la portabilité, mais pour ma part, je n’en ai jamais rien gagné.Existe-t-il un système utile que vous pouvez porter d'une cible 32b à une cible 16b ?Existe-t-il un système 16b qui n'est pas trivial à porter sur une cible 32b ?De plus, si la plupart des variables sont des entiers, vous gagnerez réellement quelque chose grâce aux 32 bits de la nouvelle cible, mais si elles le sont int16_t, vous ne le ferez pas.Et les endroits difficiles à mettre en communication ont de toute façon tendance à nécessiter une inspection manuelle ;avant d'essayer un port, vous ne savez pas où ils se trouvent.Maintenant, si quelqu'un pense qu'il est si facile de porter des choses si vous avez des définitions de type partout - quand vient le temps de porter, ce qui arrive à peu de systèmes, écrivez un script convertissant tous les noms dans la base de code.Cela devrait fonctionner selon la logique « aucune inspection manuelle requise », et cela retarde l'effort jusqu'au moment où il apporte réellement un bénéfice.

  4. Maintenant, si la portabilité peut être un avantage théorique des typedefs, lisibilité ça va certainement dans les égouts.Regardez simplement stdint.h : {int,uint}{max,fast,least}{8,16,32,64}_t.Beaucoup de types.Un programme comporte de nombreuses variables ;est-ce vraiment si simple de comprendre ce qui doit être int_fast16_t et qui doivent être uint_least32_t?Combien de fois nous convertissons-nous silencieusement entre eux, les rendant totalement inutiles ?(J'aime particulièrement les conversions BOOL/Bool/eBool/boolean/bool/int.Chaque programme écrit par une organisation ordonnée exigeant des typedefs en est jonché).

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

Autres conseils

La norme C99 comporte un certain nombre de types entiers de taille standard.Si vous pouvez utiliser un compilateur prenant en charge C99 (ce que fait gcc), 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 "d'unités" qui vous permettent de travailler dans des unités physiques définies par le système de types C++ (via des modèles) qui sont compilées en tant qu'opérations sur les types scalaires sous-jacents.Par exemple, ces bibliothèques ne vous permettront pas d'ajouter un distance_t à un mass_t parce que les unités ne s'alignent pas ;vous obtiendrez en fait une erreur du compilateur.

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 à détecter visuellement de telles erreurs.(C'était en fait l'intention originale de la notation hongroise de Simonyi.) Tout simplement parce que le compilateur ne vous criera pas dessus pour avoir ajouté un meter_t à un gram_t ne veut pas dire que vous ne devriez pas utiliser des types comme celui-là.Les revues de code seront alors beaucoup plus productives pour découvrir les erreurs d’unité.

Mon avis est que si vous comptez sur une taille minimum/maximum/spécifique ne le faites pas supposez simplement que (disons) un unsigned int fait 32 octets - utilisez uint32_t à la place (en supposant que votre compilateur prend en charge C99).

J'aime utiliser les types stdint.h pour définir les API système spécifiquement parce qu'ils indiquent explicitement la taille des éléments.À l'époque de Palm OS, les API système étaient définies à l'aide d'un tas de types insensés comme "Word" et "SWord" hérités de Mac OS très classique.Ils ont fait un nettoyage pour dire à la place Int16, ce qui a rendu l'API plus facile à comprendre pour les nouveaux arrivants, en particulier avec les étranges problèmes de pointeur 16 bits sur ce système.Lors de la conception de Palm OS Cobalt, ils ont à nouveau modifié ces noms pour qu'ils correspondent à ceux de stdint.h, le rendant encore plus clair et réduisant le nombre de typesdefs à gérer.

Je crois que les normes MISRA suggèrent (exigent ?) l'utilisation de typedefs.

D'un point de vue personnel, l'utilisation des typedefs ne laisse aucune confusion quant à la taille (en bits/octets) de certains types.J'ai vu des développeurs principaux tenter les deux manières de développer en utilisant des types standard, par ex.int et en utilisant des types personnalisés, par exemple.UINT32.

Si le code n'est pas portable, il y a peu réel avantage à utiliser les typedefs, cependant , si comme moi vous travaillez sur les deux types de logiciels (environnement portable et fixe) alors il peut être utile de garder un standard et d'utiliser les types personnalisés.À tout le moins, comme vous le dites, le programmeur est alors très conscient de la quantité de mémoire qu'il utilise.Un autre facteur à considérer est dans quelle mesure êtes-vous « sûr » que le code ne sera pas porté vers un autre environnement ?J'ai vu du code spécifique au processeur devoir être traduit car un ingénieur matériel a soudainement dû changer de carte, ce n'est pas une situation agréable, mais à cause des typedefs personnalisés, cela aurait pu être bien pire !

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

De plus, le compilateur et le processeur peuvent être réparé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.

Si vos systèmes embarqués sont en quelque sorte un système critique pour la sécurité (ou similaire), c'est fortement informé (si cela n'est pas nécessaire) pour utiliser les typedefs sur les types simples.

Comme savoirs traditionnels. l'a déjà dit, MISRA-C a une règle (consultative) pour le faire :

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

(extrait de MISRA-C 2004 ;c'est la règle n°13 (adv) de MISRA-C 1998)


Il en va de même pour le C++ dans ce domaine ;par exemple. Normes de codage JSF C++:

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

En utilisant <stdint.h> rend votre code plus portable pour les tests unitaires sur un PC.

Cela peut vous mordre assez fort lorsque vous avez des tests pour tout, mais cela tombe quand même en panne sur votre système cible car un int n'est soudainement que de 16 bits de long.

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

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top