2

I'm working on providing a set of serializing methods that need to be structure aware. I therefore built a class template that provides those methods to all class inheriting from it.

Here is what I have right now:

template<typename T>
class serializable
{
    protected:
        serializable() = default;
    public:
        size_t size_of() { return sizeof(T); }
        void   dump(void* data) { std::memcpy(data, this, serializable<T>::size_of()); }
        void   load(void* data) { std::memcpy(this, data, serializable<T>::size_of()); }
        virtual void serialize()
        {
            std::cout << "Default serialize : size " << serializable<T>::size_of() << std::endl;
        }
};

struct point : public serializable<point>
{
    float p[3];
};
struct particle : public point, public serializable<particle>
{
    float v[3];
};

int main()
{
    particle p;
    p.serialize();

    return 0;
}

The issue is that when calling p.serialize, the compiler issues an ambiguous overload error between serializable<point>::serialize() and serializable<particle>::serialize().

How can I solve this? Is there any way of overriding the inheritance from serializable<point> to only consider serializable<particle>? Should I consider another approach?

Cœur
  • 37,241
  • 25
  • 195
  • 267
Amxx
  • 3,020
  • 2
  • 24
  • 45
  • Inheritance doesn't seem appropriate here. A particle isn't a point. – Mike Seymour Apr 29 '15 at 18:41
  • I tried doing `struct particle : public serializable{ point p; point v; };` but that lead to a structure if size 56 and not 48 ... and I just can't accept data not to be aligned – Amxx Apr 29 '15 at 18:43

2 Answers2

5

You could bring in the one serialize you want:

struct particle : public point, public serializable<particle>
{
    using serializable<particle>::serialize;
    float v[3];
};

But likely you just want to use composition:

struct particle : public serializable<particle>
{
    point p;
    float v[3];
};
Barry
  • 286,269
  • 29
  • 621
  • 977
  • I think composition is the best approach, actually. You could add a conversion operator for `particle` to return `p`. – jxh Apr 29 '15 at 18:42
  • @jxh I agree. That's why I said "likely." Not sure about the conversion operator though... – Barry Apr 29 '15 at 18:44
  • Just a thought. It would cover passing a `particle` to a function wanting a `point` argument, for example. – jxh Apr 29 '15 at 18:47
3

How can I solve this ?

Override serialize() in particle. You'll need to serialize the point data and particle's own data anyway. The default implementation of serialize from the base class won't be able to do the right thing for particle.

You also need to override serialize() in point for the same reason.

Also, I think making point a base class of particle doesn't seem right. A particle is not a point. It has a location and velocity. An aggregation seems like the better approach to me.

struct point : public serializable<point>
{
   void serialize()
   {
      // Serialize p
      // ...
   }

   float p[3];
};

struct particle : public serializable<particle>
{
   void serialize()
   {
      // Serialize location.
      p.serialize();

      // Serialize velocity.
      // ...
   }

   point p; 
   float v[3];
};

Problem in serializable

Judging by your example usage of serializable, the lines:

    void   dump(void* data) { std::memcpy(data, this, serializable<T>::size_of()); }
    void   load(void* data) { std::memcpy(this, data, serializable<T>::size_of()); }

will lead to undefined behavior. The derived classes are not TriviallyCopyable. Using std::memcpy on such types will lead to problems in no time.

See the answers to a question I had posted just last week.

Community
  • 1
  • 1
R Sahu
  • 204,454
  • 14
  • 159
  • 270
  • I plan on overiding serialize for complex structure which includes pointers of stl container. However the point of the default serialize is to dump the data as a `char[size_of()]` using `dump` – Amxx Apr 29 '15 at 18:38
  • 1
    @Amxx, That would be a problem since these classes are not PODs. You'll have to rethink your strategy. `sizeof(T)` will include not only the data but also the pointer to the virtual table. – R Sahu Apr 29 '15 at 18:42
  • After reading your update I'm now conserded about what is and isn't trivially copyable. My idea was that is the structures contains std::string / std::vector / other similar synamic object, then one would have to overide the serializing function. However I just don't get how it could be unsafe to use `std::memcpy` on a structure like `point` which is juste an array of floats ... also I tried to run `std::is_trivially_copyable` but the compilers says it's not a member of std (gcc 4.9.2 / clang 3.6.0) – Amxx Apr 29 '15 at 18:59
  • @Amxx, http://en.cppreference.com/w/cpp/concept/TriviallyCopyable describes when an object is trivially copyable and when it is not. – R Sahu Apr 29 '15 at 19:03
  • You'll need to use the compiler flag `-std=c++11` to get the C++11 features. – R Sahu Apr 29 '15 at 19:04
  • @R Sahu: I of course have this flag active (in fact I even have `-std=c++14`) – Amxx Apr 29 '15 at 19:06
  • 2
    Not yet implemented :( https://gcc.gnu.org/onlinedocs/gcc-4.8.3/libstdc++/manual/manual/status.html#status.iso.2011 – Amxx Apr 29 '15 at 19:07
  • @Amxx, that's unfortunate. gotta live with the limitations of your compiler. – R Sahu Apr 29 '15 at 19:10