From 0955b77ad07c855d62c40f7ea22636d9c2a6d504 Mon Sep 17 00:00:00 2001
From: Justin Carpentier <justin.carpentier@inria.fr>
Date: Mon, 20 Feb 2023 19:03:00 +0100
Subject: [PATCH] core: add full support of Eigen::Tensor types

---
 CMakeLists.txt                               |   1 +
 include/eigenpy/details.hpp                  |  49 ++++-
 include/eigenpy/eigen-allocator.hpp          | 114 +++++++++-
 include/eigenpy/eigen-from-python.hpp        |  68 ++----
 include/eigenpy/eigen-to-python.hpp          |  80 ++++---
 include/eigenpy/fwd.hpp                      |  34 ++-
 include/eigenpy/numpy-allocator.hpp          | 136 ++++++++++--
 include/eigenpy/numpy-map.hpp                |  39 +++-
 include/eigenpy/stride.hpp                   |  13 ++
 include/eigenpy/tensor/eigen-from-python.hpp | 217 +++++++++++++++++++
 10 files changed, 635 insertions(+), 116 deletions(-)
 create mode 100644 include/eigenpy/tensor/eigen-from-python.hpp

diff --git a/CMakeLists.txt b/CMakeLists.txt
index b4b12e3..800c89a 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -164,6 +164,7 @@ set(${PROJECT_NAME}_HEADERS
     include/eigenpy/std-vector.hpp
     include/eigenpy/pickle-vector.hpp
     include/eigenpy/stride.hpp
+    include/eigenpy/tensor/eigen-from-python.hpp
     include/eigenpy/swig.hpp
     include/eigenpy/version.hpp)
 
