mirror of
https://github.com/pybind/pybind11.git
synced 2024-11-26 15:12:01 +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
|
enabled. The types ``std::pair<>`` and ``std::tuple<>`` are already supported
|
||||||
out of the box with just the core :file:`pybind11/pybind11.h` header.
|
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::
|
.. note::
|
||||||
|
|
||||||
Arbitrary nesting of any of these types is supported.
|
Arbitrary nesting of any of these types is supported.
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "operators.h"
|
#include "operators.h"
|
||||||
|
|
||||||
|
#include <map>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
@ -130,10 +131,12 @@ template <typename Vector, typename Class_> auto vector_if_insertion_operator(Cl
|
|||||||
|
|
||||||
NAMESPACE_END(detail)
|
NAMESPACE_END(detail)
|
||||||
|
|
||||||
|
//
|
||||||
template <typename T, typename Allocator = std::allocator<T>, typename holder_type = std::unique_ptr<std::vector<T, Allocator>>, typename... Args>
|
// std::vector
|
||||||
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>;
|
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 SizeType = typename Vector::size_type;
|
||||||
using DiffType = typename Vector::difference_type;
|
using DiffType = typename Vector::difference_type;
|
||||||
using ItType = typename Vector::iterator;
|
using ItType = typename Vector::iterator;
|
||||||
@ -350,4 +353,118 @@ pybind11::class_<std::vector<T, Allocator>, holder_type> bind_vector(pybind11::m
|
|||||||
return cl;
|
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)
|
NAMESPACE_END(pybind11)
|
||||||
|
@ -28,10 +28,18 @@ test_initializer stl_binder_vector([](py::module &m) {
|
|||||||
py::class_<El>(m, "El")
|
py::class_<El>(m, "El")
|
||||||
.def(py::init<int>());
|
.def(py::init<int>());
|
||||||
|
|
||||||
py::bind_vector<unsigned int>(m, "VectorInt");
|
py::bind_vector< std::vector<unsigned int> >(m, "VectorInt");
|
||||||
py::bind_vector<bool>(m, "VectorBool");
|
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():
|
def test_vector_int():
|
||||||
from pybind11_tests import VectorInt
|
from pybind11_tests import VectorInt
|
||||||
|
|
||||||
@ -51,3 +49,51 @@ def test_vector_bool():
|
|||||||
for i in range(10):
|
for i in range(10):
|
||||||
assert vv_c[i] == (i % 2 == 0)
|
assert vv_c[i] == (i % 2 == 0)
|
||||||
assert str(vv_c) == "VectorBool[1, 0, 1, 0, 1, 0, 1, 0, 1, 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