[PyQt] sip: add new annotation for specifying ownership of results of virtual methods

Phil Thompson phil at riverbankcomputing.com
Mon Sep 24 12:35:11 BST 2012


On Fri, 21 Sep 2012 21:17:31 +0200, Mathias.Born at gmx.de wrote:
> On 21.09.2012, 16:28:20 Phil Thompson wrote:
>> On Mon, 3 Sep 2012 22:27:41 +0200, Mathias.Born at gmx.de wrote:
>>> Hi Phil,
>>> 
>>> I've come across a case which I think deserves its own function
>> annotation.
>>> This is the proplem:
>>> 
>>> I'm embedding Python code into a C++ app. There are two wrapped
classes:
>>> 
>>> class Netlister
>>> {
>>>       virtual ~Netlister();
>>>       ...
>>> };
>>> 
>>> and
>>> 
>>> class Project
>>> {
>>>       virtual ~Project();
>>>       ...
>>>       virtual NetLister* netLister(const std::string& netListText)
>> const;
>>> };
>>> 
>>> In Python, I have ("ltse_app" is the C++ app exposed as module)
>>> 
>>> 
>>> class LTspiceNetLister(ltse_app.NetLister):
>>>       ...
>>> 
>>> and
>>> 
>>> class Project(ltse_app.Project):
>>>       ...
>>>       def netLister(self, netListText):
>>>             return LTspiceNetLister()
>>> 
>>> 
>>> First, I call a Python function which gives me an instance of the
Python
>>> class "Project". Then (in C++, using its wrapper), I call its method
>>> "netLister",
>>> which returns an instance of "LTspiceNetLister". The goal is to use
>>> this instance on the C++ side via its wrapper. Thus, ownership should
be
>>> with C++.
>>> 
>>> But there is no corresponding annotation for the sip file. I have to
>>> provide
>>> a "%VirtualCatcherCode":
>>> 
>>> virtual NetLister* netLister(const std::string& netListText /NoCopy/)
>>> const;
>>> %VirtualCatcherCode
>>>         PyObject *resObj = sipCallMethod(0, sipMethod, "D",
>>>         const_cast<std::string*>(&a0), sipType_std_string, NULL);
>>>         sipIsErr = !resObj || sipParseResult(0, sipMethod, resObj,
"H0",
>>>         sipType_NetLister, &sipRes) < 0;
>>>         if (!sipIsErr) sipTransferTo(resObj, Py_None);
>>>         Py_XDECREF(resObj);
>>> %End
>>> 
>>> Note how I use "sipTransferTo(resObj, Py_None);" to bind the Python
>> object
>>> to the C++ instance.
>>> I'm basically asking for something like the "Factory" annotation which
>>> does just that.
>>> 
>>> "Factory" tells sip that a wrapped C++ function creates a new object
>> that
>>> will be owned by the
>>> Python side. I'd like to have another annotation which tells sip that
>> the
>>> Python implementation
>>> of a class method creates a new object that will be owned by the C++
>> side.
> 
>> The documentation for /Factory/ is wrong (now fixed in hg). It
>> effectively
>> does...
> 
>>     sipTransferTo(resObj, selfObj);
> 
>> ...in the virtual catcher (where selfObj) is the class's self object
(the
>> Project instance in this case).

This is wrong as well. It actually does...

        sipTransferTo(resObj, resObj);

...so effectively creating a reference cycle.

>> You are using Py_None but I don't think it would make a difference.
> 
>> Would this remove your use case for sipTransfer(obj, Py_None)?
> 
>> Phil
> 
> No it wouldn't. My embedding currently works like this: I have a .py
file
> which is imported as module at program start. This module defines a
> factory function, which is called from C++ and returns an instance of
> a Python class derived from a sip-wrapped C++ class ("Project"). The
> PyObject* obtained this way is then fed into sip's "api_convert_to_type"
> to get hold of the wrapper instance. This instance is then used in the
> C++ code. It transparently forwards method calls to the Python part.
> (That's why it was so important to have the wrapper "re-throw" Python
> exceptions as C++ exceptions.)
> Thanks to "sipTransfer(obj, Py_None)" I don't have to manually manage
the
> PyObject representing the Python part. It just gets automatically
deleted
> by the C++ wrapper.
> 
> Further, your "sipTransferTo(resObj, selfObj);" above would bind the
> returned
> "NetLister" to the "Project" instance. But they are not related in terms
> of life time. I only want the reference count of the Python part to be
> decreased
> by one when the C++ wrapper is destroyed. Provided there are no further
> references on the Python side, this automatically destroys the Python
> part, too.
> 
> No parent-child relationships here like in Qt. Just Python objects that
> can be
> used in C++ as if they were C++ only.

So I think you can use /Factory/ for what you want to do.

However there are some problems and inconsistencies with the different
Transfer annotations so I may well make further changes.

Phil


More information about the PyQt mailing list