wrong objects addresses using virtual and diamond inheritance

Denis Rivière denis.riviere at cea.fr
Wed Dec 15 10:35:36 GMT 2021


Hi,

I experience a new virtual class address issue using sip 4.19.21-4.19.25.
I have a rather complex C++ inheritance tree, using virtual inheritance and
diamond inheritance, looking like this:

    A     B
    \\   //
     \\ //
 D     C
  \  // \\
   \//   \\
    E     F
    \\   //
     \\ //
       G

(double lines mean virtual inheritance)

In recent Sip bindings the addresses of the different parts of a G object
appear to be wrong, thus objects are unusable and cause crashes.

Here is a simple C++ implementation code:

// header declarations:

#include <iostream> // not used here but simplifies the sip MethodCodes
later

class A
{
public:
  A();
  virtual ~A();
  int a;
};


class B
{
public:
  B();
  virtual ~B();
  int b;
};

class C : public virtual A, public virtual B
{
public:
  C(): c(3) {}
  virtual ~C() {}

  int c;
};

class D
{
public:
  D();
  virtual ~D();
  int d;
};

class E: public D, public virtual C
{
public:
  E();
  virtual ~E();
};

class F : public virtual C
{
public:
  F();
  virtual ~F();
};

class G : public virtual E, public virtual F
{
public:
  G();
  virtual ~G();
  int g;
};


// C++ implementation

A::A(): a( 1 ) {}
A::~A() {}

B::B(): b( 2 ) {}
B::~B() {}

C::C() : A(), B(), c( 3 ) {}
C::~C() {}

D::D() : d( 4 ){}
D::~D() {}

E::E() : D(), C() {}
E::~E() {}

F::F() : C() {}
F::~F() {}

G::G() : E(), F(), g( 7 ) {}
G::~G() {}


// SIP bindings file


class A
{
%TypeHeaderCode
#include "test.h"
%End

public:
  A();
  virtual ~A();
  void __a__();
%MethodCode
  std::cout << "A: " << sipCpp << std::endl;
%End

  int a;
};


class B
{
%TypeHeaderCode
#include "test.h"
%End

public:
  B();
  virtual ~B();
  void __b__();
%MethodCode
  std::cout << "B: " << sipCpp << std::endl;
%End

  int b;
};


class C : A, B
{
%TypeHeaderCode
#include "test.h"
%End

public:
  C();
  virtual ~C();
  void __c__();
%MethodCode
  std::cout << "C: " << sipCpp << std::endl;
%End

  int c;
};


class D
{
%TypeHeaderCode
#include "test.h"
%End

public:
  D();
  virtual ~D();
  void __d__();
%MethodCode
  std::cout << "D: " << sipCpp << std::endl;
%End
  int d;
};

class E : D, C
{
%TypeHeaderCode
#include "test.h"
%End

public:
  E();
  virtual ~E();
  void __e__();
%MethodCode
  std::cout << "E: " << sipCpp << std::endl;
%End
};


class F : C
{
%TypeHeaderCode
#include "test.h"
%End

public:
  F();
  virtual ~F();
  void __f__();
%MethodCode
  std::cout << "F: " << sipCpp << std::endl;
%End
};


class G : E, F
{
%TypeHeaderCode
#include "test.h"
%End

public:
  G();
  virtual ~G();
  void __g__();
%MethodCode
  std::cout << "G: " << sipCpp << std::endl;
%End

  int g;
};

// end code

(the source files are also attached to the message)

Thus each class X in python has a __X__() method which prints its C++
address, and a number of classes have an int data to check the value.

Now if we run this python test:

import test

def print_all(x, methods='abcdefg'):
    for att in methods:
        m = getattr(x, '__%s__' % att, None)
        if m:
            m()
    print()
    for att in methods:
        m = getattr(x, '%s' % att, None)
        if m:
            print('%s:' % att, m)

g = test.G()

print_all(g)


This test outputs, using sip 4.19.21 to 4.19.25:

A: 0x36fd720
B: 0x36fd720
C: 0x36fd720
D: 0x36fd738
E: 0x36fd738
F: 0x36fd720
G: 0x36fd720

a: 7
b: 7
c: 7
d: 4
g: 7

As you see object parts A, B, C, F and G all have the same address in the
bindings, whereas they obviously do not have the same address in C++ (A, B
and C are not related). Moreover as the offsets of virtual tables and data
in the object are wrong, the int values are wrong for most of them (a, b
and c all seem to contain 7 whereas they should contain 1, 2 and 3).

Using sip 4.19.15, the output of the same code was:

A: 0x1f72ed8
B: 0x1f72ee8
C: 0x1f72ec8
D: 0x1f72eb8
E: 0x1f72eb8
F: 0x1f72ea0
G: 0x1f72ea0

a: 1
b: 2
c: 3
d: 4
g: 7

which is totally correct.

In the sip code, if I remove the inheritance from C in F (just in the sip
code, not changing anything in the C++ part), then things become right for
a G object (using sip 4.19.25 again):

A: 0x31b5798
B: 0x31b57a8
C: 0x31b5788
D: 0x31b5778
E: 0x31b5778
F: 0x31b5760
G: 0x31b5760

a: 1
b: 2
c: 3
d: 4
g: 7

But obviously an instance of F will now be missing its A, B and C parts.

After trying all this I remember that we have had possibly similar problems
in the first releases of sip 4.19:
https://www.riverbankcomputing.com/pipermail/pyqt/2019-February/041313.html
I haven't tested with the same example yet, so I'm not sure it's the same
problem which has re-appeared.

Thanks for your help,
Denis
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://www.riverbankcomputing.com/pipermail/pyqt/attachments/20211215/b380433c/attachment.htm>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: classes.sip
Type: application/octet-stream
Size: 1243 bytes
Desc: not available
URL: <https://www.riverbankcomputing.com/pipermail/pyqt/attachments/20211215/b380433c/attachment.obj>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: classes.h
Type: text/x-chdr
Size: 597 bytes
Desc: not available
URL: <https://www.riverbankcomputing.com/pipermail/pyqt/attachments/20211215/b380433c/attachment.h>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: classes.cpp
Type: text/x-c++src
Size: 256 bytes
Desc: not available
URL: <https://www.riverbankcomputing.com/pipermail/pyqt/attachments/20211215/b380433c/attachment.cpp>


More information about the PyQt mailing list