[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