Newsgroup: it.hobby.elettronica.digitale
Titolo: Tipi di dati

Ciao,
sto cercando di uniformare la scrittura del codice. Mi chiedevo se può 
avere senso includere un file con la definizione dei tipi di dati, così 
da poterla aggiornare al volo in caso di cambio di target o di compilatore.
Ad esempio per gli AVR potrei partire da una cosa del genere (usata ad 
esempio nella libreria FatFs)
typedef signed int		INT;
typedef unsigned int		UINT;
typedef signed char		CHAR;
typedef unsigned char		UCHAR;
typedef unsigned char		BYTE;
typedef signed short		SHORT;
typedef unsigned short		USHORT;
typedef unsigned short		WORD;
typedef signed long		LONG;
typedef unsigned long		ULONG;
typedef unsigned long		DWORD;
typedef enum { FALSE = 0, TRUE } BOOL;
Può avere senso? Si può fare di meglio?
Grazie!
Marco / iw2nzm

Marco Trapanese ha scritto:

Io credo che abbia poco senso.
Perché dovresti scrivere UINT invece del tipo standard unsigned int? 
Tanto su tutte le piattaforme un unsigned int è un unsigned int, non 
ti dà nessun beneficio. Se è solo per scrivere meno lo trovo ancora 
più opinabile, ti tiri dietro una dipendenza (questo file) 
inutilmente, rallentando le operazioni di port quando cambi CPU.
Ha senso invece definire i tipi ISO (mi sembra si era già parlato di 
stdint.h qualche tempo) per gli interi a lunghezza esatta (int8_t, 
uint8_t etc...), ma solo se non lo fa già il compilatore.
Un'altra cosaa buona sarebbe avere degli assert statici sulla 
lunghezza dei tipi, ti evitano parecchie brutte sorprese, soprattutto 
quando hai a che fare con protocolli & co.
Ciao

 che fare con protocolli & co.
Qui mi sono perso. Hai voglia di approfondire un poco?
Ti ringrazio!
Marco / iw2nzm

 typedef unsigned int		UINT;
L'unica parte che trovo utile (e uso anche io estensivamente) è quella in
cui definisci dei tipi a grandezza nota, cose del genere le trovi un po'
dovunque dai microcontrollori a 8 bit fino agli header di Linux:
typedef signed int _s16;
typedef unsigned int _u16;
typedef signed long _s32;
typedef unsigned long _u32;
Eccetera. Ovviamente esistono anche casi in cui tu *vuoi* che il numero di
bit del tipo che usi cambi con la piattaforma, soprattutto per questioni di
performances; bisogna valutare caso per caso, come sempre è un tradeoff fra
portabilità/manutenibilità del codice e prestazioni.
-- 
emboliaschizoide.splinder.com

 Qui mi sono perso. Hai voglia di approfondire un poco?
Scrivere un "assert" è un modo per "assicurarsi" che una certa 
condizione sia vera. A runtime, di solito si asserta su pre-condizioni 
e post-condizioni di un certo pezzo di codice. Di solito l'assert fa 
esplodere il programma in maniera brutale. In ambito embedded, siccome 
il programma non si può fermare, di solito viene stampata la riga e la 
condizione che si è rivelata falsa su una qualche seriale di debug.
Tutto questo vale a runtime, e gli assert si traducono in istruzioni 
macchina che occupano program space. Gli assert statici, invece, 
vengono controllati dal compilatore a compile time e quindi non 
sprecano spazio. Essendo controllati a compile time possono essere 
usati per le cose appunto, statiche, e già note a compile time.
Ti faccio un esempio:
STATIC_ASSERT(sizeof(int8_t) * CPU_BITS_PER_CHAR == 8);
Questa macro controlla che la dimensione di un int8_t sia davvero di 8 
bit. E se questa condizione è falsa la compilazione si ferma con errore.
Ma come è definita questa fantomatica STATIC_ASSERT?
Eccola qui:
/** Issue a compilation error if the \a condition is false */
#define STATIC_ASSERT(condition) extern char 
STATIC_ASSERTION_FAILED__[(condition) ? 1 : -1]
In pratica, se condition è falsa si tenta di definire un array di char 
con lunghezza negativa e questo fa fermare la compilazione. Se invece 
la condizione è vera, stiamo semplicemente dichiarando che da qualche 
parte (extern) esisterà un array lungo 1. Ma siccome nessuno usa 
davvero l'array, il linker non lo allocherà e non ci sarà spreco di 
memoria.
Se vuoi vederla nel suo ambiente naturale:
http://www.bertos.org/browser/trunk/bertos/cfg/compiler.h
riga 512.
Ciao

 STATIC_ASSERT(sizeof(int8_t) * CPU_BITS_PER_CHAR == 8);
Aggiungo un altro esempio classico:
struct IpAddress
{
	uint8_t a1;
	uint8_t a2;
	uint8_t a3;
	uint8_t a4;
}
STATIC_ASSERT(sizeof(struct IpAddress) == 4);
E così via, così se il codice lo porti su piattaforme diverse sei 
sicuro che la dimensione delle strutture non cambi. E anche se cambia 
lo STATIC_ASSERT fa sì che il compilatore dia errore proprio nel punto 
in cui devi fare i cambiamenti, che così sono più facili da individuare.
Se ti interessa questo approccio alla programmazione, esso fa parte di 
un design pattern famoso, cerca "design by contract" per maggiori info.
Ciao

 STATIC_ASSERT(sizeof(int8_t) * CPU_BITS_PER_CHAR == 8);
[cut]
Grazie! Non avevo mai incontrato gli assert, ora mi è più chiaro. Meglio 
cominciare a usarli :)
Ciao!
Marco / iw2nzm