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