GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: include/pinocchio/bindings/python/utils/std-vector.hpp Lines: 28 53 52.8 %
Date: 2024-01-23 21:41:47 Branches: 23 84 27.4 %

Line Branch Exec Source
1
//
2
// Copyright (c) 2016-2020 CNRS INRIA
3
//
4
5
#ifndef __pinocchio_python_utils_std_vector_hpp__
6
#define __pinocchio_python_utils_std_vector_hpp__
7
8
#include <boost/python.hpp>
9
#include <boost/python/stl_iterator.hpp>
10
#include <boost/python/suite/indexing/vector_indexing_suite.hpp>
11
12
#include <string>
13
#include <vector>
14
#include <iterator>
15
16
#include "pinocchio/bindings/python/utils/pickle-vector.hpp"
17
18
namespace pinocchio
19
{
20
  namespace python
21
  {
22
23
    namespace details
24
    {
25
      template<typename Container>
26
      struct overload_base_get_item_for_std_vector
27
      : public boost::python::def_visitor< overload_base_get_item_for_std_vector<Container> >
28
      {
29
        typedef typename Container::value_type value_type;
30
        typedef typename Container::value_type data_type;
31
        typedef size_t index_type;
32
33
        template <class Class>
34
76
        void visit(Class& cl) const
35
        {
36
76
          cl
37
          .def("__getitem__", &base_get_item);
38
76
        }
39
40
      private:
41
42
        static boost::python::object
43
        base_get_item(boost::python::back_reference<Container&> container, PyObject* i_)
44
        {
45
          namespace bp = ::boost::python;
46
47
          index_type idx = convert_index(container.get(), i_);
48
          typename Container::iterator i = container.get().begin();
49
          std::advance(i, idx);
50
          if (i == container.get().end())
51
          {
52
              PyErr_SetString(PyExc_KeyError, "Invalid index");
53
              bp::throw_error_already_set();
54
          }
55
56
          typename bp::to_python_indirect<data_type&,bp::detail::make_reference_holder> convert;
57
          return bp::object(bp::handle<>(convert(*i)));
58
        }
59
60
        static index_type
61
        convert_index(Container & container, PyObject* i_)
62
        {
63
          namespace bp = boost::python;
64
          bp::extract<long> i(i_);
65
          if (i.check())
66
          {
67
            long index = i();
68
            if (index < 0)
69
              index += container.size();
70
            if (index >= long(container.size()) || index < 0)
71
            {
72
              PyErr_SetString(PyExc_IndexError, "Index out of range");
73
              bp::throw_error_already_set();
74
            }
75
            return index;
76
          }
77
78
          PyErr_SetString(PyExc_TypeError, "Invalid index type");
79
          bp::throw_error_already_set();
80
          return index_type();
81
        }
82
      };
83
    }
84
85
    ///
86
    /// \brief Register the conversion from a Python list to a std::vector
87
    ///
88
    /// \tparam vector_type A std container (e.g. std::vector or std::list)
89
    ///
90
    template<typename vector_type>
91
    struct StdContainerFromPythonList
92
    {
93
      typedef typename vector_type::value_type T;
94
95
      /// \brief Check if obj_ptr can be converted
96
12
      static void* convertible(PyObject* obj_ptr)
97
      {
98
        namespace bp = boost::python;
99
100
        // Check if it is a list
101
12
        if(!PyList_Check(obj_ptr)) return 0;
102
103
        // Retrieve the underlying list
104

22
        bp::object bp_obj(bp::handle<>(bp::borrowed(obj_ptr)));
105
22
        bp::list bp_list(bp_obj);
106
11
        bp::ssize_t list_size = bp::len(bp_list);
107
108
        // Check if all the elements contained in the current vector is of type T
109
53
        for(bp::ssize_t k = 0; k < list_size; ++k)
110
        {
111

84
          bp::extract<T> elt(bp_list[k]);
112
42
          if(!elt.check()) return 0;
113
        }
114
115
11
        return obj_ptr;
116
      }
117
118
      /// \brief Allocate the std::vector and fill it with the element contained in the list
119
11
      static void construct(PyObject* obj_ptr,
120
                            boost::python::converter::rvalue_from_python_stage1_data * memory)
121
      {
122
        namespace bp = boost::python;
123
124
        // Extract the list
125

22
        bp::object bp_obj(bp::handle<>(bp::borrowed(obj_ptr)));
126
11
        bp::list bp_list(bp_obj);
127
128
11
        void * storage = reinterpret_cast< bp::converter::rvalue_from_python_storage<vector_type>*>
129
        (reinterpret_cast<void*>(memory))->storage.bytes;
130
131
        typedef bp::stl_input_iterator<T> iterator;
132
133
        // Build the std::vector
134

11
        new (storage) vector_type(iterator(bp_list),
135
                                  iterator());
136
137
        // Validate the construction
138
11
        memory->convertible = storage;
139
11
      }
140
141
513
      static void register_converter()
142
      {
143
513
        ::boost::python::converter::registry::push_back(&convertible,
144
                                                        &construct,
145
                                                        ::boost::python::type_id<vector_type>());
146
513
      }
147
148
      static ::boost::python::list tolist(vector_type & self)
149
      {
150
        namespace bp = boost::python;
151
152
        typedef bp::iterator<vector_type> iterator;
153
        bp::list python_list(iterator()(self));
154
155
        return python_list;
156
      }
157
    };
158
159
    ///
160
    /// \brief Expose an std::vector from a type given as template argument.
161
    ///
162
    /// \tparam T Type to expose as std::vector<T>.
163
    /// \tparam Allocator Type for the Allocator in std::vector<T,Allocator>.
164
    /// \tparam NoProxy When set to false, the elements will be copied when returned to Python.
165
    /// \tparam EnableFromPythonListConverter Enables the conversion from a Python list to a std::vector<T,Allocator>
166
    ///
167
    /// \sa StdAlignedVectorPythonVisitor
168
    ///
169
    template<class T, class Allocator = std::allocator<T>, bool NoProxy = false, bool EnableFromPythonListConverter = true>
170
    struct StdVectorPythonVisitor
171
    : public ::boost::python::vector_indexing_suite<typename std::vector<T,Allocator>, NoProxy>
172
    , public StdContainerFromPythonList< std::vector<T,Allocator> >
173
    {
174
      typedef std::vector<T,Allocator> vector_type;
175
      typedef StdContainerFromPythonList<vector_type> FromPythonListConverter;
176
177
152
      static ::boost::python::class_<vector_type> expose(const std::string & class_name,
178
                                                         const std::string & doc_string = "")
179
      {
180
        namespace bp = boost::python;
181
182
152
        bp::class_<vector_type> cl(class_name.c_str(),doc_string.c_str());
183
        cl
184
        .def(StdVectorPythonVisitor())
185
152
        .def("tolist",&FromPythonListConverter::tolist,bp::arg("self"),
186
             "Returns the std::vector as a Python list.")
187

152
        .def_pickle(PickleVector<vector_type>());
188
189
        // Register conversion
190
        if(EnableFromPythonListConverter)
191
152
          FromPythonListConverter::register_converter();
192
193
152
        return cl;
194
      }
195
    };
196
197
  } // namespace python
198
} // namespace pinocchio
199
200
#endif // ifndef __pinocchio_python_utils_std_vector_hpp__