From 0ff0ab3c4ce9d3ff166642abd47a07bed488491c Mon Sep 17 00:00:00 2001
From: Joris Vaillant <joris.vaillant@inria.fr>
Date: Wed, 31 Jan 2024 22:05:44 +0100
Subject: [PATCH] unique_ptr: Add a CallPolicy to copy std::unique_ptr content

---
 CMakeLists.txt                         |  1 +
 include/eigenpy/std_unique_ptr.hpp     | 49 ++++++++++++++++++++++++++
 unittest/CMakeLists.txt                |  1 +
 unittest/python/test_std_unique_ptr.py | 12 +++++++
 unittest/std_unique_ptr.cpp            | 45 +++++++++++++++++++++++
 5 files changed, 108 insertions(+)
 create mode 100644 include/eigenpy/std_unique_ptr.hpp
 create mode 100644 unittest/python/test_std_unique_ptr.py
 create mode 100644 unittest/std_unique_ptr.cpp

diff --git a/CMakeLists.txt b/CMakeLists.txt
index d8d8a79e..f280d6b5 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -167,6 +167,7 @@ set(${PROJECT_NAME}_HEADERS
     include/eigenpy/scipy-allocator.hpp
     include/eigenpy/scipy-type.hpp
     include/eigenpy/variant.hpp
+    include/eigenpy/std_unique_ptr.hpp
     include/eigenpy/swig.hpp
     include/eigenpy/version.hpp)
 
diff --git a/include/eigenpy/std_unique_ptr.hpp b/include/eigenpy/std_unique_ptr.hpp
new file mode 100644
index 00000000..c45dbd08
--- /dev/null
+++ b/include/eigenpy/std_unique_ptr.hpp
@@ -0,0 +1,49 @@
+//
+// Copyright (c) 2024 INRIA
+//
+
+#ifndef __eigenpy_utils_std_unique_ptr_hpp__
+#define __eigenpy_utils_std_unique_ptr_hpp__
+
+#include "eigenpy/fwd.hpp"
+
+#include <boost/python.hpp>
+
+#include <memory>
+#include <iostream>
+
+namespace eigenpy {
+
+/// result_converter of StdUniquePtrCallPolicies
+struct StdUniquePtrResultConverter {
+  template <class T>
+  struct apply {
+    struct type {
+      typedef typename bp::detail::value_arg<T>::type argument_type;
+
+      /// TODO, this work by copy
+      /// We maybe can transfer the ownership to Python for class type
+      /// and when argument_type is an lvalue ref
+      PyObject* operator()(argument_type x) const {
+        return bp::to_python_value<const typename T::element_type&>()(*x);
+      }
+#ifndef BOOST_PYTHON_NO_PY_SIGNATURES
+      PyTypeObject const* get_pytype() const {
+        return bp::to_python_value<const typename T::element_type&>()
+            .get_pytype();
+      }
+#endif
+      BOOST_STATIC_CONSTANT(bool, uses_registry = true);
+    };
+  };
+};
+
+/// Access CallPolicie to get std::unique_ptr value from a functio
+/// that return an std::unique_ptr
+struct StdUniquePtrCallPolicies : bp::default_call_policies {
+  typedef StdUniquePtrResultConverter result_converter;
+};
+
+}  // namespace eigenpy
+
+#endif  // ifndef __eigenpy_utils_std_unique_ptr_hpp__
diff --git a/unittest/CMakeLists.txt b/unittest/CMakeLists.txt
index 097aab1f..8dc425fa 100644
--- a/unittest/CMakeLists.txt
+++ b/unittest/CMakeLists.txt
@@ -45,6 +45,7 @@ add_lib_unit_test(std_vector)
 add_lib_unit_test(std_array)
 add_lib_unit_test(std_pair)
 add_lib_unit_test(user_struct)
+add_lib_unit_test(std_unique_ptr)
 
 function(config_test test tagname opttype)
   set(MODNAME ${test}_${tagname})
diff --git a/unittest/python/test_std_unique_ptr.py b/unittest/python/test_std_unique_ptr.py
new file mode 100644
index 00000000..cb8f7d46
--- /dev/null
+++ b/unittest/python/test_std_unique_ptr.py
@@ -0,0 +1,12 @@
+from std_unique_ptr import make_unique_int, make_unique_v1, make_unique_null, V1
+
+v = make_unique_int()
+assert isinstance(v, int)
+assert v == 10
+
+v = make_unique_v1()
+assert isinstance(v, V1)
+assert v.v == 10
+
+v = make_unique_null()
+assert v is None
diff --git a/unittest/std_unique_ptr.cpp b/unittest/std_unique_ptr.cpp
new file mode 100644
index 00000000..2fdad8c3
--- /dev/null
+++ b/unittest/std_unique_ptr.cpp
@@ -0,0 +1,45 @@
+/// @file
+/// @copyright Copyright 2023 CNRS INRIA
+
+#include <iostream>
+#include <eigenpy/eigenpy.hpp>
+#include <eigenpy/std_unique_ptr.hpp>
+#include <memory>
+
+namespace bp = boost::python;
+
+struct V1 {
+  V1() = default;
+  V1(double p_v) : v(p_v) {}
+
+  double v = 100;
+};
+
+std::unique_ptr<int> make_unique_int() { return std::make_unique<int>(10); }
+
+std::unique_ptr<V1> make_unique_v1() { return std::make_unique<V1>(10); }
+
+std::unique_ptr<V1> make_unique_null() { return nullptr; }
+
+struct UniquePtrHolder {
+  std::unique_ptr<int> int_ptr;
+  std::unique_ptr<V1> v1_ptr;
+  std::unique_ptr<V1> null_ptr;
+};
+
+BOOST_PYTHON_MODULE(std_unique_ptr) {
+  eigenpy::enableEigenPy();
+
+  bp::class_<V1>("V1", bp::init<>()).def_readwrite("v", &V1::v);
+
+  bp::def("make_unique_int", make_unique_int,
+          eigenpy::StdUniquePtrCallPolicies());
+  bp::def("make_unique_v1", make_unique_v1,
+          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));
+}
-- 
GitLab