2009-11-09 9 views
9

Quiero transmitir datos a través de la red, pero no quiero utilizar ninguna biblioteca extranjera (el estándar C/C++ está bien).Serializar cadenas, ints y flotantes a matrices de caracteres para redes SIN BIBLIOTECAS

por ejemplo:

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

Quiero que esta en una matriz

char xyz[11]; 

. Nota: Para transmitirlo a través de la red, necesito el pedido de bytes de red para la función sin firmar (htonl), entonces de alguna manera debo serializar el flotador en forma IEEE 754 (hay muchas funciones en Internet), y lo se.

¿Cómo los llego al xyz-Array, perfectamente alineado de extremo a extremo, para que pueda usar esto como un buffer para mi función socket + send()? Es evidente que tengo funciones inversas (ntohl, y una inversa IEEE 754) para sacarlos pero necesito una técnica allí también, preferiblemente el mismo ...

que sería algo como esto:

 
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 - 

¿Cómo puedo lograr esto sin bibliotecas externas y un uso mínimo de las funciones estándar de la biblioteca? Esto no es tanto para mi programa como para mí para aprender de él.

+0

En primer lugar, no ha declarado la variable y en ningún lugar. ¿Es una matriz de caracteres? ¿Sabes qué tan grandes son estas matrices o son dinámicas? –

+0

Lo siento, obviamente no puedo inicializar y con un literal de cadena entre comillas dobles, lo arreglé. Tiene 3 bytes de tamaño. – wsd

Respuesta

16

Ah, desea realizar una serie tipos de datos primitivos!En principio, hay dos enfoques: el primero es que tome la representación binaria interna en memoria de los datos que desea serializar, reinterpretémosla como un carácter y utilícela como representación:

Por lo tanto, si tiene un:

double d;

se toma la dirección de eso, reinterpretan ese puntero como un puntero a carácter, y luego usar estos caracteres:

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

Esto funciona con todos los tipos de datos primitivos. El principal problema aquí es que la representación de binray depende de la implementación (principalmente depende de la CPU). (Y se encontrará con errores sutiles cuando intente hacer esto con IEEE NAN ...).

En general, este enfoque no es portátil en absoluto, ya que no tiene ningún control sobre la representación de sus datos.

El segundo enfoque es, para usar una representación de nivel superior, que usted mismo tenga bajo control. Si el rendimiento no es un problema, puede usar std :: strstream y los operadores >> y < < para transmitir variables de tipo C primitivas a std :: cadenas. Esto es lento pero fácil de leer y depurar, y muy portátil en la parte superior.

+0

+1 para resaltar problemas y agregar relleno indefinido. Y voy a morder el bate :), ¿cuáles son los errores sutiles con NaNs IEEE en este escenario? Gracias. –

+1

Hay NaN de señalización y NaN de señalización. Cuando trabajas con estas representaciones como matrices de caracteres, puedes leerlas y escribirlas fácilmente. Pero cuando accede a ellos como flotadores, el solo hecho de leerlos puede hacer que la CPU emita una señal. Entonces, si no tienes cuidado, puedes terminar con un programa que se deserializa sin problemas, pero una vez que tocas el flotador, terminas en problemas. Y como este hilo trata de aprender, pensé que podría señalar esta área. –

+0

+1, y no lo he visto mencionar aquí en este contexto ... aunque los vendedores tienden a evitar la clasificación, así como la serialización de carrozas de cualquier tipo, finalmente :) –

0

¿Cuál es exactamente tu objetivo? ¿Y cuáles son exactamente los medios que está dispuesto a usar?

Si solo quiere hacer el trabajo con un compilador en particular en una computadora en particular, entonces la solución más rápida y fácil, pero también la más sucia, es usar una unión. Usted define una estructura que tiene sus elementos como miembros y los combina con la matriz de caracteres. Necesitas decirle al compilador que empaquete los miembros realmente, algo parecido a #pragma pack (1), y tu problema está resuelto. Simplemente almacena los tres valores en los miembros y luego los mira como una matriz de caracteres.

Si la máquina es poco endian, y necesita big endian ints/floats, simplemente cambia los caracteres relevantes.

Pero hay al menos otra docena de soluciones que te vienen a la mente si tienes otros objetivos, como portabilidad, orden de bytes no estándar, sizeof (int)! = 4, flotante no almacenado internamente en formato IEEE, etc.

+0

Quiero aprender a serializar tipos de datos C/C++ primitivos (de forma que pueda agregar conocimiento de la serialización de estructuras más adelante) en C/C++. Los medios son todas las funciones de C/C++ aceptadas por GCC y las funciones de la Biblioteca C/C++ estándar. Gracias, examinaré los sindicatos. – wsd

8

Algo como el siguiente código lo haría. Tenga cuidado con los problemas donde sizeof (unsigned int) es diferente en diferentes sistemas, esos lo atraparán. Para cosas como esta, es mejor usar tipos con tamaños bien definidos, como int32_t. De todos modos ...

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); 
[...] 

... y, por supuesto, el código de recepción haría algo similar, excepto al revés.

1

Este discussion parece relevante a su pregunta, pero utiliza impulsar API de serialización

+0

Creo que Boost le enseñará mucho (puede buscar la implementación). Y también le dará una solución preparada para numerosos problemas que usted mismo no podría pensar. –

+0

Estoy navegando por la API de serialización de Boost mientras escribo esto (en otra pestaña xD), pero parece que Overkill es lo que quiero hacer. Tratando de destilarlo por programación ... – wsd

Cuestiones relacionadas