/* 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)