-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Use return_value_policy::reference
for mutable lvalues (#1200).
#1240
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
Conversation
…bind when the object is not already registered.
36a5f75
to
6efc18e
Compare
This seems like a backwards-compatibility-breaking change, which makes it a non-starter for 2.x. Regarding #1200, I'll comment in that issue. |
I'd say it's a potential breaking change. However, this behavior was not captured by any of the other unittests (at least as seen on my system), and required zero changes in those unittests to make them pass. TBH, I couldn't imagine a scenario where this is desired functionality, and I would deem the current behavior as a bug. Copying completely makes sense for const lvalue (no modification) and rvalue ( |
Agreed with @jagerman. I don't think we'll want to change something so fundamental about return value policies (and I quite frankly don't see the benefit here). |
Gotcha, thank y'all for looking at it! Regarding value, I personally have value in it in that (a) it is closer to C++'s behavior and seems expected (since I and at least two others were surprised by it) and (b) the alternative is to manually annotate input arguments (per @jagerman's comment in #1200), which seems like this might grow rather verbose. If the route for (b) were taken and I wanted to eventually abandon our fork, I'd probably end up writing wrappers around all overloads for If backwards-incompatibility is the main issue, is it something that could be considered for 3.x? |
The biggest reason for defaulting to class Element { /* ... */ };
class HasElement {
private:
int i_;
public:
int &get() { return i_; }
}; In C++, I need to know at the point of calling whether the returned value can be referenced or not. If I do it wrong, I get into trouble: Element &e = HasElement().get(); // bad
Element e = HasElement().get(); // No problem (invokes a copy) Now in the Python interface for something like this, you'd normally bind it with e = HasElement().get() // Safe; e holds a reference to the temporary or you make sure the Python code only ever gets references that can't be deleted. As an Python def set_element(e):
global last_element
last_element = e
foo(set_element) combined with: m.def("foo", [](py::function f) { f(HasElement().get()); }); Since right now the default is m.def("foo", [](py::function f) { f<py::return_value_policy::reference>(HasElement().get()); });
// (I suspect that probably needs a `template` and/or `operator ()` to actually compile) but again, that's only something you do if you know that the returned lvalue reference is safe. TL;DR: I do think we should let a |
I don't think I agree with you, as it seems like you're wanting to by-default redirect / break a C++ mechanism (mutable lvalue references) with I see what you mean by your example for a callback However, that seems like a very unusual edge case: the callback-invoking method uses a temporary value in C++, calls a method directly using What I would see as more usual is that the user accepts a callback from Python, but then casts it to a (As a minor aside, wouldn't All that being said, ultimately a smaller workaround for me would be to wrap whatever methods, and replace any mutable rvalue references (arguments and return values) with pointers (possibly with a non-null check), as that'd be the behavior I'd want. Thank you for the explanations and taking time to respond - sorry for the stubborness, just throwing in my 2 cents! |
Oh goodness, I feel rather silly. After focusing on the issue posted here, I realized my problem was actually this and one more thing: the default behavior of I will post more about this in a follow-up issue. That being said, I still stand by my original statements. However, they now have much less value to me, so I'd say I'd be fine with closing this PR :) |
This addresses #1200 (and #1231) by ensuring that argument casting is more C++-ish: Do not default to copying for mutable lvalue references.
The problem in the above issues is that in casting a Python function to a C++ function and calling it directly on a C++ object, there's a possibility that the C++ object will have not been seen by
pybind
. Because of this, the prior policy was to copy the previously unseen object.This PR only changes function argument casting to alleviate this. It keeps the behavior of
cast<>()
the same, for reasons stated intest_methods_and_attributes.py
unittest.\cc @uentity @benEnsta