-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Allow references to objects held by smart pointers #533
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
Wouldn't it be more sensible to just add a Thoughts? |
A In C++, even if a type is usually held by a |
I feel like there's something essential that I don't get about this proposal. Currently, if you bind a function returning This function will in turn call And that is exactly what we want. The shared pointers know about each other, and the object won't be deallocated before all the shared pointers go out of scope. In other words, no double frees. |
I think I have obscured the issue in the test because I wanted to pack a lot of test coverage in a small amount of code. The basic issue looks like this: struct Widget {
struct A {};
A a;
};
py::class_<Widget::A, std::shared_ptr<Widget::A>>(m, "WidgetA");
py::class_<Widget>(m, "Widget")
.def(py::init<>())
.def_readonly("a", &Widget::a); >>> w = Widget()
>>> a = w.a
>>> del a, w
Crash: pointer freed twice This is because With the changes in this PR, the |
Ok, I see -- so the issue is that the fancier holder type disables things like |
Yeah, exactly. The docs do warn about this kind of thing, but mostly because of pointers and |
The downside of this being of course that you can get into quite bizarre situations where a I wonder if returning |
One more question: I assume that in the case that the class derives from |
You're right, an exception is more appropriate there. Overloading on that would be insanity. An informative error is better. I'll add it. (The message is also better than a double free crash.)
|
Careful about putting that in the error message: you'll only invite commentary about how judging code by its API is the wrong metric, and about Stroustrup being an idiot and therefore anything he ever suggested should be avoided. :) |
Let me know when this is good to merge. |
3b06f7a
to
d80f1f5
Compare
d80f1f5
to
6b7f4f9
Compare
OK, that should be it. There's now an error message when trying to load The tests are a bit longer now, but hopefully also clearer. The comments indicate which path of I also removed an unnecessary |
Great, thank you! |
The documentation rightfully warns against mixing smart pointers and raw pointers in interfaces. However, it's easy to forget that
const&
has a similar issue and can result in a double free (I was bitten by this recently).This PR makes it possible to return references to types which are wrapped with a
std::shared_ptr
or similar copyable smart pointer. Here, 'references' mean&
or*
with RVPreference
orreference_internal
. Returning a&
or*
with RVPtake_ownership
is still bad as described in the docs.In the added tests the values returned from
shared_ref
andfrom_this_bad_wp
would usually result in a double free. This is resolved by not constructing the holder in cases where a non-owning reference is returned.Because the holder isn't always constructed it's also not possible to cast that Python object back to the holder type. A check is added for this, which accounts for the expected
TypeError
when castingref -> holder
andbad_wp -> holder
. Bothref
andbad_wp
are still perfectly valid references and can be used per usual. I think that having this case usable with just this limitation is preferable to a crash due to a double free.The new test also covers every way of initializing a copyable holder type and casting it back to C++.