Thanks for the reply, and apologies for the delay in mine; I've been struggling with classes having protected destructors. Sorted now though.. On Sat, 23 Mar 2013 03:02:58 -0000, Niall Douglas <s_sourceforge@nedprod.com> wrote:
Separate C++ objects from their python wrappers. Have C++ objects held by shared_ptr.
Have a custom deleter on shared_ptr invoke a virtual int_PythonIsAboutToDelete() function before it actually calls delete. You see as soon as destruction begins, all virtual functions cease to work, so you need a predestructor stage.
This seems to be on the right track. I had some success when calling a Python-exposed __del__ method, with std::auto_ptr<Environment> as argument. I had found - with lots of printf calls - that the C++ destructor was being called twice. Each time, it called pthread_mutex_destroy, and being one too many times, that caused a seg-fault. Seemed like an ownership problem, and calling 'release' on std::auto_ptr<Environment>, in the exposed "__del__" method, got around that seg-fault, running without error. I was in the middle of replying with a success story, but on further testing, it seemed that this solution only worked when the library was linked against libpython3 / libboost_python3. When linking against the version 2.7 counterparts, seg-faults crept back in... I couldn't understand why... With that partial success, I thought that I should define a corresponding __new__ method, to allocate the memory for the object deleted by __del__. Although that seems okay to me in principal, I'm completely failing to write a successful implementation... Are there any boost python helpers for this? Again, any help greatly appreciated! // wrapper class namespace mylib { class IEnvironment : public notmylib::Environment { IEnvironment(const boost::python::list& envp); ~IEnvironment(void) {} // method implementing __new__ functionality? static boost::shared_ptr<IEnvironment> Create( boost::python::object type, const boost::python::list *envp ); } // custom deleter void DelEnvironment(boost::shared_ptr<IEnvironment> env); // pointer to start of environment array char** m_envp; // thread and gil state vars PyThreadState * _save; PyGILState_STATE gil_state; } // First function to be called in initialisation chain. const char* const* mylib::IEnvironment::InitEnvironment(const boost::python::list& envp) { int envc = boost::python::len(envp); pylib::m_envp = new char*[envc+1]; stl_input_iterator<char*> begin(envp) end; int i=0; while ( begin != end ) { pylib::m_envp[i++] = (*begin); begin++; } pylib::m_envp[i] = '\0'; pylib::gil_state = PyGILState_Ensure(); pylib::_save = PyEval_SaveThread(); return &pylib::m_envp[0]; } // Defines initialiser chain. The function body here runs last. mylib::IEnvironment::IEnvironment(const boost::python::list& envp) : Environment(InitIEnvironment(boost::ref(envp))) { PyEval_RestoreThread(pylib::_save); PyGILState_Release(pylib::gil_state); } // attempt at writing a __new__ method boost::shared_ptr<mylib::Environment> mylib::Environment::Create( boost::python::object cls, const boost::python::list& envp ) { PyTypeObject* cls_type = (PyTypeObject*)cls.ptr(); PyObject* cls_inst = PyType_GenericNew( cls_type, envp.ptr(), Py_None); if (PyType_Ready(cls_type) != 0) { throw error_already_set(); } // lots of trial and error here. Always in error, so far... return boost::make_shared<pylib::IEnvironment>( boost::python::extract<pylib::IEnvironment>( boost::python::object(boost::python::ptr(cls_inst)))); } BOOST_PYTHON_MODULE(foo) { boost::python::class_<mylib::Environment, boost::noncopyable, boost::shared_ptr<mylib::IEnvironment> ("Environment", init<const boost::python::list&>() ) .def("__new__", &mylib::IEnvironment::Create ) .staticmethod("__new__") //< necessary? .def("__del__", &pylib::DelEnvironment ) ; }
Do your pre destruction cleanup like destroying mutexs in that virtual function. Or indeed anything which can throw exceptions, because destructors are (soon to be) noexcept.
Hope that helps, Niall
Yep, thanks! Cheers, Alex -- Using Opera's mail client: http://www.opera.com/mail/