diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 78128f3922c8e34f07266e859af69304a8ed00d9..60f070e2f9ff5bd6f315662d0e98af90fd5afe82 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -47,3 +47,4 @@ jobs: -DPYTHON_EXECUTABLE=%CONDA_PREFIX%\python.exe ^ .. cmake --build . --config Release --target install + ctest --output-on-failure diff --git a/CMakeLists.txt b/CMakeLists.txt index 6925e0a8c9aacee8139da345c43023a9f09847b9..77d90c82455d05119b97d69ca758f508995d8d6c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -150,6 +150,7 @@ SET(${PROJECT_NAME}_SOURCES src/exception.cpp src/eigenpy.cpp src/numpy.cpp + src/numpy-type.cpp src/matrix-float.cpp src/matrix-complex-float.cpp src/matrix-complex-double.cpp diff --git a/cmake b/cmake index caad0fd63c5824c5cc352cc856b666d8842df6d1..9d6231d5743f6529bdd053b929a252ad95449162 160000 --- a/cmake +++ b/cmake @@ -1 +1 @@ -Subproject commit caad0fd63c5824c5cc352cc856b666d8842df6d1 +Subproject commit 9d6231d5743f6529bdd053b929a252ad95449162 diff --git a/include/eigenpy/eigen-from-python.hpp b/include/eigenpy/eigen-from-python.hpp index f74bdab603e09a929a195a70f129609a7efa0745..83beda216ff335ae42e7ab291d725d8dbf578c9c 100644 --- a/include/eigenpy/eigen-from-python.hpp +++ b/include/eigenpy/eigen-from-python.hpp @@ -285,7 +285,7 @@ namespace eigenpy template<typename MatType> void* EigenFromPy<MatType>::convertible(PyArrayObject* pyArray) { - if(!PyArray_Check(pyArray)) + if(!call_PyArray_Check(reinterpret_cast<PyObject*>(pyArray))) return 0; if(!np_type_is_convertible_into_scalar<Scalar>(EIGENPY_GET_PY_ARRAY_TYPE(pyArray))) @@ -476,7 +476,7 @@ namespace eigenpy /// \brief Determine if pyObj can be converted into a MatType object static void* convertible(PyArrayObject * pyArray) { - if(!PyArray_Check(pyArray)) + if(!call_PyArray_Check(reinterpret_cast<PyObject*>(pyArray))) return 0; if(!PyArray_ISWRITEABLE(pyArray)) return 0; diff --git a/include/eigenpy/numpy-type.hpp b/include/eigenpy/numpy-type.hpp index 1da922b2b378b30172f71bac452abb78ef15847c..b1705794e85afffa46edeee76f2f01b9cdd6d6d6 100644 --- a/include/eigenpy/numpy-type.hpp +++ b/include/eigenpy/numpy-type.hpp @@ -8,7 +8,6 @@ #include "eigenpy/fwd.hpp" #include "eigenpy/scalar-conversion.hpp" -#include <patchlevel.h> // For PY_MAJOR_VERSION #include <stdexcept> #include <typeinfo> #include <sstream> @@ -73,121 +72,42 @@ namespace eigenpy ARRAY_TYPE }; - struct NumpyType + struct EIGENPY_DLLEXPORT NumpyType { - static NumpyType & getInstance() - { - static NumpyType instance; - return instance; - } + static NumpyType & getInstance(); operator bp::object () { return getInstance().CurrentNumpyType; } - static bp::object make(PyArrayObject* pyArray, bool copy = false) - { return make((PyObject*)pyArray,copy); } + static bp::object make(PyArrayObject* pyArray, bool copy = false); - static bp::object make(PyObject* pyObj, bool copy = false) - { - bp::object m; - if(isMatrix()) - m = getInstance().NumpyMatrixObject(bp::object(bp::handle<>(pyObj)), bp::object(), copy); -// m = NumpyAsMatrixObject(bp::object(bp::handle<>(pyObj))); - else if(isArray()) - m = bp::object(bp::handle<>(pyObj)); // nothing to do here - - Py_INCREF(m.ptr()); - return m; - } + static bp::object make(PyObject* pyObj, bool copy = false); - static void setNumpyType(bp::object & obj) - { - PyTypeObject * obj_type = PyType_Check(obj.ptr()) ? reinterpret_cast<PyTypeObject*>(obj.ptr()) : obj.ptr()->ob_type; - if(PyType_IsSubtype(obj_type,getInstance().NumpyMatrixType)) - switchToNumpyMatrix(); - else if(PyType_IsSubtype(obj_type,getInstance().NumpyArrayType)) - switchToNumpyArray(); - } + static void setNumpyType(bp::object & obj); - static void sharedMemory(const bool value) - { - getInstance().shared_memory = value; - } + static void sharedMemory(const bool value); - static bool sharedMemory() - { - return getInstance().shared_memory; - } + static bool sharedMemory(); - static void switchToNumpyArray() - { - getInstance().CurrentNumpyType = getInstance().NumpyArrayObject; - getInstance().getType() = ARRAY_TYPE; - } + static void switchToNumpyArray(); - static void switchToNumpyMatrix() - { - getInstance().CurrentNumpyType = getInstance().NumpyMatrixObject; - getInstance().getType() = MATRIX_TYPE; - } + static void switchToNumpyMatrix(); - static NP_TYPE & getType() - { - return getInstance().np_type; - } + static NP_TYPE & getType(); - static bp::object getNumpyType() - { - return getInstance().CurrentNumpyType; - } + static bp::object getNumpyType(); - static const PyTypeObject * getNumpyMatrixType() - { - return getInstance().NumpyMatrixType; - } + static const PyTypeObject * getNumpyMatrixType(); - static const PyTypeObject * getNumpyArrayType() - { - return getInstance().NumpyArrayType; - } + static const PyTypeObject * getNumpyArrayType(); - static bool isMatrix() - { - return PyType_IsSubtype(reinterpret_cast<PyTypeObject*>(getInstance().CurrentNumpyType.ptr()), - getInstance().NumpyMatrixType); - } + static bool isMatrix(); - static bool isArray() - { - if(getInstance().isMatrix()) return false; - return PyType_IsSubtype(reinterpret_cast<PyTypeObject*>(getInstance().CurrentNumpyType.ptr()), - getInstance().NumpyArrayType); - } + static bool isArray(); protected: - NumpyType() - { - pyModule = bp::import("numpy"); - -#if PY_MAJOR_VERSION >= 3 - // TODO I don't know why this Py_INCREF is necessary. - // Without it, the destructor of NumpyType SEGV sometimes. - Py_INCREF(pyModule.ptr()); -#endif - - NumpyMatrixObject = pyModule.attr("matrix"); - NumpyMatrixType = reinterpret_cast<PyTypeObject*>(NumpyMatrixObject.ptr()); - NumpyArrayObject = pyModule.attr("ndarray"); - NumpyArrayType = reinterpret_cast<PyTypeObject*>(NumpyArrayObject.ptr()); - //NumpyAsMatrixObject = pyModule.attr("asmatrix"); - //NumpyAsMatrixType = reinterpret_cast<PyTypeObject*>(NumpyAsMatrixObject.ptr()); - - CurrentNumpyType = NumpyArrayObject; // default conversion - np_type = ARRAY_TYPE; - - shared_memory = true; - } + NumpyType(); bp::object CurrentNumpyType; bp::object pyModule; diff --git a/include/eigenpy/numpy.hpp b/include/eigenpy/numpy.hpp index 184006f93292c14aebb6d5333ac75597bda1cde3..b0de2648bdfc2953afe3e14be3eaeb68e36148b4 100644 --- a/include/eigenpy/numpy.hpp +++ b/include/eigenpy/numpy.hpp @@ -37,6 +37,8 @@ namespace eigenpy #if defined _WIN32 || defined __CYGWIN__ namespace eigenpy { + EIGENPY_DLLEXPORT bool call_PyArray_Check(PyObject *); + EIGENPY_DLLEXPORT PyObject* call_PyArray_SimpleNew(int nd, npy_intp * shape, int np_type); EIGENPY_DLLEXPORT PyObject* call_PyArray_New(PyTypeObject * py_type_ptr, int nd, npy_intp * shape, int np_type, void * data_ptr, int options); @@ -52,6 +54,7 @@ namespace eigenpy EIGENPY_DLLEXPORT int call_PyArray_RegisterDataType(PyArray_Descr * dtype); } #else + #define call_PyArray_Check(py_obj) PyArray_Check(py_obj) #define call_PyArray_SimpleNew PyArray_SimpleNew #define call_PyArray_New(py_type_ptr,nd,shape,np_type,data_ptr,options) \ PyArray_New(py_type_ptr,nd,shape,np_type,NULL,data_ptr,0,options,NULL) diff --git a/src/numpy-type.cpp b/src/numpy-type.cpp new file mode 100644 index 0000000000000000000000000000000000000000..cd235f01217b628e4bc337bafc17e33fe9b35d42 --- /dev/null +++ b/src/numpy-type.cpp @@ -0,0 +1,121 @@ +/* + * Copyright 2018-2020 INRIA +*/ + +#include "eigenpy/numpy-type.hpp" + +#include <patchlevel.h> // For PY_MAJOR_VERSION + +namespace eigenpy +{ + namespace bp = boost::python; + + NumpyType & NumpyType::getInstance() + { + static NumpyType instance; + return instance; + } + + bp::object NumpyType::make(PyArrayObject* pyArray, bool copy) + { return make((PyObject*)pyArray,copy); } + + bp::object NumpyType::make(PyObject* pyObj, bool copy) + { + bp::object m; + if(isMatrix()) + m = getInstance().NumpyMatrixObject(bp::object(bp::handle<>(pyObj)), bp::object(), copy); +// m = NumpyAsMatrixObject(bp::object(bp::handle<>(pyObj))); + else if(isArray()) + m = bp::object(bp::handle<>(pyObj)); // nothing to do here + + Py_INCREF(m.ptr()); + return m; + } + + void NumpyType::setNumpyType(bp::object & obj) + { + PyTypeObject * obj_type = PyType_Check(obj.ptr()) ? reinterpret_cast<PyTypeObject*>(obj.ptr()) : obj.ptr()->ob_type; + if(PyType_IsSubtype(obj_type,getInstance().NumpyMatrixType)) + switchToNumpyMatrix(); + else if(PyType_IsSubtype(obj_type,getInstance().NumpyArrayType)) + switchToNumpyArray(); + } + + void NumpyType::sharedMemory(const bool value) + { + getInstance().shared_memory = value; + } + + bool NumpyType::sharedMemory() + { + return getInstance().shared_memory; + } + + void NumpyType::switchToNumpyArray() + { + getInstance().CurrentNumpyType = getInstance().NumpyArrayObject; + getInstance().getType() = ARRAY_TYPE; + } + + void NumpyType::switchToNumpyMatrix() + { + getInstance().CurrentNumpyType = getInstance().NumpyMatrixObject; + getInstance().getType() = MATRIX_TYPE; + } + + NP_TYPE & NumpyType::getType() + { + return getInstance().np_type; + } + + bp::object NumpyType::getNumpyType() + { + return getInstance().CurrentNumpyType; + } + + const PyTypeObject * NumpyType::getNumpyMatrixType() + { + return getInstance().NumpyMatrixType; + } + + const PyTypeObject * NumpyType::getNumpyArrayType() + { + return getInstance().NumpyArrayType; + } + + bool NumpyType::isMatrix() + { + return PyType_IsSubtype(reinterpret_cast<PyTypeObject*>(getInstance().CurrentNumpyType.ptr()), + getInstance().NumpyMatrixType); + } + + bool NumpyType::isArray() + { + if(getInstance().isMatrix()) return false; + return PyType_IsSubtype(reinterpret_cast<PyTypeObject*>(getInstance().CurrentNumpyType.ptr()), + getInstance().NumpyArrayType); + } + + NumpyType::NumpyType() + { + pyModule = bp::import("numpy"); + +#if PY_MAJOR_VERSION >= 3 + // TODO I don't know why this Py_INCREF is necessary. + // Without it, the destructor of NumpyType SEGV sometimes. + Py_INCREF(pyModule.ptr()); +#endif + + NumpyMatrixObject = pyModule.attr("matrix"); + NumpyMatrixType = reinterpret_cast<PyTypeObject*>(NumpyMatrixObject.ptr()); + NumpyArrayObject = pyModule.attr("ndarray"); + NumpyArrayType = reinterpret_cast<PyTypeObject*>(NumpyArrayObject.ptr()); + //NumpyAsMatrixObject = pyModule.attr("asmatrix"); + //NumpyAsMatrixType = reinterpret_cast<PyTypeObject*>(NumpyAsMatrixObject.ptr()); + + CurrentNumpyType = NumpyArrayObject; // default conversion + np_type = ARRAY_TYPE; + + shared_memory = true; + } +} diff --git a/src/numpy.cpp b/src/numpy.cpp index 8833a560f3a050fa8b201e9fcdf3e5750bff792e..10c3d7ea0557a69616b4cbf6af0869d80f645605 100644 --- a/src/numpy.cpp +++ b/src/numpy.cpp @@ -22,6 +22,11 @@ namespace eigenpy #if defined _WIN32 || defined __CYGWIN__ + bool call_PyArray_Check(PyObject * py_obj) + { + return PyArray_Check(py_obj); + } + PyObject* call_PyArray_SimpleNew(int nd, npy_intp * shape, int np_type) { return PyArray_SimpleNew(nd,shape,np_type); diff --git a/unittest/matrix.cpp b/unittest/matrix.cpp index 84a116f151a37d52e4ec1b97296ed9141232deea..b7cbc593d5703e85a2d1f70de6b7f8efa8468155 100644 --- a/unittest/matrix.cpp +++ b/unittest/matrix.cpp @@ -81,12 +81,22 @@ MatrixDerived base(const Eigen::MatrixBase<MatrixDerived> & m) return m.derived(); } +template<typename Scalar> +Eigen::Matrix<Scalar,6,6> matrix6(const Scalar & value) +{ + typedef Eigen::Matrix<Scalar,6,6> ReturnType; + return ReturnType::Constant(value); +} + BOOST_PYTHON_MODULE(matrix) { using namespace Eigen; namespace bp = boost::python; eigenpy::enableEigenPy(); + typedef Eigen::Matrix<double,6,6> Matrix6; + eigenpy::enableEigenPySpecific<Matrix6>(); + Eigen::MatrixXd (*naturalsXX)(int,int,bool) = naturals; Eigen::VectorXd (*naturalsX)(int,bool) = naturals; Eigen::Matrix3d (*naturals33)(bool) = naturals; @@ -108,4 +118,6 @@ BOOST_PYTHON_MODULE(matrix) bp::def("base", base<VectorXd>); bp::def("base", base<MatrixXd>); + + bp::def("matrix6", matrix6<double>); } diff --git a/unittest/python/test_matrix.py b/unittest/python/test_matrix.py index 18fce68edb6a3bbb6936bf9922aabca5f77abfc6..5734eb92af3f25b6569cff7cb50a3f37f8e676c9 100644 --- a/unittest/python/test_matrix.py +++ b/unittest/python/test_matrix.py @@ -126,3 +126,7 @@ assert(mat1x1[0,0] == value) vec1x1 = eigenpy.vector1x1(value) assert(vec1x1.size == 1) assert(vec1x1[0] == value) + +# test registration of matrix6 +mat6 = eigenpy.matrix6(0.) +assert(mat6.size == 36)