From 1fe6a61cabf9d852e1a39091179ff8de9e8dc7a7 Mon Sep 17 00:00:00 2001
From: Justin Carpentier <justin.carpentier@inria.fr>
Date: Tue, 25 Feb 2020 16:45:47 +0100
Subject: [PATCH] core: handle const Eigen::Ref<const MatType>

---
 include/eigenpy/eigen-allocator.hpp   | 92 +++++++++++++++++++++++++++
 include/eigenpy/eigen-from-python.hpp | 73 ++++++++++++++++++++-
 2 files changed, 164 insertions(+), 1 deletion(-)

diff --git a/include/eigenpy/eigen-allocator.hpp b/include/eigenpy/eigen-allocator.hpp
index 902e69be..1a104a62 100644
--- a/include/eigenpy/eigen-allocator.hpp
+++ b/include/eigenpy/eigen-allocator.hpp
@@ -297,6 +297,98 @@ namespace eigenpy
     }
   };
 
+  template<typename MatType, int Options, typename Stride>
+  struct EigenAllocator<const Eigen::Ref<const MatType,Options,Stride> >
+  {
+    typedef const Eigen::Ref<const MatType,Options,Stride> RefType;
+    typedef typename MatType::Scalar Scalar;
+
+    typedef typename ::boost::python::detail::referent_storage<RefType&>::StorageType StorageType;
+    
+    static void allocate(PyArrayObject * pyArray,
+                         bp::converter::rvalue_from_python_storage<RefType> * storage)
+    {
+      typedef typename StrideType<MatType,Eigen::internal::traits<RefType>::StrideType::InnerStrideAtCompileTime, Eigen::internal::traits<RefType>::StrideType::OuterStrideAtCompileTime >::type NumpyMapStride;
+
+      bool need_to_allocate = false;
+      const int pyArray_Type = EIGENPY_GET_PY_ARRAY_TYPE(pyArray);
+      if(pyArray_Type != NumpyEquivalentType<Scalar>::type_code)
+        need_to_allocate |= true;
+      if(    (MatType::IsRowMajor && (PyArray_IS_C_CONTIGUOUS(pyArray) && !PyArray_IS_F_CONTIGUOUS(pyArray)))
+          || (!MatType::IsRowMajor && (PyArray_IS_F_CONTIGUOUS(pyArray) && !PyArray_IS_C_CONTIGUOUS(pyArray)))
+          || MatType::IsVectorAtCompileTime
+          || (PyArray_IS_F_CONTIGUOUS(pyArray) && PyArray_IS_C_CONTIGUOUS(pyArray))) // no need to allocate
+        need_to_allocate |= false;
+      else
+        need_to_allocate |= true;
+      if(Options != Eigen::Unaligned) // we need to check whether the memory is correctly aligned and composed of a continuous segment
+      {
+        void * data_ptr = PyArray_DATA(pyArray);
+        if(!PyArray_ISONESEGMENT(pyArray) || !is_aligned(data_ptr,Options))
+          need_to_allocate |= true;
+      }
+      
+      void * raw_ptr = storage->storage.bytes;
+      if(need_to_allocate)
+      {
+        MatType * mat_ptr;
+        mat_ptr = details::init_matrix_or_array<MatType>::run(pyArray);
+        RefType mat_ref(*mat_ptr);
+        
+        new (raw_ptr) StorageType(mat_ref,pyArray,mat_ptr);
+        
+        MatType & mat = *mat_ptr;
+        if(pyArray_Type == NumpyEquivalentType<Scalar>::type_code)
+        {
+          mat = MapNumpy<MatType,Scalar>::map(pyArray); // avoid useless cast
+          return;
+        }
+        
+        switch(pyArray_Type)
+        {
+          case NPY_INT:
+            EIGENPY_CAST_FROM_PYARRAY_TO_EIGEN_MATRIX(MatType,int,Scalar,pyArray,mat);
+            break;
+          case NPY_LONG:
+            EIGENPY_CAST_FROM_PYARRAY_TO_EIGEN_MATRIX(MatType,long,Scalar,pyArray,mat);
+            break;
+          case NPY_FLOAT:
+            EIGENPY_CAST_FROM_PYARRAY_TO_EIGEN_MATRIX(MatType,float,Scalar,pyArray,mat);
+            break;
+          case NPY_CFLOAT:
+            EIGENPY_CAST_FROM_PYARRAY_TO_EIGEN_MATRIX(MatType,std::complex<float>,Scalar,pyArray,mat);
+            break;
+          case NPY_DOUBLE:
+            EIGENPY_CAST_FROM_PYARRAY_TO_EIGEN_MATRIX(MatType,double,Scalar,pyArray,mat);
+            break;
+          case NPY_CDOUBLE:
+            EIGENPY_CAST_FROM_PYARRAY_TO_EIGEN_MATRIX(MatType,std::complex<double>,Scalar,pyArray,mat);
+            break;
+          case NPY_LONGDOUBLE:
+            EIGENPY_CAST_FROM_PYARRAY_TO_EIGEN_MATRIX(MatType,long double,Scalar,pyArray,mat);
+            break;
+          case NPY_CLONGDOUBLE:
+            EIGENPY_CAST_FROM_PYARRAY_TO_EIGEN_MATRIX(MatType,std::complex<long double>,Scalar,pyArray,mat);
+            break;
+          default:
+            throw Exception("You asked for a conversion which is not implemented.");
+        }
+      }
+      else
+      {
+        assert(pyArray_Type == NumpyEquivalentType<Scalar>::type_code);
+        typename MapNumpy<MatType,Scalar,Options,NumpyMapStride>::EigenMap numpyMap = MapNumpy<MatType,Scalar,Options,NumpyMapStride>::map(pyArray);
+        RefType mat_ref(numpyMap);
+        new (raw_ptr) StorageType(mat_ref,pyArray);
+      }
+    }
+    
+    static void copy(RefType const & ref, PyArrayObject * pyArray)
+    {
+      EigenAllocator<MatType>::copy(ref,pyArray);
+    }
+  };
+
   template<typename MatType>
   struct EigenAllocator< eigenpy::Ref<MatType> >
   {
diff --git a/include/eigenpy/eigen-from-python.hpp b/include/eigenpy/eigen-from-python.hpp
index 4551a989..325326d6 100644
--- a/include/eigenpy/eigen-from-python.hpp
+++ b/include/eigenpy/eigen-from-python.hpp
@@ -97,6 +97,15 @@ namespace boost { namespace python { namespace detail {
         ::boost::python::detail::referent_size<StorageType&>::value
     > type;
   };
+
+  template<typename MatType, int Options, typename Stride>
+  struct referent_storage<const Eigen::Ref<const MatType,Options,Stride> &>
+  {
+    typedef ::eigenpy::details::referent_storage_eigen_ref<const MatType,Options,Stride> StorageType;
+    typedef aligned_storage<
+        ::boost::python::detail::referent_size<StorageType&>::value
+    > type;
+  };
 #endif
 }}}
 
