[C++-sig] weak_ptr to this in C++ classes extended from python

Holger Brandsmeier brandsmeier at gmx.de
Mon Apr 23 20:48:33 CEST 2012


Dear Dave, dear Jim,

thanks for your help in particular to `boost::enable_shared_from_this`
which I wasn't aware about in the beginning. Unfortunately your
solutions did not yet work.

In the meantime I figured out a different way of doing the shared
pointer from this in python. Namely, to just make getThisRCP() a
virtual function, add it to the Wrapper and to implement it in the
python by implementing it as

def getThisRCP(self):
  return self

this simply works.

As for the issue that I reported, it still persists so out of interest
I did try your suggestions and here is what I found out.

the difference between the lines
  class_<IWrapper, boost::noncopyable >("I", init<>() )
and
  class_<IWrapper, boost::shared_ptr<IWrapper>, boost::noncopyable
>("I", init<>() )
didn't change anything, the getThisRCP() still doesn't work. In fact I
tried both variants before and send you just the first one. Can it be
that specifying the holder for IWrapper does not apply to the holder
for the subclass IDer from python?

In fact the statement
  class_<IWrapper, boost::shared_ptr<IWrapper>, boost::noncopyable
>("I", init<>() )
has an issue. When I call getThisRCP() later, and the return value is
not a null shared pointer, then I in fact the error
TypeError: No to_python (by-value) converter found for C++ type:
boost::shared_ptr<I>
which is quite annoying. I also implemented Dave's suggestion with
boost::enable_shared_from_this and because of the above issue I used
this for the Wrapper and not the class itself (see K and KWrapper in
the attachment).

The behaviour of `enable_shared_from_this` is different from my
initual getThisRCP() / setThisRCP() solution but still has seriour
problems. Concider the code:

class KDer(K):
  def __init__(self):
    K.__init__(self)

k = KDer()

In contrast to my initial solution the lines
  assert k.getThisRCP2() is not None
  k2 = k.getThisRCP2()
now work, which is nice.

Also this call works now:
  print k2.foo();

However, the following two lines
  del k
  print k2.foo();
give a segmentation fault. Note that the destructor for the class `K`
has not been called, which  would have printed a statement. The `gdb`
backtrace is

#0  0x00007ffff64425d7 in boost::python::objects::instance_dealloc
(inst=0xe51788) at libs/python/src/object/class.cpp:335
#1  0x00007ffff7aba7f6 in subtype_dealloc (self=0xe51788) at
Objects/typeobject.c:1014
#2  0x00007ffff7a72413 in instancemethod_dealloc (im=0xae0730) at
Objects/classobject.c:2364
#3  0x00007ffff645387e in xdecref<_object> (p=<optimized out>) at
./boost/python/refcount.hpp:36
#4  ~handle (this=<optimized out>, __in_chrg=<optimized out>) at
./boost/python/handle.hpp:211
#5  boost::python::detail::wrapper_base::get_override (this=0xaec988,
name=0x7fffe41de992 "foo", class_object=0x71bb10)
    at libs/python/src/wrapper.cpp:21
#6  0x00007fffe41d95f1 in get_override (name=<optimized out>,
this=<optimized out>, name=<optimized out>)
    at /usr/include/boost/python/wrapper.hpp:29
#7  KWrapper::foo (this=0xaec980) at
/home/bholger/HolgerBrandsmeier/parfem.h/python/thisRCPPy.cpp:112
[...]

Any ideas?

-Holger

On Mon, Apr 23, 2012 at 18:01, Dave Abrahams <dave at boostpro.com> wrote:
>
> on Sun Apr 22 2012, Holger Brandsmeier <brandsmeier-AT-gmx.de> wrote:
>
>> Dear list,
>>
>> how is it possible to have a class in C++ that can be extended from
>> python and that stores a weak_ptr to itself?
>
> Easy, I think: just like any other class you wrap.  Are you aware of
> boost::enable_shared_from_this?  It does all of that internally.
>
>> The reason why I want to do this:
>>  - using a weak_ptr to itself does not introduce a memory leak.
>>  - from a C++-member of that class I can call other functions that
>> expect a shared_ptr<>, in that case I convert the weak_ptr to a
>> shared_ptr and all the memory management works nicely.
>>
>> The way I would suspect this to work is:
>> struct I {
>>   void setThisRCP(boost::shared_ptr<I> ptr) {
>>     thisRCP = ptr;
>>   }
>>
>>   boost::shared_ptr<I> getThisRCP() const {
>>     return thisRCP.lock();
>>   }
>>
>>   boost::weak_ptr<I> thisRCP;
>> };
>>
>> Then I export this class to C++ and in python I would do:
>> class IDer(I):
>>   def __init__(self):
>>     I.__init__(self)
>>     self.setThisRCP(self)
>>
>> And I would suspect that I can use that class now in C++ and in
>> particular calling getThisRCP() from C++ returns a valid shared
>> pointer. However, immediately after the call of
>> `self.setThisRCP(self)` the weak_ptr is already invalid and getThisRCP
>> returns an shared pointer to Null (None in python).
>
> If you use boost::shared_ptr<I> as the Holder for your wrapper, you'll
> be able to do that conversion.  I see you've done that with "J" in your
> example, but not with "I".  Why not?
>
> If you use enable_shared_from_this, the internal weak_ptr will be
> initialized from the first shared_ptr that is constructed to manage the
> object.  Everything should work.
>
> --
> Dave Abrahams
> BoostPro Computing
> http://www.boostpro.com
>
> _______________________________________________
> Cplusplus-sig mailing list
> Cplusplus-sig at python.org
> http://mail.python.org/mailman/listinfo/cplusplus-sig
-------------- next part --------------
A non-text attachment was scrubbed...
Name: test_rcp.py
Type: application/octet-stream
Size: 833 bytes
Desc: not available
URL: <http://mail.python.org/pipermail/cplusplus-sig/attachments/20120423/992fe112/attachment.obj>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: thisRCPPy.cpp
Type: text/x-c++src
Size: 3024 bytes
Desc: not available
URL: <http://mail.python.org/pipermail/cplusplus-sig/attachments/20120423/992fe112/attachment.cpp>


More information about the Cplusplus-sig mailing list