0

I'm trying to design class hierarchy for my physical simulations. I really want to share variables (e.g. Vec3d pos, Quat4d rot ) between classes which depend on each other by more complex graph of multiple-inheritance. I extracted simple example what is the problem (full code is here):

// ==== prefabricates for classes ... I was thinking this will solve the problem
class _pos { public: Vec3d pos;};
class _rot { public: Quat4d rot;};

// === classes 

class KinematicBody : public _pos, public _rot{
    // pos, rot   ... inherited
    void toGLmat( float * mat ); // make Opengl 4x4 matrix for rendering
};

class PointBody : public _pos, public Updateable {
    public:
    // pos    ...  inherited
    Vec3d   vel,force;
    double  mass;

    inline  void update_(double dt){ vel.add_mul(force, dt); pos.add_mul(vel,(dt/mass)); };  // can be called as obj->PointBody::update_(dt);
    virtual void update (double t, double dt ){ update_(dt); }; // implements Updateable
};

// !!!! HERE IS THE PROBLEM
class RigidBody : public PointBody, public KinematicBody {  // WARRNING : both PointBody and KinematicBody inheriate "pos"
   public:
   // pos,vel,force,mass,rot    ...  inherited
   Mat3d  momentOfInertia;
   Vec3d  torque;
   Vec3d  angular_velocity;
};

I tried to run following test to make sure that KinematicBody::pos, PointBody::pos, RigidBody::pos all share the same piece of memory (full code here):

RigidBody        rb;
KinematicBody  * kb = &rb;
PointBody      * pb = &rb;

rb. pos.set(1.0); printf( "%f %f %f\n", rb.pos.x, kb->pos.x, pb->pos.x );
kb->pos.set(2.0); printf( "%f %f %f\n", rb.pos.x, kb->pos.x, pb->pos.x );
pb->pos.set(3.0); printf( "%f %f %f\n", rb.pos.x, kb->pos.x, pb->pos.x );

I expected result like

1.0000   1.0000   1.0000
2.0000   2.0000   2.0000
3.0000   3.0000   3.0000 

But instead I got following error:

error: request for member ‘pos’ is ambiguous  
rb. pos.set(1.0); printf( "%f %f %f\n", rb.pos.x, kb->pos.x, pb->pos.x );

This error can be probably resolved by making

class RigidBody : public PointBody, public _rot {

instead of

class RigidBody : public PointBody, public KinematicBody {

but than I cannot inherit void toGLmat( float * mat ); from KinematicBody ... and in general it makes composition of classes very unconveneient

Is there any way how to achieve this sharing? I was thinking that by making prefabricates like _pos and _rot I can achieve this, but apparently not.

Prokop Hapala
  • 2,424
  • 2
  • 30
  • 59
  • Have you considered using `public virtual _pos, public virtual _rot`? There are multiple SO questions and answers regarding the _diamond problem_ in C++. – Torbjörn Feb 19 '17 at 15:40
  • 3
    You should read about the difference between an "is a" and a "has a" relationship. Public inheritance means "is a", but a `KinematicBody` is not a position or a rotation, it just "has a" position and rotation. "has a" is expressed via members. Once you apply that throughout your code it will become much clearer and easier to use. – nwp Feb 19 '17 at 15:47
  • @Torbjörn `virtual` should be applied here: `class RigidBody : public PointBody, public KinematicBody { ...` This is where th _diamond_ is introduced. – πάντα ῥεῖ Feb 19 '17 at 15:47
  • @Torbjörn thanks, I resolved the issue by this modification: `class KinematicBody : public virtual _pos, public _rot{` and `class PointBody : public virtual _pos, public Updateable {` but I don't really know what `virtual` means in this context (I know only virtual functions) and if there is any performance or memory overhead (is the memory really shared)? – Prokop Hapala Feb 19 '17 at 15:51
  • @ProkopHapala Read about it here: http://stackoverflow.com/questions/21558/in-c-what-is-a-virtual-base-class – πάντα ῥεῖ Feb 19 '17 at 16:24
  • @ProkopHapala Inheritance is a major design flaw in your example. – πάντα ῥεῖ Feb 19 '17 at 16:34
  • @πάντα ῥεῖ thanks, I read it before, but it is mostly about functions (almost everything I read about diamond problem is about functions), but it does not answer the question of overhead. So I did some performance tests now ... acesss to `_pos::pos.x` costs 3-4 CPU ticks, `RigidBody::pos.x` costs 35 ticks without virtual inheritance and 44 ticks with virtual inheritance (I guess the biggest issue is memory latency since sizeof(RigidBody) = 240 bytes ) ... all tested by 100 traversing array of 1000000 items. So the slow-down is perhaps not so critical for large object like this. – Prokop Hapala Feb 19 '17 at 16:41
  • anyway I do not understand why there must be some performance overhead :-( . I would think that deduction of memory address of `pos` can be fully determined in compile time whether it is shared (virtual) or not. – Prokop Hapala Feb 19 '17 at 16:46
  • There's a performance overhead because `RigidBody` requires the ability to share a single base `_pos` between `KinematicBody` and `PointBody`, instead of them both having separate `_pos`es. Therefore, one or both of the classes must refer to this `_pos` by pointer/reference, which imposes a dereferencing cost. As this `_pos` must be valid before either `KineticBody` or `PointBody` are constructed, both classes will store a ptr/ref to it so that the compiler can more easily have the most-derived class (in this case, `RigidBody`) construct it. Therefore, each access requires a dereference. – Justin Time - Reinstate Monica Feb 19 '17 at 19:16
  • That said, I don't believe your inheritance hierarchy is set up properly. Neither body is a position or a rotation; instead, one is a body with a position, and one is a body with a position and a rotation. Therefore, I would suggest `class Body { public: Vec3d pos; };` as the base "body" class, and `class RotatableBody : public virtual Body { public: Quat4d rot; };` as the "rotatable body" class. This lets `KinematicBody` inherit from `RotatableBody` and `PointBody` virtually inherit from `Body`, [as so](http://coliru.stacked-crooked.com/a/191b02eddd52ed3c). – Justin Time - Reinstate Monica Feb 19 '17 at 19:35
  • @Justine: ad 2 - How I call those classes does (`_pos`) not matter, I don't want to use them anywhere (that is why I call them *prefabricates* and why I put `_` in front). The only reason why they exist is that I wanted to use them as components from which I can assemble real classes by multiple inheritance. Purpose of `class _pos` is just to add `Vec3d pos` into whatever inheriate from it ... so the name is suitable – Prokop Hapala Feb 20 '17 at 11:20
  • @Justine: ad 2 runtime cost - I still don't see why any pointer/reference and dereferencing is needed. For compiler `RigidBody::pos` means nothing else than memory location at [address of some RigidBody instance]+[offset of pos]. Without `virtual` there are two different offsets for `KineticBody::pos` and `PointBody::pos`, but I don't see why compiler cannot just translate this `virtual` inheritance such that it makes both offsets the same. It is like with templates - all is static and can be fully determined in compile time. Virtual functions with `vtable` is completely different thing. – Prokop Hapala Feb 20 '17 at 11:29

0 Answers0