ImaginaryHuman072889
ImaginaryHuman072889

Reputation: 5195

C++ Spare data to keep object same size?

I work for a company that uses a large C++ project to automate the manufacturing process and I noticed the following peculiar behavior when looking at the revision history of the C++ source code.

A simplified example of this behavior in the different revisions of the software is shown below.

Revision 1 of software:

struct Foo
{
    int x;
    int reserve[20]; // unused
};

Revision 2 of software:

struct Foo
{
    int x;
    int y[2];
    int reserve[18]; // unused
};

Revision 3 of software:

struct Foo
{
    int x;
    int y[2];
    int z[5];
    int reserve[13]; // unused
};

It is apparent that the unused reserve array is just there to ensure that any instance of the struct that is created always takes up the same amount of memory, regardless of which revision of the software is running.

My questions are: Is this common practice and/or good practice to do this for large C++ projects? And is there any general (general meaning, non-application-specific) reason that using this practice is required or advantageous?

Upvotes: 2

Views: 228

Answers (3)

ImaginaryHuman072889
ImaginaryHuman072889

Reputation: 5195

For what it is worth... I finally (over a year after posting this question) found the exact reason for this:

The program stores this struct in a shared memory file.

The purpose of keeping this structure in the same format is to preserve the exact memory location of the structure such that any past and future events of the shared memory will still work the same way.

chars are used as one-byte "buffers" to always keep the struct the same size. The reserve array is then decreased depending on which datatype is used.

I've also found that a second reserve array (e.g. reserve2) is created so that the "jumps" always occur in 4-byte jumps. See below for a visualization of this. The structure always adds up to 100 bytes.

Revision 1:

struct Foo
{
    int x;             //  4 bytes
    char reserve[96];  // 96 bytes
};

Revision 2:

struct Foo
{
    int x;             //  4 bytes
    short y;           //  2 bytes
    char reserve[94];  // 94 bytes
};

Revision 3:

struct Foo
{
    int x;             //  4 bytes
    short y;           //  2 bytes
    short reserve2;    //  2 bytes (added so that the "jump" is still 4 bytes)
    int z;             //  4 bytes
    char reserve[88];  // 88 bytes
};

Upvotes: 1

Rinat Veliakhmedov
Rinat Veliakhmedov

Reputation: 1039

Yes, it is a common practice. I can't say if this is a good or bad practice because it depends.

  • If your structure is representing certain type of a packet or a data field from some specification, those often have reserved fields. Sometimes when new version of the specification is developed, those reserved fields are used for somethings meaningful. I worked with PCIe specification compliance and this happened a couple of times.
  • If you want to be able to add some new data to your, say, network packets, but you want old version clients to be able to read that packets as if they were old version, this can be a reasonable solution sometimes.

Of course you have to be careful when doing it, since you may run out of reserved fields sometime in the future, often in the places where you'd never expect to.

Upvotes: 7

Tadeusz Kopec for Ukraine
Tadeusz Kopec for Ukraine

Reputation: 12413

If the struct you define is sent through network and you don't want to implement some network protocol negotiation, you can in new product versions use struct with more fields used and old version will be still able to receive such structs (as size will not change). It will just ignore the part it is not aware of (treat it as a part of reserve).

Upvotes: 2

Related Questions