Skip to content

Support ownership transfer between C++ and Python with shared_ptr<T> and unique_ptr<T> for pure C++ instances and single-inheritance instances #2

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

EricCousineau-TRI
Copy link
Collaborator

@EricCousineau-TRI EricCousineau-TRI commented Dec 19, 2017

This uses pybind#1146 to enable the initial PRs for the systems framework Python bindings.
In the future, we may only need a subset (or none of) this PR, and may be able to revert back to upstream pybind11.

Current state of the unittests:

  • The unittests pass on CPython 2 and 3 for Linux and Mac (one test still pending for CPython 3 on Mac).
  • The unittests fail on PyPy and Windows. Given that Drake is not presently supported on these platforms, I'd like to defer fixing this issues until later.

This change is Reviewable

@EricCousineau-TRI EricCousineau-TRI changed the title WIP: Support ownership transfer between C++ and Python with shared_ptr<T> and unique_ptr<T> for pure C++ instances and single-inheritance instances Support ownership transfer between C++ and Python with shared_ptr<T> and unique_ptr<T> for pure C++ instances and single-inheritance instances Dec 19, 2017
@EricCousineau-TRI
Copy link
Collaborator Author

+@m-chaturvedi for dev-folder feature review on testing, please.

I've paired the review down to the following files:

tests/test_class.py
tests/test_methods_and_attributes.py
tests/test_ownership_transfer.cpp
tests/test_ownership_transfer.py
tests/test_smart_ptr.cpp
tests/test_smart_ptr.py

The tests should now pass on all of TravisCI (I disabled the tests on PyPy for the time being).


Review status: 0 of 16 files reviewed at latest revision, all discussions resolved.


Comments from Reviewable

@m-chaturvedi
Copy link

docs/advanced/classes.rst, line 1010 at r2 (raw file):

note
not?


Comments from Reviewable

@m-chaturvedi
Copy link

tests/test_smart_ptr.cpp, line 43 at r2 (raw file):

T *get() const {

T* ?


Comments from Reviewable

@m-chaturvedi
Copy link

What would be the expected behavior of something like a smart pointer to a numpy matrix of objects ? Would we need tests for that ?
:lgtm:


Comments from Reviewable

@m-chaturvedi
Copy link

Reviewed 12 of 16 files at r1, 5 of 5 files at r2.
Review status: all files reviewed at latest revision, 2 unresolved discussions, some commit checks failed.


Comments from Reviewable

@EricCousineau-TRI
Copy link
Collaborator Author

I tried out placing NumPy objects in smart pointers, and pybind11 does not support that (as it only supports custom types being wrapped in a holder shared_ptr, but Eigen types are considered "built-in"):
EricCousineau-TRI@ee6a5e1
^ This creates a compiler error.

For MI, the existing unittests pass; for unique_ptrs, it will throw the appropriate exception stating that the class will be sliced. However, for shared_ptrs, it is not working.

Would you be OK with a TODO on resolving this? (At present, we are not using any MI within Drake, at least that I'm aware of for the public-facing API.)


Review status: all files reviewed at latest revision, 2 unresolved discussions, some commit checks failed.


Comments from Reviewable

@rdeits
Copy link

rdeits commented Dec 20, 2017

Wow, this is really cool!

I'm still concerned about the idea of trying to limit Python objects to a single reference when passing them to a function expecting a unique_ptr. Just as an example, any python object which is ever displayed in IPython (or in a Jupyter notebook) will have a reference stored in the special Out dictionary. Thus, for example, if foo(x) expects x to a unique_ptr, then the following will work in IPython/Jupyter:

> x = NewX()
> foo(x)

but the following will result in x having at least two references:

> x = NewX()
> x  # look at x
> foo(x)

This would seem to literally create Heisenbugs, since a wrapped object can be constructed and passed, but only if it is never examined. More confusingly, since the default python REPL does not store outputs, the above will work there.

I've still never encountered any Python code that required an object to have a single reference (and googling for things like "python unique reference", I can't find any code either). There are just so many ways for additional references to crop up that getting the refcount to exactly 1 seems impractical.

Or am I missing something?

…` and `unique_ptr<T>` for pure C++ instances and single-inheritance instances
@EricCousineau-TRI
Copy link
Collaborator Author

Or am I missing something?

Nope, not at all, you're spot on!

My desire was to have the Python bindings play well with the existing Systems framework, but then transition it over to use shared_ptr to better support REPL workflows. There is a way to get this all to work using pybind's keep_alive mechanism whenever you add an object to a container; however, that means you'll be dragging that container around for the entire lifetime of the containee, which may yield some memory issues.
Ultimately, I wanted to have flexibility and start with the more conservative set of features (lifetime-limitations, as captured here in the Python Systems PR), and then expand later (and not require everything to automatically use shared_ptr, since there's no way to get a pointer out of a shared_ptr once you put it in there).


Review status: 12 of 18 files reviewed at latest revision, 2 unresolved discussions.


docs/advanced/classes.rst, line 1010 at r2 (raw file):

Previously, m-chaturvedi (Mmanu Chaturvedi) wrote…

note
not?

Done.


tests/test_smart_ptr.cpp, line 43 at r2 (raw file):

Previously, m-chaturvedi (Mmanu Chaturvedi) wrote…

T *get() const {

T* ?

Done.


Comments from Reviewable

@EricCousineau-TRI
Copy link
Collaborator Author

@m-chaturvedi Seems that the behavior I was trying to test in my branch is also broken in master. I've documented this with a TODO, and will file a bug (with a PR to capture this at some point).


Review status: 12 of 18 files reviewed at latest revision, 2 unresolved discussions.


Comments from Reviewable

@rdeits
Copy link

rdeits commented Dec 20, 2017

Ok, well, I'm convinced that you've given this more thought than I have, so I'll trust your judgment.

@m-chaturvedi
Copy link

Reviewed 6 of 7 files at r3.
Review status: all files reviewed at latest revision, all discussions resolved, some commit checks failed.


Comments from Reviewable

@rdeits
Copy link

rdeits commented Dec 21, 2017

:lgtm: to unblock merging


Review status: all files reviewed at latest revision, all discussions resolved, some commit checks failed.


Comments from Reviewable

@jadecastro
Copy link

:lgtm:


Review status: all files reviewed at latest revision, all discussions resolved, some commit checks failed.


Comments from Reviewable

Copy link

@m-chaturvedi m-chaturvedi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done via Reviewable.

@EricCousineau-TRI EricCousineau-TRI merged commit 433a6c7 into RobotLocomotion:drake Dec 21, 2017
@EricCousineau-TRI EricCousineau-TRI mentioned this pull request Dec 21, 2017
6 tasks
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

Successfully merging this pull request may close these issues.

4 participants