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_sourcefo...@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/
_______________________________________________
Cplusplus-sig mailing list
Cplusplus-sig@python.org
http://mail.python.org/mailman/listinfo/cplusplus-sig

Reply via email to