[C++-sig] alternative smart pointer (intrusive_ptr) example

Jeff Webb jeff.webb at nta-inc.net
Tue Jul 29 00:27:27 CEST 2008


Jeff Webb wrote:
> (1) There is an object identity problem (i.e. 'r.ref is v' is False).  
> This means that there are two python wrappers for a single C++ object.  
> In some cases this is okay, but it is not ideal.

In order to solve the object identity problem mentioned above, I needed some method of storing the PyObject pointer that is wrapping a given Pointee object and a way of looking it up.  There are two methods that I have tried:

(1) storing the PyObject pointer in the Pointee object itself
(2) using a global dictionary to store the Pointee->PyObject mappings

I initially used solution (1), which worked well for me since I have control over the Pointee data structure.  I later decided that I would rather not have the overhead of an extra pointer stored in every Pointee object, whether it was accessed from python or not, so I tried approach (2), which is what I am posting here.  If you like method (1) better, I think it will be easy for you to modify the attached example to work that way.

I have modified my previous example (mod2.cpp) in several ways:

(1) I added set_pyobject_for_pointee, get_pyobject_for_pointee, and clear_pyobject_for_pointee functions that manipulate a global std::map object.

(2) I have specialized the make_instance_impl template for a T object held by an intrusive_ptr<T>.

(3) I added an init_class function template along with a helper function called install_pointer_in_pyobject.

(4) I have modified the class wrappers by adding a no_init argument to the class_ statements, adding an __init__ method to call the init_class function, and adding a __del__ method that removes the pyobject pointer for the corresponding C++ object when a python wrapper is destroyed.

The make_instance_impl template is where code was added to check for an existing python wrapper and to record the PyObject pointer when a new wrapper is generated.  The init_class method is necessary to record the PyObject pointer when an object is created from python because the make_instance_impl template is not called in that case.  The __del__ method is necessary to remove a PyObject pointer from the std::map object when a python wrapper is destroyed.

I'm sure there are better ways to do some of these things, but I think you will find my modifications understandable.  It see at least two main areas where there could be memory leaks:  the install_pointer_in_pyobject function and the code in make_instance_impl that returns a reference to an existing pyobject.  If someone could at least take a look at those sections, I would appreciate it.

Here is a sample session with the new version of our module:

>>> from mod3 import *

>>> r1 = RefHolder()
creating 0xb0741c0

>>> r2 = RefHolder()
creating 0xb077140

>>> r1.ref = r2

>>> r1.ref is r2
True

>>> v = VerbosePointee()
creating 0xb9a95f0

>>> r2.ref = v

>>> del v

>>> del r2

>>> print r1.ref
<mod3.RefHolder object at 0xb932590>

>>> r1.ref.ref = None
destroying 0xb9a95f0

>>> del r1
destroying 0xb077140
destroying 0xb0741c0

>From the output above, it appears that the object identity problem I mentioned has been solved.  The __del__ method also seems to have done it's job since we can still access r1.ref without a segmentation fault even after r2 has been deleted.  This approach has worked reliably as far as I can tell.  Any comments or questions on the implementation are more than welcome.

-Jeff

-------------- next part --------------
A non-text attachment was scrubbed...
Name: mod3.cpp
Type: text/x-c++src
Size: 10849 bytes
Desc: not available
URL: <http://mail.python.org/pipermail/cplusplus-sig/attachments/20080728/5444c9b8/attachment.cpp>


More information about the Cplusplus-sig mailing list