diff --git a/CMakeLists.txt b/CMakeLists.txt index 5568d339b32d22546d718d6661e6faecc0ec09db..818699d1dcb5e91211e2089557977f0505603f9b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -103,6 +103,7 @@ SET(${PROJECT_NAME}_HEADERS include/eigenpy/quaternion.hpp include/eigenpy/stride.hpp include/eigenpy/ref.hpp + include/eigenpy/details/rvalue_from_python_data.hpp ) INCLUDE_DIRECTORIES(${${PROJECT_NAME}_BINARY_DIR}/include) diff --git a/cmake b/cmake index ab4c798680d0a3ba4e84d3b7f25cc42d336eca1b..242dc450ae43ca2182987c9588c21e8aab32d61c 160000 --- a/cmake +++ b/cmake @@ -1 +1 @@ -Subproject commit ab4c798680d0a3ba4e84d3b7f25cc42d336eca1b +Subproject commit 242dc450ae43ca2182987c9588c21e8aab32d61c diff --git a/include/eigenpy/details.hpp b/include/eigenpy/details.hpp index 92df398aa6839859b3797cfb2a66d00198b3b214..397625a20e40480db950a148063fa150c0014222 100644 --- a/include/eigenpy/details.hpp +++ b/include/eigenpy/details.hpp @@ -6,6 +6,7 @@ #ifndef __eigenpy_details_hpp__ #define __eigenpy_details_hpp__ +#include "eigenpy/details/rvalue_from_python_data.hpp" #include "eigenpy/fwd.hpp" #include <patchlevel.h> // For PY_MAJOR_VERSION @@ -18,7 +19,6 @@ #define GET_PY_ARRAY_TYPE(array) PyArray_ObjectType(reinterpret_cast<PyObject *>(array), 0) - namespace eigenpy { template <typename SCALAR> struct NumpyEquivalentType {}; @@ -146,33 +146,75 @@ namespace eigenpy const int rows = (int)PyArray_DIMS(pyArray)[0]; const int cols = (int)PyArray_DIMS(pyArray)[1]; - Type * mat_ptr = new(storage) Type(rows,cols); + Type * mat_ptr = new (storage) Type(rows,cols); + + if(NumpyEquivalentType<Scalar>::type_code == GET_PY_ARRAY_TYPE(pyArray)) + { + *mat_ptr = MapNumpy<MatType,Scalar>::map(pyArray); // avoid useless cast + return; + } + if(GET_PY_ARRAY_TYPE(pyArray) == NPY_INT) + { *mat_ptr = MapNumpy<MatType,int>::map(pyArray).template cast<Scalar>(); + return; + } if(GET_PY_ARRAY_TYPE(pyArray) == NPY_LONG) + { *mat_ptr = MapNumpy<MatType,long>::map(pyArray).template cast<Scalar>(); + return; + } if(GET_PY_ARRAY_TYPE(pyArray) == NPY_FLOAT) + { *mat_ptr = MapNumpy<MatType,float>::map(pyArray).template cast<Scalar>(); + return; + } if(GET_PY_ARRAY_TYPE(pyArray) == NPY_DOUBLE) + { *mat_ptr = MapNumpy<MatType,double>::map(pyArray).template cast<Scalar>(); + return; + } } - static void convert(Type const & mat , PyArrayObject * pyArray) + /// \brief Copy mat into the Python array using Eigen::Map + template<typename MatrixDerived> + static void convert(const Eigen::MatrixBase<MatrixDerived> & mat_, + PyArrayObject * pyArray) { + const MatrixDerived & mat = const_cast<const MatrixDerived &>(mat_.derived()); + + if(NumpyEquivalentType<Scalar>::type_code == GET_PY_ARRAY_TYPE(pyArray)) + { + MapNumpy<MatType,Scalar>::map(pyArray) = mat; // no cast needed + return; + } + if(GET_PY_ARRAY_TYPE(pyArray) == NPY_INT) + { MapNumpy<MatType,int>::map(pyArray) = mat.template cast<int>(); + return; + } if(GET_PY_ARRAY_TYPE(pyArray) == NPY_LONG) + { MapNumpy<MatType,long>::map(pyArray) = mat.template cast<long>(); + return; + } if(GET_PY_ARRAY_TYPE(pyArray) == NPY_FLOAT) + { MapNumpy<MatType,float>::map(pyArray) = mat.template cast<float>(); + return; + } if(GET_PY_ARRAY_TYPE(pyArray) == NPY_DOUBLE) + { MapNumpy<MatType,double>::map(pyArray) = mat.template cast<double>(); + return; + } } }; @@ -186,51 +228,47 @@ namespace eigenpy static void allocate(PyArrayObject * pyArray, void * storage) { typename MapNumpy<MatType,Scalar>::EigenMap numpyMap = MapNumpy<MatType,Scalar>::map(pyArray); - new(storage) Type(numpyMap); + new (storage) Type(numpyMap); } - static void convert(Type const & mat , PyArrayObject * pyArray) + static void convert(Type const & mat, PyArrayObject * pyArray) { - if(GET_PY_ARRAY_TYPE(pyArray) == NPY_INT) - MapNumpy<MatType,int>::map(pyArray) = mat.template cast<int>(); - - if(GET_PY_ARRAY_TYPE(pyArray) == NPY_LONG) - MapNumpy<MatType,long>::map(pyArray) = mat.template cast<long>(); - - if(GET_PY_ARRAY_TYPE(pyArray) == NPY_FLOAT) - MapNumpy<MatType,float>::map(pyArray) = mat.template cast<float>(); - - if(GET_PY_ARRAY_TYPE(pyArray) == NPY_DOUBLE) - MapNumpy<MatType,double>::map(pyArray) = mat.template cast<double>(); + EigenObjectAllocator<MatType>::convert(mat,pyArray); } }; #endif + /* --- TO PYTHON -------------------------------------------------------------- */ + template<typename MatType> struct EigenToPy { - static PyObject* convert(MatType const& mat) + static PyObject* convert(MatType const & mat) { - typedef typename MatType::Scalar T; + typedef typename MatType::Scalar Scalar; assert( (mat.rows()<INT_MAX) && (mat.cols()<INT_MAX) && "Matrix range larger than int ... should never happen." ); const int R = (int)mat.rows(), C = (int)mat.cols(); PyArrayObject* pyArray; - if(C == 1 && NumpyType::getType() == ARRAY_TYPE) + // Allocate Python memory + if(C == 1 && NumpyType::getType() == ARRAY_TYPE) // Handle array with a single dimension { npy_intp shape[1] = { R }; pyArray = (PyArrayObject*) PyArray_SimpleNew(1, shape, - NumpyEquivalentType<T>::type_code); + NumpyEquivalentType<Scalar>::type_code); } else { npy_intp shape[2] = { R,C }; pyArray = (PyArrayObject*) PyArray_SimpleNew(2, shape, - NumpyEquivalentType<T>::type_code); + NumpyEquivalentType<Scalar>::type_code); } + // Allocate memory EigenObjectAllocator<MatType>::convert(mat,pyArray); + + // Create an instance (either np.array or np.matrix) return NumpyType::getInstance().make(pyArray).ptr(); } }; @@ -240,19 +278,19 @@ namespace eigenpy template<typename MatType> struct EigenFromPy { - // Determine if obj_ptr can be converted in a Eigenvec - static void* convertible(PyArrayObject* obj_ptr) + /// \brief Determine if pyObj can be converted into a MatType object + static void* convertible(PyArrayObject* pyObj) { - if (!PyArray_Check(obj_ptr)) + if (!PyArray_Check(pyObj)) return 0; if(MatType::IsVectorAtCompileTime) { // Special care of scalar matrix of dimension 1x1. - if(PyArray_DIMS(obj_ptr)[0] == 1 && PyArray_DIMS(obj_ptr)[1] == 1) - return obj_ptr; + if(PyArray_DIMS(pyObj)[0] == 1 && PyArray_DIMS(pyObj)[1] == 1) + return pyObj; - if(PyArray_DIMS(obj_ptr)[0] > 1 && PyArray_DIMS(obj_ptr)[1] > 1) + if(PyArray_DIMS(pyObj)[0] > 1 && PyArray_DIMS(pyObj)[1] > 1) { #ifndef NDEBUG std::cerr << "The number of dimension of the object does not correspond to a vector" << std::endl; @@ -260,8 +298,8 @@ namespace eigenpy return 0; } - if(((PyArray_DIMS(obj_ptr)[0] == 1) && (MatType::ColsAtCompileTime == 1)) - || ((PyArray_DIMS(obj_ptr)[1] == 1) && (MatType::RowsAtCompileTime == 1))) + if(((PyArray_DIMS(pyObj)[0] == 1) && (MatType::ColsAtCompileTime == 1)) + || ((PyArray_DIMS(pyObj)[1] == 1) && (MatType::RowsAtCompileTime == 1))) { #ifndef NDEBUG if(MatType::ColsAtCompileTime == 1) @@ -273,9 +311,9 @@ namespace eigenpy } } - if (PyArray_NDIM(obj_ptr) != 2) + if (PyArray_NDIM(pyObj) != 2) { - if ( (PyArray_NDIM(obj_ptr) !=1) || (! MatType::IsVectorAtCompileTime) ) + if ( (PyArray_NDIM(pyObj) !=1) || (! MatType::IsVectorAtCompileTime) ) { #ifndef NDEBUG std::cerr << "The number of dimension of the object is not correct." << std::endl; @@ -284,10 +322,10 @@ namespace eigenpy } } - if (PyArray_NDIM(obj_ptr) == 2) + if (PyArray_NDIM(pyObj) == 2) { - const int R = (int)PyArray_DIMS(obj_ptr)[0]; - const int C = (int)PyArray_DIMS(obj_ptr)[1]; + const int R = (int)PyArray_DIMS(pyObj)[0]; + const int C = (int)PyArray_DIMS(pyObj)[1]; if( (MatType::RowsAtCompileTime!=R) && (MatType::RowsAtCompileTime!=Eigen::Dynamic) ) @@ -298,7 +336,7 @@ namespace eigenpy } // Check if the Scalar type of the obj_ptr is compatible with the Scalar type of MatType - if ((PyArray_ObjectType(reinterpret_cast<PyObject *>(obj_ptr), 0)) == NPY_INT) + if ((PyArray_ObjectType(reinterpret_cast<PyObject *>(pyObj), 0)) == NPY_INT) { if(!FromTypeToType<int,typename MatType::Scalar>::value) { @@ -308,7 +346,7 @@ namespace eigenpy return 0; } } - else if ((PyArray_ObjectType(reinterpret_cast<PyObject *>(obj_ptr), 0)) == NPY_LONG) + else if ((PyArray_ObjectType(reinterpret_cast<PyObject *>(pyObj), 0)) == NPY_LONG) { if(!FromTypeToType<long,typename MatType::Scalar>::value) { @@ -318,7 +356,7 @@ namespace eigenpy return 0; } } - else if ((PyArray_ObjectType(reinterpret_cast<PyObject *>(obj_ptr), 0)) == NPY_FLOAT) + else if ((PyArray_ObjectType(reinterpret_cast<PyObject *>(pyObj), 0)) == NPY_FLOAT) { if(!FromTypeToType<float,typename MatType::Scalar>::value) { @@ -328,7 +366,7 @@ namespace eigenpy return 0; } } - else if ((PyArray_ObjectType(reinterpret_cast<PyObject *>(obj_ptr), 0)) == NPY_DOUBLE) + else if ((PyArray_ObjectType(reinterpret_cast<PyObject *>(pyObj), 0)) == NPY_DOUBLE) { if(!FromTypeToType<double,typename MatType::Scalar>::value) { @@ -338,7 +376,7 @@ namespace eigenpy return 0; } } - else if ((PyArray_ObjectType(reinterpret_cast<PyObject *>(obj_ptr), 0)) + else if ((PyArray_ObjectType(reinterpret_cast<PyObject *>(pyObj), 0)) != NumpyEquivalentType<typename MatType::Scalar>::type_code) { #ifndef NDEBUG @@ -349,7 +387,7 @@ namespace eigenpy } #ifdef NPY_1_8_API_VERSION - if (!(PyArray_FLAGS(obj_ptr))) + if (!(PyArray_FLAGS(pyObj))) #else if (!(PyArray_FLAGS(obj_ptr) & NPY_ALIGNED)) #endif @@ -360,15 +398,13 @@ namespace eigenpy return 0; } - return obj_ptr; + return pyObj; } - // Convert obj_ptr into an Eigen::Vector + /// \brief Allocate memory and copy pyObj in the new storage static void construct(PyObject* pyObj, bp::converter::rvalue_from_python_stage1_data* memory) { - using namespace Eigen; - PyArrayObject * pyArray = reinterpret_cast<PyArrayObject*>(pyObj); assert((PyArray_DIMS(pyArray)[0]<INT_MAX) && (PyArray_DIMS(pyArray)[1]<INT_MAX)); @@ -379,6 +415,42 @@ namespace eigenpy memory->convertible = storage; } + + static void registration() + { + bp::converter::registry::push_back + (reinterpret_cast<void *(*)(_object *)>(&EigenFromPy::convertible), + &EigenFromPy::construct,bp::type_id<MatType>()); + } + }; + + template<typename MatType> + struct EigenFromPy< Eigen::MatrixBase<MatType> > + { + typedef EigenFromPy<MatType> EigenFromPyDerived; + typedef Eigen::MatrixBase<MatType> Base; + + /// \brief Determine if pyObj can be converted into a MatType object + static void* convertible(PyArrayObject* pyObj) + { + std::cout << "call: EigenFromPy< Eigen::MatrixBase<MatType> >::convertible" << std::endl; + return EigenFromPyDerived::convertible(pyObj); + } + + /// \brief Allocate memory and copy pyObj in the new storage + static void construct(PyObject* pyObj, + bp::converter::rvalue_from_python_stage1_data* memory) + { + std::cout << "call: EigenFromPy< Eigen::MatrixBase<MatType> >::construct" << std::endl; + EigenFromPyDerived::construct(pyObj,memory); + } + + static void registration() + { + bp::converter::registry::push_back + (reinterpret_cast<void *(*)(_object *)>(&EigenFromPy::convertible), + &EigenFromPy::construct,bp::type_id<Base>()); + } }; #define numpy_import_array() {if (_import_array() < 0) {PyErr_Print(); PyErr_SetString(PyExc_ImportError, "numpy.core.multiarray failed to import"); } } @@ -394,14 +466,11 @@ namespace eigenpy { static void registration() { - bp::converter::registry::push_back - (reinterpret_cast<void *(*)(_object *)>(&EigenFromPy<MatType>::convertible), - &EigenFromPy<MatType>::construct,bp::type_id<MatType>()); - + EigenFromPy<MatType>::registration(); + // Add also conversion to Eigen::MatrixBase<MatType> - bp::converter::registry::push_back - (reinterpret_cast<void *(*)(_object *)>(&EigenFromPy<MatType>::convertible), - &EigenFromPy<MatType>::construct,bp::type_id< Eigen::MatrixBase<MatType> >()); + typedef Eigen::MatrixBase<MatType> MatTypeBase; + EigenFromPy<MatTypeBase>::registration(); } }; @@ -419,7 +488,6 @@ namespace eigenpy }; #endif - template<typename MatType> void enableEigenPySpecific() { diff --git a/include/eigenpy/details/rvalue_from_python_data.hpp b/include/eigenpy/details/rvalue_from_python_data.hpp new file mode 100644 index 0000000000000000000000000000000000000000..3cd94e979cc59f40f30d6f01131d144f07829136 --- /dev/null +++ b/include/eigenpy/details/rvalue_from_python_data.hpp @@ -0,0 +1,55 @@ +#ifndef __eigenpy_details_rvalue_from_python_data_hpp__ +#define __eigenpy_details_rvalue_from_python_data_hpp__ + +#include <boost/python/converter/rvalue_from_python_data.hpp> +#include <Eigen/Core> + +namespace boost +{ + namespace python + { + namespace converter + { + + /// \brief Template specialization of rvalue_from_python_data + template<typename Derived> + struct rvalue_from_python_data<Eigen::MatrixBase<Derived> const & > + : rvalue_from_python_storage<Eigen::MatrixBase<Derived> const & > + { + typedef Eigen::MatrixBase<Derived> const & 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() + { + if (this->stage1.convertible == this->storage.bytes) + static_cast<Derived *>((void *)this->storage.bytes)->~Derived(); + } + }; + + } + } +} // namespace boost::python::converter + +#endif // ifndef __eigenpy_details_rvalue_from_python_data_hpp__ diff --git a/include/eigenpy/eigenpy.hpp b/include/eigenpy/eigenpy.hpp index 856ea52b4c211e9d8f5ea441525fe6e647582c33..0c5cc8f0cff0323ac67bedec90b7701a3abe1c96 100644 --- a/include/eigenpy/eigenpy.hpp +++ b/include/eigenpy/eigenpy.hpp @@ -9,19 +9,20 @@ #include "eigenpy/fwd.hpp" #include "eigenpy/deprecated.hh" #include "eigenpy/eigenpy_export.h" + #if EIGEN_VERSION_AT_LEAST(3,2,0) #include "eigenpy/ref.hpp" #define ENABLE_SPECIFIC_MATRIX_TYPE(TYPE) \ - enableEigenPySpecific<TYPE>(); \ - enableEigenPySpecific< eigenpy::Ref<TYPE> >(); + ::eigenpy::enableEigenPySpecific<TYPE>(); \ + ::eigenpy::enableEigenPySpecific< eigenpy::Ref<TYPE> >(); -#else +#else // if EIGEN_VERSION_AT_LEAST(3,2,0) #define ENABLE_SPECIFIC_MATRIX_TYPE(TYPE) \ - enableEigenPySpecific<TYPE>(); + ::eigenpy::enableEigenPySpecific<TYPE>(); -#endif +#endif // if EIGEN_VERSION_AT_LEAST(3,2,0) namespace eigenpy { diff --git a/include/eigenpy/map.hpp b/include/eigenpy/map.hpp index 6ee8d742bea9fdde32cd0bd47095e9ee5be50180..77677f59e60ccb84ba59a90d2e11d5d0952f56c6 100644 --- a/include/eigenpy/map.hpp +++ b/include/eigenpy/map.hpp @@ -55,8 +55,6 @@ namespace eigenpy const int stride2 = (int)PyArray_STRIDE(pyArray, 1) / (int)itemsize; Stride stride(stride2,stride1); - - if( (MatType::RowsAtCompileTime!=R) && (MatType::RowsAtCompileTime!=Eigen::Dynamic) ) { throw eigenpy::Exception("The number of rows does not fit with the matrix type."); } diff --git a/include/eigenpy/ref.hpp b/include/eigenpy/ref.hpp index 1f05bd676e5c4d4f5ddd0b67e3465589a31dfe99..4ffff43950e4e69fd0464267de8be8a4835c281c 100644 --- a/include/eigenpy/ref.hpp +++ b/include/eigenpy/ref.hpp @@ -19,7 +19,8 @@ namespace eigenpy { template<typename PlainObjectTypeT> - struct Ref : Eigen::Ref<PlainObjectTypeT,EIGENPY_DEFAULT_ALIGNMENT_VALUE,typename StrideType<PlainObjectTypeT>::type> + struct Ref + : Eigen::Ref<PlainObjectTypeT,EIGENPY_DEFAULT_ALIGNMENT_VALUE,typename StrideType<PlainObjectTypeT>::type> { public: typedef Eigen::Ref<PlainObjectTypeT,EIGENPY_DEFAULT_ALIGNMENT_VALUE,typename eigenpy::template StrideType<PlainObjectTypeT>::type> Base; diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index 187c04c3c33944c3628201434fc17ff94b3d6cb0..5d644dbdd388d86e3fc9b5759a7e352678f6ebfb 100644 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -19,6 +19,8 @@ ENDMACRO(SYMLINK_AND_INSTALL_HEADERS HEADERS SUBPATH) # --- LIBRARY --- # SET(PYWRAP ${PROJECT_NAME}_pywrap) +SET(PYWRAP ${PYWRAP} PARENT_SCOPE) + MAKE_DIRECTORY("${${PROJECT_NAME}_BINARY_DIR}/python/${PROJECT_NAME}") ADD_LIBRARY(${PYWRAP} SHARED main.cpp) diff --git a/unittest/CMakeLists.txt b/unittest/CMakeLists.txt index 0b164f090cfbcfa79993414debb7de0860a01b47..13b61241fa08ac2291052ecb9f6515252d31c44a 100644 --- a/unittest/CMakeLists.txt +++ b/unittest/CMakeLists.txt @@ -41,5 +41,9 @@ ENDIF() ADD_PYTHON_UNIT_TEST("py-matrix" "unittest/python/test_matrix.py" "unittest") ADD_PYTHON_UNIT_TEST("py-geometry" "unittest/python/test_geometry.py" "unittest") + ADD_PYTHON_UNIT_TEST("py-switch" "unittest/python/test_switch.py" "python/eigenpy") +SET_TESTS_PROPERTIES("py-switch" PROPERTIES DEPENDS ${PYWRAP}) + ADD_PYTHON_UNIT_TEST("py-dimensions" "unittest/python/test_dimensions.py" "python/eigenpy") +SET_TESTS_PROPERTIES("py-dimensions" PROPERTIES DEPENDS ${PYWRAP})