Skip to content

Commit e297aea

Browse files
authored
Merge pull request #423 from jcarpent/devel
Fix memory when returning a list from std::vector or std::array
2 parents f01464e + 407861c commit e297aea

File tree

5 files changed

+65
-35
lines changed

5 files changed

+65
-35
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
66

77
## [Unreleased]
88

9+
### Fixed
10+
- Fix potential memory leak when returning a list from an `std::vector` or an `std::array` ([423](https://github.com/stack-of-tasks/eigenpy/pull/423))
11+
912
## [3.2.0] - 2023-12-12
1013

1114
### Added

include/eigenpy/fwd.hpp

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2014-2023 CNRS INRIA
2+
* Copyright 2014-2024 CNRS INRIA
33
*/
44

55
#ifndef __eigenpy_fwd_hpp__
@@ -73,6 +73,9 @@
7373
#include <boost/python.hpp>
7474
#include <boost/python/scope.hpp>
7575

76+
#include <type_traits>
77+
#include <utility>
78+
7679
namespace eigenpy {
7780

7881
namespace bp = boost::python;
@@ -167,6 +170,22 @@ struct get_eigen_ref_plain_type<Eigen::TensorRef<TensorType> > {
167170
typedef TensorType type;
168171
};
169172
#endif
173+
174+
namespace internal {
175+
template <class T1, class T2>
176+
struct has_operator_equal_impl {
177+
template <class U, class V>
178+
static auto check(U *) -> decltype(std::declval<U>() == std::declval<V>());
179+
template <typename, typename>
180+
static auto check(...) -> std::false_type;
181+
182+
using type = typename std::is_same<bool, decltype(check<T1, T2>(0))>::type;
183+
};
184+
} // namespace internal
185+
186+
template <class T1, class T2 = T1>
187+
struct has_operator_equal : internal::has_operator_equal_impl<T1, T2>::type {};
188+
170189
} // namespace eigenpy
171190

172191
#include "eigenpy/alignment.hpp"

include/eigenpy/std-array.hpp

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
/// Copyright (c) 2023 CNRS INRIA
1+
///
2+
/// Copyright (c) 2023-2024 CNRS INRIA
3+
///
24

35
#ifndef __eigenpy_utils_std_array_hpp__
46
#define __eigenpy_utils_std_array_hpp__
@@ -110,8 +112,8 @@ template <typename array_type, bool NoProxy = false,
110112
struct StdArrayPythonVisitor {
111113
typedef typename array_type::value_type value_type;
112114

113-
static ::boost::python::list tolist(array_type &self) {
114-
return details::build_list<array_type, NoProxy>::run(self);
115+
static ::boost::python::list tolist(array_type &self, const bool deep_copy) {
116+
return details::build_list<array_type, NoProxy>::run(self, deep_copy);
115117
}
116118

117119
static void expose(const std::string &class_name,
@@ -137,7 +139,8 @@ struct StdArrayPythonVisitor {
137139
array_indexing_suite<array_type, NoProxy, SliceAllocator> indexing_suite;
138140
cl.def(indexing_suite)
139141
.def(visitor)
140-
.def("tolist", tolist, bp::arg("self"),
142+
.def("tolist", tolist,
143+
(bp::arg("self"), bp::arg("deep_copy") = false),
141144
"Returns the std::array as a Python list.");
142145
}
143146
}

include/eigenpy/std-vector.hpp

Lines changed: 34 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
/// Copyright (c) 2016-2022 CNRS INRIA
1+
///
2+
/// Copyright (c) 2016-2024 CNRS INRIA
23
/// This file was taken from Pinocchio (header
34
/// <pinocchio/bindings/python/utils/std-vector.hpp>)
45
///
@@ -50,7 +51,9 @@ bool from_python_list(PyObject *obj_ptr, T *) {
5051

5152
template <typename vector_type, bool NoProxy>
5253
struct build_list {
53-
static ::boost::python::list run(vector_type &vec) {
54+
static ::boost::python::list run(vector_type &vec, const bool deep_copy) {
55+
if (deep_copy) return build_list<vector_type, true>::run(vec, true);
56+
5457
bp::list bp_list;
5558
for (size_t k = 0; k < vec.size(); ++k) {
5659
bp_list.append(boost::ref(vec[k]));
@@ -61,7 +64,7 @@ struct build_list {
6164

6265
template <typename vector_type>
6366
struct build_list<vector_type, true> {
64-
static ::boost::python::list run(vector_type &vec) {
67+
static ::boost::python::list run(vector_type &vec, const bool) {
6568
typedef bp::iterator<vector_type> iterator;
6669
return bp::list(iterator()(vec));
6770
}
@@ -308,37 +311,23 @@ struct StdContainerFromPythonList {
308311
&convertible, &construct, ::boost::python::type_id<vector_type>());
309312
}
310313

311-
static ::boost::python::list tolist(vector_type &self) {
312-
return details::build_list<vector_type, NoProxy>::run(self);
314+
static ::boost::python::list tolist(vector_type &self,
315+
const bool deep_copy = false) {
316+
return details::build_list<vector_type, NoProxy>::run(self, deep_copy);
313317
}
314318
};
315319

316320
namespace internal {
317321

318-
template <typename T>
319-
struct has_operator_equal
320-
: boost::mpl::if_<typename boost::is_base_of<Eigen::EigenBase<T>, T>::type,
321-
has_operator_equal<Eigen::EigenBase<T> >,
322-
boost::true_type>::type {};
323-
324-
template <typename T, class A>
325-
struct has_operator_equal<std::vector<T, A> > : has_operator_equal<T> {};
326-
327-
template <>
328-
struct has_operator_equal<bool> : boost::true_type {};
329-
330-
template <typename EigenObject>
331-
struct has_operator_equal<Eigen::EigenBase<EigenObject> >
332-
: has_operator_equal<typename EigenObject::Scalar> {};
333-
334-
template <typename T, bool has_operator_equal_value = boost::is_base_of<
335-
boost::true_type, has_operator_equal<T> >::value>
322+
template <typename T,
323+
bool has_operator_equal_value =
324+
std::is_base_of<std::true_type, has_operator_equal<T> >::value>
336325
struct contains_algo;
337326

338327
template <typename T>
339328
struct contains_algo<T, true> {
340329
template <class Container, typename key_type>
341-
static bool run(Container &container, key_type const &key) {
330+
static bool run(const Container &container, key_type const &key) {
342331
return std::find(container.begin(), container.end(), key) !=
343332
container.end();
344333
}
@@ -347,7 +336,7 @@ struct contains_algo<T, true> {
347336
template <typename T>
348337
struct contains_algo<T, false> {
349338
template <class Container, typename key_type>
350-
static bool run(Container &container, key_type const &key) {
339+
static bool run(const Container &container, key_type const &key) {
351340
for (size_t k = 0; k < container.size(); ++k) {
352341
if (&container[k] == &key) return true;
353342
}
@@ -385,7 +374,8 @@ struct ExposeStdMethodToStdVector
385374
template <class Class>
386375
void visit(Class &cl) const {
387376
cl.def(m_co_visitor)
388-
.def("tolist", &FromPythonListConverter::tolist, bp::arg("self"),
377+
.def("tolist", &FromPythonListConverter::tolist,
378+
(bp::arg("self"), bp::arg("deep_copy") = false),
389379
"Returns the std::vector as a Python list.")
390380
.def("reserve", &Container::reserve,
391381
(bp::arg("self"), bp::arg("new_cap")),
@@ -412,6 +402,20 @@ struct EmptyPythonVisitor
412402
void visit(classT &) const {}
413403
};
414404

405+
namespace internal {
406+
template <typename vector_type, bool T_picklable = false>
407+
struct def_pickle_std_vector {
408+
static void run(bp::class_<vector_type> &) {}
409+
};
410+
411+
template <typename vector_type>
412+
struct def_pickle_std_vector<vector_type, true> {
413+
static void run(bp::class_<vector_type> &cl) {
414+
cl.def_pickle(PickleVector<vector_type>());
415+
}
416+
};
417+
} // namespace internal
418+
415419
///
416420
/// \brief Expose an std::vector from a type given as template argument.
417421
/// \tparam vector_type std::vector type to expose
@@ -421,7 +425,7 @@ struct EmptyPythonVisitor
421425
/// conversion from a Python list to a std::vector<T,Allocator>
422426
///
423427
template <class vector_type, bool NoProxy = false,
424-
bool EnableFromPythonListConverter = true>
428+
bool EnableFromPythonListConverter = true, bool pickable = true>
425429
struct StdVectorPythonVisitor {
426430
typedef typename vector_type::value_type value_type;
427431
typedef StdContainerFromPythonList<vector_type, NoProxy>
@@ -464,8 +468,9 @@ struct StdVectorPythonVisitor {
464468
"Copy constructor"))
465469

466470
.def(vector_indexing)
467-
.def(add_std_visitor)
468-
.def_pickle(PickleVector<vector_type>());
471+
.def(add_std_visitor);
472+
473+
internal::def_pickle_std_vector<vector_type, pickable>::run(cl);
469474
}
470475
if (EnableFromPythonListConverter) {
471476
// Register conversion

0 commit comments

Comments
 (0)