2009-11-09 15 views
9

Voglio trasmettere i dati tramite la rete, ma non voglio utilizzare alcuna libreria esterna (Standard C/C++ è ok).Serialize Stringhe, interi e float agli array di caratteri per il networking SENZA LIBRERIE

ad esempio:

unsigned int x = 123; 
char y[3] = {'h', 'i', '\0'}; 
float z = 1.23f; 

Voglio che questo in un array

char xyz[11]; 

.

Nota: Per trasmetterlo su rete, ho bisogno di ordine Byte di rete per la funzione int (htonl) senza segno, quindi ho bisogno di serializzare in qualche modo il float per essere in forma IEEE 754 (ci sono molte funzioni su Internet), e lo so.

Come faccio a inserirli nella matrice xyz, ben allineati da un capo all'altro, quindi posso usarlo come buffer per la mia funzione socket + send()? Ovviamente ho funzioni inversa (ntohl, e un rovescio IEEE 754) per farli fuori, ma ho bisogno di una tecnica anche lì, preferibilmente lo stesso ...

sarebbe qualcosa di simile:

 
xyz in binary: 
00000000 0000000 00000000 01111011 | 01101000 | 01101001 | 00000000 | 00111111 10011101 01110000 10100100 
- big endian repr. of u. int 123 - | - 'h' - | - 'i' - | - '\0' - | - IEEE 754 repr of float 1.23 - 

Come posso realizzare questo senza librerie esterne e l'uso minimo delle funzioni di libreria standard? Questo non è tanto per il mio programma quanto per me da cui imparare.

+0

Prima di tutto, non hai effettivamente dichiarato la variabile y da nessuna parte. È un array di personaggi? Sai quanto sono grandi questi array o sono dinamici? –

+0

Mi dispiace, ovviamente non posso inizializzare y con una stringa letterale tra virgolette, l'ho risolto. Ha una dimensione di 3 byte. – wsd

risposta

16

Ah, si desidera serializzare tipi di dati primitivi!In linea di principio, ci sono due approcci: il primo è che prendi semplicemente la rappresentazione binaria interna dei dati che vuoi serializzare, reinterpretala come un personaggio e usala come rappresentazione:

Quindi se si dispone di:

doppio d;

si prende l'indirizzo di quella, reinterpretano che puntatore come un puntatore a carattere, e quindi usare questi personaggi:

double *pd=&d; 
char *pc = reinterpret_cast<char*>(pd); 
for(size_t i=0; i<sizeof(double); i++) 
{ 
    char ch = *pc; 
    DoSomethingWith(ch); 
    pc++; 
} 

Questo funziona con tutti i tipi di dati primitivi. Il problema principale qui è che la rappresentazione binray dipende dall'implementazione (principalmente dipendente dalla CPU). (E ti imbatterai in bug sottili quando proverai a farlo con I NAN IEEE ...).

Tutto sommato, questo approccio non è affatto portatile, in quanto non si ha alcun controllo sulla rappresentazione dei dati.

Il secondo approccio consiste nell'utilizzare una rappresentazione di livello superiore che voi stessi avete sotto controllo. Se le prestazioni non sono un problema, è possibile utilizzare std :: strstream e gli operatori >> e < < per lo streaming di variabili di tipo C primitive in std :: stringhe. Questo è lento ma facile da leggere ed eseguire il debug e molto portatile su di esso.

+0

+1 per evidenziare problemi e aggiungere pad indefinito. E morderò il morso :), quali sono i bug sottili con IEEE NaN in questo scenario? Grazie .. –

+1

Sono presenti NaN di segnalazione e NaN non di segnalazione. Quando lavori con queste rappresentazioni come array di caratteri, puoi leggerle e scriverle facilmente. Ma quando li si accede come float, solo l'atto di leggerli può far sì che la CPU segnali. Quindi, se non stai attento, puoi finire con un programma che deserializza senza problemi, ma quando tocchi il galleggiante, finisci nei guai. E visto che questo thread riguarda l'apprendimento, ho pensato di poter indicare quest'area. –

+0

+1, e non l'ho visto menzionato qui in questo contesto .. anche se i venditori tendono ad evitare il marshalling, oltre a serializzare float di qualsiasi tipo, finalmente :) –

0

Qual è esattamente il tuo obiettivo? E quali sono esattamente i mezzi che sei disposto a usare?

Se si desidera eseguire il lavoro con un particolare compilatore su un particolare computer, la soluzione più rapida e semplice, ma anche più sporca, è utilizzare un sindacato. Si definisce una struttura che ha i propri elementi come membri e si unisce con l'array di caratteri. Devi dire al compilatore di impacchettare i membri molto strettamente, qualcosa sulla falsariga di #pragma pack (1), e il tuo problema è risolto. È sufficiente memorizzare i tre valori nei membri e quindi considerarli come una matrice di caratteri.

Se la macchina è little endian e sono necessari integer/float big endian, basta scambiare i caratteri rilevanti.

Ma ci sono almeno un'altra dozzina di soluzioni che vengono in mente se si hanno altri obiettivi, come portabilità, ordine byte non standard, sizeof (int)! = 4, float non memorizzato nel formato IEEE internamente, ecc.

+0

Voglio imparare a serializzare i primitivi tipi di dati C/C++ (in modo tale da poter aggiungere la conoscenza di serializzare le strutture in seguito) in C/C++. I mezzi sono tutte le funzioni C/C++ accettate da GCC e le funzioni di libreria standard C/C++. Grazie, guarderò i sindacati. – wsd

8

Qualcosa come il codice qui sotto lo farebbe. Fai attenzione ai problemi in cui sizeof (unsigned int) è diverso su sistemi diversi, quelli ti prenderanno. Per cose come questa è meglio usare tipi con dimensioni ben definite, come int32_t. Comunque ...

unsigned int x = 123; 
char y[3] = {'h', 'i', '\0'}; 
float z = 1.23f; 

// The buffer we will be writing bytes into 
unsigned char outBuf[sizeof(x)+sizeof(y)+sizeof(z)]; 

// A pointer we will advance whenever we write data 
unsigned char * p = outBuf; 

// Serialize "x" into outBuf 
unsigned int32_t neX = htonl(x); 
memcpy(p, &neX, sizeof(neX)); 
p += sizeof(neX); 

// Serialize "y" into outBuf 
memcpy(p, y, sizeof(y)); 
p += sizeof(y); 

// Serialize "z" into outBuf 
int32_t neZ = htonl(*(reinterpret_cast<int32_t *>(&z))); 
memcpy(p, &neZ, sizeof(neZ)); 
p += sizeof(neZ); 

int resultCode = send(mySocket, outBuf, p-outBuf, 0); 
[...] 

... e ovviamente il codice ricevente farebbe qualcosa di simile, tranne che al contrario.

1

Questo discussion sembra rilevante alla tua domanda, ma utilizza la serializzazione dare impulso API

+0

Penso che Boost ti insegnerà molto (puoi cercare l'implementazione). E ti darà anche una soluzione pronta a numerosi problemi che non potresti pensare. –

+0

Sto navigando attorno all'API di serializzazione Boost mentre scrivo questo (in un'altra scheda xD), ma sembra eccessivo per quello che voglio fare. Cercando di distillarlo programmaticamente ... – wsd

Problemi correlati