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
[ENH] Add operator= to shared_ptr for assignments to base classes #4185
Conversation
Thanks. Could you add a test in |
I've added a test which fails without the one-line addition. However, the test below, which does the opposite -- assigns the base pointer to the pointer to the derived -- actually goes through as well even though that looks like something that should fail. def test_assignment_to_derived_class():
"""
>>> test_assignment_to_derived_class()
"""
cdef shared_ptr[A] base = shared_ptr[A](new A())
cdef shared_ptr[C] derived = base Edit: Well, okay, the incorrect test is green, but it doesn't actually compile with
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
def test_assignment_to_derived_class():
"""
>>> test_assignment_to_derived_class()
"""
cdef shared_ptr[A] base = shared_ptr[A](new A())
cdef shared_ptr[C] derived = base
When I try this test it passes through Cython but fails with the C++ compiler (which I think is what you find too). I think that's expected and OK - Cython doesn't/can't have enough information to work out what pointer types are compatible, so all it can do is assume the user knows.
Cython/Includes/libcpp/memory.pxd
Outdated
@@ -59,6 +59,7 @@ cdef extern from "<memory>" namespace "std" nogil: | |||
shared_ptr(shared_ptr[T]&, T*) | |||
shared_ptr(unique_ptr[T]&) | |||
#shared_ptr(weak_ptr[T]&) # Not Supported | |||
T& operator=[Y](const shared_ptr[Y]& ptr) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't this return a shared_ptr[T]&
?
And arguably there's a matching constructor too? (although a little harder to use from Cython since most things end up default-constructed)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the review!
Shouldn't this return a
shared_ptr[T]&
?
Hm, again, I'm a Cython newb so I'm not sure -- certainly what you're saying looks more logical and closer to what's in the reference. But I've tried both and cythonize will generate the same cpp in both cases in the test_assignment_to_base_class
example.
And arguably there's a matching constructor too?
Do you mean shared_ptr[Y](shared_ptr[Y]&)
? As the SO poster remarks, that doesn't quite work.
shared_ptr[Y](shared_ptr[Y]&)
^
memory.pxd:64:20: unknown type in template argument
...
shared_ptr[Y](shared_ptr[Y]&)
^
memory.pxd:64:34: unknown type in template argument
...
shared_ptr[Y](shared_ptr[Y]&)
^
memory.pxd:64:21: Missing name in declaration.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm, again, I'm a Cython newb so I'm not sure -- certainly what you're saying looks more logical and closer to what's in the reference. But I've tried both and they generate the same cpp in the case of the test_assignment_to_base_class example.
It should only matter in for multiple assignments (or similar). So for test_assignment_to_base_class
it makes no difference, but for second_copy = first_copy = original
it might make a difference (I think... I haven't looked into the exact code that Cython generates here).
Do you mean
shared_ptr[Y](shared_ptr[Y]&)
? As the SO poster remarks, that doesn't quite work.
I'd think it should be shared_ptr(shared_ptr[Y]&)
(note the first square brackets gone). If that doesn't work then it's fine - don't spend more time on it though. The point the SO poster is making is that Cython generally generates c++ code like:
// start of function
shared_ptr<A> a;
// function body
a = something;
and so operator=
is generally much more useful.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for
second_copy = first_copy = original
it might make a difference
I just tried
cdef shared_ptr[C] derived = shared_ptr[C](new C())
cdef shared_ptr[A] base1
cdef shared_ptr[A] base2
base1 = base2 = derived
Here, the generated code will once again be the same for both return types. In any case, I've changed it to shared_ptr[T]&
.
If that doesn't work then it's fine
Did try that one as well; doesn't quite work:
shared_ptr(shared_ptr[Y]&)
^
memory.pxd:64:31: unknown type in template argument
don't spend more time on it though
I don't mind; this is quite helpful for learning what's going on!
Thanks. Looks ok to me. It seems a rather broad change – the way I understand it, it allows assigning anything to anything, right? But users should rarely run into issues here, and the C++ compiler will catch them. |
Yeah, cf. the example in this comment above, it's lenient, but indeed, that one is caught during compilation. |
As discussed in this StackOverflow answer, having the
operator=
around enables us to assignshared_ptr[Derived]
toshared_ptr[Base]
.Now admittedly, as the question will suggest, I'm new to C++ Cython and in particular I would be unable to see what pitfalls this might have, but it does work as intended in my particular use case.