Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using read() directly into a C++ std:vector

I'm wrapping up user space linux socket functionality in some C++ for an embedded system (yes, this is probably reinventing the wheel again).

I want to offer a read and write implementation using a vector.

Doing the write is pretty easy, I can just pass &myvec[0] and avoid unnecessary copying. I'd like to do the same and read directly into a vector, rather than reading into a char buffer then copying all that into a newly created vector.

Now, I know how much data I want to read, and I can allocate appropriately (vec.reserve()). I can also read into &myvec[0], though this is probably a VERY BAD IDEA. Obviously doing this doesn't allow myvec.size to return anything sensible. Is there any way of doing this that:

  1. Doesn't completely feel yucky from a safety/C++ perspective
  2. Doesn't involve two copies of the data block - once from kernel to user space and once from a C char * style buffer into a C++ vector.
like image 711
Joe Avatar asked May 06 '10 10:05

Joe


People also ask

What does vector data () do?

vector data() function in C++ STL The std::vector::data() is an STL in C++ which returns a direct pointer to the memory array used internally by the vector to store its owned elements.

Can we use vector in C?

You can't. By definition, C knows nothing of any of the required components of a std::vector , including, but not limited to: C does not have namespaces, so it can't understand the std namespace. C does not have templates, so it can't understand the std::vector<T> type.

What does vector at do?

vector::at() at() function is used reference the element present at the position given as the parameter to the function.


4 Answers

Use resize() instead of reserve(). This will set the vector's size correctly -- and after that, &myvec[0] is, as usual, guaranteed to point to a continguous block of memory.

Edit: Using &myvec[0] as a pointer to the underlying array for both reading and writing is safe and guaranteed to work by the C++ standard. Here's what Herb Sutter has to say:

So why do people continually ask whether the elements of a std::vector (or std::array) are stored contiguously? The most likely reason is that they want to know if they can cough up pointers to the internals to share the data, either to read or to write, with other code that deals in C arrays. That’s a valid use, and one important enough to guarantee in the standard.

like image 62
Martin B Avatar answered Oct 19 '22 07:10

Martin B


I'll just add a short clarification, because the answer was already given. resize() with argument greater than current size will add elements to the collection and default - initialize them. If You create

std::vector<unsigned char> v;

and then resize

v.resize(someSize);

All unsigned chars will get initialized to 0. Btw You can do the same with a constructor

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

So theoretically it may be a little bit slower than a raw array, but if the alternative is to copy the array anyway, it's better.

Reserve only prepares the memory, so that there is no reallocation needed, if new elements are added to the collection, but You can't access that memory.

You have to get an information about the number of element written to Your vector. The vector won't know anything about it.

like image 21
Maciej Hehl Avatar answered Oct 19 '22 08:10

Maciej Hehl


Assuming it's a POD struct, call resize rather than reserve. You can define an empty default constructor if you really don't want the data zeroed out before you fill the vector.

It's somewhat low level, but the semantics of construction of POD structs is purposely murky. If memmove is allowed to copy-construct them, I don't see why a socket-read shouldn't.

EDIT: ah, bytes, not a struct. Well, you can use the same trick, and define a struct with just a char and a default constructor which neglects to initialize it… if I'm guessing correctly that you care, and that's why you wanted to call reserve instead of resize in the first place.

like image 23
Potatoswatter Avatar answered Oct 19 '22 07:10

Potatoswatter


If you want the vector to reflect the amount of data read, call resize() twice. Once before the read, to give yourself space to read into. Once again after the read, to set the size of the vector to the number of bytes actually read. reserve() is no good, since calling reserve doesn't give you permission to access the memory allocated for the capacity.

The first resize() will zero the elements of the vector, but this is unlikely to create much of a performance overhead. If it does then you could try Potatoswatter's suggestion, or you could give up on the size of the vector reflecting the size of the data read, and instead just resize() it once, then re-use it exactly as you would an allocated buffer in C.

Performance-wise, if you're reading from a socket in user mode, most likely you can easily handle data as fast as it comes in. Maybe not if you're connecting to another machine on a gigabit LAN, or if your machine is frequently running 100% CPU or 100% memory bandwidth. A bit of extra copying or memsetting is no big deal if you are eventually going to block on a read call anyway.

Like you, I'd want to avoid the extra copy in user-space, but not for performance reasons, just because if I don't do it, I don't have to write the code for it...

like image 45
Steve Jessop Avatar answered Oct 19 '22 09:10

Steve Jessop