Adding documentation for value_error. Fixing various formatting issues. Removing redundant binding for C++ style methods. Adding bindings for iterator and slicing protocol. Extending examples.

This commit is contained in:
Sergey Lyskov 2016-05-08 19:31:55 -04:00
parent 71c084f57f
commit 1bfe606611
5 changed files with 122 additions and 62 deletions

View File

@ -779,6 +779,10 @@ exceptions:
| | accesses in ``__getitem__``, | | | accesses in ``__getitem__``, |
| | ``__setitem__``, etc.) | | | ``__setitem__``, etc.) |
+--------------------------------------+------------------------------+ +--------------------------------------+------------------------------+
| :class:`pybind11::value_error` | ``ValueError`` (used to |
| | indicate wrong value passed |
| | in ``container.remove(...)`` |
+--------------------------------------+------------------------------+
| :class:`pybind11::error_already_set` | Indicates that the Python | | :class:`pybind11::error_already_set` | Indicates that the Python |
| | exception flag has already | | | exception flag has already |
| | been initialized | | | been initialized |
@ -903,7 +907,7 @@ its popularity and widespread adoption, pybind11 provides transparent
conversion support between Eigen and Scientific Python linear algebra data types. conversion support between Eigen and Scientific Python linear algebra data types.
Specifically, when including the optional header file :file:`pybind11/eigen.h`, Specifically, when including the optional header file :file:`pybind11/eigen.h`,
pybind11 will automatically and transparently convert pybind11 will automatically and transparently convert
1. Static and dynamic Eigen dense vectors and matrices to instances of 1. Static and dynamic Eigen dense vectors and matrices to instances of
``numpy.ndarray`` (and vice versa). ``numpy.ndarray`` (and vice versa).
@ -1481,4 +1485,3 @@ work, it is important that all lines are indented consistently, i.e.:
.. [#f4] http://www.sphinx-doc.org .. [#f4] http://www.sphinx-doc.org
.. [#f5] http://github.com/pybind/pbtest .. [#f5] http://github.com/pybind/pbtest

View File

@ -1,5 +1,5 @@
/* /*
example/example17.cpp -- Usade of stl_binders functions example/example17.cpp -- Usage of stl_binders functions
Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch> Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>
@ -11,17 +11,27 @@
#include <pybind11/stl_binders.h> #include <pybind11/stl_binders.h>
class A
{ class A {
public: public:
A() = delete; A() = delete;
A(int v) :a(v) {}
int a;
}; };
void init_ex17(py::module &m)
{
pybind11::class_<A>(m, "A");
py::vector_binder<int>(m, "VectorInt"); std::ostream & operator<<(std::ostream &s, A const&v) {
s << "A{" << v.a << '}';
py::vector_binder<A>(m, "VectorA"); return s;
}
void init_ex17(py::module &m) {
pybind11::class_<A>(m, "A")
.def(pybind11::init<int>());
pybind11::vector_binder<int>(m, "VectorInt");
pybind11::vector_binder<A>(m, "VectorA");
} }

View File

@ -1,24 +1,31 @@
#!/usr/bin/env python #!/usr/bin/env python
from __future__ import print_function from __future__ import print_function
from example import VectorInt, VectorA from example import VectorInt, VectorA, A
v_int = VectorInt(2) v_int = VectorInt(2)
print( v_int.size() ) print(len(v_int))
print( bool(v_int) ) print(bool(v_int))
v_int2 = VectorInt(2) v_int2 = VectorInt(2)
print( v_int == v_int2 ) print(v_int == v_int2)
v_int2[1] = 1 v_int2[1] = 1
print( v_int != v_int2 ) print(v_int != v_int2)
v_int2.push_back(2) v_int2.append(2)
v_int2.push_back(3) v_int2.append(3)
v_int2.insert(0, 1) v_int2.insert(0, 1)
v_int2.insert(0, 2) v_int2.insert(0, 2)
v_int2.insert(0, 3) v_int2.insert(0, 3)
print(v_int2) print(v_int2)
v_int.append(99)
v_int2[2:-2] = v_int
print(v_int2)
v_a = VectorA() v_a = VectorA()
v_a.append(A(1))
v_a.append(A(2))
print(v_a)

View File

@ -3,3 +3,9 @@ True
True True
True True
VectorInt[3, 2, 1, 0, 1, 2, 3] VectorInt[3, 2, 1, 0, 1, 2, 3]
VectorInt[3, 2, 0, 0, 99, 2, 3]
A constructor
A destructor
A constructor
A destructor
VectorA[A{1}, A{2}]

View File

@ -27,14 +27,14 @@ NAMESPACE_BEGIN(pybind11)
template<typename T> template<typename T>
constexpr auto has_equal_operator(int) -> decltype( std::declval<T>() == std::declval<T>(), bool()) { return true; } constexpr auto has_equal_operator(int) -> decltype(std::declval<T>() == std::declval<T>(), bool()) { return true; }
template<typename T> template<typename T>
constexpr bool has_equal_operator(...) { return false; } constexpr bool has_equal_operator(...) { return false; }
template<typename T> template<typename T>
constexpr auto has_not_equal_operator(int) -> decltype( std::declval<T>() != std::declval<T>(), bool()) { return true; } constexpr auto has_not_equal_operator(int) -> decltype(std::declval<T>() != std::declval<T>(), bool()) { return true; }
template<typename T> template<typename T>
constexpr bool has_not_equal_operator(...) { return false; } constexpr bool has_not_equal_operator(...) { return false; }
@ -43,22 +43,20 @@ constexpr bool has_not_equal_operator(...) { return false; }
namespace has_insertion_operator_implementation { namespace has_insertion_operator_implementation {
enum class False {}; enum class False {};
struct any_type { struct any_type {
template<typename T> any_type( T const& ); template<typename T> any_type(T const&);
}; };
False operator<<( std::ostream const&, any_type const& ); False operator<<(std::ostream const&, any_type const&);
} }
template<typename T> template<typename T>
constexpr bool has_insertion_operator() constexpr bool has_insertion_operator() {
{
using namespace has_insertion_operator_implementation; using namespace has_insertion_operator_implementation;
return std::is_same< decltype( std::declval<std::ostringstream&>() << std::declval<T>() ), std::ostream & >::value; return std::is_same< decltype(std::declval<std::ostream&>() << std::declval<T>()), std::ostream & >::value;
} }
#endif #endif
template <typename T, typename Allocator = std::allocator<T>, typename holder_type = std::unique_ptr< std::vector<T, Allocator> > > template <typename T, typename Allocator = std::allocator<T>, typename holder_type = std::unique_ptr< std::vector<T, Allocator> > >
class vector_binder class vector_binder {
{
using Vector = std::vector<T, Allocator>; using Vector = std::vector<T, Allocator>;
using SizeType = typename Vector::size_type; using SizeType = typename Vector::size_type;
@ -72,16 +70,28 @@ class vector_binder
// void maybe_constructible(Class_ &cl) {} // void maybe_constructible(Class_ &cl) {}
template<typename U = T, typename std::enable_if< std::is_default_constructible<U>{} >::type * = nullptr> template<typename U = T, typename std::enable_if< std::is_default_constructible<U>{} >::type * = nullptr>
void maybe_default_constructible(Class_ &cl) { void maybe_default_constructible() {
cl.def(pybind11::init<SizeType>()); cl.def(pybind11::init<SizeType>());
cl.def("resize", (void (Vector::*)(SizeType count)) &Vector::resize, "changes the number of elements stored"); cl.def("resize", (void (Vector::*)(SizeType count)) &Vector::resize, "changes the number of elements stored");
/// Slicing protocol
cl.def("__getitem__", [](Vector const &v, pybind11::slice slice) -> Vector * {
pybind11::ssize_t start, stop, step, slicelength;
if(!slice.compute(v.size(), &start, &stop, &step, &slicelength))
throw pybind11::error_already_set();
Vector *seq = new Vector(slicelength);
for (int i=0; i<slicelength; ++i) {
(*seq)[i] = v[start]; start += step;
}
return seq;
});
} }
template<typename U = T, typename std::enable_if< !std::is_default_constructible<U>{} >::type * = nullptr> template<typename U = T, typename std::enable_if< !std::is_default_constructible<U>{} >::type * = nullptr>
void maybe_default_constructible(Class_ &) {} void maybe_default_constructible() {}
template<typename U = T, typename std::enable_if< std::is_copy_constructible<U>{} >::type * = nullptr> template<typename U = T, typename std::enable_if< std::is_copy_constructible<U>{} >::type * = nullptr>
void maybe_copy_constructible(Class_ &cl) { void maybe_copy_constructible() {
cl.def(pybind11::init< Vector const &>()); cl.def(pybind11::init< Vector const &>());
} }
template<typename U = T, typename std::enable_if< !std::is_copy_constructible<U>{} >::type * = nullptr> template<typename U = T, typename std::enable_if< !std::is_copy_constructible<U>{} >::type * = nullptr>
@ -89,39 +99,41 @@ class vector_binder
template<typename U = T, typename std::enable_if< has_equal_operator<U>(0) >::type * = nullptr> template<typename U = T, typename std::enable_if< has_equal_operator<U>(0) >::type * = nullptr>
void maybe_has_equal_operator(Class_ &cl) { void maybe_has_equal_operator() {
cl.def(pybind11::self == pybind11::self); cl.def(pybind11::self == pybind11::self);
cl.def(pybind11::self != pybind11::self); cl.def(pybind11::self != pybind11::self);
cl.def("count", [](Vector const &v, T const & value) { return std::count(v.begin(), v.end(), value); }, "counts the elements that are equal to value"); cl.def("count", [](Vector const &v, T const & value) { return std::count(v.begin(), v.end(), value); }, "counts the elements that are equal to value");
cl.def("remove", [](Vector &v, const T&t) { cl.def("remove", [](Vector &v, T const &t) {
auto p = std::find(v.begin(), v.end(), t); auto p = std::find(v.begin(), v.end(), t);
if( p != v.end() ) v.erase(p); if(p != v.end()) v.erase(p);
else throw pybind11::value_error(); else throw pybind11::value_error();
}, "Remove the first item from the list whose value is x. It is an error if there is no such item."); }, "Remove the first item from the list whose value is x. It is an error if there is no such item.");
cl.def("__contains__", [](Vector const &v, T const &t) { return std::find(v.begin(), v.end(), t) != v.end(); }, "return true if item in the container");
} }
template<typename U = T, typename std::enable_if< !has_equal_operator<U>(0) >::type * = nullptr> template<typename U = T, typename std::enable_if< !has_equal_operator<U>(0) >::type * = nullptr>
void maybe_has_equal_operator(Class_ &) {} void maybe_has_equal_operator() {}
template<typename U = T, typename std::enable_if< has_not_equal_operator<U>(0) >::type * = nullptr> template<typename U = T, typename std::enable_if< has_not_equal_operator<U>(0) >::type * = nullptr>
void maybe_has_not_equal_operator(Class_ &cl) { void maybe_has_not_equal_operator() {
cl.def(pybind11::self != pybind11::self); cl.def(pybind11::self != pybind11::self);
} }
template<typename U = T, typename std::enable_if< !has_not_equal_operator<U>(0) >::type * = nullptr> template<typename U = T, typename std::enable_if< !has_not_equal_operator<U>(0) >::type * = nullptr>
void maybe_has_not_equal_operator(Class_ &) {} void maybe_has_not_equal_operator() {}
#ifdef INSERTION_OPERATOR_IMPLEMENTATION #ifdef INSERTION_OPERATOR_IMPLEMENTATION
template<typename U = T, typename std::enable_if< has_insertion_operator<U>() >::type * = nullptr> template<typename U = T, typename std::enable_if< has_insertion_operator<U>() >::type * = nullptr>
void maybe_has_insertion_operator(Class_ &cl, std::string name) { void maybe_has_insertion_operator(char const *name) {
cl.def("__repr__", [name](typename vector_binder<T>::Vector &v) { cl.def("__repr__", [name](typename vector_binder<T>::Vector &v) {
std::ostringstream s; std::ostringstream s;
s << name << '['; s << name << '[';
for(uint i=0; i<v.size(); ++i) { for(uint i=0; i<v.size(); ++i) {
s << v[i]; s << v[i];
if( i != v.size() -1 ) s << ", "; if(i != v.size()-1) s << ", ";
} }
s << ']'; s << ']';
return s.str(); return s.str();
@ -129,64 +141,65 @@ class vector_binder
} }
template<typename U = T, typename std::enable_if< !has_insertion_operator<U>() >::type * = nullptr> template<typename U = T, typename std::enable_if< !has_insertion_operator<U>() >::type * = nullptr>
void maybe_has_insertion_operator(Class_ &, char const *) {} void maybe_has_insertion_operator(char const *) {}
#endif #endif
public: public:
vector_binder(pybind11::module &m, char const *name) { Class_ cl;
Class_ cl(m, name);
vector_binder(pybind11::module &m, char const *name, char const *doc=nullptr) : cl(m, name, doc) {
cl.def(pybind11::init<>()); cl.def(pybind11::init<>());
//maybe_constructible(cl); //maybe_constructible(cl);
maybe_default_constructible(cl); maybe_default_constructible();
maybe_copy_constructible(cl); maybe_copy_constructible();
// Element access // Element access
cl.def("front", [](Vector &v) { cl.def("front", [](Vector &v) {
if( v.size() ) return v.front(); if(v.size()) return v.front();
else throw pybind11::index_error(); else throw pybind11::index_error();
}, "access the first element"); }, "access the first element");
cl.def("back", [](Vector &v) { cl.def("back", [](Vector &v) {
if( v.size() ) return v.back(); if(v.size()) return v.back();
else throw pybind11::index_error(); else throw pybind11::index_error();
}, "access the last element "); }, "access the last element ");
// Not needed, the operator[] is already providing bounds checking cl.def("at", (T& (Vector::*)(SizeType i)) &Vector::at, "access specified element with bounds checking"); // Not needed, the operator[] is already providing bounds checking cl.def("at", (T& (Vector::*)(SizeType i)) &Vector::at, "access specified element with bounds checking");
// Capacity // Capacity, C++ style
cl.def("empty", &Vector::empty, "checks whether the container is empty");
cl.def("size", &Vector::size, "returns the number of elements");
cl.def("max_size", &Vector::max_size, "returns the maximum possible number of elements"); cl.def("max_size", &Vector::max_size, "returns the maximum possible number of elements");
cl.def("reserve", &Vector::reserve, "reserves storage"); cl.def("reserve", &Vector::reserve, "reserves storage");
cl.def("capacity", &Vector::capacity, "returns the number of elements that can be held in currently allocated storage"); cl.def("capacity", &Vector::capacity, "returns the number of elements that can be held in currently allocated storage");
cl.def("shrink_to_fit", &Vector::shrink_to_fit, "reduces memory usage by freeing unused memory"); cl.def("shrink_to_fit", &Vector::shrink_to_fit, "reduces memory usage by freeing unused memory");
// Modifiers, C++ style // Modifiers, C++ style
cl.def("clear", &Vector::clear, "clears the contents"); cl.def("clear", &Vector::clear, "clears the contents");
cl.def("push_back", (void (Vector::*)(const T&)) &Vector::push_back, "adds an element to the end"); cl.def("swap", &Vector::swap, "swaps the contents");
cl.def("pop_back", &Vector::pop_back, "removes the last element");
cl.def("swap", &Vector::swap, "swaps the contents");
// Modifiers, Python style // Modifiers, Python style
cl.def("append", (void (Vector::*)(const T&)) &Vector::push_back, "adds an element to the end"); cl.def("append", (void (Vector::*)(const T&)) &Vector::push_back, "adds an element to the end");
cl.def("insert", [](Vector &v, SizeType i, const T&t) {v.insert(v.begin()+i, t);}, "insert an item at a given position"); cl.def("insert", [](Vector &v, SizeType i, const T&t) {v.insert(v.begin()+i, t);}, "insert an item at a given position");
cl.def("pop", [](Vector &v) { cl.def("pop", [](Vector &v) {
if( v.size() ) { if(v.size()) {
T t = v.back(); T t = v.back();
v.pop_back(); v.pop_back();
return t; return t;
} }
else throw pybind11::index_error(); else throw pybind11::index_error();
}, "insert an item at a given position"); }, "remove and return last item");
cl.def("pop", [](Vector &v, SizeType i) {
if(i >= v.size()) throw pybind11::index_error();
T t = v[i];
v.erase(v.begin() + i);
return t;
}, "remove and return item at index");
cl.def("erase", [](Vector &v, SizeType i) { cl.def("erase", [](Vector &v, SizeType i) {
if( i >= v.size() ) throw pybind11::index_error(); if(i >= v.size()) throw pybind11::index_error();
v.erase( v.begin() + i ); v.erase(v.begin() + i);
}, "erases element at index"); }, "erases element at index");
@ -198,25 +211,46 @@ public:
#endif #endif
cl.def("__getitem__", [](Vector const &v, SizeType i) { cl.def("__getitem__", [](Vector const &v, SizeType i) {
if( i >= v.size() ) throw pybind11::index_error(); if(i >= v.size()) throw pybind11::index_error();
return v[i]; return v[i];
}); });
cl.def("__setitem__", [](Vector &v, SizeType i, T const & t) { cl.def("__setitem__", [](Vector &v, SizeType i, T const & t) {
if( i >= v.size() ) throw pybind11::index_error(); if(i >= v.size()) throw pybind11::index_error();
v[i] = t; v[i] = t;
}); });
cl.def("__len__", &Vector::size); cl.def("__len__", &Vector::size);
cl.def("__iter__", [](Vector &v) { return pybind11::make_iterator(v.begin(), v.end()); },
pybind11::keep_alive<0, 1>() /* Essential: keep object alive while iterator exists */);
/// Slicing protocol
cl.def("__setitem__", [](Vector &v, pybind11::slice slice, Vector const &value) {
pybind11::ssize_t start, stop, step, slicelength;
if(!slice.compute(v.size(), &start, &stop, &step, &slicelength))
throw pybind11::error_already_set();
if((size_t) slicelength != value.size())
throw std::runtime_error("Left and right hand size of slice assignment have different sizes!");
for(int i=0; i<slicelength; ++i) {
v[start] = value[i]; start += step;
}
});
// Comparisons // Comparisons
maybe_has_equal_operator(cl); maybe_has_equal_operator();
maybe_has_not_equal_operator(cl); maybe_has_not_equal_operator();
// Printing // Printing
#ifdef INSERTION_OPERATOR_IMPLEMENTATION #ifdef INSERTION_OPERATOR_IMPLEMENTATION
maybe_has_insertion_operator(cl, name); maybe_has_insertion_operator(name);
#endif #endif
// C++ style functions deprecated, leaving it here as an example
//cl.def("empty", &Vector::empty, "checks whether the container is empty");
//cl.def("size", &Vector::size, "returns the number of elements");
//cl.def("push_back", (void (Vector::*)(const T&)) &Vector::push_back, "adds an element to the end");
//cl.def("pop_back", &Vector::pop_back, "removes the last element");
} }
}; };