FW: Object Lifetime Management with Boost-Python V2
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>() ) ; } --------------------------------------- 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. 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, 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? Thanks in advance, Daniel Paull Development Lead Fractal Technologies 57 Havelock Street West Perth WA Australia 6005 PO Box 1675 West Perth WA Australia 6872 Phone: +61 8 9211 6064 Mobile: 0408 921 897 Email: Daniel.Paull@fractaltechnologies.com
"Daniel Paull"
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") ; 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@boost-consulting.com * http://www.boost-consulting.com Boost support, enhancements, training, and commercial distribution
David Abrahams
"Daniel Paull"
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.
There's currently no good way to express what you want in Boost.Python.
Daniel, Since I checked in the changes to support passing auto_ptr<> objects into and out of wrapped functions, you should be able to do what you need. You'll need to write a thin wrapper for your function which takes an auto_ptr<A> argument and calls its release() member function to get the object which will be adopted by the C++ code. HTH, Dave -- David Abrahams dave@boost-consulting.com * http://www.boost-consulting.com Boost support, enhancements, training, and commercial distribution
participants (2)
-
Daniel Paull
-
David Abrahams