[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