Boost logo

Boost Users :

From: David Abrahams (dave_at_[hidden])
Date: 2002-11-08 08:42:30


"Daniel Paull" <dlp_at_[hidden]> writes:

Please post Boost.Python questions to the C++-sig:

http://www.python.org/sigs/c++-sig/

> Hi all,
>
> I'm having trouble with some object lifetime issues using Boost Python
> V2. Basically, I'm in a situation where both C++ and Python want to
> delete the same object.

> Below is a simple example of the scenario. Consider the two classes A
> and B. An instance of A is passed to B via the setA() method. B then
> assumes ownership of A and deletes it in it's destructor. NOTE: the
> behaviour of the C++ code can not be changed currently.
>
> -------------------------------------
> class A
> {
> public:
> A() {}
> ~A() {}
> };
>
> class B
> {
> public:
> B() : m_a( 0 ) {}
> ~B(){ if ( m_a ) delete m_a; }
> void setA( A* a ){ m_a = a; }
> private:
> A* m_a;
> };
> ---------------------------------------
>
> I have defined a python module thus, being sure to indicate the pointer
> adoption by using the with_custodian_and_ward<> policy:
>
> ---------------------------------------
> BOOST_PYTHON_MODULE( my_module )
> {
> class_< A >( "A" )
> ;
>
> class_< B >( "B" )
> .def( "setA", &B::setA, with_custodian_and_ward<1, 2>()
> )
> ;
> }
> ---------------------------------------

What that says is, "keep the /Python/ A object alive as long as the
/Python/ B object is alive". This can't save you from deleting the C++
A object out-from-under the Python A object.

> All is fine, and I can show that instances of B keep their instance of A
> alive.
>
> However, executing the python code:
>
> --------------
> a = A()
> b = B()
> b.setA()
> --------------
>
> results in "a" (or rather, the wrapped C++ object) being deleted twice.

There's currently no good way to express what you want in
Boost.Python. As you can see from
http://aspn.activestate.com/ASPN/Mail/Message/1400931 and the ensuing
thread, my best solution to-date is to suggest that you hold the C++
object by shared_ptr and use shared ownership. Clearly, if you can't
change the C++ code you're wrapping, that won't work for you.

I think a new call policy which actually causes the Python A object to
/release/ its ownership of the C++ A object might be useful here. I'll
put this on my list of to-dos. In that case, there would be no reason
to keep the Python object alive, since it's no longer managing the
lifetime of the contained object. Obviously, you'd need to wrap your
class with some sort of smart pointer holder as well:

        class_<A, std::auto_ptr<A> >("A")
                  ;

So that ownership could eventually be released. Otherwise, the C++
object's memory is embedded in the Python object's memory, and you
can't use 'delete' on that address.

I'll be working on Boost.Python next week, and will put your problem
on my list of TODOs.

> I can get around this problem by using a free function "createA()"
> which returns a new instance of "A" using the
> "return_existing_object" return value policy,

How does this help?

> but this seems dangerous and can introduce a memory leak if the user
> does this from python:
>
> --------------
> def makeLeak():
> a = createA()
>
> makeLeak()
> --------------
>
> Perhaps a new call policy is the solution, but I'm not sure how to
> proceed. Has anyone solved this problem already?

If createA is doing `new A' (please don't make me guess!) then
return_existing_object is not the right policy; you'd want
manage_new_object. However, I still don't see how that could solve any
of your problems.

-- 
                       David Abrahams
   dave_at_[hidden] * http://www.boost-consulting.com
Boost support, enhancements, training, and commercial distribution

Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net