[C++-sig] Shared_ptr created by python wrapper doesn't know about the python reference?

David Abrahams dave at boost-consulting.com
Fri May 13 14:38:15 CEST 2005


Thomas Schipolowski <schipo at dynamik.fb10.TU-Berlin.DE> writes:

> The Master reference held by the Slave is valid as long as the Python 
> Master object is alive. Fine. However, this is not the case when the 
> MDerived and Slave objects are constructed from Python:
>
>  >>> m = t.MDerived()
>  >>> m.slave = t.Slave(m)
> Slave::Slave(master_ptr)
>   master_ptr use count 3
>   master_ptr addr 12410784
>   weak_ptr use count 3
>  >>> m.status()
> MDerived::status() ==> Master::status()
>   master addr 12410784
>   slave use count 1
> Slave::status()
>   master use count 0
>  >>>
>
> The Master reference held by the weak_ptr expires as soon as the Slave 
> constructor finishes, although m is still alive. I wonder what the 
> temporary shared_ptr did to the master object when the ref count went to 
> zero in this moment? Note however that the behavior is as expected if 
> the base class m = t.Master() is used instead of m = t.MDerived().
>
> Please advise me on how to handle this situation correctly. It should 
> work the same no matter whether objects are created from python or c++.

Here's what's happening: when a wrapped function takes a
boost::shared_ptr<T> by value or by const reference, a new shared_ptr
that holds a reference to the *Python* T object in its deleter is
conjured up(*) so that if that shared_ptr is ever subsequently
returned from a wrapped function we can deliver the same owning Python
object.  Of course, unless it's also stored elsewhere, that shared_ptr
goes away, the moment the wrapped function it's passed to returns, and
your weak_ptr expires.

    (*) shared_ptr<T> --------+
        (deleter) --+         |
                    |         |
       Python T     V         |
  +--------------------+      V
  |       ...          |    +---+
  |(**) shared_ptr<T> ----->| T |
  |                    |    +---+
  +--------------------+

The only way to get ahold of the shared_ptr that the Python T object
is using to hold onto the C++ T object(**) is to accept it by non-const
reference, which requires Boost.Python to find a persistent object to
which the reference can be bound.

   Slave(pMaster& m): master(m) ...

Of course, passing *that* pointer back to Python won't get you the
original Python object back.  Fortunately, from your example it seems
you don't care in this case.  

Peter: it would be interesting if there was a way to get ahold of the
weak_ptr management stuff in the deleter of the outer shared_ptr, so
that when (*) is destroyed its weak_ptrs could be re-seated to work on
(**)... am I making any sense?

-- 
Dave Abrahams
Boost Consulting
www.boost-consulting.com




More information about the Cplusplus-sig mailing list