Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Assigning base struct to derived struct

Tags:

c++

I use a struct to represent data that is written to a file. If I need to add members to this struct (i.e. save out extra data) I create a new struct (this represents a new version of the dataset) that derives from the original one. So for example:

struct data1
{
   int stuff1;
   int stuff2;
};

struct data : data1
{
   int stuff3;
};

Backward compatibility is maintained by checking if we're loading data1, and if so, convert it to data (and value-initialize only those new members in data). What would be the best way to do this? Here is what I've started:

if( loaded_data.size() == sizeof(data1) ) {
    // Old data format detected, upgrade to new structure
    data d = data(); // value-initialize everything
    // TODO: Assign 'data1' to 'data'
}

I've thought of putting a constructor in data to copy from a data1, but then that breaks the free value-initialization I get (I'd have to implement my own default constructor at that point). Should I use memcpy(), or is there a better more built-in way (perhaps some trick with copy semantics)?

like image 542
void.pointer Avatar asked Sep 18 '12 19:09

void.pointer


2 Answers

#include <cstdio>

struct A {
  int a;
};

struct B : A {
  int b;
};

int main()
{
  A a;
  a.a = 42; 
  B b;
  b.a = 0;
  b.b = 42;
  (A&)b = a;

  printf("%s\n", b.a == b.b?"it works":"nope");
}
like image 91
nanoo_linux Avatar answered Sep 30 '22 18:09

nanoo_linux


I don't like the design, I believe you should not inherit (this, for example, makes your type not be an aggregate), and that there are better approaches to serialization/deserialization than checking the size of the entity. That being said, after you force the initialization you can just assign. The implicitly declared copy constructor will be defined and the members will be copied:

data d = { 1, 2 };         // Can use this as it is an aggregate
data1 d1 = data1();
d1 = d;

Again, I would avoid this altogether...

If instead of inheritance you used composition, then you could use aggregate initialization and avoid the double initialization of the base in data1:

struct data1 {
  data base;
  int extra;
};
data d   = { 1, 2 };
data1 d1 = { d, 5 };   // or { d, 0 }

I've thought of putting a constructor in data to copy from a data1, but then that breaks the free value-initialization I get (I'd have to implement my own copy & default constructors at that point).

Not completely true. You will need to provide a default constructor, as the presence of the constructor taking data will inhibit the implicit declaration of a default constructor. But the copy constructor will still be implicitly generated.

like image 37
David Rodríguez - dribeas Avatar answered Sep 30 '22 18:09

David Rodríguez - dribeas