Newsgroup: it.comp.lang.c++
Titolo: Implementazione di un protocollo : Bits , interi  e bitfields (LUNGO ;)

Salve a tutti,
Innanzitutto mi scuso per la lunghezza, ma serve per far capire il contesto.
Sto scrivendo il codice per l'implementazione di un server di
comuniczione tra due sistemi hardware dedicati (una console di comando
intelligente ed un sistema di controllo di un nastro trasportatore).
Il protocollo ( rigidamente definito da uno dei committenti ) è
basato su messaggi asincroni di controllo e stato che hanno una struttura
del tipo:
NASTROSYSTEM_STATUS (
	NastroSystem_Health(4bit)
	Tipo_Nastro(3bit)
	spare(1bit)
	-----------------------------------------> byte #1
	Software_Build(12bit)                                           
  	Software_Rel(6bit)
	----------------------------------------->  word #1 
	CommServer_Build(16bit)
	CommServer_Rel(2bit)
	------------------------------------------word#2
)
I messaggi vanno gestiti dal mio server in vari modi(ricevuti,creati,controllati). 
Ho creato un'architettura basata su vari patterns di comunicazione
più o meno noti  ma il punto riguarda il C++ :
qual'è il modo più flessibile ed efficiente in C++ per gestire i campi dei messaggi? 
Mi spiego:
Tipo_Nastro è un campo del messaggio di 3bit. Devo poter accedere
a questi dati per scrivere e leggere un intero da 3 bit.
Lo spare di 1 bit serve per allinearsi al 1 byte(e va posto a 0).
Ma siccome il protocollo prevede la conversione(per la trasmissione) dei
dati binari in "caratteri esadecimali" devo avere la possibilità di fare le seg.
operazioni: 
1) get/set sui  campi (interi di varie lunghezze in bit) del
messaggio presi singolarmante
2) accedere ai bit di tutto il messaggio(spare compresi)
3) convertire la precedente "stringa" in caratteri esadecimali ( ogni 4bit
binari vengono rappresentati da un carattere di 8bit contenente il
carattere esadecimale che quei 4bit esprimevano (si duplca l'informazione)
es. [ 1111  --> 0xF --> "F" -> 70 -> 0100 0110 ].
Per fare ciò ho bisogno di poter accedere ai singoli campi interi da un
lato mentre dall'altro all'intera porzione di informazioni binarie.
Ho pensato di utilizzare il seguente schema con una union anonima:
class NastroStatusMessage {
NastroStatusMessage() { byte10; word10;.... }	
union {
	unsigned char byte1;
	struct {
		unsigned char  NastroSystem_Health: 4;
		unsigned char  Tipo_Nastro:3;
		unsigned char spare:1;
		}
	}
union {
	unsigned short word1;
	struct {
		unsigned short Software_Build:12;
	  	unsigned short Software_Rel:6;
		}
	}
// etc...
}
in questo modo posso accedere ai singoli campi o tramite i membri byte1 e
byte2 a tutta una porzione di dati.
Ovviamente in tutto questo devo tenere presente l'endianess della
macchina per poter effettuare il get/set dei campi in maniera corretta.
Mi chiedo: esiste un modo milgiore di gestire una situazione simile ? 
Secondo voi sarebbe meglio utilizzare vector<bool>  o i bitset<int n>
invece dei bitfields ?  O anche qualche schema alternativo rispetto a quello che
sto utilizzando che usi i bitfields ? 
Qualche consiglio su come gestire dati binari con flessibilità(accedere
ai singoli campi interi di varie lunghezze, come un tutt'uno)  ed
in maniera efficiente ? 
vorrei essere sicuro di scegliere il miglior modo di rappresentare quei messaggi 
in C++ per cui ogni consiglio idea o critica è assolutamente benvenuta!!!!
johnny

 byte2 a tutta una porzione di dati.
I bit field hanno alcune limitazioni:
- su alcune architetture sono allocati da sinistra a destra, su altre da
   destra a sinistra;
- allocazione e allineamento sono definiti dall'implementazione.
Supponendo di trovarci nel caso più comune [1], io proverei qualcosa di 
questo genere:
   namespace tape_client {
     typedef unsigned char raw_message_t[5];
     class version {
     public:
       version(unsigned int release, unsigned int build);
       unsigned int release() const; // accessor
       unsigned int build() const; // accessor
       /* ... */
     };
     enum tape_health { /* ... */ };
     enum tape_type { /* ... */ };
     class tape_message {
       public:
       tape_message(raw_tape_message_t raw_message);
       version software_version() const;
       version server_version() const;
       tape_health health() const;
       tape_type type() const;
       private:
       version software;
       version server;
       tape_health health;
       tape_type type;
       /* ... */
     };
   }
ovvero non mi preoccuperei di avere in memoria un layout corrispondente
a quanto ricevuto, ma mi limiterei a leggere i dati in un raw_message_t
per poi costruire un tape_message che ne consenta la manipolazione a
livello più alto.
In questo modo, i problemi di bit e byte saltano fuori in un posto solo,
il costruttore o alla peggio in due, il costruttore e un'eventuale
funzione membro che riassembla i frammenti e produce di nuovo un
raw_message_t. Dal punto di vista della performance, questo metodo fa
la conversione una volta per tutte, e meno di una volta non si può non
farla, e poi lavora con oggetti che sono sostanzialmente degli int e
quindi dovrebbero offrire la massima performance - al prezzo di un
certo spreco di memoria, quindi se occorre tenere in ballo parecchi
messaggi e la memoria è preziosa può essere un problema.
La funzione che scrive i "caratteri esadecimali" corrispondenti al
messaggio si scrive assai più comodamente avendo già i dati in forma
esplicita, e piuttosto che delle funzioni set che fanno tanto VB io
userei un ulteriore costruttore
   tape_message(tape_type t,
                tape_health h,
                version software,
                version server);
in modo da avere un oggetto che, una volta costruito, non può cambiare
in nessun modo: se è un "messaggio ricevuto" deve essere immutabile
perchè rappresenta un evento passato e ormai concluso.
Tra parentesi: se i dati vengono ricevuti nello stesso protocollo ASCII 
sopra descritto, è del tutto inutile impacchettarli in un raw_message_t
e conviene invece usarli direttamente
   tape_message(const char* ascii_message) throw(bad_tape_message);
che usa un'eccezione per segnalare che invece di un messaggio dal nastro
gli è stato passato l'articolo di testa della Gazzetta dello Sport.
Barone Frankenstein

 gli Ã¨ stato passato l'articolo di testa della Gazzetta dello Sport.
Perfettamente d'accordo ,
anche perchè dubito fortemente che la gazzetta dello sport sia rappresentata in HEX-chars :)
 
1000 grazie per il prezioso contributo alla discussione ;)
Johnny

 Se baso la rappresentazione interna dei messaggi sulla sequenza binaria di dati,
 > mediante i bitfields e le union anonime non dovrei avere troppi 
problemi ad
 > accedere sia ai singoli campi sia a tutta la sequenza di bit per 
effettuare
 > la conversione da/verso hex-chars.
Non è che la conversione, come mostrato sopra, richieda sforzi
sovrumani.
Barone Frankenstein