Pregunta

I'm creating bindings for a 3rd party library that takes ownership of objects so I'm trying to use auto_ptr as documented in the FAQ.

Here's an example of two classes I've wrapped:

typedef std::auto_ptr<Panel> PanelAutoPtr;

class NewPanelCallback {
public:
    NewPanelCallback(object c) { callable = c; }
    PanelAutoPtr operator() (wxWindow* parent) {
        object result = callable(boost::ref(parent));
        return extract<PanelAutoPtr>(result);
    }
private:
    object callable;
};

void Factory_register_method(Factory* f,
                             const wxString& id,
                             boost::python::object callable)
{
    f->registerFactoryMethod(id, NewPanelCallback(callable));
}

class_<Factory, boost::noncopyable>("Factory", no_init)
    .def("get", &Factory::get, return_value_policy<reference_existing_object>());
    .def("register", &Factory_register_method);

class_<Panel, std::auto_ptr<Panel>, bases<wxWindow>, boost::noncopyable)
    ("Panel", init<wxWindow*, int, const wxString&>()>;

My application allows plugin developers to register Python function as factory methods for creating widgets. An example:

class MyPanel(shell.Panel):
    def __init__(self, parent, id, name):
        super().__init__(parent, id, name)

def create_panel(parent):
    return MyPanel(parent, -1, "Test")

shell.Factory.get().register("some_panel", create_panel)

Now, my problem is that when my program calls the NewPanelCallback functor (in C++) the panel object is deleted before the call operator returns! It's like the extract function call does not take ownership of the pointer from the result object as it should.

void create_a_panel(wxFrame* frm, NewPanelCallback& cb) {
    PanelAutoPtr p = cb(frm);
    frm->Add(p.get());
    p.release();
}

Any hints?

Solution

I finally fixed this by not using "extract". This is my new NewPanelCallback():

class NewPanelItemCallback {
public:
    NewPanelItemCallback(object c) { callable = c; }
    PanelAutoPtr operator() (wxWindow* parent) {
        return call<Shell::PanelAutoPtr>(callable.ptr(), boost::ref(parent));
    }
private:
    object callable;
};

I'm not really sure why this works and the other way doesn't. Any comments on that would be appreciated.

¿Fue útil?

Solución 2

I have found a solution. I've edited the question to include the answer.

Otros consejos

Don't use auto_ptr - it's evil and deprecated. Replace it with unique_ptr, or shared_ptr. Specifically, auto_ptr is much more eager to delete its payload by inadvertently transferring ownership to temps, because it lacks the r-value copy and move semantics that unique_ptr and shared_ptr now have.

Boost supports movable semantics and unique_ptr since v.1.55.
But in my project I used previous version and made such simple wrapper:

class_<unique_ptr<HierarchyT>, noncopyable>(typpedName<LinksT>("hierarchy", false)
, "hierarchy holder")
    .def("__call__", &unique_ptr<HierarchyT>::get,
        return_internal_reference<>(),
        "get holding hierarchy")
    .def("reset", &unique_ptr<HierarchyT>::reset,
        "reset holding hierarhy")
    ;

to create unique_ptr<HierarchyT> and pass it to the function that accepts it by reference. Python code:

hier = mc.shierarchy()
mc.clusterize(hier, nds)

where C++ function is float clusterize(unique_ptr<HierarchyT>& hier,...).
Then to access results in Python: output(hier(), nds).
Keep it simple :-)

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top