<div dir="ltr"><div dir="ltr">This test example demonstrates a problem in handling virtual / diamond inheritance in conjunction with multiple SIP modules uing sip 4.19.x.<br><br>We are working with the following class schema:<br><br><br>AClass       BClass<br>   \        /     \<br>    \   (virtual)  (virtual)<br>     \    /         \<br>      CClass        DClass<br>          \        /<br>           \      /<br>            EClass<br><br><br>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.<br>There is an old-school Makefile to have the simplest possible build.<br><br><br>A. Single module:<br>In this case we bind all 4 classes in a single sip module, testmodule (testmodule.sip).<br>In python we can run:<br><br>In [1]: import testmodule<br><br>In [2]: a = testmodule.get_a()<br>B: 0x7f282a26be78<br>A: 0x7f282a26be68<br>C: 0x7f282a26be68<br>D: 0x7f282a26be88<br>E: 0x7f282a26be60<br>ConvertToSubClassCode AClass: 0x7f282a26be68<br>I'm a CClass: 0x7f282a26be68<br>I'm a EClass: 0x7f282a26be60<br><br>In [3]: c = testmodule.get_c()<br>ConvertToSubClassCode AClass: 0x7f282a26be68<br>I'm a CClass: 0x7f282a26be68<br>I'm a EClass: 0x7f282a26be60<br><br>In [4]: a<br>Out[4]: <testmodule.EClass at 0x7f282b344478><br><br>In [5]: c<br>Out[5]: <testmodule.EClass at 0x7f282b344478><br><br>In [6]: d = testmodule.get_d()<br><br>In [7]: d<br>Out[7]: <testmodule.EClass at 0x7f282b344478><br><br>In [8]: e = testmodule.get_e()<br><br>In [9]: e<br>Out[9]: <testmodule.EClass at 0x7f282b344478><br><br>In [10]: a.print_this_a()<br>A: 0x7f282a26be68<br><br>In [11]: a.print_this_b()<br>B: 0x7f282a26be60<br><br>In [12]: a.print_this_c()<br>C: 0x7f282a26be68<br><br>In [13]: a.print_this_d()<br>D: 0x7f282a26be88<br><br>In [14]: a.print_this_e()<br>E: 0x7f282a26be60<br><br>Here all seems OK, all get_*() functioons return a EClass instance, and the pointers to different "parts" of the class seem consistent.<br><br><br>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).<br>Here we have to make %ConvertToSubClassCode a bit differently due to the split in 2 modules.<br><br><br>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:<br><br>In [1]: import testmodule2<br><br>In [2]: a = testmodule2.get_a()<br>B: 0x7fd9ba00a898<br>A: 0x7fd9ba00a888<br>C: 0x7fd9ba00a888<br>D: 0x7fd9ba00a8a8<br>E: 0x7fd9ba00a880<br>ConvertToSubClassCode AClass: 0x7fd9ba00a888<br>I'm a CClass: 0x7fd9ba00a888<br><br>In [3]: a<br>Out[3]: <testmodule1.CClass at 0x7fd9bb0e2478><br><br>In [4]: a.print_this_a()<br>A: 0x7fd9ba00a888<br><br>In [5]: a.print_this_b()<br>B: 0x7fd9ba00a898<br><br>In [6]: a.print_this_c()<br>C: 0x7fd9ba00a888<br><br>In [7]: c = testmodule2.get_c()<br><br>In [8]: c<br>Out[8]: <testmodule1.CClass at 0x7fd9bb0e2478><br><br>In [9]: d = testmodule2.get_d()<br>ConvertToSubClassCode DClass: 0x7fd9ba00a8a8<br>Erreur de segmentation (core dumped)<br><br>Here we see that:<br>* AClass and CClass are not cast to EClass, the %ConvertToSubClassCode for testmodule2 classes are not called<br>* get_d() segfaults. In the sip generated code siptestmodule2part0.cpp, in the function sipSubClass_DClass(), we see that sipCpp is a BClass:<br><br>     ::BClass *sipCpp = reinterpret_cast< ::BClass *>(*sipCppRet);<br><br>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.<br><br><br>B2. If I remove all the ConvertToSubClassCode for all classes, then all pointers seem OK, but we have no conversion to subtypes.<br><br><br>B3. If I set the ConvertToSubClassCode for class AClass only, then testmodule2.get_e() segfaults:<br><br>n [1]: import testmodule2<br><br>In [2]: a = testmodule2.get_a()<br>B: 0x7f928d44b838<br>A: 0x7f928d44b828<br>C: 0x7f928d44b828<br>D: 0x7f928d44b848<br>E: 0x7f928d44b820<br>ConvertToSubClassCode AClass: 0x7f928d44b828<br>I'm a CClass: 0x7f928d44b828<br><br>In [3]: a<br>Out[3]: <testmodule1.CClass at 0x7f928dd24770><br><br>In [4]: e = testmodule2.get_e()<br>ConvertToSubClassCode AClass: 0x7f928d44b820<br>Erreur de segmentation (core dumped)<br><br>Here the pointer value corresponding to EClass part is reinterpreted as a AClass.<br><br><br>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:<br><br>In [1]: import testmodule2<br><br>In [2]: a = testmodule2.get_a()<br>B: 0x7f6cc373e878<br>A: 0x7f6cc373e868<br>C: 0x7f6cc373e868<br>D: 0x7f6cc373e888<br>E: 0x7f6cc373e860<br>ConvertToSubClassCode AClass: 0x7f6cc373e868<br>I'm a CClass: 0x7f6cc373e868<br><br>In [3]: c = testmodule2.get_c()<br>ConvertToSubClassCode DClass: 0x7f6cc373e878<br><br>In [4]: d = testmodule2.get_d()<br>ConvertToSubClassCode DClass: 0x7f6cc373e878<br><br>In [5]: e = testmodule2.get_e()<br>ConvertToSubClassCode DClass: 0x7f6cc373e878<br>ConvertToSubClassCode EClass: 0x7f6cc373e878<br>I'm a EClass: 0x7f6cc373e860<br>ConvertToSubClassCode AClass: 0x7f6cc373e868<br>I'm a CClass: 0x7f6cc373e868<br>ConvertToSubClassCode BClass: 0x7f6cc373e878<br>I'm a CClass: 0x7f6cc373e868<br><br>In [6]: a<br>Out[6]: <testmodule1.CClass at 0x7f6cc8870510><br><br>In [7]: c<br>Out[7]: <testmodule2.EClass at 0x7f6cc88708a0><br><br>In [8]: d<br>Out[8]: <testmodule2.EClass at 0x7f6cc88708a0><br><br>In [9]: e<br>Out[9]: <testmodule2.EClass at 0x7f6cc88708a0><br><br>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.<br>So here I just wonder why get_a() does not call conversion to more subtypes.<br><br><br>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.<br><br>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.<br><br>Thanks for any help, and sorry for the long message.<br><br>Denis<br><br><br>As I'm not sure the mailing list <a href="mailto:pyqt@riverbankcomputing.com">pyqt@riverbankcomputing.com</a> accepts attachments, here is a dump of the files<br><br><br>/* --- testmodule1.h --- */<br><br>#ifndef TESTMODULE1_H<br>#define TESTMODULE1_H<br><br>class AClass<br>{<br>public:<br>  AClass( int x = 1 );<br>  AClass( const AClass & x );<br>  virtual ~AClass();<br><br>  int a() const { return _number; }<br>  void set_a( int x ) { _number = x; }<br><br>  void print_this_a();<br><br>private:<br>  int _number;<br>};<br><br><br>class BClass<br>{<br>public:<br>  BClass();<br>  virtual ~BClass();<br><br>  void set_b( int x ) { _bnumber = x; }<br>  int b() const { return _bnumber; }<br><br>  void print_this_b();<br><br>private:<br>  int _bnumber;<br>};<br><br><br>class CClass: public AClass, public virtual BClass<br>{<br>public:<br>  CClass();<br>  virtual ~CClass();<br><br>  void print_this_c();<br>};<br><br>#endif<br><br><br>/* --- testmodule2.h --- */<br><br>#ifndef TESTMODULE2_H<br>#define TESTMODULE2_H<br><br>#include "testmodule1.h"<br><br>class DClass : public virtual BClass<br>{<br>public:<br>  DClass();<br>  virtual ~DClass();<br><br>  void set_d( int x ) { _dnumber = x; }<br>  int d() const { return _dnumber; }<br><br>  void print_this_d();<br><br>private:<br>  int _dnumber;<br>};<br><br><br>class EClass: public virtual CClass, public virtual DClass<br>{<br>public:<br>  EClass();<br>  virtual ~EClass();<br><br>  void print_this_e();<br>};<br><br><br>AClass* get_a();<br>// BClass* get_b();<br>CClass* get_c();<br>DClass* get_d();<br>EClass* get_e();<br><br>#endif<br><br><br>/* --- testmodule1.cpp --- */<br><br><br>#include "testmodule1.h"<br>#include <iostream><br><br><br>AClass::AClass( int x )<br>  : _number( x )<br>{<br>    std::cout << "A: " << this << std::endl;<br>}<br><br>AClass::AClass( const AClass & x )<br>  : _number( x.a() )<br>{<br>}<br><br>AClass::~AClass()<br>{<br>}<br><br><br>void AClass::print_this_a()<br>{<br>  std::cout << "A: " << this << std::endl;<br>}<br><br><br>BClass::BClass()<br>    : _bnumber(3)<br>{<br>  std::cout << "B: " << this << std::endl;<br>}<br><br><br>BClass::~BClass()<br>{<br>}<br><br><br>void BClass::print_this_b()<br>{<br>  std::cout << "B: " << this << std::endl;<br>}<br><br><br>CClass::CClass() : AClass(), BClass()<br>{<br>  std::cout << "C: " << this << std::endl;<br>}<br><br><br>CClass::~CClass()<br>{<br>}<br><br><br>void CClass::print_this_c()<br>{<br>  std::cout << "C: " << this << std::endl;<br>}<br><br><br>/* --- testmodule2.cpp --- */<br><br><br>#include "testmodule2.h"<br>#include <iostream><br><br><br>DClass::DClass()<br>  : BClass(), _dnumber( 7 )<br>{<br>  std::cout << "D: " << this << std::endl;<br>}<br><br><br>DClass::~DClass()<br>{<br>}<br><br><br>void DClass::print_this_d()<br>{<br>  std::cout << "D: " << this << std::endl;<br>}<br><br><br>EClass::EClass()<br>  : CClass(), DClass()<br>{<br>  std::cout << "E: " << this << std::endl;<br>}<br><br><br>EClass::~EClass()<br>{<br>}<br><br><br>void EClass::print_this_e()<br>{<br>  std::cout << "E: " << this << std::endl;<br>}<br><br><br>EClass & e_instance()<br>{<br>  static EClass e;<br>  return e;<br>}<br><br><br>AClass *get_a()<br>{<br>  return &e_instance();<br>}<br><br><br>// BClass *get_b()<br>// {<br>//   return static_cast<BClass *>( &f_instance() );<br>// }<br><br><br>CClass *get_c()<br>{<br>  return &e_instance();<br>}<br><br><br>DClass *get_d()<br>{<br>  return &e_instance();<br>}<br><br><br>EClass *get_e()<br>{<br>  return &e_instance();<br>}<br><br><br>/* --- testmodule1.sip --- */<br><br><br>%Module testmodule1<br><br>class AClass<br>{<br>public:<br>%TypeHeaderCode<br>#include "testmodule1.h"<br>%End<br><br>%ConvertToSubClassCode<br>  std::cout << "ConvertToSubClassCode AClass: " << sipCpp << std::endl;<br>  // sipType = sipType_AClass;<br>  if( dynamic_cast<CClass *>( sipCpp ) )<br>  {<br>    sipType = sipType_CClass;<br>    *sipCppRet = dynamic_cast<CClass *>( sipCpp );<br>    std::cout << "I'm a CClass: " << *sipCppRet << std::endl;<br>  }<br>%End<br><br>  AClass( int x );<br>  AClass( const AClass & x );<br>  virtual ~AClass();<br>  int a() const;<br>  void set_a( int x );<br>  void print_this_a();<br>};<br><br><br>class BClass<br>{<br>public:<br>%TypeHeaderCode<br>#include "testmodule1.h"<br>%End<br><br>%ConvertToSubClassCode<br>  std::cout << "ConvertToSubClassCode BClass: " << sipCpp << std::endl;<br>  // sipType = sipType_BClass;<br>  if( dynamic_cast<CClass *>( sipCpp ) )<br>  {<br>    sipType = sipType_CClass;<br>    *sipCppRet = dynamic_cast<CClass *>( sipCpp );<br>    std::cout << "I'm a CClass: " << *sipCppRet << std::endl;<br>  }<br>%End<br><br>  BClass();<br>  virtual ~BClass();<br>  int b() const;<br>  void set_b( int x );<br>  void print_this_b();<br><br>};<br><br>class CClass: AClass, BClass<br>{<br>public:<br>%TypeHeaderCode<br>#include "testmodule1.h"<br>%End<br><br>  CClass();<br>  virtual ~CClass();<br>  void print_this_c();<br>};<br><br><br>%ModuleCode<br>#include "testmodule1.h"<br>#include <iostream><br>%End<br><br><br>/* --- testmodule2.sip --- */<br><br><br>%Import testmodule1.sip<br><br>%Module testmodule2<br><br>class DClass : BClass<br>{<br>public:<br>%TypeHeaderCode<br>#include "testmodule2.h"<br>%End<br><br>%ConvertToSubClassCode<br>  std::cout << "ConvertToSubClassCode DClass: " << sipCpp << std::endl;<br>  // sipType = sipType_DClass;<br>  if( dynamic_cast<DClass *>( sipCpp ) )<br>  {<br>    sipType = sipType_DClass;<br>    *sipCppRet = dynamic_cast<DClass *>( sipCpp );<br>  }<br>  if( dynamic_cast<EClass *>( sipCpp ) )<br>  {<br>    sipType = sipType_EClass;<br>    *sipCppRet = dynamic_cast<EClass *>( sipCpp );<br>  }<br>%End<br><br>  DClass();<br>  virtual ~DClass();<br>  int d() const;<br>  void set_d( int x );<br>  void print_this_d();<br>};<br><br><br>class EClass : CClass, DClass<br>{<br>public:<br>%TypeHeaderCode<br>#include "testmodule2.h"<br>%End<br><br>%ConvertToSubClassCode<br>  std::cout << "ConvertToSubClassCode EClass: " << sipCpp << std::endl;<br>  // sipType = sipType_EClass;<br>  if( dynamic_cast<EClass *>( sipCpp ) )<br>  {<br>    sipType = sipType_EClass;<br>    *sipCppRet = dynamic_cast<EClass *>( sipCpp );<br>    std::cout << "I'm a EClass: " << *sipCppRet << std::endl;<br>  }<br>  else if( dynamic_cast<CClass *>( sipCpp ) )<br>  {<br>    sipType = sipType_CClass;<br>    *sipCppRet = dynamic_cast<CClass *>( sipCpp );<br>    std::cout << "I'm a CClass: " << *sipCppRet << std::endl;<br>  }<br>  else if( dynamic_cast<DClass *>( sipCpp ) )<br>  {<br>    sipType = sipType_DClass;<br>    *sipCppRet = dynamic_cast<DClass *>( sipCpp );<br>    std::cout << "I'm a DClass: " << *sipCppRet << std::endl;<br>  }<br>%End<br><br>  EClass();<br>  virtual ~EClass();<br>  void print_this_e();<br><br>};<br><br><br>AClass* get_a();<br>CClass* get_c();<br>DClass* get_d();<br>EClass* get_e();<br><br>%ModuleCode<br>#include "testmodule2.h"<br>#include <iostream><br>%End<br><br><br>/* --- testmodule.sip --- */<br><br><br>%Module testmodule<br><br>class AClass<br>{<br>public:<br>%TypeHeaderCode<br>#include "testmodule1.h"<br>%End<br><br>%ConvertToSubClassCode<br>  std::cout << "ConvertToSubClassCode AClass: " << sipCpp << std::endl;<br>  // sipType = sipType_AClass;<br>  if( dynamic_cast<CClass *>( sipCpp ) )<br>  {<br>    sipType = sipType_CClass;<br>    *sipCppRet = dynamic_cast<CClass *>( sipCpp );<br>    std::cout << "I'm a CClass: " << *sipCppRet << std::endl;<br>  }<br>  if( dynamic_cast<EClass *>( sipCpp ) )<br>  {<br>    sipType = sipType_EClass;<br>    *sipCppRet = dynamic_cast<EClass *>( sipCpp );<br>    std::cout << "I'm a EClass: " << *sipCppRet << std::endl;<br>  }<br>%End<br><br>  AClass( int x );<br>  AClass( const AClass & x );<br>  virtual ~AClass();<br>  int a() const;<br>  void set_a( int x );<br>  void print_this_a();<br>};<br><br><br>class BClass<br>{<br>public:<br>%TypeHeaderCode<br>#include "testmodule1.h"<br>%End<br><br>%ConvertToSubClassCode<br>  std::cout << "ConvertToSubClassCode BClass: " << sipCpp << std::endl;<br>  // sipType = sipType_BClass;<br>  if( dynamic_cast<CClass *>( sipCpp ) )<br>  {<br>    sipType = sipType_CClass;<br>    *sipCppRet = dynamic_cast<CClass *>( sipCpp );<br>    std::cout << "I'm a CClass: " << *sipCppRet << std::endl;<br>  }<br>  if( dynamic_cast<EClass *>( sipCpp ) )<br>  {<br>    sipType = sipType_EClass;<br>    *sipCppRet = dynamic_cast<EClass *>( sipCpp );<br>    std::cout << "I'm a EClass: " << *sipCppRet << std::endl;<br>  }<br>%End<br><br><br>  BClass();<br>  virtual ~BClass();<br>  int b() const;<br>  void set_b( int x );<br>  void print_this_b();<br><br>};<br><br><br>class CClass: AClass, BClass<br>{<br>public:<br>%TypeHeaderCode<br>#include "testmodule1.h"<br>%End<br><br>  CClass();<br>  virtual ~CClass();<br>  void print_this_c();<br>};<br><br><br>class DClass : BClass<br>{<br>public:<br>%TypeHeaderCode<br>#include "testmodule2.h"<br>%End<br><br>%ConvertToSubClassCode<br>  std::cout << "ConvertToSubClassCode EClass: " << sipCpp << std::endl;<br>  // sipType = sipType_DClass;<br>  if( dynamic_cast<DClass *>( sipCpp ) )<br>  {<br>    sipType = sipType_DClass;<br>    *sipCppRet = dynamic_cast<DClass *>( sipCpp );<br>  }<br>  if( dynamic_cast<EClass *>( sipCpp ) )<br>  {<br>    sipType = sipType_EClass;<br>    *sipCppRet = dynamic_cast<EClass *>( sipCpp );<br>  }<br>%End<br><br>  DClass();<br>  virtual ~DClass();<br>  int d() const;<br>  void set_d( int x );<br>  void print_this_d();<br>};<br><br><br>class EClass : CClass, DClass<br>{<br>public:<br>%TypeHeaderCode<br>#include "testmodule2.h"<br>%End<br><br>  EClass();<br>  virtual ~EClass();<br>  void print_this_e();<br><br>};<br><br><br>AClass* get_a();<br>CClass* get_c();<br>DClass* get_d();<br>EClass* get_e();<br><br><br>%ModuleCode<br>#include "testmodule2.h"<br>#include <iostream><br>%End<br><br><br>/* --- Makefile --- */<br><br>PYTHON_INCLUDE=/usr/include/python2.7<br><br># SIP_INCLUDE=/usr/include/python2.7<br>SIP_EXE=sip<br><br>SIP_INCLUDE=/usr/include/python2.7<br># SIP_INCLUDE=/casa/build/sip/include/python2.7<br># SIP_EXE=/casa/build/sip/bin/sip<br><br>all:    testmodule2.so testmodule.so<br><br>siptestmodule1part0.cpp: testmodule1.sip testmodule1.cpp testmodule1.h<br>    $(SIP_EXE) -j1 -c . testmodule1.sip<br><br>siptestmodule2part0.cpp: testmodule2.sip testmodule2.cpp testmodule2.h testmodule1.h<br>    $(SIP_EXE) -j1 -c . testmodule2.sip<br><br>siptestmodulepart0.cpp: testmodule.sip testmodule1.cpp testmodule2.cpp testmodule1.h testmodule2.h<br>    $(SIP_EXE) -j1 -c . testmodule.sip<br><br>testmodule1.so:    siptestmodule1part0.cpp<br>    g++ -fPIC -shared -o testmodule1.so -I$(SIP_INCLUDE) -I$(PYTHON_INCLUDE) siptestmodule1part0.cpp testmodule1.cpp<br><br>testmodule2.so:    siptestmodule2part0.cpp testmodule1.so<br>    g++ -fPIC -shared -o testmodule2.so -I$(SIP_INCLUDE) -I$(PYTHON_INCLUDE) siptestmodule2part0.cpp testmodule2.cpp testmodule1.so<br><br>testmodule.so:    siptestmodulepart0.cpp<br>    g++ -fPIC -shared -o testmodule.so -I$(SIP_INCLUDE) -I$(PYTHON_INCLUDE) siptestmodulepart0.cpp testmodule1.cpp testmodule2.cpp<br><br>clean:<br>    rm testmodule.so testmodule1.so testmodule2.so siptestmodule1part0.cpp sipAPItestmodule1.h sipAPItestmodule2.h siptestmodule2part0.cpp siptestmodulepart0.cpp sipAPItestmodule.h<br><br></div></div>