[PyQt] wrapped C/C++ object of type has been deleted - yet it hasn't!

Phil Thompson phil at riverbankcomputing.com
Tue Jul 25 12:57:21 BST 2017


On 25 Jul 2017, at 8:04 am, Nyall Dawson <nyall.dawson at gmail.com> wrote:
> 
> On 14 July 2017 at 11:54, Nyall Dawson <nyall.dawson at gmail.com> wrote:
>> Hi list,
>> 
>> I've recently encountered an odd issue which has me stumped. I have a
>> c++ method which returns a new object (QgsProcessingAlgorithm* -
>> (which is not a QObject)), and the method is marked with a /Factory/
>> annotation.
>> 
>> Usually this approach works fine. But in this case the returned object
>> behaves oddly, and frequently encounters the "wrapped C/C++ object of
>> type has been deleted" error. Yet - it hasn't been deleted. I can put
>> a breakpoint in the c++ destructor for the class and it's not being
>> triggered. It's just that somehow the python wrapper has been mislead
>> into thinking this has occurred.
>> 
>> I can scatter various checks using sip.isdeleted around the code and
>> it all looks ok at first. Right up until a few references to this
>> object have been made in various other python objects. And then
>> boom... I suddenly get the "wrapped object has been deleted" error
>> from one of these references (without the c++ destructor being called)
>> 
>> I'm at a loss how to debug this, or what could be happening here. Any
>> hints on what could be causing this? Any likely suspects?
> 
> I'm still hitting this issue, but after doing a bunch of digging I
> think I've narrowed down my issue.
> 
> If I have a  c++ class:
> 
> class MyClass
> {
> 
>  public:
> 
>    MyClass() {}
> 
>    MyClass *create() const
>    {
>          MyClass * newInstance = createInstance();
>          // do some common stuff to newInstance here
>          return newInstance;
>     }
> 
>  protected:
> 
>    virtual MyClass *createInstance() const = 0;
> 
> };
> 
> With the accompanying sip definition:
> 
> 
> class MyClass
> {
> 
> %TypeHeaderCode
> #include "myclass.h"
> %End
>  public:
> 
>    MyClass();
> 
>    MyClass *create() const /Factory/;
> 
>  protected:
> 
>    virtual MyClasss *createInstance() const = 0 /Factory/;
> 
> };
> 
> 
> (i.e. subclasses must implement createInstance to return a new copy of
> the class, which is called by create() to do some common setup tasks
> on the instance before returning it)
> 
> This works OK if the MyClass subclass is implemented in c++ - python
> code can call create() and everything looks good. The returned object
> is owned by Python.
> 
> But if I make a MyClass subclass in Python:
> 
> class x(MyClass):
> 
>    def __init__(self):
>        MyClass.__init__(self)
> 
>    def createInstance(self):
>        new = x()
>        return new
> 
> Then things start to go strange. Calling create on an instance of this
> python subclass returns <MyClass object at 0x...>, instead of the
> expected <x object at 0x....>.
> 
> I suspect I'm misusing the /Factory/ annotation for createInstance()
> here. But I can't see what the correct annotation should be. Any tips?

/Factory/ is used when the instance returned is guaranteed to be new to Python. In this case it isn't because it has already been seen when being returned by createInstance() - in which case the /Factory/ on create() should be removed. However for a different sub-class implemented in C++ then it would be the first time it was seen by Python so the /Factory/ on create() would be correct.

You might try using /TransferBack/ on create() instead - that might be the best compromise.

Phil


More information about the PyQt mailing list