-
Notifications
You must be signed in to change notification settings - Fork 205
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
Add nb::bind_map
#114
Add nb::bind_map
#114
Conversation
Hi Nicholas, this looks like a great start. A few comments: nanobind uses C++17, which brought one huge simplification: That way, you can e.g. merge the various Minor: Again, due to C++17, type trait-derived values like The use of polymorphism seems overkill to me (it might have been more relevant for pybind11, where creating bindings for things in general has a larger cost). Let's instead use map-specific variants (e.g. replacing You can probably even declare the helper view types inline within the main binding function. The Likewise, the What was the static assertion you got? |
Thanks for the suggestions! I read up on If so, I can also redesign the tests later. Regarding the compile-time assertion, this is the CMake output I'm getting without |
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.
A few minor comments. I think the most important missing part are testcases -- we will want to cover all the functions and also account for special cases (for example non-copy-assignable types that are mentioned in one place.)
My suggestion would be that you simply reuse the existing tests by porting them from pybind11.
tests/test_stl_bind_map.cpp
Outdated
#include <nanobind/nanobind.h> | ||
#include <nanobind/stl/bind_map.h> | ||
#include <nanobind/stl/string.h> | ||
#include <nanobind/stl/map.h> |
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.
Can you remove stl/map.h
and stl/unordered_map.h
from this file? This is likely the source of your error message regarding NB_OPAQUE
(you imported the default STL map type caster and tried to declare bindings for a specific STL map).
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.
No, removing them makes the compiler complain (no template 'map' in namespace 'std'
).
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.
You can include map
from the STL:
#include <map>
#include <unordered_map>
The nanobind header pulls in type casters that are unwanted in this context.
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, that works. Should I do the same with <string>
instead of <nanobind/stl/string.h>
?
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.
right. You only need nanobind/stl/..
to have automated conversion of the associated type between Python and C++ (and those automated converters work by always performing a copy).
This means that you would only need nanobind/stl/string.h
if there is a testcase which returns a string to Python (or takes one as an input).
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.
Included <string>
now instead of nanobind/stl/string.h
, but the tests are still failing (the maps with non-copyable values). I don't know what is going wrong there, the branch seems correct - I will look at it later with a fresh mind.
Thanks for the review, I just ported over the relevant tests from pybind. I am still getting compile errors, since nanobind attempts to bind a copy constructor to the map here for the non-copyable value class nanobind/include/nanobind/nb_class.h Lines 277 to 279 in f4d5e12
I think this might be an error in my |
I went into pybind's codebase to check why it works there, and they are using SFINAE to overload their own version of Is there an obvious way to implement this as a |
How about
? |
That works for non-copyable value types, but fails for nested maps with non-copyable values (since
|
Ah, I understand now. The logic in pybind11 actually looks good to me, and implementing this using partial overloads is probably still the best way to go. You can basically copy those definitions of |
Can you add these type traits to a new file |
Will do, thanks for the clarification! |
Ah wait -- if I understand correctly, the |
Yes, that's due to the same phenomenon - a map with a non-copyable value is copyable, and if I understand correctly, the default copy constructor of a map tries to call the copy constructor of its value (or rather, the EDIT: Related pybind PR: pybind/pybind11#1886 |
I pushed a fix, see 94bdb05 |
Thanks, looks good to me. Should I add aliases for the values ( |
Sure, let's do that. The goal of my commit was just to fix the parts affecting core parts of nanobind without adding the complicated templates there by default. |
41a44ca
to
000140e
Compare
Thanks, just pushed the ported templates and values. I'm confident that that fixed the |
Adds a port of pybind11's `bind_map` to nanobind, including tests. For now, all additions have been made inline in the new header file, nanobind/stl/bind_map.h. This file opts the user into the standard library headers <string> (due to its use of string concatenation for constructing dynamic keys/values view names) and <memory> (for dynamically allocating keys/values/items views.)
An overhaul of the previous commit with suggestions: * The map assignment operator logic was merged with the use of `if constexpr`. Instead of template values, we now use the shorter std::*template*_v with trailing underscore-v. * Polymorphism in the view classes was removed. * Unique pointers were dropped in favor of `new Keys/Values/ItemsView`. * String construction of view names specialized on their types was dropped, now instead we install the views into the scope defined by the map class.
Also remove superfluous comments about the necessity of nb::keep_alive() during view iterator usage.
Some exceptions apply due to design changes: * str() call tests on maps have been deleted, since no __str__ operator was added. * The Python type name assertions (last 3 lines) need to be changed, since the logic was changed in the port.
1. Exposing detail::is_copy_assignable and detail::is_copy_assignable_v in nanobind/stl/detail/traits.h; 2. Exposing detail::is_copy_constructible_v in core nanobind (nanobind/nb_class.h); 3. Switching implementations to detail::is_copy{constructible,assignable}_v in bind_map.h;
Hi @nicholasjng -- I made a simplification pass over your code, please check that it still okay to you. I had to make a change to nanobind on master for this to work (8916f51) and I then rebased your code & force-pushed to your branch. 😇 |
Nice, thank you for the fixes! Could you briefly clarify why it needs to be |
The |
Shall I merge this then? |
Yep, looks good to me! |
This commit adds a port of nanobind's `nb::bind_map<T>` feature to create bindings of STL map types (`map`, `unordered_map`). The implementation contains the following simplifications: 1. The C++17 constexpr feature was used to considerably reduce the size of the implementation. 2. The key/value/item views are simple wrappers without the need for polymorphism or STL unique pointers. They are created once per map type. The commit also includes a port of the associated pybind11 test suite parts. Co-authored by: Nicholas Junge <[email protected]> Co-authored by: Wenzel Jakob <[email protected]>
Thanks! |
Thanks for the help and the review! See you again soon for |
That one should be refreshingly easy in comparison. |
Adds a port of pybind11's
bind_map
to nanobind, including tests.For now, all additions have been made inline in the new header file, nanobind/stl/bind_map.h. This file opts the user into the standard library headers
<string>
(due to its use of string concatenation for constructing dynamic keys/values view names) and<memory>
(for dynamically allocating keys/values/items views.)This is my best effort of porting over
py::bind_map
, so far. Since I am not writing C++ that often, please be relentless so that I can learn and do better :)The use of
<string>
and<memory>
are only necessary because I ported over pybind/pybind11#4353, which pulls them in for dynamically constructing string reprentations for keys/value/items views. Please give guidance on whether this is ok, or whether that makesbind_map
incompatible with nanobind's design goals.I would like guidance on the following questions that came up over the course of developing (I also left some TODOs which are essentially questions that I asked myself while implementing):
using
defs (I assume they are detail-namespace-wide)?KeysViewImpl
et al. compatible with nanobind? (this ties into the above)NB_MAKE_OPAQUE
in the tests (they are essentially ported verbatim frompybind
), to avoid a static assertion about a missing type caster, where pybind does apparently not. What am I doing wrong there?Thanks in advance for guidance and comments.