From 1bfe60661197738e0bfefa92702af2034729c734 Mon Sep 17 00:00:00 2001 From: Sergey Lyskov Date: Sun, 8 May 2016 19:31:55 -0400 Subject: [PATCH] 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. --- docs/advanced.rst | 7 +- example/example17.cpp | 28 +++++--- example/example17.py | 21 ++++-- example/example17.ref | 6 ++ include/pybind11/stl_binders.h | 122 +++++++++++++++++++++------------ 5 files changed, 122 insertions(+), 62 deletions(-) diff --git a/docs/advanced.rst b/docs/advanced.rst index 901faa73f..b17e0f113 100644 --- a/docs/advanced.rst +++ b/docs/advanced.rst @@ -779,6 +779,10 @@ exceptions: | | accesses in ``__getitem__``, | | | ``__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 | | | exception flag has already | | | 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. 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 ``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 .. [#f5] http://github.com/pybind/pbtest - diff --git a/example/example17.cpp b/example/example17.cpp index 8c30457e8..8370b5c68 100644 --- a/example/example17.cpp +++ b/example/example17.cpp @@ -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 @@ -11,17 +11,27 @@ #include -class A -{ + +class A { public: A() = delete; + A(int v) :a(v) {} + + int a; }; -void init_ex17(py::module &m) -{ - pybind11::class_(m, "A"); - py::vector_binder(m, "VectorInt"); - - py::vector_binder(m, "VectorA"); +std::ostream & operator<<(std::ostream &s, A const&v) { + s << "A{" << v.a << '}'; + return s; +} + + +void init_ex17(py::module &m) { + pybind11::class_(m, "A") + .def(pybind11::init()); + + pybind11::vector_binder(m, "VectorInt"); + + pybind11::vector_binder(m, "VectorA"); } diff --git a/example/example17.py b/example/example17.py index d41a3254b..52cda4238 100644 --- a/example/example17.py +++ b/example/example17.py @@ -1,24 +1,31 @@ #!/usr/bin/env python from __future__ import print_function -from example import VectorInt, VectorA +from example import VectorInt, VectorA, A v_int = VectorInt(2) -print( v_int.size() ) +print(len(v_int)) -print( bool(v_int) ) +print(bool(v_int)) v_int2 = VectorInt(2) -print( v_int == v_int2 ) +print(v_int == v_int2) v_int2[1] = 1 -print( v_int != v_int2 ) +print(v_int != v_int2) -v_int2.push_back(2) -v_int2.push_back(3) +v_int2.append(2) +v_int2.append(3) v_int2.insert(0, 1) v_int2.insert(0, 2) v_int2.insert(0, 3) print(v_int2) +v_int.append(99) +v_int2[2:-2] = v_int +print(v_int2) + v_a = VectorA() +v_a.append(A(1)) +v_a.append(A(2)) +print(v_a) diff --git a/example/example17.ref b/example/example17.ref index 249fbabe5..388e0f0d5 100644 --- a/example/example17.ref +++ b/example/example17.ref @@ -3,3 +3,9 @@ True True True 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}] \ No newline at end of file diff --git a/include/pybind11/stl_binders.h b/include/pybind11/stl_binders.h index a2af0ac8f..7b0047d99 100644 --- a/include/pybind11/stl_binders.h +++ b/include/pybind11/stl_binders.h @@ -27,14 +27,14 @@ NAMESPACE_BEGIN(pybind11) template -constexpr auto has_equal_operator(int) -> decltype( std::declval() == std::declval(), bool()) { return true; } +constexpr auto has_equal_operator(int) -> decltype(std::declval() == std::declval(), bool()) { return true; } template constexpr bool has_equal_operator(...) { return false; } template -constexpr auto has_not_equal_operator(int) -> decltype( std::declval() != std::declval(), bool()) { return true; } +constexpr auto has_not_equal_operator(int) -> decltype(std::declval() != std::declval(), bool()) { return true; } template 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 { enum class False {}; struct any_type { - template any_type( T const& ); + template any_type(T const&); }; -False operator<<( std::ostream const&, any_type const& ); +False operator<<(std::ostream const&, any_type const&); } template -constexpr bool has_insertion_operator() -{ +constexpr bool has_insertion_operator() { using namespace has_insertion_operator_implementation; - return std::is_same< decltype( std::declval() << std::declval() ), std::ostream & >::value; + return std::is_same< decltype(std::declval() << std::declval()), std::ostream & >::value; } #endif template , typename holder_type = std::unique_ptr< std::vector > > -class vector_binder -{ +class vector_binder { using Vector = std::vector; using SizeType = typename Vector::size_type; @@ -72,16 +70,28 @@ class vector_binder // void maybe_constructible(Class_ &cl) {} template{} >::type * = nullptr> - void maybe_default_constructible(Class_ &cl) { + void maybe_default_constructible() { cl.def(pybind11::init()); 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{} >::type * = nullptr> - void maybe_default_constructible(Class_ &) {} + void maybe_default_constructible() {} template{} >::type * = nullptr> - void maybe_copy_constructible(Class_ &cl) { + void maybe_copy_constructible() { cl.def(pybind11::init< Vector const &>()); } template{} >::type * = nullptr> @@ -89,39 +99,41 @@ class vector_binder template(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("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); - if( p != v.end() ) v.erase(p); + if(p != v.end()) v.erase(p); 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."); + + 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(0) >::type * = nullptr> - void maybe_has_equal_operator(Class_ &) {} + void maybe_has_equal_operator() {} template(0) >::type * = nullptr> - void maybe_has_not_equal_operator(Class_ &cl) { + void maybe_has_not_equal_operator() { cl.def(pybind11::self != pybind11::self); } template(0) >::type * = nullptr> - void maybe_has_not_equal_operator(Class_ &) {} + void maybe_has_not_equal_operator() {} #ifdef INSERTION_OPERATOR_IMPLEMENTATION template() >::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::Vector &v) { std::ostringstream s; s << name << '['; for(uint i=0; i() >::type * = nullptr> - void maybe_has_insertion_operator(Class_ &, char const *) {} + void maybe_has_insertion_operator(char const *) {} #endif public: - vector_binder(pybind11::module &m, char const *name) { - Class_ cl(m, name); + Class_ cl; + vector_binder(pybind11::module &m, char const *name, char const *doc=nullptr) : cl(m, name, doc) { cl.def(pybind11::init<>()); //maybe_constructible(cl); - maybe_default_constructible(cl); - maybe_copy_constructible(cl); + maybe_default_constructible(); + maybe_copy_constructible(); // Element access cl.def("front", [](Vector &v) { - if( v.size() ) return v.front(); + if(v.size()) return v.front(); else throw pybind11::index_error(); }, "access the first element"); cl.def("back", [](Vector &v) { - if( v.size() ) return v.back(); + if(v.size()) return v.back(); else throw pybind11::index_error(); }, "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"); - // Capacity - cl.def("empty", &Vector::empty, "checks whether the container is empty"); - cl.def("size", &Vector::size, "returns the number of elements"); + // Capacity, C++ style cl.def("max_size", &Vector::max_size, "returns the maximum possible number of elements"); 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("shrink_to_fit", &Vector::shrink_to_fit, "reduces memory usage by freeing unused memory"); // Modifiers, C++ style - 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("pop_back", &Vector::pop_back, "removes the last element"); - cl.def("swap", &Vector::swap, "swaps the contents"); - + cl.def("clear", &Vector::clear, "clears the contents"); + cl.def("swap", &Vector::swap, "swaps the contents"); // Modifiers, Python style 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("pop", [](Vector &v) { - if( v.size() ) { + if(v.size()) { T t = v.back(); v.pop_back(); return t; } 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) { - if( i >= v.size() ) throw pybind11::index_error(); - v.erase( v.begin() + i ); + if(i >= v.size()) throw pybind11::index_error(); + v.erase(v.begin() + i); }, "erases element at index"); @@ -198,25 +211,46 @@ public: #endif 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]; }); 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; }); 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