-
Notifications
You must be signed in to change notification settings - Fork 2.1k
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
Adding bind_map. Adding key_error exception. #235
Conversation
Hi Sergey, thank you for the PR. This is in my queue for the next release, but it may take me a bit to review it (I'm leaving for a vacation). Cheers, |
Sure thing Wenzel! This is certainly not urgent. And please let me know if there is anything i can do to help with this. Have a good vacation! Sergey. |
00cec38
to
5130212
Compare
@@ -424,13 +424,18 @@ pybind11::class_<std::map<Key, T, Compare, Allocator>, holder_type> bind_map(pyb | |||
|
|||
cl.def("__getitem__", | |||
[](Map &m, const KeyType &k) -> MappedType { | |||
if (m.count(k)) return m[k]; | |||
if (m.count(k)) return m.at(k); |
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.
This could potentially require two lookups--one for the one for the count
, and one for the element access, and the at()
is a bit wasteful (as it checks existence again). The following would avoid both, while being const-usable:
auto it = m.find(k);
if (it != m.end()) return *m;
else throw pybind11::key_error();
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.
Thank you for checking on this! Good point, I will path this!
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.
The __delitem__
implementation can do the same thing as well, via m.erase(it);
|
||
} | ||
|
||
template<typename Map, typename Class_, typename std::enable_if<std::is_const<typename Map::mapped_type>::value, int>::type = 0> |
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.
I think you could generalize this a bit by changing
std::is_const<typename Map::mapped_type>::value
to
!std::is_copy_assignable<Map::mapped_type>::value
which would still catch const
values, but would also catch non-const types that don't have a copy assignment operator.
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.
Good point!
Hi Sergey, could you rebase this onto master? Also, here are a few more comments:
THanks, |
Re make_key_iterator: right, i just pushed the commit that uses it. Please let me know if this is the right way to do it. @wjakob do i need to register Python testing functions somewhere to make sure that they called during the testing phase? Because right now it does not look like the testing functions is called at all (at least in my setup) (because assert in test_map_string_double should certainly fail due to different numbers of key's, - but it does not). Thought i do see parsing errors if i introduce them into test_stl_binders.py... thoughts? |
Looks like the tests run fine on appveyor... i guess it is a problem with my setup. |
The It would be nice to also add: cl.def("items",
[](Map &m) { return pybind11::make_iterator(m.begin(), m.end()); },
pybind11::keep_alive<0, 1>() /* Essential: keep list alive while iterator exists */
); to allow pairwise iteration from Python: for k, v in mymap.items():
print("%s = %s" % (k, v)) |
Good idea about the 'items' @jagerman ! - I will add this shortly! |
@wjakob i think the last commit addressed the remaining issues, please let me know if there is anything else that needs to be done. Thanks! |
@@ -550,6 +550,18 @@ and the Python ``list``, ``set`` and ``dict`` data structures are automatically | |||
enabled. The types ``std::pair<>`` and ``std::tuple<>`` are already supported | |||
out of the box with just the core :file:`pybind11/pybind11.h` header. | |||
|
|||
Alternatively it might be desirable to bind STL containers as native C++ classes | |||
therefor eliminating the need of converting back and forth between C++ representation |
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.
"thereforE" (or even better: just a comma)
@wjakob thank you for looking this up! I updated the docs as you suggested and untabifyed the source files. |
@@ -979,6 +991,12 @@ exceptions: | |||
| | accesses in ``__getitem__``, | | |||
| | ``__setitem__``, etc.) | | |||
+--------------------------------------+------------------------------+ | |||
| :class:`pybind11::key_error` | ``KeyError`` (used to | |
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.
Duplicate: key_error
is already documented a few lines down.
This looks good to me now. Two more things: can you remove the duplicate docs pointed out by @dean0x7d and squash this into a single commit? |
Just a heads up: it looks like Also a space-less |
63236eb
to
c86faf6
Compare
Ok, this is all should be fixed now. @wjakob i did squash the commits into one as we'll. (Btw, i think GitHub now have option of doing this automatically when merging PR: after clicking 'merge' on confirmation button there is 'squash and merge' or something) |
This PR appears to only support // In pybind11::detail namespace:
template <typename MapType, typename holder_type, typename... Args...> bind_map_type(module &m, const std::string &name, Args&&... args) {
// ... current bind_map() code
}
// In pybind11 namespace:
template <typename Key, typename T, typename Compare = std::less<Key>, typename Allocator = std::allocator<std::pair<const Key, T> >, typename holder_type = std::unique_ptr<std::map<Key, T, Compare, Allocator>>, typename... Args>
pybind11::class_<std::map<Key, T, Compare, Allocator>, holder_type> bind_map(pybind11::module &m, std::string const &name, Args&&... args) {
return detail::bind_map_type<std::map<Key, T, Compare, Allocator>, holder_type>(m, name, std::forward<Args>(args)...);
}
template <typename Key, typename T, typename Hash = std::hash<Key>, KeyEqual = std::equal_to<Key>, typename Allocator = std::allocator<std::pair<const Key, T> >, typename holder_type = std::unique_ptr<std::unordered_map<Key, T, Hash, KeyEqual, Allocator>>, typename... Args>
pybind11::class_<std::unordered_map<Key, T, Hash, KeyEqual, Allocator>, holder_type> bind_unordered_map(pybind11::module &m, std::string const &name, Args&&... args) {
return detail::bind_map_type<std::unordered_map<Key, T, Hash, KeyEqual, Allocator>, holder_type>(m, name, std::forward<Args>(args)...);
} |
Actually, a nicer alternative might be to just make template <typename Map, typename holder_type = std::unique_ptr<Map>, typename... Args>
pybind11::class_<Map, holder_type> bind_map(module &m, const std::string &name, Args&&... args) {
// ... same body as existing bind_map but without the "using Map = ..." line.
} Then the caller can call it with any compatible map-like class, e.g. pybind11::bind_map<std::unordered_map<...>>(m, "Map1");
pybind11::bind_map<std::map<...>>(m, "Map2");
pybind11::bind_map<MyCustomMapType>(m, "Map3"); |
@jagerman good idea about generalizing this for different map types! I will keep this in mind for future PR's. Re passing |
Hi Sergey, can you rebase this commit to the master branch? (can't be merged ATM). Thanks, |
By the way: if the generalization to unordered maps is really as simple as @jagerman writes above, it would be a pity to not include it in this commit as well. |
89d550a
to
bb76756
Compare
bb76756
to
7520418
Compare
@wjakob i just double checked: - the initial reason for passing arguments for map type is no longer relevant so @jagerman suggestion is now implemented and relevant tests for unordered_map is added. I also refactored bind_vector so it now take Vector type as template parameter instead of pair <T, Allocator> which should allow it to be used with vector-like types. This PR is now rebased on current upstream so it is mergeable. Please let me know if there is anything else thats need to be done. |
The updated vector interface is nice, to keep it consistent with the map interface, but I think that's going to need an extra templated definition for backwards compatibility. |
This wasn't official API thus far (not documented in any way), so I think it's fine. Furthermore, we are heading towards a major release which can break compatibility with impunity. (Also, I don't think it's possible to address this incompatibility with an extra template definition in a straightforward way) |
Great, thank you for taking care of this Wenzel! |
Adding bind_map which provide minimal bindings for std::map. Adding key_error exception.