11// /
22// / Copyright (c) 2016-2024 CNRS INRIA
3+ // / Copyright (c) 2025-2025 Heriot-Watt University
34// / This file was taken from Pinocchio (header
45// / <pinocchio/bindings/python/utils/std-vector.hpp>)
56// /
@@ -84,11 +85,14 @@ struct overload_base_get_item_for_std_vector
8485
8586 template <class Class >
8687 void visit (Class &cl) const {
87- cl.def (" __getitem__" , &base_get_item);
88+ cl.def (" __getitem__" , &base_get_item_int)
89+ .def (" __getitem__" , &base_get_item_slice)
90+ .def (" __getitem__" , &base_get_item_list)
91+ .def (" __getitem__" , &base_get_item_tuple);
8892 }
8993
9094 private:
91- static boost::python::object base_get_item (
95+ static boost::python::object base_get_item_int (
9296 boost::python::back_reference<Container &> container, PyObject *i_) {
9397 index_type idx = convert_index (container.get (), i_);
9498 typename Container::iterator i = container.get ().begin ();
@@ -104,6 +108,83 @@ struct overload_base_get_item_for_std_vector
104108 return bp::object (bp::handle<>(convert (*i)));
105109 }
106110
111+ static boost::python::object base_get_item_slice (
112+ boost::python::back_reference<Container &> container,
113+ boost::python::slice slice) {
114+ bp::list out;
115+ try {
116+ auto rng =
117+ slice.get_indices (container.get ().begin (), container.get ().end ());
118+ // rng.start, rng.stop are iterators; rng.step is int; [start, stop] is
119+ // closed
120+ typename bp::to_python_indirect<value_type &,
121+ bp::detail::make_reference_holder>
122+ convert;
123+ // forward or backward
124+ for (typename Container::iterator it = rng.start ;;
125+ std::advance (it, rng.step )) {
126+ out.append (bp::object (bp::handle<>(convert (*it))));
127+ if (it == rng.stop ) break ; // closed interval, include stop
128+ }
129+ } catch (const std::invalid_argument &) {
130+ // Boost.Python specifies empty ranges throw invalid_argument.
131+ // Return [] (matches Python's behavior for empty slices).
132+ return bp::list ();
133+ }
134+ return out;
135+ }
136+
137+ static bp::object base_get_item_list (bp::back_reference<Container &> c,
138+ bp::list idxs) {
139+ const Py_ssize_t m = bp::len (idxs);
140+ bp::list out;
141+ for (Py_ssize_t k = 0 ; k < m; ++k) {
142+ bp::object obj = idxs[k];
143+ bp::extract<long > ei (obj);
144+ if (!ei.check ()) {
145+ PyErr_SetString (PyExc_TypeError, " indices must be integers" );
146+ bp::throw_error_already_set ();
147+ }
148+ auto idx = normalize_index (c.get ().size (), ei ());
149+ out.append (elem_ref (c.get (), idx));
150+ }
151+ return out;
152+ }
153+
154+ static bp::object base_get_item_tuple (bp::back_reference<Container &> c,
155+ bp::tuple idxs) {
156+ const Py_ssize_t m = bp::len (idxs);
157+ bp::list out;
158+ for (Py_ssize_t k = 0 ; k < m; ++k) {
159+ bp::object obj = idxs[k];
160+ bp::extract<long > ei (obj);
161+ if (!ei.check ()) {
162+ PyErr_SetString (PyExc_TypeError, " indices must be integers" );
163+ bp::throw_error_already_set ();
164+ }
165+ auto idx = normalize_index (c.get ().size (), ei ());
166+ out.append (elem_ref (c.get (), idx));
167+ }
168+ return out;
169+ }
170+
171+ static index_type normalize_index (std::size_t n, long i) {
172+ long idx = i;
173+ if (idx < 0 ) idx += static_cast <long >(n);
174+ if (idx < 0 || idx >= static_cast <long >(n)) {
175+ PyErr_SetString (PyExc_IndexError, " index out of range" );
176+ bp::throw_error_already_set ();
177+ }
178+ return static_cast <index_type>(idx);
179+ }
180+
181+ static bp::object elem_ref (Container &c, index_type i) {
182+ typename bp::to_python_indirect<value_type &,
183+ bp::detail::make_reference_holder>
184+ conv;
185+ return bp::object (bp::handle<>(conv (c[i])));
186+ }
187+
107188 static index_type convert_index (Container &container, PyObject *i_) {
108189 bp::extract<long > i (i_);
109190 if (i.check ()) {
0 commit comments