From 558e3919c89fc46c33a304963dd3a6624e5f9680 Mon Sep 17 00:00:00 2001 From: Joris Vaillant Date: Mon, 5 Feb 2024 16:48:36 +0100 Subject: [PATCH] unique_ptr: Manage string and complex type --- CMakeLists.txt | 3 ++- include/eigenpy/std-unique-ptr.hpp | 16 ++++++------- include/eigenpy/ufunc.hpp | 7 ++---- include/eigenpy/utils/python-compat.hpp | 23 ++++++++++++++++++ include/eigenpy/utils/traits.hpp | 32 +++++++++++++++++++++---- include/eigenpy/variant.hpp | 10 ++++---- unittest/python/test_std_unique_ptr.py | 25 ++++++++++++++++++- unittest/std_unique_ptr.cpp | 25 ++++++++++++++++++- 8 files changed, 115 insertions(+), 26 deletions(-) create mode 100644 include/eigenpy/utils/python-compat.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 7002b5af..e48c9105 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -104,7 +104,8 @@ search_for_boost_python(REQUIRED) # ---------------------------------------------------- set(${PROJECT_NAME}_UTILS_HEADERS include/eigenpy/utils/scalar-name.hpp include/eigenpy/utils/is-approx.hpp - include/eigenpy/utils/is-aligned.hpp) + include/eigenpy/utils/is-aligned.hpp include/eigenpy/utils/traits.hpp + include/eigenpy/utils/python-compat.hpp) set(${PROJECT_NAME}_SOLVERS_HEADERS include/eigenpy/solvers/solvers.hpp diff --git a/include/eigenpy/std-unique-ptr.hpp b/include/eigenpy/std-unique-ptr.hpp index dcb95dc3..dfc9f5a1 100644 --- a/include/eigenpy/std-unique-ptr.hpp +++ b/include/eigenpy/std-unique-ptr.hpp @@ -7,6 +7,7 @@ #include "eigenpy/fwd.hpp" #include "eigenpy/utils/traits.hpp" +#include "eigenpy/utils/python-compat.hpp" #include @@ -19,8 +20,7 @@ namespace details { /// Transfer std::unique_ptr ownership to an owning holder template -typename std::enable_if::value, - PyObject*>::type +typename std::enable_if::value, PyObject*>::type unique_ptr_to_python(std::unique_ptr&& x) { typedef bp::objects::pointer_holder, T> holder_t; if (!x) { @@ -32,8 +32,7 @@ unique_ptr_to_python(std::unique_ptr&& x) { /// Convert and copy the primitive value to python template -typename std::enable_if::value, - PyObject*>::type +typename std::enable_if::value, PyObject*>::type unique_ptr_to_python(std::unique_ptr&& x) { if (!x) { return bp::detail::none(); @@ -45,8 +44,7 @@ unique_ptr_to_python(std::unique_ptr&& x) { /// std::unique_ptr keep the ownership but a reference to the std::unique_ptr /// value is created template -typename std::enable_if::value, - PyObject*>::type +typename std::enable_if::value, PyObject*>::type internal_unique_ptr_to_python(std::unique_ptr& x) { if (!x) { return bp::detail::none(); @@ -57,8 +55,7 @@ internal_unique_ptr_to_python(std::unique_ptr& x) { /// Convert and copy the primitive value to python template -typename std::enable_if::value, - PyObject*>::type +typename std::enable_if::value, PyObject*>::type internal_unique_ptr_to_python(std::unique_ptr& x) { if (!x) { return bp::detail::none(); @@ -123,7 +120,8 @@ struct ReturnInternalStdUniquePtr : bp::return_internal_reference<> { template static PyObject* postcall(ArgumentPackage const& args_, PyObject* result) { // Don't run return_internal_reference postcall on primitive type - if (PyLong_Check(result) || PyBool_Check(result) || PyFloat_Check(result)) { + if (PyInt_Check(result) || PyBool_Check(result) || PyFloat_Check(result) || + PyStr_Check(result) || PyComplex_Check(result)) { return result; } return bp::return_internal_reference<>::postcall(args_, result); diff --git a/include/eigenpy/ufunc.hpp b/include/eigenpy/ufunc.hpp index cb6695ac..129438cf 100644 --- a/include/eigenpy/ufunc.hpp +++ b/include/eigenpy/ufunc.hpp @@ -9,6 +9,7 @@ #include "eigenpy/register.hpp" #include "eigenpy/user-type.hpp" +#include "eigenpy/utils/python-compat.hpp" namespace eigenpy { namespace internal { @@ -207,11 +208,7 @@ void registerCommonUfunc() { const int type_code = Register::getTypeCode(); PyObject *numpy_str; -#if PY_MAJOR_VERSION >= 3 - numpy_str = PyUnicode_FromString("numpy"); -#else - numpy_str = PyString_FromString("numpy"); -#endif + numpy_str = PyStr_FromString("numpy"); PyObject *numpy; numpy = PyImport_Import(numpy_str); Py_DECREF(numpy_str); diff --git a/include/eigenpy/utils/python-compat.hpp b/include/eigenpy/utils/python-compat.hpp new file mode 100644 index 00000000..7ffbc9de --- /dev/null +++ b/include/eigenpy/utils/python-compat.hpp @@ -0,0 +1,23 @@ +// +// Copyright (c) 2024 INRIA +// +// + +#ifndef __eigenpy_utils_python_compat_hpp__ +#define __eigenpy_utils_python_compat_hpp__ + +#if PY_MAJOR_VERSION >= 3 + +#define PyInt_Check PyLong_Check + +#define PyStr_Check PyUnicode_Check +#define PyStr_FromString PyUnicode_FromString + +#else + +#define PyStr_Check PyString_Check +#define PyStr_FromString PyString_FromString + +#endif + +#endif // ifndef __eigenpy_utils_python_compat_hpp__ diff --git a/include/eigenpy/utils/traits.hpp b/include/eigenpy/utils/traits.hpp index 9b8e020c..b7525dea 100644 --- a/include/eigenpy/utils/traits.hpp +++ b/include/eigenpy/utils/traits.hpp @@ -7,25 +7,47 @@ #define __eigenpy_utils_traits_hpp__ #include +#include +#include namespace eigenpy { namespace details { +/// Trait to remove const& +template +struct remove_cvref : std::remove_cv::type> { +}; + /// Trait to detect if T is a class or an union template struct is_class_or_union : std::integral_constant::value || std::is_union::value> {}; +/// trait to detect if T is a std::complex managed by Boost Python template -struct remove_cvref : std::remove_cv::type> { -}; +struct is_python_complex : std::false_type {}; + +/// From boost/python/converter/builtin_converters +template <> +struct is_python_complex > : std::true_type {}; +template <> +struct is_python_complex > : std::true_type {}; +template <> +struct is_python_complex > : std::true_type {}; + +template +struct is_python_primitive_type_helper + : std::integral_constant::value || + std::is_same::value || + std::is_same::value || + is_python_complex::value> {}; -/// Trait to remove cvref and call is_class_or_union +/// Trait to detect if T is a Python primitive type template -struct is_class_or_union_remove_cvref - : is_class_or_union::type> {}; +struct is_python_primitive_type + : is_python_primitive_type_helper::type> {}; } // namespace details diff --git a/include/eigenpy/variant.hpp b/include/eigenpy/variant.hpp index f989a0f9..bf787c3f 100644 --- a/include/eigenpy/variant.hpp +++ b/include/eigenpy/variant.hpp @@ -7,6 +7,7 @@ #include "eigenpy/fwd.hpp" #include "eigenpy/utils/traits.hpp" +#include "eigenpy/utils/python-compat.hpp" #include #include @@ -147,7 +148,7 @@ struct NumericConvertibleImpl< std::is_integral::value>::type> { static void* convertible(PyObject* obj) { // PyLong return true for bool type - return (PyLong_Check(obj) && !PyBool_Check(obj)) ? obj : nullptr; + return (PyInt_Check(obj) && !PyBool_Check(obj)) ? obj : nullptr; } static PyTypeObject const* expected_pytype() { return &PyLong_Type; } @@ -220,14 +221,14 @@ struct VariantRefToObject : VariantVisitorType { } template ::value, + typename std::enable_if::value, bool>::type = true> result_type operator()(T t) const { return bp::incref(bp::object(t).ptr()); } template ::value, + typename std::enable_if::value, bool>::type = true> result_type operator()(T& t) const { return bp::detail::make_reference_holder::execute(&t); @@ -301,7 +302,8 @@ struct ReturnInternalVariant : bp::return_internal_reference<> { template static PyObject* postcall(ArgumentPackage const& args_, PyObject* result) { // Don't run return_internal_reference postcall on primitive type - if (PyLong_Check(result) || PyBool_Check(result) || PyFloat_Check(result)) { + if (PyInt_Check(result) || PyBool_Check(result) || PyFloat_Check(result) || + PyStr_Check(result) || PyComplex_Check(result)) { return result; } return bp::return_internal_reference<>::postcall(args_, result); diff --git a/unittest/python/test_std_unique_ptr.py b/unittest/python/test_std_unique_ptr.py index 6feb408f..8b56460f 100644 --- a/unittest/python/test_std_unique_ptr.py +++ b/unittest/python/test_std_unique_ptr.py @@ -2,6 +2,8 @@ make_unique_int, make_unique_v1, make_unique_null, + make_unique_str, + make_unique_complex, V1, UniquePtrHolder, ) @@ -17,6 +19,14 @@ v = make_unique_null() assert v is None +v = make_unique_str() +assert isinstance(v, str) +assert v == "str" + +v = make_unique_complex() +assert isinstance(v, complex) +assert v == 1 + 0j + unique_ptr_holder = UniquePtrHolder() v = unique_ptr_holder.int_ptr @@ -33,6 +43,19 @@ v.v = 10 assert unique_ptr_holder.v1_ptr.v == 10 - v = unique_ptr_holder.null_ptr assert v is None + +v = unique_ptr_holder.str_ptr +assert isinstance(v, str) +assert v == "str" +# v is a copy, str_ptr will not be updated +v = "str_updated" +assert unique_ptr_holder.str_ptr == "str" + +v = unique_ptr_holder.complex_ptr +assert isinstance(v, complex) +assert v == 1 + 0j +# v is a copy, complex_ptr will not be updated +v = 1 + 2j +assert unique_ptr_holder.complex_ptr == 1 + 0j diff --git a/unittest/std_unique_ptr.cpp b/unittest/std_unique_ptr.cpp index ab99a4da..a95a5d24 100644 --- a/unittest/std_unique_ptr.cpp +++ b/unittest/std_unique_ptr.cpp @@ -5,6 +5,8 @@ #include #include +#include +#include namespace bp = boost::python; @@ -21,13 +23,26 @@ std::unique_ptr make_unique_v1() { return std::make_unique(10); } std::unique_ptr make_unique_null() { return nullptr; } +std::unique_ptr make_unique_str() { + return std::make_unique("str"); +} + +std::unique_ptr > make_unique_complex() { + return std::make_unique >(1., 0.); +} + struct UniquePtrHolder { UniquePtrHolder() - : int_ptr(std::make_unique(20)), v1_ptr(std::make_unique(200)) {} + : int_ptr(std::make_unique(20)), + v1_ptr(std::make_unique(200)), + str_ptr(std::make_unique("str")), + complex_ptr(std::make_unique >(1., 0.)) {} std::unique_ptr int_ptr; std::unique_ptr v1_ptr; std::unique_ptr null_ptr; + std::unique_ptr str_ptr; + std::unique_ptr > complex_ptr; }; BOOST_PYTHON_MODULE(std_unique_ptr) { @@ -39,6 +54,8 @@ BOOST_PYTHON_MODULE(std_unique_ptr) { bp::def("make_unique_v1", make_unique_v1); bp::def("make_unique_null", make_unique_null, eigenpy::StdUniquePtrCallPolicies()); + bp::def("make_unique_str", make_unique_str); + bp::def("make_unique_complex", make_unique_complex); boost::python::class_("UniquePtrHolder", bp::init<>()) @@ -50,5 +67,11 @@ BOOST_PYTHON_MODULE(std_unique_ptr) { eigenpy::ReturnInternalStdUniquePtr())) .add_property("null_ptr", bp::make_getter(&UniquePtrHolder::null_ptr, + eigenpy::ReturnInternalStdUniquePtr())) + .add_property("str_ptr", + bp::make_getter(&UniquePtrHolder::str_ptr, + eigenpy::ReturnInternalStdUniquePtr())) + .add_property("complex_ptr", + bp::make_getter(&UniquePtrHolder::complex_ptr, eigenpy::ReturnInternalStdUniquePtr())); }