[PyQt] sip 4.19 and virtual inheritance
Denis Rivière
denis.riviere at cea.fr
Fri Feb 8 02:03:48 GMT 2019
This test example demonstrates a problem in handling virtual / diamond
inheritance in conjunction with multiple SIP modules uing sip 4.19.x.
We are working with the following class schema:
AClass BClass
\ / \
\ (virtual) (virtual)
\ / \
CClass DClass
\ /
\ /
EClass
The archive attached contains the code for these classes, and sip bindings
in 2 different situations: a single sip module, or 2 modules (to "simulate"
bindings to 2 different dependent libraries). We experience difficulties in
the second situation using sip 4.19.x when it works mainly well using sip
4.16.
There is an old-school Makefile to have the simplest possible build.
A. Single module:
In this case we bind all 4 classes in a single sip module, testmodule
(testmodule.sip).
In python we can run:
In [1]: import testmodule
In [2]: a = testmodule.get_a()
B: 0x7f282a26be78
A: 0x7f282a26be68
C: 0x7f282a26be68
D: 0x7f282a26be88
E: 0x7f282a26be60
ConvertToSubClassCode AClass: 0x7f282a26be68
I'm a CClass: 0x7f282a26be68
I'm a EClass: 0x7f282a26be60
In [3]: c = testmodule.get_c()
ConvertToSubClassCode AClass: 0x7f282a26be68
I'm a CClass: 0x7f282a26be68
I'm a EClass: 0x7f282a26be60
In [4]: a
Out[4]: <testmodule.EClass at 0x7f282b344478>
In [5]: c
Out[5]: <testmodule.EClass at 0x7f282b344478>
In [6]: d = testmodule.get_d()
In [7]: d
Out[7]: <testmodule.EClass at 0x7f282b344478>
In [8]: e = testmodule.get_e()
In [9]: e
Out[9]: <testmodule.EClass at 0x7f282b344478>
In [10]: a.print_this_a()
A: 0x7f282a26be68
In [11]: a.print_this_b()
B: 0x7f282a26be60
In [12]: a.print_this_c()
C: 0x7f282a26be68
In [13]: a.print_this_d()
D: 0x7f282a26be88
In [14]: a.print_this_e()
E: 0x7f282a26be60
Here all seems OK, all get_*() functioons return a EClass instance, and the
pointers to different "parts" of the class seem consistent.
B. Split in 2 modules: classes AClass, BClass, CClass are in module
testmodule1, and DClass, EClass are in module testmodule2 (testmodule1.sip
and testmodule2.sip).
Here we have to make %ConvertToSubClassCode a bit differently due to the
split in 2 modules.
B1. When built using sip 4.19 (tried with 4.19.3 from ubuntu 18.04 and
4.19.13 built from sources), we are running:
In [1]: import testmodule2
In [2]: a = testmodule2.get_a()
B: 0x7fd9ba00a898
A: 0x7fd9ba00a888
C: 0x7fd9ba00a888
D: 0x7fd9ba00a8a8
E: 0x7fd9ba00a880
ConvertToSubClassCode AClass: 0x7fd9ba00a888
I'm a CClass: 0x7fd9ba00a888
In [3]: a
Out[3]: <testmodule1.CClass at 0x7fd9bb0e2478>
In [4]: a.print_this_a()
A: 0x7fd9ba00a888
In [5]: a.print_this_b()
B: 0x7fd9ba00a898
In [6]: a.print_this_c()
C: 0x7fd9ba00a888
In [7]: c = testmodule2.get_c()
In [8]: c
Out[8]: <testmodule1.CClass at 0x7fd9bb0e2478>
In [9]: d = testmodule2.get_d()
ConvertToSubClassCode DClass: 0x7fd9ba00a8a8
Erreur de segmentation (core dumped)
Here we see that:
* AClass and CClass are not cast to EClass, the %ConvertToSubClassCode for
testmodule2 classes are not called
* get_d() segfaults. In the sip generated code siptestmodule2part0.cpp, in
the function sipSubClass_DClass(), we see that sipCpp is a BClass:
::BClass *sipCpp = reinterpret_cast< ::BClass *>(*sipCppRet);
but this is a reinterpret_cast to BClass* from a pointer actually being the
address of the DClass part, which is not the location of the virtual BClass
part, which explains the crash.
B2. If I remove all the ConvertToSubClassCode for all classes, then all
pointers seem OK, but we have no conversion to subtypes.
B3. If I set the ConvertToSubClassCode for class AClass only, then
testmodule2.get_e() segfaults:
n [1]: import testmodule2
In [2]: a = testmodule2.get_a()
B: 0x7f928d44b838
A: 0x7f928d44b828
C: 0x7f928d44b828
D: 0x7f928d44b848
E: 0x7f928d44b820
ConvertToSubClassCode AClass: 0x7f928d44b828
I'm a CClass: 0x7f928d44b828
In [3]: a
Out[3]: <testmodule1.CClass at 0x7f928dd24770>
In [4]: e = testmodule2.get_e()
ConvertToSubClassCode AClass: 0x7f928d44b820
Erreur de segmentation (core dumped)
Here the pointer value corresponding to EClass part is reinterpreted as a
AClass.
B4. Using sip 4.16 (as in Ubuntu 16.04) I don't have any of these crashes,
all pointers are consistent. When all ConvertToSubClassCode are here, we
have:
In [1]: import testmodule2
In [2]: a = testmodule2.get_a()
B: 0x7f6cc373e878
A: 0x7f6cc373e868
C: 0x7f6cc373e868
D: 0x7f6cc373e888
E: 0x7f6cc373e860
ConvertToSubClassCode AClass: 0x7f6cc373e868
I'm a CClass: 0x7f6cc373e868
In [3]: c = testmodule2.get_c()
ConvertToSubClassCode DClass: 0x7f6cc373e878
In [4]: d = testmodule2.get_d()
ConvertToSubClassCode DClass: 0x7f6cc373e878
In [5]: e = testmodule2.get_e()
ConvertToSubClassCode DClass: 0x7f6cc373e878
ConvertToSubClassCode EClass: 0x7f6cc373e878
I'm a EClass: 0x7f6cc373e860
ConvertToSubClassCode AClass: 0x7f6cc373e868
I'm a CClass: 0x7f6cc373e868
ConvertToSubClassCode BClass: 0x7f6cc373e878
I'm a CClass: 0x7f6cc373e868
In [6]: a
Out[6]: <testmodule1.CClass at 0x7f6cc8870510>
In [7]: c
Out[7]: <testmodule2.EClass at 0x7f6cc88708a0>
In [8]: d
Out[8]: <testmodule2.EClass at 0x7f6cc88708a0>
In [9]: e
Out[9]: <testmodule2.EClass at 0x7f6cc88708a0>
so here get_a() (returning AClass* in C++) is cast to CClass, and all other
get_* functions are cast to EClass. Otherwise all the conversion functions
get a pointer to BClass (except the one for AClass), which is consistent
with the type used in the function.
So here I just wonder why get_a() does not call conversion to more subtypes.
So we definitely see different behaviours using sip 4.16 and 4.19 when
subclass conversion code is used, and I wonder if I am doing something
wrong in these conversion codes, or if there is a problem in sip 4.19. The
B3 test above, at least, let me think that there is a problem in sip, but I
can't be sure of it.
In a different aspect, I also wonder: what is the expected type of sipCpp
in a ConvertToSubClassCode section ? The doc is not completely explicit
about it, since it both seems to suggest that it has the type of the class
where the %ConvertToSubClassCode section is, and also says that this
conversion code can be in any of the classes in the inheritance three,
which is in contradiction with this suggestion. Here we get a BClass in
some functions, and AClass in another. In a set of bindings of several of
our libraries, I think I have seen cases when the type of sipCpp is not the
same using sip 4.16 and 4.19 but I have not been able to reproduce it in a
simple example.
Thanks for any help, and sorry for the long message.
Denis
As I'm not sure the mailing list pyqt at riverbankcomputing.com accepts
attachments, here is a dump of the files
/* --- testmodule1.h --- */
#ifndef TESTMODULE1_H
#define TESTMODULE1_H
class AClass
{
public:
AClass( int x = 1 );
AClass( const AClass & x );
virtual ~AClass();
int a() const { return _number; }
void set_a( int x ) { _number = x; }
void print_this_a();
private:
int _number;
};
class BClass
{
public:
BClass();
virtual ~BClass();
void set_b( int x ) { _bnumber = x; }
int b() const { return _bnumber; }
void print_this_b();
private:
int _bnumber;
};
class CClass: public AClass, public virtual BClass
{
public:
CClass();
virtual ~CClass();
void print_this_c();
};
#endif
/* --- testmodule2.h --- */
#ifndef TESTMODULE2_H
#define TESTMODULE2_H
#include "testmodule1.h"
class DClass : public virtual BClass
{
public:
DClass();
virtual ~DClass();
void set_d( int x ) { _dnumber = x; }
int d() const { return _dnumber; }
void print_this_d();
private:
int _dnumber;
};
class EClass: public virtual CClass, public virtual DClass
{
public:
EClass();
virtual ~EClass();
void print_this_e();
};
AClass* get_a();
// BClass* get_b();
CClass* get_c();
DClass* get_d();
EClass* get_e();
#endif
/* --- testmodule1.cpp --- */
#include "testmodule1.h"
#include <iostream>
AClass::AClass( int x )
: _number( x )
{
std::cout << "A: " << this << std::endl;
}
AClass::AClass( const AClass & x )
: _number( x.a() )
{
}
AClass::~AClass()
{
}
void AClass::print_this_a()
{
std::cout << "A: " << this << std::endl;
}
BClass::BClass()
: _bnumber(3)
{
std::cout << "B: " << this << std::endl;
}
BClass::~BClass()
{
}
void BClass::print_this_b()
{
std::cout << "B: " << this << std::endl;
}
CClass::CClass() : AClass(), BClass()
{
std::cout << "C: " << this << std::endl;
}
CClass::~CClass()
{
}
void CClass::print_this_c()
{
std::cout << "C: " << this << std::endl;
}
/* --- testmodule2.cpp --- */
#include "testmodule2.h"
#include <iostream>
DClass::DClass()
: BClass(), _dnumber( 7 )
{
std::cout << "D: " << this << std::endl;
}
DClass::~DClass()
{
}
void DClass::print_this_d()
{
std::cout << "D: " << this << std::endl;
}
EClass::EClass()
: CClass(), DClass()
{
std::cout << "E: " << this << std::endl;
}
EClass::~EClass()
{
}
void EClass::print_this_e()
{
std::cout << "E: " << this << std::endl;
}
EClass & e_instance()
{
static EClass e;
return e;
}
AClass *get_a()
{
return &e_instance();
}
// BClass *get_b()
// {
// return static_cast<BClass *>( &f_instance() );
// }
CClass *get_c()
{
return &e_instance();
}
DClass *get_d()
{
return &e_instance();
}
EClass *get_e()
{
return &e_instance();
}
/* --- testmodule1.sip --- */
%Module testmodule1
class AClass
{
public:
%TypeHeaderCode
#include "testmodule1.h"
%End
%ConvertToSubClassCode
std::cout << "ConvertToSubClassCode AClass: " << sipCpp << std::endl;
// sipType = sipType_AClass;
if( dynamic_cast<CClass *>( sipCpp ) )
{
sipType = sipType_CClass;
*sipCppRet = dynamic_cast<CClass *>( sipCpp );
std::cout << "I'm a CClass: " << *sipCppRet << std::endl;
}
%End
AClass( int x );
AClass( const AClass & x );
virtual ~AClass();
int a() const;
void set_a( int x );
void print_this_a();
};
class BClass
{
public:
%TypeHeaderCode
#include "testmodule1.h"
%End
%ConvertToSubClassCode
std::cout << "ConvertToSubClassCode BClass: " << sipCpp << std::endl;
// sipType = sipType_BClass;
if( dynamic_cast<CClass *>( sipCpp ) )
{
sipType = sipType_CClass;
*sipCppRet = dynamic_cast<CClass *>( sipCpp );
std::cout << "I'm a CClass: " << *sipCppRet << std::endl;
}
%End
BClass();
virtual ~BClass();
int b() const;
void set_b( int x );
void print_this_b();
};
class CClass: AClass, BClass
{
public:
%TypeHeaderCode
#include "testmodule1.h"
%End
CClass();
virtual ~CClass();
void print_this_c();
};
%ModuleCode
#include "testmodule1.h"
#include <iostream>
%End
/* --- testmodule2.sip --- */
%Import testmodule1.sip
%Module testmodule2
class DClass : BClass
{
public:
%TypeHeaderCode
#include "testmodule2.h"
%End
%ConvertToSubClassCode
std::cout << "ConvertToSubClassCode DClass: " << sipCpp << std::endl;
// sipType = sipType_DClass;
if( dynamic_cast<DClass *>( sipCpp ) )
{
sipType = sipType_DClass;
*sipCppRet = dynamic_cast<DClass *>( sipCpp );
}
if( dynamic_cast<EClass *>( sipCpp ) )
{
sipType = sipType_EClass;
*sipCppRet = dynamic_cast<EClass *>( sipCpp );
}
%End
DClass();
virtual ~DClass();
int d() const;
void set_d( int x );
void print_this_d();
};
class EClass : CClass, DClass
{
public:
%TypeHeaderCode
#include "testmodule2.h"
%End
%ConvertToSubClassCode
std::cout << "ConvertToSubClassCode EClass: " << sipCpp << std::endl;
// sipType = sipType_EClass;
if( dynamic_cast<EClass *>( sipCpp ) )
{
sipType = sipType_EClass;
*sipCppRet = dynamic_cast<EClass *>( sipCpp );
std::cout << "I'm a EClass: " << *sipCppRet << std::endl;
}
else if( dynamic_cast<CClass *>( sipCpp ) )
{
sipType = sipType_CClass;
*sipCppRet = dynamic_cast<CClass *>( sipCpp );
std::cout << "I'm a CClass: " << *sipCppRet << std::endl;
}
else if( dynamic_cast<DClass *>( sipCpp ) )
{
sipType = sipType_DClass;
*sipCppRet = dynamic_cast<DClass *>( sipCpp );
std::cout << "I'm a DClass: " << *sipCppRet << std::endl;
}
%End
EClass();
virtual ~EClass();
void print_this_e();
};
AClass* get_a();
CClass* get_c();
DClass* get_d();
EClass* get_e();
%ModuleCode
#include "testmodule2.h"
#include <iostream>
%End
/* --- testmodule.sip --- */
%Module testmodule
class AClass
{
public:
%TypeHeaderCode
#include "testmodule1.h"
%End
%ConvertToSubClassCode
std::cout << "ConvertToSubClassCode AClass: " << sipCpp << std::endl;
// sipType = sipType_AClass;
if( dynamic_cast<CClass *>( sipCpp ) )
{
sipType = sipType_CClass;
*sipCppRet = dynamic_cast<CClass *>( sipCpp );
std::cout << "I'm a CClass: " << *sipCppRet << std::endl;
}
if( dynamic_cast<EClass *>( sipCpp ) )
{
sipType = sipType_EClass;
*sipCppRet = dynamic_cast<EClass *>( sipCpp );
std::cout << "I'm a EClass: " << *sipCppRet << std::endl;
}
%End
AClass( int x );
AClass( const AClass & x );
virtual ~AClass();
int a() const;
void set_a( int x );
void print_this_a();
};
class BClass
{
public:
%TypeHeaderCode
#include "testmodule1.h"
%End
%ConvertToSubClassCode
std::cout << "ConvertToSubClassCode BClass: " << sipCpp << std::endl;
// sipType = sipType_BClass;
if( dynamic_cast<CClass *>( sipCpp ) )
{
sipType = sipType_CClass;
*sipCppRet = dynamic_cast<CClass *>( sipCpp );
std::cout << "I'm a CClass: " << *sipCppRet << std::endl;
}
if( dynamic_cast<EClass *>( sipCpp ) )
{
sipType = sipType_EClass;
*sipCppRet = dynamic_cast<EClass *>( sipCpp );
std::cout << "I'm a EClass: " << *sipCppRet << std::endl;
}
%End
BClass();
virtual ~BClass();
int b() const;
void set_b( int x );
void print_this_b();
};
class CClass: AClass, BClass
{
public:
%TypeHeaderCode
#include "testmodule1.h"
%End
CClass();
virtual ~CClass();
void print_this_c();
};
class DClass : BClass
{
public:
%TypeHeaderCode
#include "testmodule2.h"
%End
%ConvertToSubClassCode
std::cout << "ConvertToSubClassCode EClass: " << sipCpp << std::endl;
// sipType = sipType_DClass;
if( dynamic_cast<DClass *>( sipCpp ) )
{
sipType = sipType_DClass;
*sipCppRet = dynamic_cast<DClass *>( sipCpp );
}
if( dynamic_cast<EClass *>( sipCpp ) )
{
sipType = sipType_EClass;
*sipCppRet = dynamic_cast<EClass *>( sipCpp );
}
%End
DClass();
virtual ~DClass();
int d() const;
void set_d( int x );
void print_this_d();
};
class EClass : CClass, DClass
{
public:
%TypeHeaderCode
#include "testmodule2.h"
%End
EClass();
virtual ~EClass();
void print_this_e();
};
AClass* get_a();
CClass* get_c();
DClass* get_d();
EClass* get_e();
%ModuleCode
#include "testmodule2.h"
#include <iostream>
%End
/* --- Makefile --- */
PYTHON_INCLUDE=/usr/include/python2.7
# SIP_INCLUDE=/usr/include/python2.7
SIP_EXE=sip
SIP_INCLUDE=/usr/include/python2.7
# SIP_INCLUDE=/casa/build/sip/include/python2.7
# SIP_EXE=/casa/build/sip/bin/sip
all: testmodule2.so testmodule.so
siptestmodule1part0.cpp: testmodule1.sip testmodule1.cpp testmodule1.h
$(SIP_EXE) -j1 -c . testmodule1.sip
siptestmodule2part0.cpp: testmodule2.sip testmodule2.cpp testmodule2.h
testmodule1.h
$(SIP_EXE) -j1 -c . testmodule2.sip
siptestmodulepart0.cpp: testmodule.sip testmodule1.cpp testmodule2.cpp
testmodule1.h testmodule2.h
$(SIP_EXE) -j1 -c . testmodule.sip
testmodule1.so: siptestmodule1part0.cpp
g++ -fPIC -shared -o testmodule1.so -I$(SIP_INCLUDE)
-I$(PYTHON_INCLUDE) siptestmodule1part0.cpp testmodule1.cpp
testmodule2.so: siptestmodule2part0.cpp testmodule1.so
g++ -fPIC -shared -o testmodule2.so -I$(SIP_INCLUDE)
-I$(PYTHON_INCLUDE) siptestmodule2part0.cpp testmodule2.cpp testmodule1.so
testmodule.so: siptestmodulepart0.cpp
g++ -fPIC -shared -o testmodule.so -I$(SIP_INCLUDE) -I$(PYTHON_INCLUDE)
siptestmodulepart0.cpp testmodule1.cpp testmodule2.cpp
clean:
rm testmodule.so testmodule1.so testmodule2.so siptestmodule1part0.cpp
sipAPItestmodule1.h sipAPItestmodule2.h siptestmodule2part0.cpp
siptestmodulepart0.cpp sipAPItestmodule.h
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://www.riverbankcomputing.com/pipermail/pyqt/attachments/20190208/13b77766/attachment-0001.html>
More information about the PyQt
mailing list