Skip to content

Commit 558e391

Browse files
committed
unique_ptr: Manage string and complex type
1 parent f565ce8 commit 558e391

File tree

8 files changed

+115
-26
lines changed

8 files changed

+115
-26
lines changed

CMakeLists.txt

+2-1
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,8 @@ search_for_boost_python(REQUIRED)
104104
# ----------------------------------------------------
105105
set(${PROJECT_NAME}_UTILS_HEADERS
106106
include/eigenpy/utils/scalar-name.hpp include/eigenpy/utils/is-approx.hpp
107-
include/eigenpy/utils/is-aligned.hpp)
107+
include/eigenpy/utils/is-aligned.hpp include/eigenpy/utils/traits.hpp
108+
include/eigenpy/utils/python-compat.hpp)
108109

109110
set(${PROJECT_NAME}_SOLVERS_HEADERS
110111
include/eigenpy/solvers/solvers.hpp

include/eigenpy/std-unique-ptr.hpp

+7-9
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
#include "eigenpy/fwd.hpp"
99
#include "eigenpy/utils/traits.hpp"
10+
#include "eigenpy/utils/python-compat.hpp"
1011

1112
#include <boost/python.hpp>
1213

@@ -19,8 +20,7 @@ namespace details {
1920

2021
/// Transfer std::unique_ptr ownership to an owning holder
2122
template <typename T>
22-
typename std::enable_if<is_class_or_union_remove_cvref<T>::value,
23-
PyObject*>::type
23+
typename std::enable_if<!is_python_primitive_type<T>::value, PyObject*>::type
2424
unique_ptr_to_python(std::unique_ptr<T>&& x) {
2525
typedef bp::objects::pointer_holder<std::unique_ptr<T>, T> holder_t;
2626
if (!x) {
@@ -32,8 +32,7 @@ unique_ptr_to_python(std::unique_ptr<T>&& x) {
3232

3333
/// Convert and copy the primitive value to python
3434
template <typename T>
35-
typename std::enable_if<!is_class_or_union_remove_cvref<T>::value,
36-
PyObject*>::type
35+
typename std::enable_if<is_python_primitive_type<T>::value, PyObject*>::type
3736
unique_ptr_to_python(std::unique_ptr<T>&& x) {
3837
if (!x) {
3938
return bp::detail::none();
@@ -45,8 +44,7 @@ unique_ptr_to_python(std::unique_ptr<T>&& x) {
4544
/// std::unique_ptr keep the ownership but a reference to the std::unique_ptr
4645
/// value is created
4746
template <typename T>
48-
typename std::enable_if<is_class_or_union_remove_cvref<T>::value,
49-
PyObject*>::type
47+
typename std::enable_if<!is_python_primitive_type<T>::value, PyObject*>::type
5048
internal_unique_ptr_to_python(std::unique_ptr<T>& x) {
5149
if (!x) {
5250
return bp::detail::none();
@@ -57,8 +55,7 @@ internal_unique_ptr_to_python(std::unique_ptr<T>& x) {
5755

5856
/// Convert and copy the primitive value to python
5957
template <typename T>
60-
typename std::enable_if<!is_class_or_union_remove_cvref<T>::value,
61-
PyObject*>::type
58+
typename std::enable_if<is_python_primitive_type<T>::value, PyObject*>::type
6259
internal_unique_ptr_to_python(std::unique_ptr<T>& x) {
6360
if (!x) {
6461
return bp::detail::none();
@@ -123,7 +120,8 @@ struct ReturnInternalStdUniquePtr : bp::return_internal_reference<> {
123120
template <class ArgumentPackage>
124121
static PyObject* postcall(ArgumentPackage const& args_, PyObject* result) {
125122
// Don't run return_internal_reference postcall on primitive type
126-
if (PyLong_Check(result) || PyBool_Check(result) || PyFloat_Check(result)) {
123+
if (PyInt_Check(result) || PyBool_Check(result) || PyFloat_Check(result) ||
124+
PyStr_Check(result) || PyComplex_Check(result)) {
127125
return result;
128126
}
129127
return bp::return_internal_reference<>::postcall(args_, result);

include/eigenpy/ufunc.hpp

+2-5
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
#include "eigenpy/register.hpp"
1111
#include "eigenpy/user-type.hpp"
12+
#include "eigenpy/utils/python-compat.hpp"
1213

1314
namespace eigenpy {
1415
namespace internal {
@@ -207,11 +208,7 @@ void registerCommonUfunc() {
207208
const int type_code = Register::getTypeCode<Scalar>();
208209

209210
PyObject *numpy_str;
210-
#if PY_MAJOR_VERSION >= 3
211-
numpy_str = PyUnicode_FromString("numpy");
212-
#else
213-
numpy_str = PyString_FromString("numpy");
214-
#endif
211+
numpy_str = PyStr_FromString("numpy");
215212
PyObject *numpy;
216213
numpy = PyImport_Import(numpy_str);
217214
Py_DECREF(numpy_str);
+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
//
2+
// Copyright (c) 2024 INRIA
3+
//
4+
//
5+
6+
#ifndef __eigenpy_utils_python_compat_hpp__
7+
#define __eigenpy_utils_python_compat_hpp__
8+
9+
#if PY_MAJOR_VERSION >= 3
10+
11+
#define PyInt_Check PyLong_Check
12+
13+
#define PyStr_Check PyUnicode_Check
14+
#define PyStr_FromString PyUnicode_FromString
15+
16+
#else
17+
18+
#define PyStr_Check PyString_Check
19+
#define PyStr_FromString PyString_FromString
20+
21+
#endif
22+
23+
#endif // ifndef __eigenpy_utils_python_compat_hpp__

include/eigenpy/utils/traits.hpp

+27-5
Original file line numberDiff line numberDiff line change
@@ -7,25 +7,47 @@
77
#define __eigenpy_utils_traits_hpp__
88

99
#include <type_traits>
10+
#include <string>
11+
#include <complex>
1012

1113
namespace eigenpy {
1214

1315
namespace details {
1416

17+
/// Trait to remove const&
18+
template <typename T>
19+
struct remove_cvref : std::remove_cv<typename std::remove_reference<T>::type> {
20+
};
21+
1522
/// Trait to detect if T is a class or an union
1623
template <typename T>
1724
struct is_class_or_union
1825
: std::integral_constant<bool, std::is_class<T>::value ||
1926
std::is_union<T>::value> {};
2027

28+
/// trait to detect if T is a std::complex managed by Boost Python
2129
template <typename T>
22-
struct remove_cvref : std::remove_cv<typename std::remove_reference<T>::type> {
23-
};
30+
struct is_python_complex : std::false_type {};
31+
32+
/// From boost/python/converter/builtin_converters
33+
template <>
34+
struct is_python_complex<std::complex<float> > : std::true_type {};
35+
template <>
36+
struct is_python_complex<std::complex<double> > : std::true_type {};
37+
template <>
38+
struct is_python_complex<std::complex<long double> > : std::true_type {};
39+
40+
template <typename T>
41+
struct is_python_primitive_type_helper
42+
: std::integral_constant<bool, !is_class_or_union<T>::value ||
43+
std::is_same<T, std::string>::value ||
44+
std::is_same<T, std::wstring>::value ||
45+
is_python_complex<T>::value> {};
2446

25-
/// Trait to remove cvref and call is_class_or_union
47+
/// Trait to detect if T is a Python primitive type
2648
template <typename T>
27-
struct is_class_or_union_remove_cvref
28-
: is_class_or_union<typename remove_cvref<T>::type> {};
49+
struct is_python_primitive_type
50+
: is_python_primitive_type_helper<typename remove_cvref<T>::type> {};
2951

3052
} // namespace details
3153

include/eigenpy/variant.hpp

+6-4
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
#include "eigenpy/fwd.hpp"
99
#include "eigenpy/utils/traits.hpp"
10+
#include "eigenpy/utils/python-compat.hpp"
1011

1112
#include <boost/python.hpp>
1213
#include <boost/variant.hpp>
@@ -147,7 +148,7 @@ struct NumericConvertibleImpl<
147148
std::is_integral<T>::value>::type> {
148149
static void* convertible(PyObject* obj) {
149150
// PyLong return true for bool type
150-
return (PyLong_Check(obj) && !PyBool_Check(obj)) ? obj : nullptr;
151+
return (PyInt_Check(obj) && !PyBool_Check(obj)) ? obj : nullptr;
151152
}
152153

153154
static PyTypeObject const* expected_pytype() { return &PyLong_Type; }
@@ -220,14 +221,14 @@ struct VariantRefToObject : VariantVisitorType<PyObject*, Variant> {
220221
}
221222

222223
template <typename T,
223-
typename std::enable_if<!is_class_or_union_remove_cvref<T>::value,
224+
typename std::enable_if<is_python_primitive_type<T>::value,
224225
bool>::type = true>
225226
result_type operator()(T t) const {
226227
return bp::incref(bp::object(t).ptr());
227228
}
228229

229230
template <typename T,
230-
typename std::enable_if<is_class_or_union_remove_cvref<T>::value,
231+
typename std::enable_if<!is_python_primitive_type<T>::value,
231232
bool>::type = true>
232233
result_type operator()(T& t) const {
233234
return bp::detail::make_reference_holder::execute(&t);
@@ -301,7 +302,8 @@ struct ReturnInternalVariant : bp::return_internal_reference<> {
301302
template <class ArgumentPackage>
302303
static PyObject* postcall(ArgumentPackage const& args_, PyObject* result) {
303304
// Don't run return_internal_reference postcall on primitive type
304-
if (PyLong_Check(result) || PyBool_Check(result) || PyFloat_Check(result)) {
305+
if (PyInt_Check(result) || PyBool_Check(result) || PyFloat_Check(result) ||
306+
PyStr_Check(result) || PyComplex_Check(result)) {
305307
return result;
306308
}
307309
return bp::return_internal_reference<>::postcall(args_, result);

unittest/python/test_std_unique_ptr.py

+24-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
make_unique_int,
33
make_unique_v1,
44
make_unique_null,
5+
make_unique_str,
6+
make_unique_complex,
57
V1,
68
UniquePtrHolder,
79
)
@@ -17,6 +19,14 @@
1719
v = make_unique_null()
1820
assert v is None
1921

22+
v = make_unique_str()
23+
assert isinstance(v, str)
24+
assert v == "str"
25+
26+
v = make_unique_complex()
27+
assert isinstance(v, complex)
28+
assert v == 1 + 0j
29+
2030
unique_ptr_holder = UniquePtrHolder()
2131

2232
v = unique_ptr_holder.int_ptr
@@ -33,6 +43,19 @@
3343
v.v = 10
3444
assert unique_ptr_holder.v1_ptr.v == 10
3545

36-
3746
v = unique_ptr_holder.null_ptr
3847
assert v is None
48+
49+
v = unique_ptr_holder.str_ptr
50+
assert isinstance(v, str)
51+
assert v == "str"
52+
# v is a copy, str_ptr will not be updated
53+
v = "str_updated"
54+
assert unique_ptr_holder.str_ptr == "str"
55+
56+
v = unique_ptr_holder.complex_ptr
57+
assert isinstance(v, complex)
58+
assert v == 1 + 0j
59+
# v is a copy, complex_ptr will not be updated
60+
v = 1 + 2j
61+
assert unique_ptr_holder.complex_ptr == 1 + 0j

unittest/std_unique_ptr.cpp

+24-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
#include <eigenpy/std-unique-ptr.hpp>
66

77
#include <memory>
8+
#include <string>
9+
#include <complex>
810

911
namespace bp = boost::python;
1012

@@ -21,13 +23,26 @@ std::unique_ptr<V1> make_unique_v1() { return std::make_unique<V1>(10); }
2123

2224
std::unique_ptr<V1> make_unique_null() { return nullptr; }
2325

26+
std::unique_ptr<std::string> make_unique_str() {
27+
return std::make_unique<std::string>("str");
28+
}
29+
30+
std::unique_ptr<std::complex<double> > make_unique_complex() {
31+
return std::make_unique<std::complex<double> >(1., 0.);
32+
}
33+
2434
struct UniquePtrHolder {
2535
UniquePtrHolder()
26-
: int_ptr(std::make_unique<int>(20)), v1_ptr(std::make_unique<V1>(200)) {}
36+
: int_ptr(std::make_unique<int>(20)),
37+
v1_ptr(std::make_unique<V1>(200)),
38+
str_ptr(std::make_unique<std::string>("str")),
39+
complex_ptr(std::make_unique<std::complex<double> >(1., 0.)) {}
2740

2841
std::unique_ptr<int> int_ptr;
2942
std::unique_ptr<V1> v1_ptr;
3043
std::unique_ptr<V1> null_ptr;
44+
std::unique_ptr<std::string> str_ptr;
45+
std::unique_ptr<std::complex<double> > complex_ptr;
3146
};
3247

3348
BOOST_PYTHON_MODULE(std_unique_ptr) {
@@ -39,6 +54,8 @@ BOOST_PYTHON_MODULE(std_unique_ptr) {
3954
bp::def("make_unique_v1", make_unique_v1);
4055
bp::def("make_unique_null", make_unique_null,
4156
eigenpy::StdUniquePtrCallPolicies());
57+
bp::def("make_unique_str", make_unique_str);
58+
bp::def("make_unique_complex", make_unique_complex);
4259

4360
boost::python::class_<UniquePtrHolder, boost::noncopyable>("UniquePtrHolder",
4461
bp::init<>())
@@ -50,5 +67,11 @@ BOOST_PYTHON_MODULE(std_unique_ptr) {
5067
eigenpy::ReturnInternalStdUniquePtr()))
5168
.add_property("null_ptr",
5269
bp::make_getter(&UniquePtrHolder::null_ptr,
70+
eigenpy::ReturnInternalStdUniquePtr()))
71+
.add_property("str_ptr",
72+
bp::make_getter(&UniquePtrHolder::str_ptr,
73+
eigenpy::ReturnInternalStdUniquePtr()))
74+
.add_property("complex_ptr",
75+
bp::make_getter(&UniquePtrHolder::complex_ptr,
5376
eigenpy::ReturnInternalStdUniquePtr()));
5477
}

0 commit comments

Comments
 (0)