diff --git a/CMakeLists.txt b/CMakeLists.txt index 38297dbbb..24dd8cbc4 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 b4199e8aa..470684a3b 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 000000000..8c30457e8 --- /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 000000000..538528022 --- /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 000000000..09489f1d2 --- /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 000000000..4dbd0b61c --- /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_