@@ -167,7 +176,8 @@ namespace boost { namespace python { namespace converter {
 #undef RVALUE_FROM_PYTHON_DATA_INIT
 
   template<typename MatType, int Options, typename Stride>
-  struct rvalue_from_python_data<Eigen::Ref<MatType,Options,Stride> &> : rvalue_from_python_storage<Eigen::Ref<MatType,Options,Stride> &>
+  struct rvalue_from_python_data<Eigen::Ref<MatType,Options,Stride> &>
+  : rvalue_from_python_storage<Eigen::Ref<MatType,Options,Stride> &>
   {
     typedef Eigen::Ref<MatType,Options,Stride> T;
 
@@ -202,6 +212,43 @@ namespace boost { namespace python { namespace converter {
     }
   };
 
+  template<typename MatType, int Options, typename Stride>
+  struct rvalue_from_python_data<const Eigen::Ref<const MatType,Options,Stride> &>
+  : rvalue_from_python_storage<const Eigen::Ref<const MatType,Options,Stride> &>
+  {
+    typedef const Eigen::Ref<const MatType,Options,Stride> 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<const MatType, Options,Stride> StorageType;
+      if (this->stage1.convertible == this->storage.bytes)
+        static_cast<StorageType *>((void *)this->storage.bytes)->~StorageType();
+    }
+  };
+
 } } }
 
 namespace eigenpy
@@ -384,6 +431,10 @@ namespace eigenpy
       // Add conversion to Eigen::Ref<MatType>
       typedef Eigen::Ref<MatType> RefType;
       EigenFromPy<RefType>::registration();
+      
+      // Add conversion to Eigen::Ref<MatType>
+      typedef const Eigen::Ref<const MatType> ConstRefType;
+      EigenFromPy<ConstRefType>::registration();
 #endif
     }
   };
@@ -442,6 +493,26 @@ namespace eigenpy
     }
   };
 
+  template<typename MatType, int Options, typename Stride>
+  struct EigenFromPy<const Eigen::Ref<const MatType,Options,Stride> >
+  {
+    typedef const Eigen::Ref<const MatType,Options,Stride> ConstRefType;
+    typedef typename MatType::Scalar Scalar;
+    
+    /// \brief Determine if pyObj can be converted into a MatType object
+    static void* convertible(PyArrayObject * pyArray)
+    {
+      return EigenFromPy<MatType>::convertible(pyArray);
+    }
+    
+    static void registration()
+    {
+      bp::converter::registry::push_back
+      (reinterpret_cast<void *(*)(_object *)>(&EigenFromPy::convertible),
+       &eigen_from_py_construct<ConstRefType>,bp::type_id<ConstRefType>());
+    }
+  };
+
   // Template specialization for Eigen::Ref
   template<typename MatType>
   struct EigenFromPyConverter< eigenpy::Ref<MatType> >
-- 
GitLab