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

Jeff Webb jeff.webb at nta-inc.net
Fri Jul 25 20:29:02 CEST 2008


At the end of my last post, I listed several deficiencies in my proposed python wrapper, mod1.cpp.  Let me start by addressing item (2):

> (2) RefHolder objects cannot hold a reference to another RefHolder 
> object, even though RefHolder is a subclass of VerbosePointee.

A simple solution to this problem is to add the following statement at the end of BOOST_PYTHON_MODULE(mod1):

  implicitly_convertible<intrusive_ptr<RefHolder>, 
                         intrusive_ptr<VerbosePointee> >();

This statement tells boost::python about the relationship between intrusive_ptr<RefHolder> and intrusive_ptr<VerbosePointee> and enables a python user to pass wrapped RefHolder objects to the RefHolder::set_ref function, which is expecting an intrusive_ptr<VerbosePointee>.

Let's see if this actually works:

>>> from mod1 import *

>>> r1 = RefHolder()
creating 0xe49c6e0

>>> r2 = RefHolder()
creating 0xda13bb0

>>> r1.ref = r2

>>> type(r1.ref)
<class 'mod1.RefHolder'>

So far, so good.  Now on to the next problem:

> (3) Although a null pointer is translated to python as None, a 
> RefHolder's ref member cannot be assigned a value of None.

Let's make sure adding the implicitly_convertible call didn't somehow fix the problem:

>>> r1.ref = None
---------------------------------------------------------------------------
Boost.Python.ArgumentError
Traceback (most recent call last)

ArgumentError: Python argument types in
    None.None(RefHolder, NoneType)
did not match C++ signature:
    None(RefHolder {lvalue}, boost::intrusive_ptr<VerbosePointee>)

As you can see, this is still an issue.  To find a solution, I looked at how the problem was solved for shared_ptr.  The solution can be found in this file:

  include/boost/python/converter/shared_ptr_from_python.hpp

I basically made a copy of this function, named it 'intrusive_ptr_from_python', replaced shared_ptr with intrusive_ptr, and left the shared_ptr_deleter argument off of the call to the pointer constructor.  In order to register this conversion, the function template must be called from BOOST_PYTHON_MODULE(mod1):

  boost::python::converter::intrusive_ptr_from_python<VerbosePointee>();

As it turns out, this converter also handles the implicit conversion discussed above, so the implicitly_convertible call is no longer necessary.

Now let's test out our new changes, as shown in the attached file, mod2.cpp.

>>> from mod2 import *

>>> r1 = RefHolder()
creating 0xd78af0

>>> r2 = RefHolder()
creating 0x17ff260

>>> r1.ref = r2

>>> r1.ref is r2
False

>>> r1.ref.id == r2.id
True

>>> type(r1.ref)
<class 'mod2.RefHolder'>

>>> v = VerbosePointee()
creating 0x1817370

>>> r2.ref = v

>>> del r2

>>> r1.ref.ref.id == v.id
True

>>> del v

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

>>> del r1
destroying 0x17ff260
destroying 0xd78af0

So, it looks like this approach handles two of our issues.  The object identity problem still remains, but I will save that discussion for another post.  Any questions or comments are welcome.

-Jeff

-------------- next part --------------
A non-text attachment was scrubbed...
Name: mod2.cpp
Type: text/x-c++src
Size: 4506 bytes
Desc: not available
URL: <http://mail.python.org/pipermail/cplusplus-sig/attachments/20080725/1fd4f568/attachment.cpp>


More information about the Cplusplus-sig mailing list