eigenpy 3.12.0
Bindings between Numpy and Eigen using Boost.Python
Loading...
Searching...
No Matches
std-vector.hpp
1
6
7#ifndef __eigenpy_utils_std_vector_hpp__
8#define __eigenpy_utils_std_vector_hpp__
9
10#include <boost/mpl/if.hpp>
11#include <boost/python.hpp>
12#include <boost/python/stl_iterator.hpp>
13#include <boost/python/suite/indexing/vector_indexing_suite.hpp>
14#include <iterator>
15#include <string>
16#include <vector>
17
18#include "eigenpy/eigenpy.hpp"
19#include "eigenpy/config.hpp"
20#include "eigenpy/copyable.hpp"
21#include "eigenpy/eigen-to-python.hpp"
22#include "eigenpy/pickle-vector.hpp"
23#include "eigenpy/registration.hpp"
24#include "eigenpy/utils/empty-visitor.hpp"
25
26namespace eigenpy {
27// Forward declaration
28template <typename vector_type, bool NoProxy = false>
30
31namespace details {
32
34template <typename T>
35bool from_python_list(PyObject *obj_ptr, T *) {
36 // Check if it is a list
37 if (!PyList_Check(obj_ptr)) return false;
38
39 // Retrieve the underlying list
40 bp::object bp_obj(bp::handle<>(bp::borrowed(obj_ptr)));
41 bp::list bp_list(bp_obj);
42 bp::ssize_t list_size = bp::len(bp_list);
43
44 // Check if all the elements contained in the current vector is of type T
45 for (bp::ssize_t k = 0; k < list_size; ++k) {
46 bp::extract<T> elt(bp_list[k]);
47 if (!elt.check()) return false;
48 }
49
50 return true;
51}
52
53template <typename vector_type, bool NoProxy>
54struct build_list {
55 static ::boost::python::list run(vector_type &vec, const bool deep_copy) {
56 if (deep_copy) return build_list<vector_type, true>::run(vec, true);
57
58 bp::list bp_list;
59 for (size_t k = 0; k < vec.size(); ++k) {
60 bp_list.append(boost::ref(vec[k]));
61 }
62 return bp_list;
63 }
64};
65
66template <typename vector_type>
67struct build_list<vector_type, true> {
68 static ::boost::python::list run(vector_type &vec, const bool) {
69 typedef bp::iterator<vector_type> iterator;
70 return bp::list(iterator()(vec));
71 }
72};
73
77template <typename Container>
79 : public boost::python::def_visitor<
80 overload_base_get_item_for_std_vector<Container>> {
81 typedef typename Container::value_type value_type;
82 typedef typename Container::value_type data_type;
83 typedef size_t index_type;
84
85 template <class Class>
86 void visit(Class &cl) const {
87 cl.def("__getitem__", &base_get_item);
88 }
89
90 private:
91 static boost::python::object base_get_item(
92 boost::python::back_reference<Container &> container, PyObject *i_) {
93 index_type idx = convert_index(container.get(), i_);
94 typename Container::iterator i = container.get().begin();
95 std::advance(i, idx);
96 if (i == container.get().end()) {
97 PyErr_SetString(PyExc_KeyError, "Invalid index");
98 bp::throw_error_already_set();
99 }
100
101 typename bp::to_python_indirect<data_type &,
102 bp::detail::make_reference_holder>
103 convert;
104 return bp::object(bp::handle<>(convert(*i)));
105 }
106
107 static index_type convert_index(Container &container, PyObject *i_) {
108 bp::extract<long> i(i_);
109 if (i.check()) {
110 long index = i();
111 if (index < 0) index += (long)container.size();
112 if (index >= long(container.size()) || index < 0) {
113 PyErr_SetString(PyExc_IndexError, "Index out of range");
114 bp::throw_error_already_set();
115 }
116 return (index_type)index;
117 }
118
119 PyErr_SetString(PyExc_TypeError, "Invalid index type");
120 bp::throw_error_already_set();
121 return index_type();
122 }
123};
124} // namespace details
125} // namespace eigenpy
126
127namespace boost {
128namespace python {
129
130template <typename MatrixType>
131struct extract_to_eigen_ref
132 : converter::extract_rvalue<Eigen::Ref<MatrixType>> {
133 typedef Eigen::Ref<MatrixType> RefType;
134
135 protected:
136 typedef converter::extract_rvalue<RefType> base;
137
138 public:
139 typedef RefType result_type;
140
141 operator result_type() const { return (*this)(); }
142
143 extract_to_eigen_ref(PyObject *o) : base(o) {}
144 extract_to_eigen_ref(api::object const &o) : base(o.ptr()) {}
145};
146
149template <typename Scalar, int Rows, int Cols, int Options, int MaxRows,
150 int MaxCols>
151struct extract<Eigen::Matrix<Scalar, Rows, Cols, Options, MaxRows, MaxCols> &>
152 : extract_to_eigen_ref<
153 Eigen::Matrix<Scalar, Rows, Cols, Options, MaxRows, MaxCols>> {
154 typedef Eigen::Matrix<Scalar, Rows, Cols, Options, MaxRows, MaxCols>
155 MatrixType;
156 typedef extract_to_eigen_ref<MatrixType> base;
157 extract(PyObject *o) : base(o) {}
158 extract(api::object const &o) : base(o.ptr()) {}
159};
160
161template <typename Derived>
162struct extract<Eigen::MatrixBase<Derived> &>
163 : extract_to_eigen_ref<Eigen::MatrixBase<Derived>> {
164 typedef Eigen::MatrixBase<Derived> MatrixType;
165 typedef extract_to_eigen_ref<MatrixType> base;
166 extract(PyObject *o) : base(o) {}
167 extract(api::object const &o) : base(o.ptr()) {}
168};
169
170template <typename Derived>
171struct extract<Eigen::RefBase<Derived> &>
172 : extract_to_eigen_ref<Eigen::RefBase<Derived>> {
173 typedef Eigen::RefBase<Derived> MatrixType;
174 typedef extract_to_eigen_ref<MatrixType> base;
175 extract(PyObject *o) : base(o) {}
176 extract(api::object const &o) : base(o.ptr()) {}
177};
178
179namespace converter {
180
181template <typename Type, class Allocator>
182struct reference_arg_from_python<std::vector<Type, Allocator> &>
183 : arg_lvalue_from_python_base {
184 typedef std::vector<Type, Allocator> vector_type;
185 typedef vector_type &ref_vector_type;
186 typedef ref_vector_type result_type;
187 typedef extract<Type &> extract_type;
188
189 reference_arg_from_python(PyObject *py_obj)
190 : arg_lvalue_from_python_base(converter::get_lvalue_from_python(
191 py_obj, registered<vector_type>::converters)),
192 m_data(NULL),
193 m_source(py_obj),
194 vec_ptr(NULL) {
195 if (result() != 0) // we have found a lvalue converter
196 return;
197
198 // Check if py_obj is a py_list, which can then be converted to an
199 // std::vector
200 bool is_convertible =
201 ::eigenpy::details::from_python_list(py_obj, (Type *)(0));
202 if (!is_convertible) return;
203
204 typedef ::eigenpy::StdContainerFromPythonList<vector_type> Constructor;
205 Constructor::construct(py_obj, &m_data.stage1);
206
207 void *&m_result = const_cast<void *&>(result());
208 m_result = m_data.stage1.convertible;
209 vec_ptr = reinterpret_cast<vector_type *>(m_data.storage.bytes);
210 }
211
212 result_type operator()() const {
213 return ::boost::python::detail::void_ptr_to_reference(result(),
214 (result_type (*)())0);
215 }
216
217 ~reference_arg_from_python() {
218 if (m_data.stage1.convertible == m_data.storage.bytes) {
219 // Copy back the reference
220 const vector_type &vec = *vec_ptr;
221 list bp_list(handle<>(borrowed(m_source)));
222 for (size_t i = 0; i < vec.size(); ++i) {
223 typename extract_type::result_type elt = extract_type(bp_list[i]);
224 elt = vec[i];
225 }
226 }
227 }
228
229 private:
230 rvalue_from_python_data<ref_vector_type> m_data;
231 PyObject *m_source;
232 vector_type *vec_ptr;
233};
234
235} // namespace converter
236} // namespace python
237} // namespace boost
238
239namespace eigenpy {
240
241namespace details {
243template <class Container>
245 // default behavior expects allocators
246 typedef typename Container::allocator_type Allocator;
247};
248
249template <typename _Tp, std::size_t Size>
250struct container_traits<std::array<_Tp, Size>> {
251 typedef void Allocator;
252};
253}; // namespace details
254
260template <typename vector_type, bool NoProxy>
262 typedef typename vector_type::value_type T;
263 typedef typename details::container_traits<vector_type>::Allocator Allocator;
264
266 static void *convertible(PyObject *obj_ptr) {
267 namespace bp = boost::python;
268
269 // Check if it is a list
270 if (!PyList_Check(obj_ptr)) return 0;
271
272 // Retrieve the underlying list
273 bp::object bp_obj(bp::handle<>(bp::borrowed(obj_ptr)));
274 bp::list bp_list(bp_obj);
275 bp::ssize_t list_size = bp::len(bp_list);
276
277 // Check if all the elements contained in the current vector is of type T
278 for (bp::ssize_t k = 0; k < list_size; ++k) {
279 bp::extract<T> elt(bp_list[k]);
280 if (!elt.check()) return 0;
281 }
282
283 return obj_ptr;
284 }
285
288 static void construct(
289 PyObject *obj_ptr,
290 boost::python::converter::rvalue_from_python_stage1_data *memory) {
291 // Extract the list
292 bp::object bp_obj(bp::handle<>(bp::borrowed(obj_ptr)));
293 bp::list bp_list(bp_obj);
294
295 void *storage =
296 reinterpret_cast<
297 bp::converter::rvalue_from_python_storage<vector_type> *>(
298 reinterpret_cast<void *>(memory))
299 ->storage.bytes;
300
301 typedef bp::stl_input_iterator<T> iterator;
302
303 // Build the std::vector
304 new (storage) vector_type(iterator(bp_list), iterator());
305
306 // Validate the construction
307 memory->convertible = storage;
308 }
309
310 static void register_converter() {
311 ::boost::python::converter::registry::push_back(
312 &convertible, &construct, ::boost::python::type_id<vector_type>());
313 }
314
315 static ::boost::python::list tolist(vector_type &self,
316 const bool deep_copy = false) {
317 return details::build_list<vector_type, NoProxy>::run(self, deep_copy);
318 }
319};
320
321namespace internal {
322
323template <typename T,
324 bool has_operator_equal_value =
325 std::is_base_of<std::true_type, has_operator_equal<T>>::value>
326struct contains_algo;
327
328template <typename T>
329struct contains_algo<T, true> {
330 template <class Container, typename key_type>
331 static bool run(const Container &container, key_type const &key) {
332 return std::find(container.begin(), container.end(), key) !=
333 container.end();
334 }
335};
336
337template <typename T>
338struct contains_algo<T, false> {
339 template <class Container, typename key_type>
340 static bool run(const Container &container, key_type const &key) {
341 for (size_t k = 0; k < container.size(); ++k) {
342 if (&container[k] == &key) return true;
343 }
344 return false;
345 }
346};
347
348template <class Container, bool NoProxy>
349struct contains_vector_derived_policies
350 : public ::boost::python::vector_indexing_suite<
351 Container, NoProxy,
352 contains_vector_derived_policies<Container, NoProxy>> {
353 typedef typename Container::value_type key_type;
354
355 static bool contains(Container &container, key_type const &key) {
356 return contains_algo<key_type>::run(container, key);
357 }
358};
359
365template <typename Container, bool NoProxy, typename CoVisitor>
366struct ExposeStdMethodToStdVector
367 : public boost::python::def_visitor<
368 ExposeStdMethodToStdVector<Container, NoProxy, CoVisitor>> {
369 typedef StdContainerFromPythonList<Container, NoProxy>
370 FromPythonListConverter;
371
372 ExposeStdMethodToStdVector(const CoVisitor &co_visitor)
373 : m_co_visitor(co_visitor) {}
374
375 template <class Class>
376 void visit(Class &cl) const {
377 cl.def(m_co_visitor)
378 .def("tolist", &FromPythonListConverter::tolist,
379 (bp::arg("self"), bp::arg("deep_copy") = false),
380 "Returns the std::vector as a Python list.")
381 .def("reserve", &Container::reserve,
382 (bp::arg("self"), bp::arg("new_cap")),
383 "Increase the capacity of the vector to a value that's greater "
384 "or equal to new_cap.")
385 .def(CopyableVisitor<Container>());
386 }
387
388 const CoVisitor &m_co_visitor;
389};
390
392template <typename Container, bool NoProxy, typename CoVisitor>
393static ExposeStdMethodToStdVector<Container, NoProxy, CoVisitor>
394createExposeStdMethodToStdVector(const CoVisitor &co_visitor) {
395 return ExposeStdMethodToStdVector<Container, NoProxy, CoVisitor>(co_visitor);
396}
397
398} // namespace internal
399
400namespace internal {
401template <typename vector_type, bool T_picklable = false>
402struct def_pickle_std_vector {
403 static void run(bp::class_<vector_type> &) {}
404};
405
406template <typename vector_type>
407struct def_pickle_std_vector<vector_type, true> {
408 static void run(bp::class_<vector_type> &cl) {
409 cl.def_pickle(PickleVector<vector_type>());
410 }
411};
412} // namespace internal
413
422template <class vector_type, bool NoProxy = false,
423 bool EnableFromPythonListConverter = true, bool pickable = true>
425 typedef typename vector_type::value_type value_type;
427 FromPythonListConverter;
428
429 static void expose(const std::string &class_name,
430 const std::string &doc_string = "") {
431 expose(class_name, doc_string, EmptyPythonVisitor());
432 }
433
434 template <typename DerivedVisitor>
435 static void expose(const std::string &class_name,
436 const bp::def_visitor<DerivedVisitor> &visitor) {
437 expose(class_name, "", visitor);
438 }
439
440 template <typename DerivedVisitor>
441 static void expose(const std::string &class_name,
442 const std::string &doc_string,
443 const bp::def_visitor<DerivedVisitor> &visitor) {
444 // Apply visitor on already registered type or if type is not already
445 // registered, we define and apply the visitor on it
446 auto add_std_visitor =
447 internal::createExposeStdMethodToStdVector<vector_type, NoProxy>(
448 visitor);
450 add_std_visitor)) {
451 bp::class_<vector_type> cl(class_name.c_str(), doc_string.c_str());
452 cl.def(IdVisitor<vector_type>());
453
454 // Standard vector indexing definition
455 boost::python::vector_indexing_suite<
456 vector_type, NoProxy,
457 internal::contains_vector_derived_policies<vector_type, NoProxy>>
458 vector_indexing;
459
460 cl.def(bp::init<size_t, const value_type &>(
461 bp::args("self", "size", "value"),
462 "Constructor from a given size and a given value."))
463 .def(bp::init<const vector_type &>(bp::args("self", "other"),
464 "Copy constructor"))
465
466 .def(vector_indexing)
467 .def(add_std_visitor);
468
469 internal::def_pickle_std_vector<vector_type, pickable>::run(cl);
470 }
471 if (EnableFromPythonListConverter) {
472 // Register conversion
473 FromPythonListConverter::register_converter();
474 }
475 }
476};
477
481void EIGENPY_DLLAPI exposeStdVector();
482
483template <typename MatType, typename Alloc = Eigen::aligned_allocator<MatType>>
484void exposeStdVectorEigenSpecificType(const char *name) {
485 typedef std::vector<MatType, Alloc> VecMatType;
486 std::string full_name = "StdVec_";
487 full_name += name;
488 StdVectorPythonVisitor<VecMatType>::expose(
489 full_name.c_str(),
491}
492
493} // namespace eigenpy
494
495#endif // ifndef __eigenpy_utils_std_vector_hpp__
void expose()
Call the expose function of a given type T.
Definition expose.hpp:23
void EIGENPY_DLLAPI exposeStdVector()
bool register_symbolic_link_to_registered_type()
Symlink to the current scope the already registered class T.
Add the Python method id to retrieving a unique id for a given object exposed with Boost....
Definition id.hpp:18
Register the conversion from a Python list to a std::vector.
static void construct(PyObject *obj_ptr, boost::python::converter::rvalue_from_python_stage1_data *memory)
Allocate the std::vector and fill it with the element contained in the list.
static void * convertible(PyObject *obj_ptr)
Check if obj_ptr can be converted.
Expose an std::vector from a type given as template argument.
Change the behavior of indexing (method getitem in Python). This is suitable for container of Eigen m...