From 7652c8377be0af2788010ac84b46fa42ff0657cb Mon Sep 17 00:00:00 2001
From: Justin Carpentier <justin.carpentier@inria.fr>
Date: Mon, 24 Feb 2020 22:18:40 +0100
Subject: [PATCH] core: add exposition of Eigen::Ref<MatrixType>

---
 include/eigenpy/eigen-from-python.hpp | 156 +++++++++++++++++++++++++-
 1 file changed, 155 insertions(+), 1 deletion(-)

diff --git a/include/eigenpy/eigen-from-python.hpp b/include/eigenpy/eigen-from-python.hpp
index 6b8e439d..a92c60ac 100644
--- a/include/eigenpy/eigen-from-python.hpp
+++ b/include/eigenpy/eigen-from-python.hpp
@@ -12,6 +12,94 @@
 
 #include <boost/python/converter/rvalue_from_python_data.hpp>
 
+namespace eigenpy
+{
+  namespace details
+  {
+    template<typename MatType, bool is_const = boost::is_const<MatType>::value >
+    struct copy_if_non_const
+    {
+      static void run(const Eigen::MatrixBase<MatType> & input,
+                      PyArrayObject * pyArray)
+      {
+        EigenAllocator<MatType>::copy(input,pyArray);
+      }
+    };
+  
+    template<typename MatType>
+    struct copy_if_non_const<MatType,true>
+    {
+      static void run(const Eigen::MatrixBase<MatType> & /*input*/,
+                      PyArrayObject * /*pyArray*/)
+      {}
+    };
+  
+#if EIGEN_VERSION_AT_LEAST(3,2,0)
+    template<typename MatType> struct referent_storage_eigen_ref;
+  
+    template<typename MatType>
+    struct referent_storage_eigen_ref
+    {
+      typedef Eigen::Ref<MatType> RefType;
+      
+      typedef ::boost::python::detail::aligned_storage<
+          ::boost::python::detail::referent_size<RefType&>::value
+      > AlignedStorage;
+      
+      referent_storage_eigen_ref()
+      : pyArray(NULL)
+      , mat_ptr(NULL)
+      , ref_ptr(reinterpret_cast<RefType*>(ref_storage.bytes))
+      {
+      }
+      
+      referent_storage_eigen_ref(const RefType & ref,
+                                 PyArrayObject * pyArray,
+                                 MatType * mat_ptr = NULL)
+      : pyArray(pyArray)
+      , mat_ptr(mat_ptr)
+      , ref_ptr(reinterpret_cast<RefType*>(ref_storage.bytes))
+      {
+        Py_INCREF(pyArray);
+        new (ref_storage.bytes) RefType(ref);
+      }
+      
+      ~referent_storage_eigen_ref()
+      {
+        if(mat_ptr != NULL && PyArray_ISWRITEABLE(pyArray))
+          copy_if_non_const<MatType>::run(*mat_ptr,pyArray);
+        
+        Py_DECREF(pyArray);
+          
+        if(mat_ptr != NULL)
+          mat_ptr->~MatType();
+        
+        ref_ptr->~RefType();
+      }
+ 
+      AlignedStorage ref_storage;
+      PyArrayObject * pyArray;
+      MatType * mat_ptr;
+      RefType * ref_ptr;
+    };
+#endif
+    
+  }
+}
+
+namespace boost { namespace python { namespace detail {
+#if EIGEN_VERSION_AT_LEAST(3,2,0)
+  template<typename MatType>
+  struct referent_storage<Eigen::Ref<MatType> &>
+  {
+    typedef ::eigenpy::details::referent_storage_eigen_ref<MatType> StorageType;
+    typedef aligned_storage<
+        ::boost::python::detail::referent_size<StorageType&>::value
+    > type;
+  };
+#endif
+}}}
+
 namespace boost { namespace python { namespace converter {
 
   template<typename MatrixReference>
@@ -51,7 +139,6 @@ namespace boost { namespace python { namespace converter {
     }
   };
 
-
 #define RVALUE_FROM_PYTHON_DATA_INIT(type)                                 \
   typedef rvalue_from_python_data_eigen<type> Base;                        \
                                                                            \
@@ -79,6 +166,42 @@ namespace boost { namespace python { namespace converter {
 
 #undef RVALUE_FROM_PYTHON_DATA_INIT
 
+  template<typename MatType>
+  struct rvalue_from_python_data<Eigen::Ref<MatType> &> : rvalue_from_python_storage<Eigen::Ref<MatType> &>
+  {
+    typedef Eigen::Ref<MatType> T;
+
+# if (!defined(__MWERKS__) || __MWERKS__ >= 0x3000) \
+&& (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= 245) \
+&& (!defined(__DECCXX_VER) || __DECCXX_VER > 60590014) \
+&& !defined(BOOST_PYTHON_SYNOPSIS) /* Synopsis' OpenCXX has trouble parsing this */
+    // This must always be a POD struct with m_data its first member.
+    BOOST_STATIC_ASSERT(BOOST_PYTHON_OFFSETOF(rvalue_from_python_storage<T>,stage1) == 0);
+# endif
+
+    // The usual constructor
+    rvalue_from_python_data(rvalue_from_python_stage1_data const & _stage1)
+    {
+      this->stage1 = _stage1;
+    }
+
+    // This constructor just sets m_convertible -- used by
+    // implicitly_convertible<> to perform the final step of the
+    // conversion, where the construct() function is already known.
+    rvalue_from_python_data(void* convertible)
+    {
+      this->stage1.convertible = convertible;
+    }
+
+    // Destroys any object constructed in the storage.
+    ~rvalue_from_python_data()
+    {
+      typedef ::eigenpy::details::referent_storage_eigen_ref<MatType> StorageType;
+      if (this->stage1.convertible == this->storage.bytes)
+        static_cast<StorageType *>((void *)this->storage.bytes)->~StorageType();
+    }
+  };
+
 } } }
 
 namespace eigenpy
@@ -256,6 +379,12 @@ namespace eigenpy
       // Add conversion to Eigen::EigenBase<MatType>
       typedef Eigen::EigenBase<MatType> EigenBase;
       EigenFromPy<EigenBase>::registration();
+
+#if EIGEN_VERSION_AT_LEAST(3,2,0)
+      // Add conversion to Eigen::Ref<MatType>
+      typedef Eigen::Ref<MatType> RefType;
+      EigenFromPy<RefType>::registration();
+#endif
     }
   };
 
@@ -288,6 +417,31 @@ namespace eigenpy
   };
 
 #if EIGEN_VERSION_AT_LEAST(3,2,0)
+
+  template<typename MatType, int Options, typename StrideType>
+  struct EigenFromPy<Eigen::Ref<MatType,Options,StrideType> >
+  {
+    typedef Eigen::Ref<MatType,Options,StrideType> RefType;
+    typedef typename MatType::Scalar Scalar;
+    
+    /// \brief Determine if pyObj can be converted into a MatType object
+    static void* convertible(PyArrayObject * pyArray)
+    {
+      if(!PyArray_Check(pyArray))
+        return 0;
+      if(!PyArray_ISWRITEABLE(pyArray))
+        return 0;
+      return EigenFromPy<MatType>::convertible(pyArray);
+    }
+    
+    static void registration()
+    {
+      bp::converter::registry::push_back
+      (reinterpret_cast<void *(*)(_object *)>(&EigenFromPy::convertible),
+       &eigen_from_py_construct<RefType>,bp::type_id<RefType>());
+    }
+  };
+
   // Template specialization for Eigen::Ref
   template<typename MatType>
   struct EigenFromPyConverter< eigenpy::Ref<MatType> >
-- 
GitLab