diff --git a/include/eigenpy/std_unique_ptr.hpp b/include/eigenpy/std_unique_ptr.hpp index 45ac265dcef49068f762990c25c927165632e7cd..15e1dd6c287237281a901375d4c295f32c0892cb 100644 --- a/include/eigenpy/std_unique_ptr.hpp +++ b/include/eigenpy/std_unique_ptr.hpp @@ -16,6 +16,7 @@ namespace eigenpy { namespace details { +/// Transfer std::unique_ptr ownership to an owning holder template <typename T> typename std::enable_if<is_class_or_union_remove_cvref<T>::value, PyObject*>::type @@ -27,6 +28,7 @@ unique_ptr_to_python(std::unique_ptr<T>&& x) { } } +/// Convert and copy the primitive value to python template <typename T> typename std::enable_if<!is_class_or_union_remove_cvref<T>::value, PyObject*>::type @@ -38,7 +40,30 @@ unique_ptr_to_python(std::unique_ptr<T>&& x) { } } -} // namespace details +/// std::unique_ptr keep the ownership but a reference to the std::unique_ptr +/// value is created +template <typename T> +typename std::enable_if<is_class_or_union_remove_cvref<T>::value, + PyObject*>::type +internal_unique_ptr_to_python(std::unique_ptr<T>& x) { + if (!x) { + return bp::detail::none(); + } else { + return bp::detail::make_reference_holder::execute(x.get()); + } +} + +/// Convert and copy the primitive value to python +template <typename T> +typename std::enable_if<!is_class_or_union_remove_cvref<T>::value, + PyObject*>::type +internal_unique_ptr_to_python(std::unique_ptr<T>& x) { + if (!x) { + return bp::detail::none(); + } else { + return bp::to_python_value<const T&>()(*x); + } +} /// result_converter of StdUniquePtrCallPolicies struct StdUniquePtrResultConverter { @@ -60,12 +85,48 @@ struct StdUniquePtrResultConverter { }; }; +/// result_converter of ReturnInternalStdUniquePtr +struct InternalStdUniquePtrConverter { + template <typename T> + struct apply { + struct type { + typedef typename remove_cvref<T>::type::element_type element_type; + + PyObject* operator()(T x) const { + return details::internal_unique_ptr_to_python(x); + } +#ifndef BOOST_PYTHON_NO_PY_SIGNATURES + PyTypeObject const* get_pytype() const { + return bp::to_python_value<const element_type&>().get_pytype(); + } +#endif + }; + }; +}; + +} // namespace details + /// CallPolicies to get std::unique_ptr value from a function /// that return an std::unique_ptr. /// If the object inside the std::unique_ptr is a class or an union /// it will be moved. In other case, it will be copied. struct StdUniquePtrCallPolicies : bp::default_call_policies { - typedef StdUniquePtrResultConverter result_converter; + typedef details::StdUniquePtrResultConverter result_converter; +}; + +/// Variant of \see bp::return_internal_reference that extract std::unique_ptr +/// content reference before converting it into a PyObject +struct ReturnInternalStdUniquePtr : bp::return_internal_reference<> { + typedef details::InternalStdUniquePtrConverter result_converter; + + template <class ArgumentPackage> + 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)) { + return result; + } + return bp::return_internal_reference<>::postcall(args_, result); + } }; } // namespace eigenpy diff --git a/include/eigenpy/utils/traits.hpp b/include/eigenpy/utils/traits.hpp index 9ce6b4fae5c8a8cd63defe1f622e299f56342e42..1f78a8d56f61acad3c5089d8b32484bb5e34ba9a 100644 --- a/include/eigenpy/utils/traits.hpp +++ b/include/eigenpy/utils/traits.hpp @@ -17,11 +17,14 @@ struct is_class_or_union : std::integral_constant<bool, std::is_class<T>::value || std::is_union<T>::value> {}; +template <typename T> +struct remove_cvref : std::remove_cv<typename std::remove_reference<T>::type> { +}; + /// Trait to remove cvref and call is_class_or_union template <typename T> struct is_class_or_union_remove_cvref - : is_class_or_union<typename std::remove_cv< - typename std::remove_reference<T>::type>::type> {}; + : is_class_or_union<typename remove_cvref<T>::type> {}; } // namespace details diff --git a/unittest/python/test_std_unique_ptr.py b/unittest/python/test_std_unique_ptr.py index cb8f7d462c033c3d928b38f42564312a85a13648..6feb408f8c4b93c90d338cc398b88c92c04fdcae 100644 --- a/unittest/python/test_std_unique_ptr.py +++ b/unittest/python/test_std_unique_ptr.py @@ -1,4 +1,10 @@ -from std_unique_ptr import make_unique_int, make_unique_v1, make_unique_null, V1 +from std_unique_ptr import ( + make_unique_int, + make_unique_v1, + make_unique_null, + V1, + UniquePtrHolder, +) v = make_unique_int() assert isinstance(v, int) @@ -10,3 +16,23 @@ assert v.v == 10 v = make_unique_null() assert v is None + +unique_ptr_holder = UniquePtrHolder() + +v = unique_ptr_holder.int_ptr +assert isinstance(v, int) +assert v == 20 +# v is a copy, int_ptr will not be updated +v = 10 +assert unique_ptr_holder.int_ptr == 20 + +v = unique_ptr_holder.v1_ptr +assert isinstance(v, V1) +assert v.v == 200 +# v is a ref, v1_ptr will be updated +v.v = 10 +assert unique_ptr_holder.v1_ptr.v == 10 + + +v = unique_ptr_holder.null_ptr +assert v is None diff --git a/unittest/std_unique_ptr.cpp b/unittest/std_unique_ptr.cpp index 2fdad8c33085c1baaed09ab14a88d3ca1d89e313..9c163ac88f27401bac2f73819afd347840e009b4 100644 --- a/unittest/std_unique_ptr.cpp +++ b/unittest/std_unique_ptr.cpp @@ -22,6 +22,9 @@ std::unique_ptr<V1> make_unique_v1() { return std::make_unique<V1>(10); } std::unique_ptr<V1> make_unique_null() { return nullptr; } struct UniquePtrHolder { + UniquePtrHolder() + : int_ptr(std::make_unique<int>(20)), v1_ptr(std::make_unique<V1>(200)) {} + std::unique_ptr<int> int_ptr; std::unique_ptr<V1> v1_ptr; std::unique_ptr<V1> null_ptr; @@ -38,8 +41,16 @@ BOOST_PYTHON_MODULE(std_unique_ptr) { eigenpy::StdUniquePtrCallPolicies()); bp::def("make_unique_null", make_unique_null, eigenpy::StdUniquePtrCallPolicies()); - // TODO allow access with a CallPolicie like return_internal_reference - // boost::python::class_<UniquePtrHolder>("UniquePtrHolder", bp::init<>()) - // .add_property("int_ptr", bp::make_getter(&UniquePtrHolder::int_ptr), - // bp::make_setter(&UniquePtrHolder::int_ptr)); + + boost::python::class_<UniquePtrHolder, boost::noncopyable>("UniquePtrHolder", + bp::init<>()) + .add_property("int_ptr", + bp::make_getter(&UniquePtrHolder::int_ptr, + eigenpy::ReturnInternalStdUniquePtr())) + .add_property("v1_ptr", + bp::make_getter(&UniquePtrHolder::v1_ptr, + eigenpy::ReturnInternalStdUniquePtr())) + .add_property("null_ptr", + bp::make_getter(&UniquePtrHolder::null_ptr, + eigenpy::ReturnInternalStdUniquePtr())); }