Când trebuie să utilizez abstractizarea de tip în sistemele încorporate

Am lucrat la mai multe sisteme încorporate diferite. Ei au folosit typedef s (sau #defines ) pentru tipuri precum UINT32 .

Aceasta este o tehnică bună pe măsură ce acționează acasă dimensiunea tipului la programator și vă face mai conștienți de șansele de supraîncălzire etc.

Dar pe unele sisteme știi că compilatorul și procesorul nu se vor schimba pe durata proiectului.

Deci, ce ar trebui să influențeze decizia dvs. de a crea și de a impune anumite tipuri de proiecte?

EDITAȚI | × Cred că am reușit să pierd ideea întrebării mele și poate că sunt cu adevărat două.

Cu programare încorporată, este posibil să aveți nevoie de tipuri de dimensiuni specifice pentru interfețe și de a face față și resurselor restricționate, cum ar fi RAM. Acest lucru nu poate fi evitat, dar puteți alege să utilizați tipurile de bază din compilator.

Pentru orice altceva, tipurile au o importanță mai mică Trebuie să aveți grijă să nu provoacă prea mult și este posibil să aveți grijă să folosiți înregistrarea și utilizarea stivei. Care vă poate conduce la UINT16 , UCHAR . Folosind tipuri precum UCHAR se poate adăuga, totuși, "fluff" de compilator. Deoarece registrele sunt de obicei mai mari, unii compilatori pot adăuga un cod pentru a forța rezultatul în tip

i++;
can become
ADD REG,1
AND REG, 0xFF
which is unecessary.

Deci, cred că întrebarea mea ar fi trebuit să fie:

având în vedere constrângerile software-ului încorporat, care este cea mai bună politică de setare pentru un proiect care va avea mulți oameni care lucrează la acesta - nu toți vor avea același nivel de experiență.

0
fr hi bn

9 răspunsuri

Opinia mea este dacă depindeți de o dimensiune minimă / maximă / specifică nu presupuneți doar că un unsigned int are 32 de octeți - utilizați uint32_t (presupunând că compilatorul dvs. suportă C99).

0
adăugat

Poate că sunt ciudat, dar folosesc ub, ui, ul, sb, si, și sl pentru tipurile mele întregi. Poate că "i" pentru 16 biți pare un pic datat, dar îmi place aspectul lui ui / si mai bine decât uw / sw.

0
adăugat
Cred că acest lucru se limitează la context. Pentru programarea încorporată, mărimea este foarte importantă, astfel că i & w sunt efectiv "nu-mi pasă" valori.
adăugat autor itj, sursa
@itj: Nu sunt sigur ce vrei să spui cu asta. Eu folosesc identificatorii de tip de două caractere pentru că sunt scurți și vizuale curat și distinct. Nu mă pot gândi la niciun alt identificator cu două caractere pe care îl folosesc pentru orice scop care începe cu 's' sau 'u', așa că pare destul de evident ce semnifică tipurile (cu excepția posibil pentru "ui" sau "si" În izolare).
adăugat autor supercat, sursa

Cred că standardele MISRA sugerează (necesită?) Utilizarea tipelor.

Din perspectivă personală, folosirea tippedefelor nu lasă nici o confuzie cu privire la mărimea (în biți / octeți) de anumite tipuri. Am văzut dezvoltatorii de plumb încercând ambele moduri de dezvoltare folosind tipuri standard, de ex. int și utilizând tipuri personalizate, de ex. UINT32.

În cazul în care codul nu este portabil, există beneficii reale mici în utilizarea typedefs, cu toate acestea , dacă, ca mine, atunci lucrați la ambele tipuri de software (mediu portabil și fix) poate fi utilă păstrarea unui standard și utilizarea tipurilor cutomizate. Cel puțin așa cum spui tu, programatorul este foarte conștient de cât de multă memorie folosesc. Un alt factor care trebuie luat în considerare este cât de sigur sunteți că codul nu va fi portat în alt mediu? Am văzut codul specific procesorului trebuie să fie tradus ca un angajat hardware a brusc a trebuit să schimbe o bord, aceasta nu este o situație frumos să fie în, dar datorită typedefs personalizat ar fi putut fi mult mai rău!

0
adăugat
Da, este o regulă de consiliere (# 6.3 din MISRA-C 2004, respectiv # 13 din MISRA-C '98) .
adăugat autor ollo, sursa

Îmi place să folosesc tipurile stdint.h pentru definirea API-urilor de sistem, în special pentru că ele explică în mod explicit cât de mari sunt elementele. Înapoi în cele mai vechi timpuri ale sistemului de operare Palm OS, API-urile de sistem au fost definite folosind o grămadă de tipuri de tip "Word" și "SWord" care au fost moștenite de la Mac OS foarte clasic. Ei au făcut o curățare pentru a spune în schimb Int16 și a făcut API mai ușor pentru noii veniți să înțeleagă, mai ales cu ciudat probleme de 16 biți pointer pe acest sistem. Când proiectau Palm OS Cobalt, le-au schimbat din nou numele pentru a se potrivi cu numele lui stdint.h, făcând-o mai clară și reducând cantitatea de typedef-uri pe care trebuiau să le gestioneze.

0
adăugat
+1 pentru utilizarea tipurilor în stdint.h . Cel mai bun mod de a merge pentru portabilitate. Dacă o platformă nu o are, este banal să o creați.
adăugat autor tomlogic, sursa

Dacă sistemele dvs. încorporate sunt într-un fel sistem critic de siguranță (sau similare), este foarte recomandat (dacă nu este necesar) .

După cum spune TK. , MISRA-C are o regulă (de consiliere) pentru a face acest lucru:

Regula 6.3 (consultativ): Tippedefele care indică dimensiunea și semnătura trebuie folosite în locul tipurilor numerice de bază.

(din MISRA-C 2004, este Regula # 13 (adv) din MISRA-C 1998)


Același lucru se aplică și în cazul C ++ în această zonă; de exemplu. Standarde de codare JSF C ++ :

Regulă AV 209 Se va crea un fișier UniversalTypes pentru a defini toate setările   ndard tipuri pentru dezvoltatori de a utiliza. Tipurile includ: [uint16, int16, uint32_t etc]

0
adăugat

Coerență, confort și lizibilitate. "UINT32" este mult mai ușor de citit și mai ușor de scris decât "unsigned long long", echivalent pentru unele sisteme.

De asemenea, compilatorul și procesorul pot fi fixate pe durata unui proiect, dar codul din acel proiect poate găsi o nouă viață într-un alt proiect. În acest caz, având tipuri de date consecvente este foarte convenabil.

0
adăugat

The C99 standard has a number of standard sized-integer types. If you can use a compiler that supports C99 (gcc does), you'll find these in and you can just use them in your projects.

De asemenea, poate fi deosebit de important ca proiectele încorporate să folosească tipuri ca un fel de "plasă de siguranță" pentru lucruri precum conversii de unități. Dacă puteți folosi C ++, înțeleg că există câteva biblioteci "unitate" care vă permit să lucrați în unități fizice definite de sistemul tip C ++ (prin șabloane) care sunt compilate ca operații pe tipurile scalare de bază. De exemplu, aceste biblioteci nu vă vor lăsa să adăugați un distance_t la un mass_t deoarece unitățile nu se aliniază; veți avea de fapt o eroare de compilator.

Chiar dacă nu puteți lucra în C sau într-o altă limbă care vă permite să scrieți codul în acest fel, puteți folosi cel puțin sistemul de tip C pentru a vă ajuta să prindeți erori de genul asta cu ochii. (Aceasta a fost de fapt intenția inițială a notației ungare a lui Simonyi.) Doar pentru că compilatorul nu va striga la tine pentru adăugarea unui meter_t la un gram_t nu înseamnă că nu trebuie Nu folosiți astfel de tipuri. Revizuirile de cod vor fi mult mai productive la descoperirea erorilor de unitate atunci.

0
adăugat

Using makes your code more portable for unit testing on a pc.

Se poate să te muște destul de tare atunci când ai teste pentru tot, dar tot rupe sistemul tau, deoarece un int este brusc de numai 16 biți lungi.

0
adăugat
Da, aceasta este una dintre cele mai pragmatice demonstrații ale utilizării tipurilor de dimensiuni explicite, care are mult sens. Desigur, dacă nu faci vreodată testul găzduit de PC, atunci de ce ți-ar păsa?
adăugat autor barny, sursa

Folosesc abstractizarea de tip foarte rar. Iată argumentele mele, sortate în ordinea crescătoare a subiectivității:

  1. Local variables are different from struct members and arrays in the sense that you want them to fit in a register. On a 32b/64b target, a local int16_t can make code slower compared to a local int since the compiler will have to add operations to /force/ overflow according to the semantics of int16_t. While C99 defines an intfast_t typedef, AFAIK a plain int will fit in a register just as well, and it sure is a shorter name.

  2. Organizations which like these typedefs almost invariably end up with several of them (INT32, int32_t, INT32_T, ad infinitum). Organizations using built-in types are thus better off, in a way, having just one set of names. I wish people used the typedefs from stdint.h or windows.h or anything existing; and when a target doesn't have that .h file, how hard is it to add one?

  3. The typedefs can theoretically aid portability, but I, for one, never gained a thing from them. Is there a useful system you can port from a 32b target to a 16b one? Is there a 16b system that isn't trivial to port to a 32b target? Moreover, if most vars are ints, you'll actually gain something from the 32 bits on the new target, but if they are int16_t, you won't. And the places which are hard to port tend to require manual inspection anyway; before you try a port, you don't know where they are. Now, if someone thinks it's so easy to port things if you have typedefs all over the place - when time comes to port, which happens to few systems, write a script converting all names in the code base. This should work according to the "no manual inspection required" logic, and it postpones the effort to the point in time where it actually gives benefit.

  4. Now if portability may be a theoretical benefit of the typedefs, readability sure goes down the drain. Just look at stdint.h: {int,uint}{max,fast,least}{8,16,32,64}_t. Lots of types. A program has lots of variables; is it really that easy to understand which need to be int_fast16_t and which need to be uint_least32_t? How many times are we silently converting between them, making them entirely pointless? (I particularly like BOOL/Bool/eBool/boolean/bool/int conversions. Every program written by an orderly organization mandating typedefs is littered with that).

  5. Of course in C++ we could make the type system more strict, by wrapping numbers in template class instantiations with overloaded operators and stuff. This means that you'll now get error messages of the form "class Number has no operator+ overload for argument of type class Number, candidates are..." I don't call this "readability", either. Your chances of implementing these wrapper classes correctly are microscopic, and most of the time you'll wait for the innumerable template instantiations to compile.

0
adăugat
O altă optimizare pe care am văzut-o și o apreciez este folosirea tipurilor FIXED și a tipurilor "USE BEST". adică tipărite nesignificate char UINT8 typedef nesigned uint255; uint 255 specifică intervalul valorilor, dar permite ca dimensiunile optime să fie specificate pe sistem
adăugat autor itj, sursa
@itj: În loc de uint255, folosiți uint_fast8_t din stdint.h . Este definit ca un tip rapid care poate suporta o valoare nesemnată de 8 biți. Pe o singură platformă, aceasta ar putea fi un char unsigned . Pe altul poate fi pur și simplu un unsigned int .
adăugat autor tomlogic, sursa
Acest lucru este bine până când codul dvs. trebuie să funcționeze pe un procesor foarte limitat și doriți sau trebuie să testați un procesor diferit, probabil pentru că testarea și depanarea țintă reală este dificil / imposibil. În această situație, trebuie să testați pe gazda dvs. dev cu variabile de aceeași dimensiune ca și pe țintă și dacă nu aveți codificat cu typedefs specifice dimensiunii, procesul dvs. va fi complet borken.
adăugat autor barny, sursa