Search code examples
c++iomemcpy

How to make C++ class have specific size in memory


I am given a C++ framework (that I can't change) for IO operations that works like this:

  • It has block_size which is the size of block that is read or written
  • Each read/write operation gets this operation struct:
struct Operation {
   size_t block;  // index of external memory block
   void * data;   // here the data will be written or read from
}

The the actual read or write looks like this:

Operation o; 
o.block = 42; // index of block i want to read from
o.data = // where i want the data to end up
io.read(o);

The problem is that the read/write is implemented with memcpy and it always moves block of block_size bytes. However when I want to save and load my class X to this external memory I have a problem, because probably sizeof(X) != block_size.

So when I would do this:

X x;
Operation o; 
o.block = 42; // index of block i want to read from
o.data = &x;
io.read(o);

io.write(o);

If sizeof(X) < block_size I have a problem during the read, because more bytes than I want will be read, possibly corrupting the stack. If sizeof(X) > block_size I have a problem during write, because not every byte of X will be written so I have an incomplete backup of it.

I want to use each block for one instance of X and I can make sure that sizeof(X) < block_size. Is there some way how to add some padding bytes to X, so it has exactly block_size bytes?


Solution

  • X x;
    Operation o; 
    o.block = 42; // index of block i want to read from
    o.data = &x;
    io.read(o);
    

    Well, you can't do that, as you yourself said. You must point to a block_size block, then copy into the relevant class:

    BYTE buffer[block_size];
    X x;
    Operation o; 
    o.block = 42; // index of block i want to write to
    o.data = buffer;
    io.read(o);
    memcpy(&x, buffer, sizeof(X));
    

    Ditto for writes (you can't simply write whatever happens to be after the end of X because you may fall off the cliff into an unmapped page):

    BYTE buffer[block_size];
    X x;
    Operation o; 
    o.block = 42; // index of block i want to read from
    o.data = buffer;
    memcpy(buffer, &x, sizeof(X));
    io.write(o);
    

    I won't comment on the sanity of the interface itself. And static_assert(sizeof(X) <= block_size). And the type X must be memcopy safe.

    There are more tricks in the book, like make the sizeof(X) always match block_size (via padding) or use allocation tricks (always allocate X in block_size regions, never use on stack). But copy before write/after read is the simplest, least tricky.