mirror of
https://github.com/pybind/pybind11.git
synced 2024-11-11 08:03:55 +00:00
Merge pull request #235 from RosettaCommons/stl
Adding bind_map. Adding key_error exception.
This commit is contained in:
commit
07082eecc3
@ -551,6 +551,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,
|
||||
eliminating the need of converting back and forth between C++ representation
|
||||
and Python one. The downside of this approach in this case users will have to
|
||||
deal with C++ containers directly instead of using already familiar Python lists
|
||||
or dicts.
|
||||
|
||||
Pybind11 provide set of binder functions to bind various STL containers like vectors,
|
||||
maps etc. All binder functions are designed to return instances of pybind11::class_
|
||||
objects so developers can bind extra functions if needed. For complete set of
|
||||
available functions please see :file:`pybind11/stl_bind.h`. For an example on using
|
||||
this feature, please see :file:`tests/test_stl_binders.cpp`.
|
||||
|
||||
.. note::
|
||||
|
||||
Arbitrary nesting of any of these types is supported.
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "common.h"
|
||||
#include "operators.h"
|
||||
|
||||
#include <map>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <algorithm>
|
||||
@ -130,10 +131,12 @@ template <typename Vector, typename Class_> auto vector_if_insertion_operator(Cl
|
||||
|
||||
NAMESPACE_END(detail)
|
||||
|
||||
|
||||
template <typename T, typename Allocator = std::allocator<T>, typename holder_type = std::unique_ptr<std::vector<T, Allocator>>, typename... Args>
|
||||
pybind11::class_<std::vector<T, Allocator>, holder_type> bind_vector(pybind11::module &m, std::string const &name, Args&&... args) {
|
||||
using Vector = std::vector<T, Allocator>;
|
||||
//
|
||||
// std::vector
|
||||
//
|
||||
template <typename Vector, typename holder_type = std::unique_ptr<Vector>, typename... Args>
|
||||
pybind11::class_<Vector, holder_type> bind_vector(pybind11::module &m, std::string const &name, Args&&... args) {
|
||||
using T = typename Vector::value_type;
|
||||
using SizeType = typename Vector::size_type;
|
||||
using DiffType = typename Vector::difference_type;
|
||||
using ItType = typename Vector::iterator;
|
||||
@ -350,4 +353,118 @@ pybind11::class_<std::vector<T, Allocator>, holder_type> bind_vector(pybind11::m
|
||||
return cl;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// std::map
|
||||
//
|
||||
|
||||
NAMESPACE_BEGIN(detail)
|
||||
|
||||
/* Fallback functions */
|
||||
template <typename, typename, typename... Args> void map_if_insertion_operator(const Args&...) { }
|
||||
|
||||
template <typename Map, typename Class_, typename... Args> void map_if_copy_assignable(Class_ &cl, const Args&...) {
|
||||
using KeyType = typename Map::key_type;
|
||||
using MappedType = typename Map::mapped_type;
|
||||
|
||||
cl.def("__setitem__",
|
||||
[](Map &m, const KeyType &k, const MappedType &v) {
|
||||
auto it = m.find(k);
|
||||
if (it != m.end()) it->second = v;
|
||||
else m.emplace(k, v);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
template<typename Map, typename Class_, typename std::enable_if<!std::is_copy_assignable<typename Map::mapped_type>::value, int>::type = 0>
|
||||
void map_if_copy_assignable(Class_ &cl) {
|
||||
using KeyType = typename Map::key_type;
|
||||
using MappedType = typename Map::mapped_type;
|
||||
|
||||
cl.def("__setitem__",
|
||||
[](Map &m, const KeyType &k, const MappedType &v) {
|
||||
auto r = m.insert( std::make_pair(k, v) ); // We can't use m[k] = v; because value type might not be default constructable
|
||||
if (!r.second) { // value type might be const so the only way to insert it is to errase it first...
|
||||
m.erase(r.first);
|
||||
m.insert( std::make_pair(k, v) );
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
template <typename Map, typename Class_> auto map_if_insertion_operator(Class_ &cl, std::string const &name)
|
||||
-> decltype(std::declval<std::ostream&>() << std::declval<typename Map::key_type>() << std::declval<typename Map::mapped_type>(), void()) {
|
||||
|
||||
cl.def("__repr__",
|
||||
[name](Map &m) {
|
||||
std::ostringstream s;
|
||||
s << name << '{';
|
||||
bool f = false;
|
||||
for (auto const & kv : m) {
|
||||
if (f) s << ", ";
|
||||
s << kv.first << ": " << kv.second;
|
||||
f = true;
|
||||
}
|
||||
s << '}';
|
||||
return s.str();
|
||||
},
|
||||
"Return the canonical string representation of this map."
|
||||
);
|
||||
}
|
||||
NAMESPACE_END(detail)
|
||||
|
||||
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) {
|
||||
using KeyType = typename Map::key_type;
|
||||
using MappedType = typename Map::mapped_type;
|
||||
using Class_ = pybind11::class_<Map, holder_type>;
|
||||
|
||||
Class_ cl(m, name.c_str(), std::forward<Args>(args)...);
|
||||
|
||||
cl.def(pybind11::init<>());
|
||||
|
||||
// Register stream insertion operator (if possible)
|
||||
detail::map_if_insertion_operator<Map, Class_>(cl, name);
|
||||
|
||||
cl.def("__bool__",
|
||||
[](const Map &m) -> bool {
|
||||
return !m.empty();
|
||||
},
|
||||
"Check whether the map is nonempty"
|
||||
);
|
||||
|
||||
cl.def("__iter__",
|
||||
[](Map &m) {
|
||||
return pybind11::make_key_iterator(m.begin(), m.end());
|
||||
},
|
||||
pybind11::keep_alive<0, 1>() /* Essential: keep list alive while iterator exists */
|
||||
);
|
||||
|
||||
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 */
|
||||
);
|
||||
|
||||
cl.def("__getitem__",
|
||||
[](Map &m, const KeyType &k) -> MappedType {
|
||||
auto it = m.find(k);
|
||||
if (it != m.end()) return it->second;
|
||||
else throw pybind11::key_error(); // it is not always possible to convert key to string // pybind11::key_error(k)
|
||||
});
|
||||
|
||||
detail::map_if_copy_assignable<Map, Class_>(cl);
|
||||
|
||||
cl.def("__delitem__",
|
||||
[](Map &m, const KeyType &k) {
|
||||
auto it = m.find(k);
|
||||
if (it != m.end()) return m.erase(it);
|
||||
else throw pybind11::key_error(); // it is not always possible to convert key to string // pybind11::key_error(k)
|
||||
});
|
||||
|
||||
cl.def("__len__", &Map::size);
|
||||
|
||||
return cl;
|
||||
}
|
||||
|
||||
NAMESPACE_END(pybind11)
|
||||
|
@ -28,10 +28,18 @@ test_initializer stl_binder_vector([](py::module &m) {
|
||||
py::class_<El>(m, "El")
|
||||
.def(py::init<int>());
|
||||
|
||||
py::bind_vector<unsigned int>(m, "VectorInt");
|
||||
py::bind_vector<bool>(m, "VectorBool");
|
||||
py::bind_vector< std::vector<unsigned int> >(m, "VectorInt");
|
||||
py::bind_vector< std::vector<bool> >(m, "VectorBool");
|
||||
|
||||
py::bind_vector<El>(m, "VectorEl");
|
||||
py::bind_vector< std::vector<El> >(m, "VectorEl");
|
||||
|
||||
py::bind_vector<std::vector<El>>(m, "VectorVectorEl");
|
||||
py::bind_vector< std::vector< std::vector<El> > >(m, "VectorVectorEl");
|
||||
});
|
||||
|
||||
test_initializer stl_binder_map([](py::module &m) {
|
||||
py::bind_map< std::map<std::string, double> >(m, "MapStringDouble");
|
||||
py::bind_map< std::unordered_map<std::string, double> >(m, "UnorderedMapStringDouble");
|
||||
|
||||
py::bind_map< std::map<std::string, double const> >(m, "MapStringDoubleConst");
|
||||
py::bind_map< std::unordered_map<std::string, double const> >(m, "UnorderedMapStringDoubleConst");
|
||||
});
|
||||
|
@ -1,5 +1,3 @@
|
||||
|
||||
|
||||
def test_vector_int():
|
||||
from pybind11_tests import VectorInt
|
||||
|
||||
@ -51,3 +49,51 @@ def test_vector_bool():
|
||||
for i in range(10):
|
||||
assert vv_c[i] == (i % 2 == 0)
|
||||
assert str(vv_c) == "VectorBool[1, 0, 1, 0, 1, 0, 1, 0, 1, 0]"
|
||||
|
||||
|
||||
def test_map_string_double():
|
||||
from pybind11_tests import MapStringDouble, UnorderedMapStringDouble
|
||||
|
||||
m = MapStringDouble()
|
||||
m['a'] = 1
|
||||
m['b'] = 2.5
|
||||
|
||||
keys = []
|
||||
for k in m: keys.append(k)
|
||||
assert keys == ['a', 'b']
|
||||
|
||||
key_values = []
|
||||
for k, v in m.items(): key_values.append( (k, v) )
|
||||
assert key_values == [('a', 1), ('b', 2.5) ]
|
||||
|
||||
assert str(m) == "MapStringDouble{a: 1, b: 2.5}"
|
||||
|
||||
|
||||
um = UnorderedMapStringDouble()
|
||||
um['ua'] = 1.1
|
||||
um['ub'] = 2.6
|
||||
|
||||
keys = []
|
||||
for k in um: keys.append(k)
|
||||
assert sorted(keys) == ['ua', 'ub']
|
||||
|
||||
key_values = []
|
||||
for k, v in um.items(): key_values.append( (k, v) )
|
||||
assert sorted(key_values) == [('ua', 1.1), ('ub', 2.6) ]
|
||||
|
||||
str(um)
|
||||
|
||||
|
||||
def test_map_string_double_const():
|
||||
from pybind11_tests import MapStringDoubleConst, UnorderedMapStringDoubleConst
|
||||
|
||||
mc = MapStringDoubleConst()
|
||||
mc['a'] = 10
|
||||
mc['b'] = 20.5
|
||||
assert str(mc) == "MapStringDoubleConst{a: 10, b: 20.5}"
|
||||
|
||||
umc = UnorderedMapStringDoubleConst()
|
||||
umc['a'] = 11
|
||||
umc['b'] = 21.5
|
||||
|
||||
str(umc)
|
||||
|
Loading…
Reference in New Issue
Block a user