I’m creating bindings for a subset of wxWidgets using Boost Python. Window objects in wxWidgets should not be deleted manually since they handle their own deletion: for example, when a top level window is closed by the user clicking the close button it automatically deletes itself. If a window is deleted strange things will happen with event handlers etc.
(Details: http://docs.wxwidgets.org/2.8/wx_windowdeletionoverview.html)
This however leads to a problem with window objects created in Python: on garbage collection the C++ object is always deleted!
Is there any way to tell Boost Python to not take ownership of C++ objects it creates? Something like a call policy for the constructor perhaps?
(Also, I’m a little bit worried about how to handle objects deleted from C++. What should happen to the Python object when an associated C++ object is deleted? Python will not get notified about this in any way.)
This can be accomplished with a
boost::shared_ptrand some setup in Boost.Python.boost::shared_ptrhas constructors that accept a custom deleter. When theshared_ptr‘s reference count reaches zero, theshared_ptrwill invoke the customer deleter, passing the previously managed pointer as an argument. This allows for different deallocation strategies to be used, such as a “no-op”, or one that invokeswxWindow::Destroy().When exposing a class to Python, Boost.Python allows for types to be managed via a
HeldType. In this case, theHeldTypewill beboost::shared_ptr. This allows for proper referencing counting to occur between C++ and Python, and allows for the custom deallocation strategy.The trick to getting these to work together transparently is to:
__init__).__init__function that will invoke a factory function returning ashared_ptrwith a custom deleter.Here is the a mocked up
windowclass that is intended to only be destroyed through thedestroy()member function.A factory function is defined that will create a reference counted
windowobject with a custom deleter. The custom deleter will invokedestroy()on the object when the reference count reaches zero.Finally, use Boost.Python to expose the
windowclass, but suppress the default initializer, and transparently replace it with thecreate_windowfactory function.Here is a complete example:
And its usage:
Notice how Python only informs C++ to delete the object once Python no longer has a reference to the instance. Thus, in this scenario, Python will not try to interact on an object that has been deleted. Hopefully this alleviates concerns expressed when an object is deleted in C++.
If there are situations where C++ will be deleting objects that are still active in Python, then consider using an opaque pointer to separate the implementation class and the handle class. The handle class could check if the associated implementation instance has been deleted before forwarding the call, allowing an exception to be thrown up to Python.