diff --git a/include/eigenpy/details.hpp b/include/eigenpy/details.hpp
index 12be4b0..a71c59f 100644
--- a/include/eigenpy/details.hpp
+++ b/include/eigenpy/details.hpp
@@ -17,24 +17,49 @@
 #include "eigenpy/scalar-conversion.hpp"
 
 namespace eigenpy {
-template <typename MatType, typename EigenEquivalentType>
-EIGENPY_DEPRECATED void enableEigenPySpecific() {
-  enableEigenPySpecific<MatType>();
-}
+
+template <typename EigenType,
+          typename BaseType = typename get_eigen_base_type<EigenType>::type>
+struct expose_eigen_type_impl;
 
 template <typename MatType>
-void enableEigenPySpecific() {
-  if (check_registration<MatType>()) return;
+struct expose_eigen_type_impl<MatType, Eigen::MatrixBase<MatType> > {
+  static void run() {
+    if (check_registration<MatType>()) return;
 
-  // to-python
-  EigenToPyConverter<MatType>::registration();
+    // to-python
+    EigenToPyConverter<MatType>::registration();
 #if EIGEN_VERSION_AT_LEAST(3, 2, 0)
-  EigenToPyConverter<Eigen::Ref<MatType> >::registration();
-  EigenToPyConverter<const Eigen::Ref<const MatType> >::registration();
+    EigenToPyConverter<Eigen::Ref<MatType> >::registration();
+    EigenToPyConverter<const Eigen::Ref<const MatType> >::registration();
+#endif
+
+    // from-python
+    EigenFromPyConverter<MatType>::registration();
+  }
+};
+
+#ifdef EIGENPY_WITH_TENSOR_SUPPORT
+template <typename TensorType>
+struct expose_eigen_type_impl<TensorType, Eigen::TensorBase<TensorType> > {
+  static void run() {
+    if (check_registration<TensorType>()) return;
+
+    // to-python
+    EigenToPyConverter<TensorType>::registration();
+    EigenToPyConverter<Eigen::TensorRef<TensorType> >::registration();
+    EigenToPyConverter<
+        const Eigen::TensorRef<const TensorType> >::registration();
+
+    // from-python
+    EigenFromPyConverter<TensorType>::registration();
+  }
+};
 #endif
 
-  // from-python
-  EigenFromPyConverter<MatType>::registration();
+template <typename MatType>
+void enableEigenPySpecific() {
+  expose_eigen_type_impl<MatType>::run();
 }
 
 }  // namespace eigenpy
diff --git a/include/eigenpy/eigen-allocator.hpp b/include/eigenpy/eigen-allocator.hpp
index 8f1a669..1aa0b6a 100644
--- a/include/eigenpy/eigen-allocator.hpp
+++ b/include/eigenpy/eigen-allocator.hpp
@@ -78,7 +78,9 @@ struct init_tensor {
     assert(PyArray_NDIM(pyArray) == Rank);
     typedef typename Tensor::Index Index;
 
-    Eigen::array<Index, Rank> dimensions = PyArray_DIMS(pyArray);
+    Eigen::array<Index, Rank> dimensions;
+    for (int k = 0; k < PyArray_NDIM(pyArray); ++k)
+      dimensions[k] = PyArray_DIMS(pyArray)[k];
 
     if (storage)
       return new (storage) Tensor(dimensions);
@@ -169,9 +171,13 @@ template <typename MatType>
 struct eigen_allocator_impl_matrix;
 
 template <typename MatType>
-struct eigen_allocator_impl<MatType, Eigen::MatrixBase<MatType> >
+struct eigen_allocator_impl<MatType, Eigen::MatrixBase<MatType>>
     : eigen_allocator_impl_matrix<MatType> {};
 
+template <typename MatType>
+struct eigen_allocator_impl<const MatType, const Eigen::MatrixBase<MatType>>
+    : eigen_allocator_impl_matrix<const MatType> {};
+
 template <typename MatType>
 struct eigen_allocator_impl_matrix {
   typedef MatType Type;
@@ -299,7 +305,19 @@ struct eigen_allocator_impl_matrix {
 
 #ifdef EIGENPY_WITH_TENSOR_SUPPORT
 template <typename TensorType>
-struct eigen_allocator_impl<TensorType, Eigen::TensorBase<TensorType> > {
+struct eigen_allocator_impl_tensor;
+
+template <typename TensorType>
+struct eigen_allocator_impl<TensorType, Eigen::TensorBase<TensorType>>
+    : eigen_allocator_impl_tensor<TensorType> {};
+
+template <typename TensorType>
+struct eigen_allocator_impl<const TensorType,
+                            const Eigen::TensorBase<TensorType>>
+    : eigen_allocator_impl_tensor<const TensorType> {};
+
+template <typename TensorType>
+struct eigen_allocator_impl_tensor {
   typedef typename TensorType::Scalar Scalar;
   static void allocate(
       PyArrayObject *pyArray,
@@ -324,8 +342,11 @@ struct eigen_allocator_impl<TensorType, Eigen::TensorBase<TensorType> > {
       tensor)
 
   /// \brief Copy Python array into the input matrix mat.
-  template <typename MatrixDerived>
-  static void copy(PyArrayObject *pyArray, const TensorType &tensor) {
+  template <typename TensorDerived>
+  static void copy(PyArrayObject *pyArray,
+                   const Eigen::TensorBase<TensorDerived> &tensor_) {
+    TensorDerived &tensor = const_cast<TensorDerived &>(
+        static_cast<const TensorDerived &>(tensor_));
     const int pyArray_type_code = EIGENPY_GET_PY_ARRAY_TYPE(pyArray);
     const int Scalar_type_code = Register::getTypeCode<Scalar>();
 
@@ -450,7 +471,7 @@ inline bool is_arr_layout_compatible_with_mat_type(PyArrayObject *pyArray) {
 }
 
 template <typename MatType, int Options, typename Stride>
-struct eigen_allocator_impl_matrix<Eigen::Ref<MatType, Options, Stride> > {
+struct eigen_allocator_impl_matrix<Eigen::Ref<MatType, Options, Stride>> {
   typedef Eigen::Ref<MatType, Options, Stride> RefType;
   typedef typename MatType::Scalar Scalar;
 
@@ -511,7 +532,7 @@ struct eigen_allocator_impl_matrix<Eigen::Ref<MatType, Options, Stride> > {
 
 template <typename MatType, int Options, typename Stride>
 struct eigen_allocator_impl_matrix<
-    const Eigen::Ref<const MatType, Options, Stride> > {
+    const Eigen::Ref<const MatType, Options, Stride>> {
   typedef const Eigen::Ref<const MatType, Options, Stride> RefType;
   typedef typename MatType::Scalar Scalar;
 
@@ -572,6 +593,85 @@ struct eigen_allocator_impl_matrix<
 };
 #endif
 
+#ifdef EIGENPY_WITH_TENSOR_SUPPORT
+
+template <typename TensorType, typename TensorRef>
+struct eigen_allocator_impl_tensor_ref;
+
+template <typename TensorType>
+struct eigen_allocator_impl_tensor<Eigen::TensorRef<TensorType>>
+    : eigen_allocator_impl_tensor_ref<TensorType,
+                                      Eigen::TensorRef<TensorType>> {};
+
+template <typename TensorType>
+struct eigen_allocator_impl_tensor<const Eigen::TensorRef<const TensorType>>
+    : eigen_allocator_impl_tensor_ref<
+          const TensorType, const Eigen::TensorRef<const TensorType>> {};
+
+template <typename TensorType, typename RefType>
+struct eigen_allocator_impl_tensor_ref {
+  typedef typename TensorType::Scalar Scalar;
+
+  typedef
+      typename ::boost::python::detail::referent_storage<RefType &>::StorageType
+          StorageType;
+
+  static void allocate(
+      PyArrayObject *pyArray,
+      ::boost::python::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;
+
+    static const int Options = Eigen::internal::traits<TensorType>::Options;
+
+    bool need_to_allocate = false;
+    const int pyArray_type_code = EIGENPY_GET_PY_ARRAY_TYPE(pyArray);
+    const int Scalar_type_code = Register::getTypeCode<Scalar>();
+    if (pyArray_type_code != Scalar_type_code) need_to_allocate |= true;
+    //    bool incompatible_layout =
+    //        !is_arr_layout_compatible_with_mat_type<MatType>(pyArray);
+    //    need_to_allocate |= incompatible_layout;
+    //    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) {
+      TensorType *tensor_ptr;
+      tensor_ptr = details::init_tensor<TensorType>::run(pyArray);
+      RefType tensor_ref(*tensor_ptr);
+
+      new (raw_ptr) StorageType(tensor_ref, pyArray, tensor_ptr);
+
+      RefType &tensor = *reinterpret_cast<RefType *>(raw_ptr);
+      EigenAllocator<TensorType>::copy(pyArray, tensor);
+    } else {
+      assert(pyArray_type_code == Scalar_type_code);
+      typename NumpyMap<TensorType, Scalar, Options>::EigenMap numpyMap =
+          NumpyMap<TensorType, Scalar, Options>::map(pyArray);
+      RefType tensor_ref(numpyMap);
+      new (raw_ptr) StorageType(tensor_ref, pyArray);
+    }
+  }
+
+  static void copy(RefType const &ref, PyArrayObject *pyArray) {
+    EigenAllocator<TensorType>::copy(ref, pyArray);
+  }
+};
+
+#endif
+
 template <typename EigenType>
 struct EigenAllocator : eigen_allocator_impl<EigenType> {};
 
diff --git a/include/eigenpy/eigen-from-python.hpp b/include/eigenpy/eigen-from-python.hpp
index 27f066c..c8556e3 100644
--- a/include/eigenpy/eigen-from-python.hpp
+++ b/include/eigenpy/eigen-from-python.hpp
@@ -24,16 +24,6 @@ struct expected_pytype_for_arg<MatType, Eigen::MatrixBase<MatType> > {
   }
 };
 
-#ifdef EIGENPY_WITH_TENSOR_SUPPORT
-template <typename TensorType>
-struct expected_pytype_for_arg<TensorType, Eigen::TensorBase<TensorType> > {
-  static PyTypeObject const *get_pytype() {
-    PyTypeObject const *py_type = eigenpy::getPyArrayType();
-    return py_type;
-  }
-};
-#endif
-
 }  // namespace eigenpy
 
 namespace boost {
@@ -47,13 +37,6 @@ struct expected_pytype_for_arg<
     : eigenpy::expected_pytype_for_arg<
           Eigen::Matrix<Scalar, Rows, Cols, Options, MaxRows, MaxCols> > {};
 
-#ifdef EIGENPY_WITH_TENSOR_SUPPORT
-template <typename Scalar, int Rank, int Options, typename IndexType>
-struct expected_pytype_for_arg<Eigen::Tensor<Scalar, Rank, Options, IndexType> >
-    : eigenpy::expected_pytype_for_arg<
-          Eigen::Tensor<Scalar, Rank, Options, IndexType> > {};
-#endif
-
 }  // namespace converter
 }  // namespace python
 }  // namespace boost
@@ -75,44 +58,43 @@ struct copy_if_non_const<const MatType, true> {
 };
 
 #if EIGEN_VERSION_AT_LEAST(3, 2, 0)
-template <typename MatType, int Options, typename Stride>
-struct referent_storage_eigen_ref;
 
-template <typename MatType, int Options, typename Stride>
+template <typename _RefType>
 struct referent_storage_eigen_ref {
-  typedef Eigen::Ref<MatType, Options, Stride> RefType;
+  typedef _RefType RefType;
+  typedef typename get_eigen_ref_plain_type<RefType>::type PlainObjectType;
   typedef typename ::eigenpy::aligned_storage<
       ::boost::python::detail::referent_size<RefType &>::value>::type
       AlignedStorage;
 
   referent_storage_eigen_ref()
       : pyArray(NULL),
-        mat_ptr(NULL),
+        plain_ptr(NULL),
         ref_ptr(reinterpret_cast<RefType *>(ref_storage.bytes)) {}
 
   referent_storage_eigen_ref(const RefType &ref, PyArrayObject *pyArray,
-                             MatType *mat_ptr = NULL)
+                             PlainObjectType *plain_ptr = NULL)
       : pyArray(pyArray),
-        mat_ptr(mat_ptr),
+        plain_ptr(plain_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);
+    if (plain_ptr != NULL && PyArray_ISWRITEABLE(pyArray))
+      copy_if_non_const<PlainObjectType>::run(*plain_ptr, pyArray);
 
     Py_DECREF(pyArray);
 
-    if (mat_ptr != NULL) mat_ptr->~MatType();
+    if (plain_ptr != NULL) plain_ptr->~PlainObjectType();
 
     ref_ptr->~RefType();
   }
 
   AlignedStorage ref_storage;
   PyArrayObject *pyArray;
-  MatType *mat_ptr;
+  PlainObjectType *plain_ptr;
   RefType *ref_ptr;
 };
 #endif
@@ -126,18 +108,16 @@ namespace detail {
 #if EIGEN_VERSION_AT_LEAST(3, 2, 0)
 template <typename MatType, int Options, typename Stride>
 struct referent_storage<Eigen::Ref<MatType, Options, Stride> &> {
-  typedef ::eigenpy::details::referent_storage_eigen_ref<MatType, Options,
-                                                         Stride>
-      StorageType;
+  typedef Eigen::Ref<MatType, Options, Stride> RefType;
+  typedef ::eigenpy::details::referent_storage_eigen_ref<RefType> StorageType;
   typedef typename ::eigenpy::aligned_storage<
       referent_size<StorageType &>::value>::type 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 Eigen::Ref<const MatType, Options, Stride> RefType;
+  typedef ::eigenpy::details::referent_storage_eigen_ref<RefType> StorageType;
   typedef typename ::eigenpy::aligned_storage<
       referent_size<StorageType &>::value>::type type;
 };
@@ -189,7 +169,7 @@ struct rvalue_from_python_data<Eigen::PlainObjectBase<Derived> const &>
 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> &> {
-  typedef Eigen::Ref<MatType, Options, Stride> T;
+  typedef Eigen::Ref<MatType, Options, Stride> RefType;
 
 #if (!defined(__MWERKS__) || __MWERKS__ >= 0x3000) &&                        \
     (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= 245) &&                 \
@@ -197,7 +177,7 @@ struct rvalue_from_python_data<Eigen::Ref<MatType, Options, Stride> &>
     !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>,
+  BOOST_STATIC_ASSERT(BOOST_PYTHON_OFFSETOF(rvalue_from_python_storage<RefType>,
                                             stage1) == 0);
 #endif
 
@@ -215,9 +195,7 @@ struct rvalue_from_python_data<Eigen::Ref<MatType, Options, Stride> &>
 
   // Destroys any object constructed in the storage.
   ~rvalue_from_python_data() {
-    typedef ::eigenpy::details::referent_storage_eigen_ref<MatType, Options,
-                                                           Stride>
-        StorageType;
+    typedef ::eigenpy::details::referent_storage_eigen_ref<RefType> StorageType;
     if (this->stage1.convertible == this->storage.bytes)
       static_cast<StorageType *>((void *)this->storage.bytes)->~StorageType();
   }
@@ -228,7 +206,7 @@ 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;
+  typedef Eigen::Ref<const MatType, Options, Stride> RefType;
 
 #if (!defined(__MWERKS__) || __MWERKS__ >= 0x3000) &&                        \
     (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= 245) &&                 \
@@ -236,7 +214,7 @@ struct rvalue_from_python_data<
     !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>,
+  BOOST_STATIC_ASSERT(BOOST_PYTHON_OFFSETOF(rvalue_from_python_storage<RefType>,
                                             stage1) == 0);
 #endif
 
@@ -254,9 +232,7 @@ struct rvalue_from_python_data<
 
   // Destroys any object constructed in the storage.
   ~rvalue_from_python_data() {
-    typedef ::eigenpy::details::referent_storage_eigen_ref<const MatType,
-                                                           Options, Stride>
-        StorageType;
+    typedef ::eigenpy::details::referent_storage_eigen_ref<RefType> StorageType;
     if (this->stage1.convertible == this->storage.bytes)
       static_cast<StorageType *>((void *)this->storage.bytes)->~StorageType();
   }
@@ -577,4 +553,8 @@ struct EigenFromPy<const Eigen::Ref<const MatType, Options, Stride> > {
 
 }  // namespace eigenpy
 
+#ifdef EIGENPY_WITH_TENSOR_SUPPORT
+#include "eigenpy/tensor/eigen-from-python.hpp"
+#endif
+
 #endif  // __eigenpy_eigen_from_python_hpp__
diff --git a/include/eigenpy/eigen-to-python.hpp b/include/eigenpy/eigen-to-python.hpp
index b6735aa..013f8cf 100644
--- a/include/eigenpy/eigen-to-python.hpp
+++ b/include/eigenpy/eigen-to-python.hpp
@@ -12,6 +12,7 @@
 #include "eigenpy/eigen-allocator.hpp"
 #include "eigenpy/numpy-allocator.hpp"
 #include "eigenpy/numpy-type.hpp"
+#include "eigenpy/registration.hpp"
 
 namespace boost {
 namespace python {
@@ -62,8 +63,31 @@ namespace eigenpy {
 
 EIGENPY_DOCUMENTATION_START_IGNORE
 
-template <typename MatType, typename Scalar>
-struct eigen_to_py_impl {
+template <typename EigenType,
+          typename BaseType = typename get_eigen_base_type<EigenType>::type>
+struct eigen_to_py_impl;
+
+template <typename MatType>
+struct eigen_to_py_impl_matrix;
+
+template <typename MatType>
+struct eigen_to_py_impl<MatType, Eigen::MatrixBase<MatType> >
+    : eigen_to_py_impl_matrix<MatType> {};
+
+template <typename MatType>
+struct eigen_to_py_impl<MatType&, Eigen::MatrixBase<MatType> >
+    : eigen_to_py_impl_matrix<MatType&> {};
+
+template <typename MatType>
+struct eigen_to_py_impl<const MatType, const Eigen::MatrixBase<MatType> >
+    : eigen_to_py_impl_matrix<const MatType> {};
+
+template <typename MatType>
+struct eigen_to_py_impl<const MatType&, const Eigen::MatrixBase<MatType> >
+    : eigen_to_py_impl_matrix<const MatType&> {};
+
+template <typename MatType>
+struct eigen_to_py_impl_matrix {
   static PyObject* convert(
       typename boost::add_reference<
           typename boost::add_const<MatType>::type>::type mat) {
@@ -95,40 +119,44 @@ struct eigen_to_py_impl {
   }
 };
 
-template <typename MatType, int Options, typename Stride, typename Scalar>
-struct eigen_to_py_impl<Eigen::Ref<MatType, Options, Stride>, Scalar> {
-  static PyObject* convert(const Eigen::Ref<MatType, Options, Stride>& mat) {
-    typedef Eigen::Ref<MatType, Options, Stride> EigenRef;
+#ifdef EIGENPY_WITH_TENSOR_SUPPORT
+template <typename TensorType>
+struct eigen_to_py_impl_tensor;
 
-    assert((mat.rows() < INT_MAX) && (mat.cols() < INT_MAX) &&
-           "Matrix range larger than int ... should never happen.");
-    const npy_intp R = (npy_intp)mat.rows(), C = (npy_intp)mat.cols();
+template <typename TensorType>
+struct eigen_to_py_impl<TensorType, Eigen::TensorBase<TensorType> >
+    : eigen_to_py_impl_tensor<TensorType> {};
 
-    PyArrayObject* pyArray;
-    // Allocate Python memory
-    if ((((!(C == 1) != !(R == 1)) && !MatType::IsVectorAtCompileTime) ||
-         MatType::IsVectorAtCompileTime) &&
-        NumpyType::getType() ==
-            ARRAY_TYPE)  // Handle array with a single dimension
-    {
-      npy_intp shape[1] = {C == 1 ? R : C};
-      pyArray = NumpyAllocator<EigenRef>::allocate(const_cast<EigenRef&>(mat),
-                                                   1, shape);
-    } else {
-      npy_intp shape[2] = {R, C};
-      pyArray = NumpyAllocator<EigenRef>::allocate(const_cast<EigenRef&>(mat),
-                                                   2, shape);
-    }
+template <typename TensorType>
+struct eigen_to_py_impl<const TensorType, const Eigen::TensorBase<TensorType> >
+    : eigen_to_py_impl_tensor<const TensorType> {};
+
+template <typename TensorType>
+struct eigen_to_py_impl_tensor {
+  static PyObject* convert(
+      typename boost::add_reference<
+          typename boost::add_const<TensorType>::type>::type tensor) {
+    //    typedef typename boost::remove_const<
+    //        typename boost::remove_reference<Tensor>::type>::type
+    //        TensorDerived;
+
+    static const int NumIndices = TensorType::NumIndices;
+    npy_intp shape[NumIndices];
+    for (int k = 0; k < NumIndices; ++k) shape[k] = tensor.dimension(k);
+
+    PyArrayObject* pyArray = NumpyAllocator<TensorType>::allocate(
+        const_cast<TensorType&>(tensor), NumIndices, shape);
 
     // Create an instance (either np.array or np.matrix)
     return NumpyType::make(pyArray).ptr();
   }
 };
+#endif
 
 EIGENPY_DOCUMENTATION_END_IGNORE
 
-template <typename MatType, typename Scalar>
-struct EigenToPy : eigen_to_py_impl<MatType, Scalar> {
+template <typename EigenType, typename Scalar>
+struct EigenToPy : eigen_to_py_impl<EigenType> {
   static PyTypeObject const* get_pytype() { return getPyArrayType(); }
 };
 
diff --git a/include/eigenpy/fwd.hpp b/include/eigenpy/fwd.hpp
index 35ea873..4652110 100644
--- a/include/eigenpy/fwd.hpp
+++ b/include/eigenpy/fwd.hpp
@@ -124,23 +124,49 @@ template <typename MatType,
               typename boost::remove_reference<MatType>::type::Scalar>
 struct EigenFromPy;
 
+template <typename T>
+struct remove_const_reference {
+  typedef typename boost::remove_const<
+      typename boost::remove_reference<T>::type>::type type;
+};
+
 template <typename EigenType>
 struct get_eigen_base_type {
-  typedef typename boost::remove_const<EigenType>::type EigenType_;
+  typedef typename remove_const_reference<EigenType>::type EigenType_;
   typedef typename boost::mpl::if_<
       boost::is_base_of<Eigen::MatrixBase<EigenType_>, EigenType_>,
-      Eigen::MatrixBase<EigenType>
+      Eigen::MatrixBase<EigenType_>
 #ifdef EIGENPY_WITH_TENSOR_SUPPORT
       ,
       typename boost::mpl::if_<
           boost::is_base_of<Eigen::TensorBase<EigenType_>, EigenType_>,
-          Eigen::TensorBase<EigenType>, void>::type
+          Eigen::TensorBase<EigenType_>, void>::type
 #else
       ,
       void
 #endif
-      >::type type;
+      >::type _type;
+
+  typedef typename boost::mpl::if_<
+      boost::is_const<typename boost::remove_reference<EigenType>::type>,
+      const _type, _type>::type type;
+};
+
+template <typename EigenType>
+struct get_eigen_ref_plain_type;
+
+template <typename MatType, int Options, typename Stride>
+struct get_eigen_ref_plain_type<Eigen::Ref<MatType, Options, Stride> > {
+  typedef typename Eigen::internal::traits<
+      Eigen::Ref<MatType, Options, Stride> >::PlainObjectType type;
+};
+
+#ifdef EIGENPY_WITH_TENSOR_SUPPORT
+template <typename TensorType>
+struct get_eigen_ref_plain_type<Eigen::TensorRef<TensorType> > {
+  typedef TensorType type;
 };
+#endif
 }  // namespace eigenpy
 
 #include "eigenpy/alignment.hpp"
diff --git a/include/eigenpy/numpy-allocator.hpp b/include/eigenpy/numpy-allocator.hpp
index 0a52255..3382eb3 100644
--- a/include/eigenpy/numpy-allocator.hpp
+++ b/include/eigenpy/numpy-allocator.hpp
@@ -12,8 +12,38 @@
 
 namespace eigenpy {
 
+template <typename EigenType, typename BaseType>
+struct numpy_allocator_impl;
+
+template <typename EigenType>
+struct numpy_allocator_impl_matrix;
+
+template <typename MatType>
+struct numpy_allocator_impl<
+    MatType, Eigen::MatrixBase<typename remove_const_reference<MatType>::type> >
+    : numpy_allocator_impl_matrix<MatType> {};
+
 template <typename MatType>
-struct NumpyAllocator {
+struct numpy_allocator_impl<
+    const MatType,
+    const Eigen::MatrixBase<typename remove_const_reference<MatType>::type> >
+    : numpy_allocator_impl_matrix<const MatType> {};
+
+// template <typename MatType>
+// struct numpy_allocator_impl<MatType &, Eigen::MatrixBase<MatType> > :
+// numpy_allocator_impl_matrix<MatType &>
+//{};
+
+template <typename MatType>
+struct numpy_allocator_impl<const MatType &, const Eigen::MatrixBase<MatType> >
+    : numpy_allocator_impl_matrix<const MatType &> {};
+
+template <typename EigenType,
+          typename BaseType = typename get_eigen_base_type<EigenType>::type>
+struct NumpyAllocator : numpy_allocator_impl<EigenType, BaseType> {};
+
+template <typename MatType>
+struct numpy_allocator_impl_matrix {
   template <typename SimilarMatrixType>
   static PyArrayObject *allocate(
       const Eigen::MatrixBase<SimilarMatrixType> &mat, npy_intp nd,
@@ -29,27 +59,42 @@ struct NumpyAllocator {
 
     return pyArray;
   }
+};
 
 #ifdef EIGENPY_WITH_TENSOR_SUPPORT
-  template <typename Scalar, int Rank, int Options, typename IndexType>
-  static PyArrayObject *allocate(
-      const Eigen::Tensor<Scalar, Rank, Options, IndexType> &tensor,
-      npy_intp nd, npy_intp *shape) {
-    typedef Eigen::Tensor<Scalar, Rank, Options, IndexType> Tensor;
-    const int code = Register::getTypeCode<Scalar>();
+
+template <typename TensorType>
+struct numpy_allocator_impl_tensor;
+
+template <typename TensorType>
+struct numpy_allocator_impl<TensorType, Eigen::TensorBase<TensorType> >
+    : numpy_allocator_impl_tensor<TensorType> {};
+
+template <typename TensorType>
+struct numpy_allocator_impl<const TensorType,
+                            const Eigen::TensorBase<TensorType> >
+    : numpy_allocator_impl_tensor<const TensorType> {};
+
+template <typename TensorType>
+struct numpy_allocator_impl_tensor {
+  template <typename TensorDerived>
+  static PyArrayObject *allocate(const Eigen::TensorBase<TensorDerived> &tensor,
+                                 npy_intp nd, npy_intp *shape) {
+    const int code = Register::getTypeCode<typename TensorDerived::Scalar>();
     PyArrayObject *pyArray = (PyArrayObject *)call_PyArray_SimpleNew(
         static_cast<int>(nd), shape, code);
 
     // Copy data
-    EigenAllocator<Tensor>::copy(tensor, pyArray);
+    EigenAllocator<TensorDerived>::copy(
+        static_cast<const TensorDerived &>(tensor), pyArray);
 
     return pyArray;
   }
-#endif
 };
+#endif
 
 template <typename MatType>
-struct NumpyAllocator<MatType &> {
+struct numpy_allocator_impl_matrix<MatType &> {
   template <typename SimilarMatrixType>
   static PyArrayObject *allocate(Eigen::PlainObjectBase<SimilarMatrixType> &mat,
                                  npy_intp nd, npy_intp *shape) {
@@ -75,7 +120,7 @@ struct NumpyAllocator<MatType &> {
 #if EIGEN_VERSION_AT_LEAST(3, 2, 0)
 
 template <typename MatType, int Options, typename Stride>
-struct NumpyAllocator<Eigen::Ref<MatType, Options, Stride> > {
+struct numpy_allocator_impl_matrix<Eigen::Ref<MatType, Options, Stride> > {
   typedef Eigen::Ref<MatType, Options, Stride> RefType;
 
   static PyArrayObject *allocate(RefType &mat, npy_intp nd, npy_intp *shape) {
@@ -110,7 +155,7 @@ struct NumpyAllocator<Eigen::Ref<MatType, Options, Stride> > {
 #endif
 
 template <typename MatType>
-struct NumpyAllocator<const MatType &> {
+struct numpy_allocator_impl_matrix<const MatType &> {
   template <typename SimilarMatrixType>
   static PyArrayObject *allocate(
       const Eigen::PlainObjectBase<SimilarMatrixType> &mat, npy_intp nd,
@@ -139,7 +184,8 @@ struct NumpyAllocator<const MatType &> {
 #if EIGEN_VERSION_AT_LEAST(3, 2, 0)
 
 template <typename MatType, int Options, typename Stride>
-struct NumpyAllocator<const Eigen::Ref<const MatType, Options, Stride> > {
+struct numpy_allocator_impl_matrix<
+    const Eigen::Ref<const MatType, Options, Stride> > {
   typedef const Eigen::Ref<const MatType, Options, Stride> RefType;
 
   static PyArrayObject *allocate(RefType &mat, npy_intp nd, npy_intp *shape) {
@@ -173,6 +219,70 @@ struct NumpyAllocator<const Eigen::Ref<const MatType, Options, Stride> > {
   }
 };
 
+#endif
+
+#ifdef EIGENPY_WITH_TENSOR_SUPPORT
+template <typename TensorType>
+struct numpy_allocator_impl_tensor<Eigen::TensorRef<TensorType> > {
+  typedef Eigen::TensorRef<TensorType> RefType;
+
+  static PyArrayObject *allocate(RefType &tensor, npy_intp nd,
+                                 npy_intp *shape) {
+    typedef typename RefType::Scalar Scalar;
+    static const bool IsRowMajor = TensorType::Options & Eigen::RowMajorBit;
+    enum {
+      NPY_ARRAY_MEMORY_CONTIGUOUS =
+          IsRowMajor ? NPY_ARRAY_CARRAY : NPY_ARRAY_FARRAY
+    };
+
+    if (NumpyType::sharedMemory()) {
+      const int Scalar_type_code = Register::getTypeCode<Scalar>();
+      //      static const Index NumIndices = TensorType::NumIndices;
+
+      //      const int elsize =
+      //      call_PyArray_DescrFromType(Scalar_type_code)->elsize; npy_intp
+      //      strides[NumIndices];
+
+      PyArrayObject *pyArray = (PyArrayObject *)call_PyArray_New(
+          getPyArrayType(), static_cast<int>(nd), shape, Scalar_type_code, NULL,
+          const_cast<Scalar *>(tensor.data()),
+          NPY_ARRAY_MEMORY_CONTIGUOUS | NPY_ARRAY_ALIGNED);
+
+      return pyArray;
+    } else {
+      return NumpyAllocator<TensorType>::allocate(tensor, nd, shape);
+    }
+  }
+};
+
+template <typename TensorType>
+struct numpy_allocator_impl_tensor<const Eigen::TensorRef<const TensorType> > {
+  typedef const Eigen::TensorRef<const TensorType> RefType;
+
+  static PyArrayObject *allocate(RefType &tensor, npy_intp nd,
+                                 npy_intp *shape) {
+    typedef typename RefType::Scalar Scalar;
+    static const bool IsRowMajor = TensorType::Options & Eigen::RowMajorBit;
+    enum {
+      NPY_ARRAY_MEMORY_CONTIGUOUS_RO =
+          IsRowMajor ? NPY_ARRAY_CARRAY_RO : NPY_ARRAY_FARRAY_RO
+    };
+
+    if (NumpyType::sharedMemory()) {
+      const int Scalar_type_code = Register::getTypeCode<Scalar>();
+
+      PyArrayObject *pyArray = (PyArrayObject *)call_PyArray_New(
+          getPyArrayType(), static_cast<int>(nd), shape, Scalar_type_code, NULL,
+          const_cast<Scalar *>(tensor.data()),
+          NPY_ARRAY_MEMORY_CONTIGUOUS_RO | NPY_ARRAY_ALIGNED);
+
+      return pyArray;
+    } else {
+      return NumpyAllocator<TensorType>::allocate(tensor, nd, shape);
+    }
+  }
+};
+
 #endif
 }  // namespace eigenpy
 
diff --git a/include/eigenpy/numpy-map.hpp b/include/eigenpy/numpy-map.hpp
index b881ee9..f085a03 100644
--- a/include/eigenpy/numpy-map.hpp
+++ b/include/eigenpy/numpy-map.hpp
@@ -24,15 +24,15 @@ struct numpy_map_impl;
 template <typename MatType, typename InputScalar, int AlignmentValue,
           typename Stride>
 struct numpy_map_impl<MatType, InputScalar, AlignmentValue, Stride,
-                      Eigen::MatrixBase<MatType> >
+                      Eigen::MatrixBase<MatType>>
     : numpy_map_impl_matrix<MatType, InputScalar, AlignmentValue, Stride> {};
 
-#ifdef EIGENPY_WITH_TENSOR_SUPPORT
-template <typename TensorType, typename InputScalar, int AlignmentValue,
+template <typename MatType, typename InputScalar, int AlignmentValue,
           typename Stride>
-struct numpy_map_impl_tensor;
-
-#endif
+struct numpy_map_impl<const MatType, InputScalar, AlignmentValue, Stride,
+                      const Eigen::MatrixBase<MatType>>
+    : numpy_map_impl_matrix<const MatType, InputScalar, AlignmentValue,
+                            Stride> {};
 
 template <typename MatType, typename InputScalar, int AlignmentValue,
           typename Stride>
@@ -175,20 +175,39 @@ struct numpy_map_impl_matrix<MatType, InputScalar, AlignmentValue, Stride,
 };
 
 #ifdef EIGENPY_WITH_TENSOR_SUPPORT
+
+template <typename TensorType, typename InputScalar, int AlignmentValue,
+          typename Stride>
+struct numpy_map_impl_tensor;
+
 template <typename TensorType, typename InputScalar, int AlignmentValue,
           typename Stride>
 struct numpy_map_impl<TensorType, InputScalar, AlignmentValue, Stride,
-                      Eigen::TensorBase<TensorType> > {
+                      Eigen::TensorBase<TensorType>>
+    : numpy_map_impl_tensor<TensorType, InputScalar, AlignmentValue, Stride> {};
+
+template <typename TensorType, typename InputScalar, int AlignmentValue,
+          typename Stride>
+struct numpy_map_impl<const TensorType, InputScalar, AlignmentValue, Stride,
+                      const Eigen::TensorBase<TensorType>>
+    : numpy_map_impl_tensor<const TensorType, InputScalar, AlignmentValue,
+                            Stride> {};
+
+template <typename TensorType, typename InputScalar, int AlignmentValue,
+          typename Stride>
+struct numpy_map_impl_tensor {
   typedef TensorType Tensor;
   typedef typename Eigen::internal::traits<TensorType>::Index Index;
-  static const Index NumIndices = TensorType::NumIndices;
+  static const int Options = Eigen::internal::traits<TensorType>::Options;
+  static const int NumIndices = TensorType::NumIndices;
 
-  typedef Eigen::Tensor<InputScalar, NumIndices, Tensor::Options, Index>
+  typedef Eigen::Tensor<InputScalar, NumIndices, Options, Index>
       EquivalentInputTensorType;
   typedef typename EquivalentInputTensorType::Dimensions Dimensions;
-  typedef Eigen::TensorMap<EquivalentInputTensorType, Tensor::Options> EigenMap;
+  typedef Eigen::TensorMap<EquivalentInputTensorType, Options> EigenMap;
 
   static EigenMap map(PyArrayObject* pyArray, bool swap_dimensions = false) {
+    EIGENPY_UNUSED_VARIABLE(swap_dimensions);
     assert(PyArray_NDIM(pyArray) == NumIndices || NumIndices == Eigen::Dynamic);
 
     Eigen::DSizes<Index, NumIndices> dimensions(PyArray_NDIM(pyArray));
diff --git a/include/eigenpy/stride.hpp b/include/eigenpy/stride.hpp
index 0c10a24..4168098 100644
--- a/include/eigenpy/stride.hpp
+++ b/include/eigenpy/stride.hpp
@@ -33,12 +33,25 @@ struct stride_type<MatrixType, InnerStride, OuterStride,
           type;
 };
 
+template <typename MatrixType, int InnerStride, int OuterStride>
+struct stride_type<const MatrixType, InnerStride, OuterStride,
+                   const Eigen::MatrixBase<MatrixType> > {
+  typedef typename stride_type_matrix<const MatrixType, InnerStride,
+                                      OuterStride>::type type;
+};
+
 #ifdef EIGENPY_WITH_TENSOR_SUPPORT
 template <typename TensorType, int InnerStride, int OuterStride>
 struct stride_type<TensorType, InnerStride, OuterStride,
                    Eigen::TensorBase<TensorType> > {
   typedef Eigen::Stride<OuterStride, InnerStride> type;
 };
+
+template <typename TensorType, int InnerStride, int OuterStride>
+struct stride_type<const TensorType, InnerStride, OuterStride,
+                   const Eigen::TensorBase<TensorType> > {
+  typedef Eigen::Stride<OuterStride, InnerStride> type;
+};
 #endif
 
 template <typename EigenType, int InnerStride = Eigen::Dynamic,
diff --git a/include/eigenpy/tensor/eigen-from-python.hpp b/include/eigenpy/tensor/eigen-from-python.hpp
new file mode 100644
index 0000000..b9bbdb6
--- /dev/null
+++ b/include/eigenpy/tensor/eigen-from-python.hpp
@@ -0,0 +1,217 @@
+//
+// Copyright (c) 2023 INRIA
+//
+
+#ifndef __eigenpy_tensor_eigen_from_python_hpp__
+#define __eigenpy_tensor_eigen_from_python_hpp__
+
+#include "eigenpy/fwd.hpp"
+#include "eigenpy/eigen-allocator.hpp"
+#include "eigenpy/numpy-type.hpp"
+#include "eigenpy/scalar-conversion.hpp"
+
+namespace eigenpy {
+
+template <typename TensorType>
+struct expected_pytype_for_arg<TensorType, Eigen::TensorBase<TensorType> > {
+  static PyTypeObject const *get_pytype() {
+    PyTypeObject const *py_type = eigenpy::getPyArrayType();
+    return py_type;
+  }
+};
+
+}  // namespace eigenpy
+
+namespace boost {
+namespace python {
+namespace converter {
+
+template <typename Scalar, int Rank, int Options, typename IndexType>
+struct expected_pytype_for_arg<Eigen::Tensor<Scalar, Rank, Options, IndexType> >
+    : eigenpy::expected_pytype_for_arg<
+          Eigen::Tensor<Scalar, Rank, Options, IndexType> > {};
+
+template <typename Derived>
+struct rvalue_from_python_data<Eigen::TensorBase<Derived> const &>
+    : ::eigenpy::rvalue_from_python_data<Derived const &> {
+  EIGENPY_RVALUE_FROM_PYTHON_DATA_INIT(Derived const &)
+};
+
+}  // namespace converter
+}  // namespace python
+}  // namespace boost
+
+namespace boost {
+namespace python {
+namespace detail {
+template <typename TensorType>
+struct referent_storage<Eigen::TensorRef<TensorType> &> {
+  typedef Eigen::TensorRef<TensorType> RefType;
+  typedef ::eigenpy::details::referent_storage_eigen_ref<RefType> StorageType;
+  typedef typename ::eigenpy::aligned_storage<
+      referent_size<StorageType &>::value>::type type;
+};
+
+template <typename TensorType>
+struct referent_storage<const Eigen::TensorRef<const TensorType> &> {
+  typedef Eigen::TensorRef<const TensorType> RefType;
+  typedef ::eigenpy::details::referent_storage_eigen_ref<RefType> StorageType;
+  typedef typename ::eigenpy::aligned_storage<
+      referent_size<StorageType &>::value>::type type;
+};
+}  // namespace detail
+}  // namespace python
+}  // namespace boost
+
+namespace eigenpy {
+
+template <typename TensorType>
+struct eigen_from_py_impl<TensorType, Eigen::TensorBase<TensorType> > {
+  typedef typename TensorType::Scalar Scalar;
+
+  /// \brief Determine if pyObj can be converted into a MatType object
+  static void *convertible(PyObject *pyObj);
+
+  /// \brief Allocate memory and copy pyObj in the new storage
+  static void construct(PyObject *pyObj,
+                        bp::converter::rvalue_from_python_stage1_data *memory);
+
+  static void registration();
+};
+
+template <typename TensorType>
+void *
+eigen_from_py_impl<TensorType, Eigen::TensorBase<TensorType> >::convertible(
+    PyObject *pyObj) {
+  if (!call_PyArray_Check(reinterpret_cast<PyObject *>(pyObj))) return 0;
+
+  typedef typename Eigen::internal::traits<TensorType>::Index Index;
+  static const Index NumIndices = TensorType::NumIndices;
+
+  PyArrayObject *pyArray = reinterpret_cast<PyArrayObject *>(pyObj);
+
+  if (!np_type_is_convertible_into_scalar<Scalar>(
+          EIGENPY_GET_PY_ARRAY_TYPE(pyArray)))
+    return 0;
+
+  if (!(PyArray_NDIM(pyArray) == NumIndices || NumIndices == Eigen::Dynamic))
+    return 0;
+
+#ifdef NPY_1_8_API_VERSION
+  if (!(PyArray_FLAGS(pyArray)))
+#else
+  if (!(PyArray_FLAGS(pyArray) & NPY_ALIGNED))
+#endif
+  {
+    return 0;
+  }
+
+  return pyArray;
+}
+
+template <typename TensorType>
+void eigen_from_py_impl<TensorType, Eigen::TensorBase<TensorType> >::construct(
+    PyObject *pyObj, bp::converter::rvalue_from_python_stage1_data *memory) {
+  eigen_from_py_construct<TensorType>(pyObj, memory);
+}
+
+template <typename TensorType>
+void eigen_from_py_impl<TensorType,
+                        Eigen::TensorBase<TensorType> >::registration() {
+  bp::converter::registry::push_back(
+      reinterpret_cast<void *(*)(_object *)>(&eigen_from_py_impl::convertible),
+      &eigen_from_py_impl::construct, bp::type_id<TensorType>()
+#ifndef BOOST_PYTHON_NO_PY_SIGNATURES
+                                          ,
+      &eigenpy::expected_pytype_for_arg<TensorType>::get_pytype
+#endif
+  );
+}
+
+template <typename TensorType>
+struct eigen_from_py_converter_impl<TensorType,
+                                    Eigen::TensorBase<TensorType> > {
+  static void registration() {
+    EigenFromPy<TensorType>::registration();
+
+    // Add conversion to Eigen::TensorBase<TensorType>
+    typedef Eigen::TensorBase<TensorType> TensorBase;
+    EigenFromPy<TensorBase>::registration();
+
+    // Add conversion to Eigen::TensorRef<TensorType>
+    typedef Eigen::TensorRef<TensorType> RefType;
+    EigenFromPy<RefType>::registration();
+
+    // Add conversion to Eigen::TensorRef<const TensorType>
+    typedef const Eigen::TensorRef<const TensorType> ConstRefType;
+    EigenFromPy<ConstRefType>::registration();
+  }
+};
+
+template <typename TensorType>
+struct EigenFromPy<Eigen::TensorBase<TensorType> > : EigenFromPy<TensorType> {
+  typedef EigenFromPy<TensorType> EigenFromPyDerived;
+  typedef Eigen::TensorBase<TensorType> Base;
+
+  static void registration() {
+    bp::converter::registry::push_back(
+        reinterpret_cast<void *(*)(_object *)>(&EigenFromPy::convertible),
+        &EigenFromPy::construct, bp::type_id<Base>()
+#ifndef BOOST_PYTHON_NO_PY_SIGNATURES
+                                     ,
+        &eigenpy::expected_pytype_for_arg<TensorType>::get_pytype
+#endif
+    );
+  }
+};
+
+template <typename TensorType>
+struct EigenFromPy<Eigen::TensorRef<TensorType> > {
+  typedef Eigen::TensorRef<TensorType> RefType;
+  typedef typename TensorType::Scalar Scalar;
+
+  /// \brief Determine if pyObj can be converted into a MatType object
+  static void *convertible(PyObject *pyObj) {
+    if (!call_PyArray_Check(pyObj)) return 0;
+    PyArrayObject *pyArray = reinterpret_cast<PyArrayObject *>(pyObj);
+    if (!PyArray_ISWRITEABLE(pyArray)) return 0;
+    return EigenFromPy<TensorType>::convertible(pyObj);
+  }
+
+  static void registration() {
+    bp::converter::registry::push_back(
+        reinterpret_cast<void *(*)(_object *)>(&EigenFromPy::convertible),
+        &eigen_from_py_construct<RefType>, bp::type_id<RefType>()
+#ifndef BOOST_PYTHON_NO_PY_SIGNATURES
+                                               ,
+        &eigenpy::expected_pytype_for_arg<TensorType>::get_pytype
+#endif
+    );
+  }
+};
+
+template <typename TensorType>
+struct EigenFromPy<const Eigen::TensorRef<const TensorType> > {
+  typedef const Eigen::TensorRef<const TensorType> ConstRefType;
+  typedef typename TensorType::Scalar Scalar;
+
+  /// \brief Determine if pyObj can be converted into a MatType object
+  static void *convertible(PyObject *pyObj) {
+    return EigenFromPy<TensorType>::convertible(pyObj);
+  }
+
+  static void registration() {
+    bp::converter::registry::push_back(
+        reinterpret_cast<void *(*)(_object *)>(&EigenFromPy::convertible),
+        &eigen_from_py_construct<ConstRefType>, bp::type_id<ConstRefType>()
+#ifndef BOOST_PYTHON_NO_PY_SIGNATURES
+                                                    ,
+        &eigenpy::expected_pytype_for_arg<TensorType>::get_pytype
+#endif
+    );
+  }
+};
+
+}  // namespace eigenpy
+
+#endif  // __eigenpy_tensor_eigen_from_python_hpp__
-- 
GitLab