I have a struct with a flexible array member that I need to use.
struct Record
{
uint32_t length;
Data contents[];
};
I'm able to initialize this and use it by doing something like this: (it would also work with malloc or any other dynamic allocation)
vector<Data> members;
vector<uint8_t> buffer;
Record myRecord;
buffer.resize(sizeof(Record) + members.size() * sizeof(Data));
myRecord = *(reinterpret_cast<Record*>(buffer.data());
myRecord.length = static_cast<uint32_t>(members.size());
// copy members to myRecord.contents
That works just fine. But now I need to have an interface that operates on batches of Record, and I have been trying to use an std::vector for this. Then problems start appearing, and I'm guessing it's because std::vector arranges all elements contiguously on memory, and since sizeof(Record) won't take into account the size of the contents (each vector element will hold only 4 bytes, instead of 4 bytes + size_of_contents * sizeof(Data)), the vector elements are actually sharing memory and then each element starts overwriting the contents of the previous element. Does that make sense?
If this really is the problem, I was wondering if there's any way to "force" the vector to allocate a specific size for each element (instead of whatever sizeof returns for the element's type). That way I could make sure that each vector element would have enough size. If that's not possible, is there an alternative solution? Maybe a different container that would allow me to do so? Please keep in mind that I do need to use the struct as it's defined (I would love to just replace the whole thing for a vector but unfortunately that's not possible)
Your principle problem is this:
myRecord = *(reinterpret_cast<Record*>(buffer.data());
That's simply overwriting the data in a stack variable. That does not change the address of myRecord
to suddenly point to buffer.data()
. Which means when you later do myRecord.contents[...] = ...
, you're going to be trashing the stack.
What you almost certainly intended was:
Record *myRecord = (reinterpret_cast<Record*>(buffer.data());
Then you would have a pointer to memory managed by buffer
, which would have sufficient storage for the myRecord->contents
array.
You cannot treat Record
like a value type. As far as C++'s object model is concerned, it's not a value type. It cannot be copied or moved like most C++ types. You can only manipulate it through a pointer/reference to the specific allocation you use here.
That being said, using a vector
to manage the storage for your Record*
like this is really weird. It'd be better to use a unique_ptr
, since resizing the allocation would be a really bad idea.
std::unique_ptr<char[]> storage = new char[sizeof(Record) + (members.size() * sizeof(Data))];
This also prevents the system from initializing the memory, since you're going to overwrite it anyway.
I was wondering if there's any way to "force" the vector to allocate a specific size for each element (instead of whatever sizeof returns for the element's type).
No. vector
manages a contiguous array of elements of the same type. And in C++, all objects of the same type have the same size.