2010-05-06 24 views
15

Estoy terminando la funcionalidad de socket de Linux del espacio de usuario en C++ para un sistema integrado (sí, probablemente reinvente la rueda de nuevo).Uso de read() directamente en C++ std: vector

Quiero ofrecer una implementación de lectura y escritura utilizando un vector.

Hacer la escritura es bastante fácil, puedo pasar &myvec[0] y evitar copiar innecesariamente. Me gustaría hacer lo mismo y leer directamente en un vector, en lugar de leer en un búfer char y luego copiar todo eso en un vector recién creado.

Ahora, sé la cantidad de datos que quiero leer, y puedo asignarlos adecuadamente (vec.reserve()). También puedo leer en &myvec[0], aunque esta es probablemente una MUY MALA IDEA. Obviamente, hacer esto no permite a myvec.size devolver nada sensato. ¿Hay alguna manera de hacer esto que:

  1. no sentir asco por completo a partir de una seguridad/C++ perspectiva
  2. no involucra dos copias del bloque de datos - una vez en el núcleo para el espacio de usuario y una vez de un buffer de estilo C char * en un vector C++.
+0

¿Un vector de qué? –

+0

Un vector de caracteres (bueno, bytes)! – Joe

+0

Probablemente quiera usar 'uint8_t' o' unsigned char' para bytes. – tzaman

Respuesta

22

Use resize() en lugar de reserve(). Esto establecerá el tamaño del vector correctamente, y después de eso, &myvec[0] está garantizado, como de costumbre, para apuntar a un bloque de memoria contiguo.

Editar: El uso de &myvec[0] como un puntero a la matriz subyacente para lectura y escritura es seguro y está garantizado para trabajar con el estándar de C++. Esto es lo que Herb Sutter has to say:

Entonces, ¿por qué la gente pregunte continuamente si los elementos de un std :: vector (o std :: matriz) se almacenan de forma contigua? La razón más probable es que quieran saber si pueden arrojar punteros a las partes internas para compartir los datos, ya sea para leer o escribir, con otro código que se ocupa de las matrices en C. Ese es un uso válido, y uno lo suficientemente importante como para garantizar en el estándar.

+0

Eso es color de rosa, y ciertamente responde el punto 2 (probado y funciona también, gracias! :-)), pero ¿este enfoque es seguro y limpio, o hay alguna otra manera? No puedo evitar sentir que pasar la dirección de los vectores contiguos al bloque de memoria para escribir parece peligroso? – Joe

+0

@Joe: En palabras de Herb Sutter: "Cringe no": es perfectamente seguro. Ver mi edición arriba y el enlace a la entrada del blog de Herb Sutter. –

+0

¡Excelente! Gracias :-) ¡Como siempre, stackoverflow.com ofrece! – Joe

1

Suponiendo que es una estructura POD, llame al resize en lugar de reserve. Puede definir un constructor predeterminado vacío si realmente no desea que los datos se pongan a cero antes de llenar el vector.

Es un nivel algo bajo, pero la semántica de la construcción de las estructuras POD es deliberadamente turbia. Si memmove está permitido copiar-construirlos, no veo por qué una lectura de socket no debería.

EDITAR: ah, bytes, no una estructura. Bueno, puedes usar el mismo truco y definir una estructura con solo char y un constructor predeterminado que descuida inicializarlo ... si estoy adivinando correctamente que te importa, y es por eso que querías llamar al reserve en lugar de resize en el primer lugar.

1

Si desea que el vector refleje la cantidad de datos leídos, llame al resize() dos veces. Una vez antes de la lectura, dése espacio para leer. Una vez más después de la lectura, para establecer el tamaño del vector en la cantidad de bytes realmente leídos. reserve() no es bueno, ya que llamar a reserva no le da permiso para acceder a la memoria asignada para la capacidad.

El primer resize() pondrá a cero los elementos del vector, pero es poco probable que esto genere una sobrecarga de rendimiento. Si lo hace, podría intentar la sugerencia de Potatoswatter, o podría renunciar al tamaño del vector que refleja el tamaño de la lectura de datos, y en su lugar solo resize() una vez, luego volver a usarlo exactamente como lo haría con un búfer asignado en C .

En lo que respecta al rendimiento, si está leyendo desde un socket en modo de usuario, lo más probable es que pueda manejar datos tan rápido como aparece. Tal vez no si se está conectando a otra máquina en una LAN gigabit. o si su máquina está ejecutando con frecuencia 100% de CPU o 100% de ancho de banda de memoria. Un poco de copia adicional o memsetting no es gran cosa si finalmente va a bloquear en una llamada read de todos modos.

Como usted, me gustaría evitar la copia adicional en el espacio de usuario, pero no por motivos de rendimiento, simplemente porque si no lo hago, no tengo que escribir el código para ello ..

+0

Normalmente, no me interesaría la copia extra, pero estoy en un sistema de tiempo real con pocos recursos. Todo el proceso de lectura/escritura no es bloqueante. Por supuesto, esto obviamente lleva a preguntas sobre los contenedores STL en un sistema de tiempo real frente a la asignación de memoria estática, etc. – Joe

1

Voy a agregar una breve aclaración, porque la respuesta ya fue dada. resize() con un argumento mayor que el tamaño actual agregará elementos a la colección y por defecto: inicialízalos. Si Usted crea

std::vector<unsigned char> v; 

y luego cambiar el tamaño

v.resize(someSize); 

Todos los caracteres sin signo conseguirán inicializado a 0. Por cierto Usted puede hacer lo mismo con un constructor

std::vector<unsigned char> v(someSize); 

Así, teóricamente, puede ser un poco más lento que una matriz en bruto, pero si la alternativa es copiar la matriz de todos modos, es mejor.

Reserve solo prepara la memoria, de modo que no se necesita una reasignación, si se agregan nuevos elementos a la colección, pero no puede acceder a esa memoria.

Tienes que obtener información sobre la cantidad de elementos escritos en Tu vector. El vector no sabrá nada al respecto.

Cuestiones relacionadas