From eae7744c0e143b6866b9239f85531decd07d4705 Mon Sep 17 00:00:00 2001 From: Sergey Lyskov Date: Sat, 7 May 2016 00:26:19 -0400 Subject: [PATCH 1/9] adding stl_binders --- CMakeLists.txt | 1 + example/example.cpp | 2 + example/example17.cpp | 27 +++++ example/example17.py | 21 ++++ example/example17.ref | 5 + include/pybind11/stl_binders.h | 184 +++++++++++++++++++++++++++++++++ 6 files changed, 240 insertions(+) create mode 100644 example/example17.cpp create mode 100644 example/example17.py create mode 100644 example/example17.ref create mode 100644 include/pybind11/stl_binders.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 38297dbb..24dd8cbc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -128,6 +128,7 @@ set(PYBIND11_EXAMPLES example/example14.cpp example/example15.cpp example/example16.cpp + example/example17.cpp example/issues.cpp ) diff --git a/example/example.cpp b/example/example.cpp index b4199e8a..470684a3 100644 --- a/example/example.cpp +++ b/example/example.cpp @@ -25,6 +25,7 @@ void init_ex13(py::module &); void init_ex14(py::module &); void init_ex15(py::module &); void init_ex16(py::module &); +void init_ex17(py::module &); void init_issues(py::module &); #if defined(PYBIND11_TEST_EIGEN) @@ -50,6 +51,7 @@ PYBIND11_PLUGIN(example) { init_ex14(m); init_ex15(m); init_ex16(m); + init_ex17(m); init_issues(m); #if defined(PYBIND11_TEST_EIGEN) diff --git a/example/example17.cpp b/example/example17.cpp new file mode 100644 index 00000000..8c30457e --- /dev/null +++ b/example/example17.cpp @@ -0,0 +1,27 @@ +/* + example/example17.cpp -- Usade of stl_binders functions + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#include "example.h" + +#include + +class A +{ +public: + A() = delete; +}; + +void init_ex17(py::module &m) +{ + pybind11::class_(m, "A"); + + py::vector_binder(m, "VectorInt"); + + py::vector_binder(m, "VectorA"); +} diff --git a/example/example17.py b/example/example17.py new file mode 100644 index 00000000..53852802 --- /dev/null +++ b/example/example17.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python +from __future__ import print_function + +from example import VectorInt, VectorA + +v_int = VectorInt(2) +print( v_int.size() ) + +print( bool(v_int) ) + +v_int2 = VectorInt(2) +print( v_int == v_int2 ) + +v_int2[1] = 1 +print( v_int != v_int2 ) + +v_int2.push_back(2) +v_int2.push_back(3) +print(v_int2) + +v_a = VectorA() diff --git a/example/example17.ref b/example/example17.ref new file mode 100644 index 00000000..09489f1d --- /dev/null +++ b/example/example17.ref @@ -0,0 +1,5 @@ +2 +True +True +True +VectorInt[0, 1, 2, 3] diff --git a/include/pybind11/stl_binders.h b/include/pybind11/stl_binders.h new file mode 100644 index 00000000..4dbd0b61 --- /dev/null +++ b/include/pybind11/stl_binders.h @@ -0,0 +1,184 @@ +/* + pybind11/std_binders.h: Convenience wrapper functions for STL containers with C++ like interface + + Copyright (c) 2016 Sergey Lyskov + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#ifndef _INCLUDED_std_binders_h_ +#define _INCLUDED_std_binders_h_ + +#include "common.h" +#include "operators.h" + +#include +#include +#include +#include + + +NAMESPACE_BEGIN(pybind11) + +template +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; } +template +constexpr bool has_not_equal_operator(...) { return false; } + + + +namespace has_insertion_operator_implementation { +enum class False {}; +struct any_type { + template any_type( T const& ); +}; +False operator<<( std::ostream const&, any_type const& ); +} +template +constexpr bool has_insertion_operator() +{ + using namespace has_insertion_operator_implementation; + return std::is_same< decltype( std::declval() << std::declval() ), std::ostream & >::value; +} + + +template , typename holder_type = std::unique_ptr< std::vector > > +class vector_binder +{ + using Vector = std::vector; + using SizeType = typename Vector::size_type; + + using Class_ = pybind11::class_; + + // template{} >::type * = nullptr> + // void maybe_constructible(Class_ &cl) { + // cl.def(pybind11::init<>()); + // } + // template{} >::type * = nullptr> + // void maybe_constructible(Class_ &cl) {} + + template{} >::type * = nullptr> + void maybe_default_constructible(Class_ &cl) { + cl.def(pybind11::init()); + cl.def("resize", (void (Vector::*)(SizeType count)) &Vector::resize, "changes the number of elements stored"); + } + template{} >::type * = nullptr> + void maybe_default_constructible(Class_ &) {} + + + template{} >::type * = nullptr> + void maybe_copy_constructible(Class_ &cl) { + cl.def(pybind11::init< Vector const &>()); + } + template{} >::type * = nullptr> + void vector_bind_maybe_copy_constructible(Class_ &) {} + + + template(0) >::type * = nullptr> + void maybe_has_equal_operator(Class_ &cl) { + 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"); + } + template(0) >::type * = nullptr> + void maybe_has_equal_operator(Class_ &) {} + + + template(0) >::type * = nullptr> + void maybe_has_not_equal_operator(Class_ &cl) { + cl.def(pybind11::self != pybind11::self); + } + template(0) >::type * = nullptr> + void maybe_has_not_equal_operator(Class_ &) {} + + + template() >::type * = nullptr> + void maybe_has_insertion_operator(Class_ &cl, std::string 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 *) {} + + + + +public: + vector_binder(pybind11::module &m, char const *name) { + Class_ cl(m, name); + + cl.def(pybind11::init<>()); + + //maybe_constructible(cl); + maybe_default_constructible(cl); + maybe_copy_constructible(cl); + + // Capacity + 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("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 + 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("erase", [](Vector &v, SizeType i) { + if (i >= v.size()) throw pybind11::index_error(); + v.erase( v.begin() + i ); + }, "erases element at index"); + + // Python friendly bindings + #ifdef PYTHON_ABI_VERSION // Python 3+ + cl.def("__bool__", [](Vector &v) -> bool { return v.size(); }); // checks whether the container has any elements in it + #else + cl.def("__nonzero__", [](Vector &v) -> bool { return v.size(); }); // checks whether the container has any elements in it + #endif + + cl.def("__getitem__", [](Vector const &v, SizeType i) { + 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(); + v[i] = t; + }); + + cl.def("__len__", &Vector::size); + + // Comparisons + maybe_has_equal_operator(cl); + maybe_has_not_equal_operator(cl); + + // Printing + maybe_has_insertion_operator(cl, name); + } +}; + + +NAMESPACE_END(pybind11) + +#endif // _INCLUDED_std_binders_h_ From a315c7a25afa55c62b7857d7d92be6402d4823f1 Mon Sep 17 00:00:00 2001 From: Sergey Lyskov Date: Sat, 7 May 2016 18:50:26 -0400 Subject: [PATCH 2/9] =?UTF-8?q?Adding=20Python-style=20modifiers=20for=20v?= =?UTF-8?q?ector.=20Wrapping=20has=5Finsertion=5Foperator=5Fimplementation?= =?UTF-8?q?=20in=20=5FMSC=5FVER=20ifdef=E2=80=99s.=20Adding=20=E2=80=98!?= =?UTF-8?q?=3D0=E2=80=99=20instead=20of=20bool=20cast=E2=80=99s.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- example/example17.py | 3 ++ example/example17.ref | 2 +- include/pybind11/common.h | 1 + include/pybind11/stl_binders.h | 76 ++++++++++++++++++++++++++-------- 4 files changed, 64 insertions(+), 18 deletions(-) diff --git a/example/example17.py b/example/example17.py index 53852802..d41a3254 100644 --- a/example/example17.py +++ b/example/example17.py @@ -16,6 +16,9 @@ print( v_int != v_int2 ) v_int2.push_back(2) v_int2.push_back(3) +v_int2.insert(0, 1) +v_int2.insert(0, 2) +v_int2.insert(0, 3) print(v_int2) v_a = VectorA() diff --git a/example/example17.ref b/example/example17.ref index 09489f1d..249fbabe 100644 --- a/example/example17.ref +++ b/example/example17.ref @@ -2,4 +2,4 @@ True True True -VectorInt[0, 1, 2, 3] +VectorInt[3, 2, 1, 0, 1, 2, 3] diff --git a/include/pybind11/common.h b/include/pybind11/common.h index 38f8bc8c..e60684fc 100644 --- a/include/pybind11/common.h +++ b/include/pybind11/common.h @@ -305,6 +305,7 @@ NAMESPACE_END(detail) class error_already_set : public std::runtime_error { public: error_already_set() : std::runtime_error(detail::error_string()) {} }; PYBIND11_RUNTIME_EXCEPTION(stop_iteration) PYBIND11_RUNTIME_EXCEPTION(index_error) +PYBIND11_RUNTIME_EXCEPTION(value_error) PYBIND11_RUNTIME_EXCEPTION(cast_error) /// Thrown when pybind11::cast or handle::call fail due to a type casting error [[noreturn]] PYBIND11_NOINLINE inline void pybind11_fail(const char *reason) { throw std::runtime_error(reason); } diff --git a/include/pybind11/stl_binders.h b/include/pybind11/stl_binders.h index 4dbd0b61..a2af0ac8 100644 --- a/include/pybind11/stl_binders.h +++ b/include/pybind11/stl_binders.h @@ -21,6 +21,11 @@ NAMESPACE_BEGIN(pybind11) +#if (!defined _MSC_VER) || (_MSC_VER > 1900) + #define INSERTION_OPERATOR_IMPLEMENTATION +#endif + + template constexpr auto has_equal_operator(int) -> decltype( std::declval() == std::declval(), bool()) { return true; } template @@ -34,7 +39,7 @@ template constexpr bool has_not_equal_operator(...) { return false; } - +#ifdef INSERTION_OPERATOR_IMPLEMENTATION namespace has_insertion_operator_implementation { enum class False {}; struct any_type { @@ -48,6 +53,7 @@ constexpr bool has_insertion_operator() using namespace has_insertion_operator_implementation; return std::is_same< decltype( std::declval() << std::declval() ), std::ostream & >::value; } +#endif template , typename holder_type = std::unique_ptr< std::vector > > @@ -88,6 +94,12 @@ class vector_binder 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) { + auto p = std::find(v.begin(), v.end(), t); + 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."); } template(0) >::type * = nullptr> void maybe_has_equal_operator(Class_ &) {} @@ -101,6 +113,7 @@ class vector_binder void maybe_has_not_equal_operator(Class_ &) {} + #ifdef INSERTION_OPERATOR_IMPLEMENTATION template() >::type * = nullptr> void maybe_has_insertion_operator(Class_ &cl, std::string name) { cl.def("__repr__", [name](typename vector_binder::Vector &v) { @@ -117,7 +130,7 @@ class vector_binder } template() >::type * = nullptr> void maybe_has_insertion_operator(Class_ &, char const *) {} - + #endif @@ -131,6 +144,18 @@ public: maybe_default_constructible(cl); maybe_copy_constructible(cl); + // Element access + cl.def("front", [](Vector &v) { + 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(); + 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"); @@ -139,32 +164,47 @@ public: 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 - 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"); + // 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"); + + + // 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() ) { + T t = v.back(); + v.pop_back(); + return t; + } + else throw pybind11::index_error(); + }, "insert an item at a given position"); + cl.def("erase", [](Vector &v, SizeType i) { - if (i >= v.size()) throw pybind11::index_error(); - v.erase( v.begin() + i ); - }, "erases element at index"); + if( i >= v.size() ) throw pybind11::index_error(); + v.erase( v.begin() + i ); + }, "erases element at index"); + // Python friendly bindings #ifdef PYTHON_ABI_VERSION // Python 3+ - cl.def("__bool__", [](Vector &v) -> bool { return v.size(); }); // checks whether the container has any elements in it + cl.def("__bool__", [](Vector &v) -> bool { return v.size() != 0; }); // checks whether the container has any elements in it #else - cl.def("__nonzero__", [](Vector &v) -> bool { return v.size(); }); // checks whether the container has any elements in it + cl.def("__nonzero__", [](Vector &v) -> bool { return v.size() != 0; }); // checks whether the container has any elements in it #endif cl.def("__getitem__", [](Vector const &v, SizeType i) { - if (i >= v.size()) throw pybind11::index_error(); - return v[i]; - }); + 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(); - v[i] = t; + if( i >= v.size() ) throw pybind11::index_error(); + v[i] = t; }); cl.def("__len__", &Vector::size); @@ -174,7 +214,9 @@ public: maybe_has_not_equal_operator(cl); // Printing + #ifdef INSERTION_OPERATOR_IMPLEMENTATION maybe_has_insertion_operator(cl, name); + #endif } }; From 25ac21903af7c798aaef9e5408a1b38be782c1c4 Mon Sep 17 00:00:00 2001 From: Sergey Lyskov Date: Sat, 7 May 2016 19:30:33 -0400 Subject: [PATCH 3/9] properly adding value_error exception --- include/pybind11/pybind11.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index bd0eb509..f144bfc6 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -394,6 +394,7 @@ protected: } } catch (const error_already_set &) { return nullptr; } catch (const index_error &e) { PyErr_SetString(PyExc_IndexError, e.what()); return nullptr; + } catch (const value_error &e) { PyErr_SetString(PyExc_ValueError, e.what()); return nullptr; } catch (const stop_iteration &e) { PyErr_SetString(PyExc_StopIteration, e.what()); return nullptr; } catch (const std::bad_alloc &e) { PyErr_SetString(PyExc_MemoryError, e.what()); return nullptr; } catch (const std::domain_error &e) { PyErr_SetString(PyExc_ValueError, e.what()); return nullptr; From a95bde1ea57f55799311d9c90434a95cb133b232 Mon Sep 17 00:00:00 2001 From: Sergey Lyskov Date: Sun, 8 May 2016 19:31:55 -0400 Subject: [PATCH 4/9] 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 | 5 +- example/example17.cpp | 28 +++++--- example/example17.py | 21 ++++-- example/example17.ref | 6 ++ include/pybind11/stl_binders.h | 122 +++++++++++++++++++++------------ 5 files changed, 121 insertions(+), 61 deletions(-) diff --git a/docs/advanced.rst b/docs/advanced.rst index e83ba273..837450ec 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 | @@ -1531,4 +1535,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 8c30457e..8370b5c6 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 d41a3254..52cda423 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 249fbabe..388e0f0d 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 a2af0ac8..7b0047d9 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 Date: Sun, 8 May 2016 20:41:39 -0400 Subject: [PATCH 5/9] Fixing VC build, apparently MSVC implementation of std::is_default_constructible had no bool() operator --- include/pybind11/stl_binders.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/include/pybind11/stl_binders.h b/include/pybind11/stl_binders.h index 7b0047d9..e5ec700e 100644 --- a/include/pybind11/stl_binders.h +++ b/include/pybind11/stl_binders.h @@ -69,7 +69,7 @@ class vector_binder { // template{} >::type * = nullptr> // void maybe_constructible(Class_ &cl) {} - template{} >::type * = nullptr> + template::value >::type * = nullptr> void maybe_default_constructible() { cl.def(pybind11::init()); cl.def("resize", (void (Vector::*)(SizeType count)) &Vector::resize, "changes the number of elements stored"); @@ -86,16 +86,16 @@ class vector_binder { return seq; }); } - template{} >::type * = nullptr> + template::value >::type * = nullptr> void maybe_default_constructible() {} - template{} >::type * = nullptr> + template::value >::type * = nullptr> void maybe_copy_constructible() { cl.def(pybind11::init< Vector const &>()); } - template{} >::type * = nullptr> - void vector_bind_maybe_copy_constructible(Class_ &) {} + template::value >::type * = nullptr> + void maybe_copy_constructible() {} template(0) >::type * = nullptr> From 2de5af998d1aca7d308e9a4127ea0631c1f6e542 Mon Sep 17 00:00:00 2001 From: Sergey Lyskov Date: Mon, 9 May 2016 15:57:35 -0400 Subject: [PATCH 6/9] Adding workarounds for MSVC 2015 --- include/pybind11/stl_binders.h | 44 ++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/include/pybind11/stl_binders.h b/include/pybind11/stl_binders.h index e5ec700e..7fcf635a 100644 --- a/include/pybind11/stl_binders.h +++ b/include/pybind11/stl_binders.h @@ -21,16 +21,18 @@ NAMESPACE_BEGIN(pybind11) -#if (!defined _MSC_VER) || (_MSC_VER > 1900) - #define INSERTION_OPERATOR_IMPLEMENTATION -#endif - template constexpr auto has_equal_operator(int) -> decltype(std::declval() == std::declval(), bool()) { return true; } template constexpr bool has_equal_operator(...) { return false; } +// Workaround for MSVC 2015 +template +struct has_equal_operator_s { + static const bool value = has_equal_operator(0); +}; + template @@ -38,8 +40,13 @@ constexpr auto has_not_equal_operator(int) -> decltype(std::declval() != std: template constexpr bool has_not_equal_operator(...) { return false; } +// Workaround for MSVC 2015 +template +struct has_not_equal_operator_s { + static const bool value = has_not_equal_operator(0); +}; + -#ifdef INSERTION_OPERATOR_IMPLEMENTATION namespace has_insertion_operator_implementation { enum class False {}; struct any_type { @@ -52,7 +59,13 @@ constexpr bool has_insertion_operator() { using namespace has_insertion_operator_implementation; return std::is_same< decltype(std::declval() << std::declval()), std::ostream & >::value; } -#endif + +// Workaround for MSVC 2015 +template +struct has_insertion_operator_s { + static const bool value = has_insertion_operator(); +}; + template , typename holder_type = std::unique_ptr< std::vector > > @@ -98,7 +111,7 @@ class vector_binder { void maybe_copy_constructible() {} - template(0) >::type * = nullptr> + template::value >::type * = nullptr> void maybe_has_equal_operator() { cl.def(pybind11::self == pybind11::self); cl.def(pybind11::self != pybind11::self); @@ -113,25 +126,24 @@ class vector_binder { 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> + template::value >::type * = nullptr> void maybe_has_equal_operator() {} - template(0) >::type * = nullptr> + template::value >::type * = nullptr> void maybe_has_not_equal_operator() { cl.def(pybind11::self != pybind11::self); } - template(0) >::type * = nullptr> + template::value >::type * = nullptr> void maybe_has_not_equal_operator() {} - #ifdef INSERTION_OPERATOR_IMPLEMENTATION - template() >::type * = nullptr> + template::value >::type * = nullptr> 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> + template::value >::type * = nullptr> void maybe_has_insertion_operator(char const *) {} - #endif - public: @@ -242,9 +252,7 @@ public: maybe_has_not_equal_operator(); // Printing - #ifdef INSERTION_OPERATOR_IMPLEMENTATION maybe_has_insertion_operator(name); - #endif // C++ style functions deprecated, leaving it here as an example //cl.def("empty", &Vector::empty, "checks whether the container is empty"); From 9ee4f92b063e5053d652ab7f55b7725bfe960dcf Mon Sep 17 00:00:00 2001 From: Sergey Lyskov Date: Tue, 10 May 2016 19:40:28 -0400 Subject: [PATCH 7/9] Refactoring vector_binder so it now a function --- example/example17.ref | 2 +- include/pybind11/stl_binders.h | 338 ++++++++++++++++----------------- 2 files changed, 168 insertions(+), 172 deletions(-) diff --git a/example/example17.ref b/example/example17.ref index 388e0f0d..d54c85ce 100644 --- a/example/example17.ref +++ b/example/example17.ref @@ -8,4 +8,4 @@ A constructor A destructor A constructor A destructor -VectorA[A{1}, A{2}] \ No newline at end of file +VectorA[A{1}, A{2}] diff --git a/include/pybind11/stl_binders.h b/include/pybind11/stl_binders.h index 7fcf635a..09871379 100644 --- a/include/pybind11/stl_binders.h +++ b/include/pybind11/stl_binders.h @@ -20,7 +20,7 @@ NAMESPACE_BEGIN(pybind11) - +NAMESPACE_BEGIN(detail) template constexpr auto has_equal_operator(int) -> decltype(std::declval() == std::declval(), bool()) { return true; } @@ -67,200 +67,196 @@ struct has_insertion_operator_s { }; +template::value >::type * = nullptr> +void vector_maybe_default_constructible(Class_ &cl) { + using size_type = typename Vector::size_type; + + cl.def(pybind11::init()); + cl.def("resize", (void (Vector::*)(size_type 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::value >::type * = nullptr> +void vector_maybe_default_constructible(Class_ &) {} + + +template::value >::type * = nullptr> +void vector_maybe_copy_constructible(Class_ &cl) { + cl.def(pybind11::init< Vector const &>()); +} +template::value >::type * = nullptr> +void vector_maybe_copy_constructible(Class_ &) {} + + +template::value >::type * = nullptr> +void vector_maybe_has_equal_operator(Class_ &cl) { + using T = typename Vector::value_type; + + 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, T const &t) { + auto p = std::find(v.begin(), v.end(), t); + 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::value >::type * = nullptr> +void vector_maybe_has_equal_operator(Class_ &) {} + + +template::value >::type * = nullptr> +void vector_maybe_has_not_equal_operator(Class_ &cl) { + cl.def(pybind11::self != pybind11::self); +} +template::value >::type * = nullptr> +void vector_maybe_has_not_equal_operator(Class_ &) {} + + +template::value >::type * = nullptr> +void vector_maybe_has_insertion_operator(char const *name, Class_ &cl) { + using size_type = typename Vector::size_type; + + cl.def("__repr__", [name](Vector &v) { + std::ostringstream s; + s << name << '['; + for(size_type i=0; i::value >::type * = nullptr> +void vector_maybe_has_insertion_operator(char const *, Class_ &) {} + + +NAMESPACE_END(detail) + template , typename holder_type = std::unique_ptr< std::vector > > -class vector_binder { +pybind11::class_, holder_type > vector_binder(pybind11::module &m, char const *name, char const *doc=nullptr) { using Vector = std::vector; using SizeType = typename Vector::size_type; - using Class_ = pybind11::class_; - // template{} >::type * = nullptr> - // void maybe_constructible(Class_ &cl) { - // cl.def(pybind11::init<>()); - // } - // template{} >::type * = nullptr> - // void maybe_constructible(Class_ &cl) {} + Class_ cl(m, name, doc); - template::value >::type * = nullptr> - void maybe_default_constructible() { - cl.def(pybind11::init()); - cl.def("resize", (void (Vector::*)(SizeType count)) &Vector::resize, "changes the number of elements stored"); + cl.def(pybind11::init<>()); - /// 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::value >::type * = nullptr> - void maybe_default_constructible() {} + detail::vector_maybe_default_constructible(cl); + detail::vector_maybe_copy_constructible(cl); + // Element access + cl.def("front", [](Vector &v) { + 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(); + 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"); - template::value >::type * = nullptr> - void maybe_copy_constructible() { - cl.def(pybind11::init< Vector const &>()); - } - template::value >::type * = nullptr> - void maybe_copy_constructible() {} + // 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("swap", &Vector::swap, "swaps the contents"); - template::value >::type * = nullptr> - 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, T const &t) { - auto p = std::find(v.begin(), v.end(), t); - 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::value >::type * = nullptr> - void maybe_has_equal_operator() {} - - - template::value >::type * = nullptr> - void maybe_has_not_equal_operator() { - cl.def(pybind11::self != pybind11::self); - } - template::value >::type * = nullptr> - void maybe_has_not_equal_operator() {} - - - template::value >::type * = nullptr> - void maybe_has_insertion_operator(char const *name) { - cl.def("__repr__", [name](typename vector_binder::Vector &v) { - std::ostringstream s; - s << name << '['; - for(SizeType i=0; i::value >::type * = nullptr> - void maybe_has_insertion_operator(char const *) {} - - -public: - 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(); - maybe_copy_constructible(); - - // Element access - cl.def("front", [](Vector &v) { - 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(); - 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, 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("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()) { - T t = v.back(); - v.pop_back(); - return t; - } - else throw pybind11::index_error(); - }, "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); + // 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()) { + T t = v.back(); + v.pop_back(); return t; - }, "remove and return item at index"); + } + else throw pybind11::index_error(); + }, "remove and return last item"); - cl.def("erase", [](Vector &v, SizeType i) { - if(i >= v.size()) throw pybind11::index_error(); - v.erase(v.begin() + i); - }, "erases element at index"); + 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); + }, "erases element at index"); - // Python friendly bindings - #ifdef PYTHON_ABI_VERSION // Python 3+ - cl.def("__bool__", [](Vector &v) -> bool { return v.size() != 0; }); // checks whether the container has any elements in it - #else - cl.def("__nonzero__", [](Vector &v) -> bool { return v.size() != 0; }); // checks whether the container has any elements in it - #endif + // Python friendly bindings + #ifdef PYTHON_ABI_VERSION // Python 3+ + cl.def("__bool__", [](Vector &v) -> bool { return v.size() != 0; }); // checks whether the container has any elements in it + #else + cl.def("__nonzero__", [](Vector &v) -> bool { return v.size() != 0; }); // checks whether the container has any elements in it + #endif - cl.def("__getitem__", [](Vector const &v, SizeType i) { - 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(); - v[i] = t; + cl.def("__getitem__", [](Vector const &v, SizeType i) { + if(i >= v.size()) throw pybind11::index_error(); + return v[i]; }); - cl.def("__len__", &Vector::size); + cl.def("__setitem__", [](Vector &v, SizeType i, T const & t) { + if(i >= v.size()) throw pybind11::index_error(); + v[i] = t; + }); - 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 */); + cl.def("__len__", &Vector::size); - /// 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() /* Essential: keep object alive while iterator exists */); - // Comparisons - maybe_has_equal_operator(); - maybe_has_not_equal_operator(); + /// 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(cl); + detail::vector_maybe_has_not_equal_operator(cl); - // 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"); - } -}; + // Printing + detail::vector_maybe_has_insertion_operator(name, cl); + + // 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"); + + return cl; +} NAMESPACE_END(pybind11) From 26aca3d8adb7c81a93963f321d0740d740f36ebb Mon Sep 17 00:00:00 2001 From: Sergey Lyskov Date: Wed, 11 May 2016 18:35:47 -0400 Subject: [PATCH 8/9] =?UTF-8?q?Adding=20vector=20=E2=80=99extend=E2=80=99?= =?UTF-8?q?=20member=20function?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/pybind11/stl_binders.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/pybind11/stl_binders.h b/include/pybind11/stl_binders.h index 09871379..93b5cad3 100644 --- a/include/pybind11/stl_binders.h +++ b/include/pybind11/stl_binders.h @@ -103,7 +103,6 @@ void vector_maybe_has_equal_operator(Class_ &cl) { using T = typename Vector::value_type; 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"); @@ -186,6 +185,7 @@ pybind11::class_, holder_type > vector_binder(pybind11 // 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("extend", [](Vector &v, Vector &src) { v.reserve( v.size() + src.size() ); v.insert(v.end(), src.begin(), src.end()); }, "extend the list by appending all the items in the given vector"); cl.def("pop", [](Vector &v) { if(v.size()) { T t = v.back(); From 25c03cecfaa8fef2137e1f2c11c6e534177cbc86 Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Sun, 15 May 2016 20:50:38 +0200 Subject: [PATCH 9/9] stl_bind redesign & cleanup pass --- CMakeLists.txt | 1 + README.md | 1 + example/example17.cpp | 25 ++- example/example17.py | 21 +- example/example17.ref | 9 +- include/pybind11/pybind11.h | 2 +- include/pybind11/stl_bind.h | 349 +++++++++++++++++++++++++++++++++ include/pybind11/stl_binders.h | 264 ------------------------- setup.py | 1 + 9 files changed, 384 insertions(+), 289 deletions(-) create mode 100644 include/pybind11/stl_bind.h delete mode 100644 include/pybind11/stl_binders.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 24dd8cbc..3f10bd37 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -108,6 +108,7 @@ set(PYBIND11_HEADERS include/pybind11/pybind11.h include/pybind11/pytypes.h include/pybind11/stl.h + include/pybind11/stl_bind.h include/pybind11/typeid.h ) diff --git a/README.md b/README.md index 93e30dc6..376f5629 100644 --- a/README.md +++ b/README.md @@ -99,6 +99,7 @@ Jonas Adler, Sylvain Corlay, Axel Huebl, @hulucc, +Sergey Lyskov Johan Mabille, Tomasz Miąsko, and Ben Pritchard. diff --git a/example/example17.cpp b/example/example17.cpp index 8370b5c6..87902344 100644 --- a/example/example17.cpp +++ b/example/example17.cpp @@ -1,7 +1,7 @@ /* example/example17.cpp -- Usage of stl_binders functions - Copyright (c) 2016 Wenzel Jakob + Copyright (c) 2016 Sergey Lyskov All rights reserved. Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. @@ -9,29 +9,28 @@ #include "example.h" -#include +#include - -class A { +class El { public: - A() = delete; - A(int v) :a(v) {} + El() = delete; + El(int v) : a(v) { } int a; }; - -std::ostream & operator<<(std::ostream &s, A const&v) { - s << "A{" << v.a << '}'; +std::ostream & operator<<(std::ostream &s, El const&v) { + s << "El{" << v.a << '}'; return s; } - void init_ex17(py::module &m) { - pybind11::class_(m, "A") + pybind11::class_(m, "El") .def(pybind11::init()); - pybind11::vector_binder(m, "VectorInt"); + pybind11::bind_vector(m, "VectorInt"); - pybind11::vector_binder(m, "VectorA"); + pybind11::bind_vector(m, "VectorEl"); + + pybind11::bind_vector>(m, "VectorVectorEl"); } diff --git a/example/example17.py b/example/example17.py index 52cda423..65e586bc 100644 --- a/example/example17.py +++ b/example/example17.py @@ -1,14 +1,14 @@ #!/usr/bin/env python from __future__ import print_function -from example import VectorInt, VectorA, A +from example import VectorInt, El, VectorEl, VectorVectorEl -v_int = VectorInt(2) +v_int = VectorInt([0, 0]) print(len(v_int)) print(bool(v_int)) -v_int2 = VectorInt(2) +v_int2 = VectorInt([0, 0]) print(v_int == v_int2) v_int2[1] = 1 @@ -24,8 +24,17 @@ print(v_int2) v_int.append(99) v_int2[2:-2] = v_int print(v_int2) +del v_int2[1:3] +print(v_int2) +del v_int2[0] +print(v_int2) -v_a = VectorA() -v_a.append(A(1)) -v_a.append(A(2)) +v_a = VectorEl() +v_a.append(El(1)) +v_a.append(El(2)) print(v_a) + +vv_a = VectorVectorEl() +vv_a.append(v_a) +vv_b = vv_a[0] +print(vv_b) diff --git a/example/example17.ref b/example/example17.ref index d54c85ce..55e47a68 100644 --- a/example/example17.ref +++ b/example/example17.ref @@ -4,8 +4,7 @@ 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}] +VectorInt[3, 0, 99, 2, 3] +VectorInt[0, 99, 2, 3] +VectorEl[El{1}, El{2}] +VectorEl[El{1}, El{2}] diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index f144bfc6..85e3792b 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -212,7 +212,7 @@ protected: rec->is_constructor = !strcmp(rec->name, "__init__"); rec->has_args = false; rec->has_kwargs = false; - rec->nargs = args; + rec->nargs = (uint16_t) args; #if PY_MAJOR_VERSION < 3 if (rec->sibling && PyMethod_Check(rec->sibling.ptr())) diff --git a/include/pybind11/stl_bind.h b/include/pybind11/stl_bind.h new file mode 100644 index 00000000..e27dcfd9 --- /dev/null +++ b/include/pybind11/stl_bind.h @@ -0,0 +1,349 @@ +/* + pybind11/std_bind.h: Binding generators for STL data types + + Copyright (c) 2016 Sergey Lyskov and Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "common.h" +#include "operators.h" + +#include +#include +#include +#include + +NAMESPACE_BEGIN(pybind11) +NAMESPACE_BEGIN(detail) + +/* SFINAE helper class used by 'is_comparable */ +template struct container_traits { + template static std::true_type test_comparable(decltype(std::declval() == std::declval())*); + template static std::false_type test_comparable(...); + template static std::true_type test_value(typename T2::value_type *); + template static std::false_type test_value(...); + template static std::true_type test_pair(typename T2::first_type *, typename T2::second_type *); + template static std::false_type test_pair(...); + + static constexpr const bool is_comparable = std::is_same(nullptr))>::value; + static constexpr const bool is_pair = std::is_same(nullptr, nullptr))>::value; + static constexpr const bool is_vector = std::is_same(nullptr))>::value; + static constexpr const bool is_element = !is_pair && !is_vector; +}; + +/* Default: is_comparable -> std::false_type */ +template +struct is_comparable : std::false_type { }; + +/* For non-map data structures, check whether operator== can be instantiated */ +template +struct is_comparable< + T, typename std::enable_if::is_element && + container_traits::is_comparable>::type> + : std::true_type { }; + +/* For a vector/map data structure, recursively check the value type (which is std::pair for maps) */ +template +struct is_comparable::is_vector>::type> { + static constexpr const bool value = + is_comparable::value; +}; + +/* For pairs, recursively check the two data types */ +template +struct is_comparable::is_pair>::type> { + static constexpr const bool value = + is_comparable::value && + is_comparable::value; +}; + +/* Fallback functions */ +template void vector_if_copy_constructible(const Args&...) { } +template void vector_if_equal_operator(const Args&...) { } +template void vector_if_insertion_operator(const Args&...) { } + +template::value, int>::type = 0> +void vector_if_copy_constructible(Class_ &cl) { + cl.def(pybind11::init(), + "Copy constructor"); +} + +template::value, int>::type = 0> +void vector_if_equal_operator(Class_ &cl) { + using T = typename Vector::value_type; + + cl.def(self == self); + cl.def(self != self); + + cl.def("count", + [](const Vector &v, const T &x) { + return std::count(v.begin(), v.end(), x); + }, + arg("x"), + "Return the number of times ``x`` appears in the list" + ); + + cl.def("remove", [](Vector &v, const T &x) { + auto p = std::find(v.begin(), v.end(), x); + if (p != v.end()) + v.erase(p); + else + throw pybind11::value_error(); + }, + arg("x"), + "Remove the first item from the list whose value is x. " + "It is an error if there is no such item." + ); + + cl.def("__contains__", + [](const Vector &v, const T &x) { + return std::find(v.begin(), v.end(), x) != v.end(); + }, + arg("x"), + "Return true the container contains ``x``" + ); +} + +template auto vector_if_insertion_operator(Class_ &cl, const char *name) + -> decltype(std::declval() << std::declval(), void()) { + using size_type = typename Vector::size_type; + + cl.def("__repr__", + [name](Vector &v) { + std::ostringstream s; + s << name << '['; + for (size_type i=0; i < v.size(); ++i) { + s << v[i]; + if (i != v.size() - 1) + s << ", "; + } + s << ']'; + return s.str(); + }, + "Return the canonical string representation of this list." + ); +} + +NAMESPACE_END(detail) + + +template , typename holder_type = std::unique_ptr>, typename... Args> +pybind11::class_, holder_type> bind_vector(pybind11::module &m, const char *name, Args&&... args) { + using Vector = std::vector; + using SizeType = typename Vector::size_type; + using Class_ = pybind11::class_; + + Class_ cl(m, name, std::forward(args)...); + + cl.def(pybind11::init<>()); + + detail::vector_if_copy_constructible(cl); + + cl.def("__init__", [](Vector &v, iterable it) { + new (&v) Vector(); + try { + v.reserve(len(it)); + for (handle h : it) + v.push_back(h.cast()); + } catch (...) { + v.~Vector(); + throw; + } + }); + + cl.def("append", (void (Vector::*) (const T &)) & Vector::push_back, + arg("x"), + "Add an item to the end of the list"); + + cl.def("extend", + [](Vector &v, Vector &src) { + v.reserve(v.size() + src.size()); + v.insert(v.end(), src.begin(), src.end()); + }, + arg("L"), + "Extend the list by appending all the items in the given list" + ); + + cl.def("insert", + [](Vector &v, SizeType i, const T &x) { + v.insert(v.begin() + i, x); + }, + arg("i") , arg("x"), + "Insert an item at a given position." + ); + + cl.def("pop", + [](Vector &v) { + if (v.empty()) + throw pybind11::index_error(); + T t = v.back(); + v.pop_back(); + return t; + }, + "Remove and return the 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; + }, + arg("i"), + "Remove and return the item at index ``i``" + ); + + cl.def("__bool__", + [](const Vector &v) -> bool { + return !v.empty(); + }, + "Check whether the list is nonempty" + ); + + cl.def("__getitem__", + [](const Vector &v, SizeType i) { + if (i >= v.size()) + throw pybind11::index_error(); + return v[i]; + } + ); + + cl.def("__setitem__", + [](Vector &v, SizeType i, const T &t) { + if (i >= v.size()) + throw pybind11::index_error(); + v[i] = t; + } + ); + + cl.def("__delitem__", + [](Vector &v, SizeType i) { + if (i >= v.size()) + throw pybind11::index_error(); + v.erase(v.begin() + i); + }, + "Delete list elements using a slice object" + ); + + 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 list alive while iterator exists */ + ); + + /// Slicing protocol + cl.def("__getitem__", + [](const Vector &v, slice slice) -> Vector * { + ssize_t start, stop, step, slicelength; + + if (!slice.compute(v.size(), &start, &stop, &step, &slicelength)) + throw pybind11::error_already_set(); + + Vector *seq = new Vector(); + seq->reserve((size_t) slicelength); + + for (int i=0; ipush_back(v[start]); + start += step; + } + return seq; + }, + arg("s"), + "Retrieve list elements using a slice object" + ); + + cl.def("__setitem__", + [](Vector &v, slice slice, const Vector &value) { + 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(cl); + + // Printing + detail::vector_if_insertion_operator(cl, name); + +#if 0 + // C++ style functions deprecated, leaving it here as an example + cl.def(pybind11::init()); + + cl.def("resize", + (void (Vector::*) (size_type count)) & Vector::resize, + "changes the number of elements stored"); + + cl.def("erase", + [](Vector &v, SizeType i) { + if (i >= v.size()) + throw pybind11::index_error(); + v.erase(v.begin() + i); + }, "erases element at index ``i``"); + + 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"); + + 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"); + + cl.def("clear", &Vector::clear, "clears the contents"); + cl.def("swap", &Vector::swap, "swaps the contents"); + + cl.def("front", [](Vector &v) { + 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(); + else throw pybind11::index_error(); + }, "access the last element "); + +#endif + + return cl; +} + +NAMESPACE_END(pybind11) diff --git a/include/pybind11/stl_binders.h b/include/pybind11/stl_binders.h deleted file mode 100644 index 93b5cad3..00000000 --- a/include/pybind11/stl_binders.h +++ /dev/null @@ -1,264 +0,0 @@ -/* - pybind11/std_binders.h: Convenience wrapper functions for STL containers with C++ like interface - - Copyright (c) 2016 Sergey Lyskov - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#ifndef _INCLUDED_std_binders_h_ -#define _INCLUDED_std_binders_h_ - -#include "common.h" -#include "operators.h" - -#include -#include -#include -#include - - -NAMESPACE_BEGIN(pybind11) -NAMESPACE_BEGIN(detail) - -template -constexpr auto has_equal_operator(int) -> decltype(std::declval() == std::declval(), bool()) { return true; } -template -constexpr bool has_equal_operator(...) { return false; } - -// Workaround for MSVC 2015 -template -struct has_equal_operator_s { - static const bool value = has_equal_operator(0); -}; - - - -template -constexpr auto has_not_equal_operator(int) -> decltype(std::declval() != std::declval(), bool()) { return true; } -template -constexpr bool has_not_equal_operator(...) { return false; } - -// Workaround for MSVC 2015 -template -struct has_not_equal_operator_s { - static const bool value = has_not_equal_operator(0); -}; - - -namespace has_insertion_operator_implementation { -enum class False {}; -struct any_type { - template any_type(T const&); -}; -False operator<<(std::ostream const&, any_type const&); -} -template -constexpr bool has_insertion_operator() { - using namespace has_insertion_operator_implementation; - return std::is_same< decltype(std::declval() << std::declval()), std::ostream & >::value; -} - -// Workaround for MSVC 2015 -template -struct has_insertion_operator_s { - static const bool value = has_insertion_operator(); -}; - - -template::value >::type * = nullptr> -void vector_maybe_default_constructible(Class_ &cl) { - using size_type = typename Vector::size_type; - - cl.def(pybind11::init()); - cl.def("resize", (void (Vector::*)(size_type 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::value >::type * = nullptr> -void vector_maybe_default_constructible(Class_ &) {} - - -template::value >::type * = nullptr> -void vector_maybe_copy_constructible(Class_ &cl) { - cl.def(pybind11::init< Vector const &>()); -} -template::value >::type * = nullptr> -void vector_maybe_copy_constructible(Class_ &) {} - - -template::value >::type * = nullptr> -void vector_maybe_has_equal_operator(Class_ &cl) { - using T = typename Vector::value_type; - - 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, T const &t) { - auto p = std::find(v.begin(), v.end(), t); - 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::value >::type * = nullptr> -void vector_maybe_has_equal_operator(Class_ &) {} - - -template::value >::type * = nullptr> -void vector_maybe_has_not_equal_operator(Class_ &cl) { - cl.def(pybind11::self != pybind11::self); -} -template::value >::type * = nullptr> -void vector_maybe_has_not_equal_operator(Class_ &) {} - - -template::value >::type * = nullptr> -void vector_maybe_has_insertion_operator(char const *name, Class_ &cl) { - using size_type = typename Vector::size_type; - - cl.def("__repr__", [name](Vector &v) { - std::ostringstream s; - s << name << '['; - for(size_type i=0; i::value >::type * = nullptr> -void vector_maybe_has_insertion_operator(char const *, Class_ &) {} - - -NAMESPACE_END(detail) - - -template , typename holder_type = std::unique_ptr< std::vector > > -pybind11::class_, holder_type > vector_binder(pybind11::module &m, char const *name, char const *doc=nullptr) { - using Vector = std::vector; - using SizeType = typename Vector::size_type; - using Class_ = pybind11::class_; - - Class_ cl(m, name, doc); - - cl.def(pybind11::init<>()); - - detail::vector_maybe_default_constructible(cl); - detail::vector_maybe_copy_constructible(cl); - - // Element access - cl.def("front", [](Vector &v) { - 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(); - 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, 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("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("extend", [](Vector &v, Vector &src) { v.reserve( v.size() + src.size() ); v.insert(v.end(), src.begin(), src.end()); }, "extend the list by appending all the items in the given vector"); - cl.def("pop", [](Vector &v) { - if(v.size()) { - T t = v.back(); - v.pop_back(); - return t; - } - else throw pybind11::index_error(); - }, "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); - }, "erases element at index"); - - - // Python friendly bindings - #ifdef PYTHON_ABI_VERSION // Python 3+ - cl.def("__bool__", [](Vector &v) -> bool { return v.size() != 0; }); // checks whether the container has any elements in it - #else - cl.def("__nonzero__", [](Vector &v) -> bool { return v.size() != 0; }); // checks whether the container has any elements in it - #endif - - cl.def("__getitem__", [](Vector const &v, SizeType i) { - 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(); - 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(cl); - detail::vector_maybe_has_not_equal_operator(cl); - - // Printing - detail::vector_maybe_has_insertion_operator(name, cl); - - // 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"); - - return cl; -} - - -NAMESPACE_END(pybind11) - -#endif // _INCLUDED_std_binders_h_ diff --git a/setup.py b/setup.py index 4c6e156a..07465bb6 100644 --- a/setup.py +++ b/setup.py @@ -24,6 +24,7 @@ setup( 'include/pybind11/numpy.h', 'include/pybind11/pybind11.h', 'include/pybind11/stl.h', + 'include/pybind11/stl_bind.h', 'include/pybind11/common.h', 'include/pybind11/functional.h', 'include/pybind11/operators.h',