Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Object lifetime management #609

Closed
elypma opened this issue Jan 20, 2017 · 2 comments
Closed

Object lifetime management #609

elypma opened this issue Jan 20, 2017 · 2 comments

Comments

@elypma
Copy link

elypma commented Jan 20, 2017

I am trying to solve an ownership problem in pybind11.

I have to configure a hierarchy of objects in python.
In c++ this is done like:

InputVariable* _angle = new InputVariable;
_small = new Bell("small", -5.000, 5.000, 8.000);
_angle->addTerm(_small);
_big = new Bell("big", 5.000, 5.000, 8.000);
_angle->addTerm(_big);

The python equivalent:

_angle = InputVariable("angle", -5.0, 5.0)
_small = PF.Bell("small", -5.0, 5.0, 8.0, 1.0)
_angle.addTerm(_small)
_big = PF.Bell("big", 5.0, 5.0, 8.0, 1.0)
_angle.addTerm(_big) 

In c++ the destructor of the Inputvariable delete's its Bell's.
Thus the InputVariable owns its Bells(and is responsible for cleaning them up).

When using python, this results in a conflict, because when creating the Bell's these are owned by the python interpreter, adding those with the addTerm function does not transfer this membership:

void InputVariable::addTerm(Term* term) 
{
   _terms.push_back(term);
}

During destruction (or memory collection in python) the d'tor of InputVariable is called which contains these statements:

InputVariable::~InputVariable()
{
    for (std::size_t i = 0; i < _terms.size(); ++i) {
        delete _terms.at(i);
    }
}

The result is: kabang, since the Bell's are destructed twice.
What is the best approach to solve this, without changing the existing c++ code?
I hope this is the right platform to ask this question. If not could you please refer me to the place that is?

@dean0x7d
Copy link
Member

This is usually what Return Value Policies are for, and if you want to return an unowned pointer from a function you would specify return_value_policy::reference. But this won't work for a constructor/__init__, so you need a helper function to do the construction and return the pointer with the appropriate policy:

py::class_<Bell>(m, "Bell"); // no init

m.def("make_bell", []() { return new Bell; }, return_value_policy::reference);

Note that make_bell can easily be named Bell to mimic the original syntax in Python:

py::class_<Bell>(m, "BellClass"); // no init

m.def("Bell", []() { return new Bell; }, return_value_policy::reference);

Alternatively, if you really need to keep the constructor, you could use py::nodelete to make sure Python never deletes any objects of a certain type. Beware: this would be like applying return_value_policy::reference to any function which returns Bell in any form, even if it's returned by value instead of by pointer. The phrase "running with scissors" comes to mind.

@jagerman
Copy link
Member

Closing: There's been no reply for a month, so I assume this is resolved.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants