diff --git a/c++/cpp2py/converters/std_array.hpp b/c++/cpp2py/converters/std_array.hpp index b939c41..3a509fa 100644 --- a/c++/cpp2py/converters/std_array.hpp +++ b/c++/cpp2py/converters/std_array.hpp @@ -1,54 +1,127 @@ #pragma once #include +#include + +#include "../numpy_proxy.hpp" +#include "../py_converter.hpp" +#include "./vector.hpp" namespace cpp2py { - template struct py_converter> { - // -------------------------------------- + template numpy_proxy make_numpy_proxy_from_heap_array(std::array *arr_heap) { - static PyObject *c2py(std::array const &v) { - PyObject *list = PyList_New(0); - for (auto const &x : v) { - pyref y = py_converter::c2py(x); - if (y.is_null() or (PyList_Append(list, y) == -1)) { - Py_DECREF(list); - return NULL; - } // error - } - return list; + auto delete_pycapsule = [](PyObject *capsule) { + auto *ptr = static_cast *>(PyCapsule_GetPointer(capsule, "guard")); + delete ptr; + }; + PyObject *capsule = PyCapsule_New(arr_heap, "guard", delete_pycapsule); + + return numpy_proxy{1, // rank + npy_type>, + (void *)arr_heap->data(), + std::is_const_v, + std::vector{long(R)}, // extents + std::vector{sizeof(T)}, // strides + capsule}; + }; + + template numpy_proxy make_numpy_proxy_from_array(std::array const &arr) { + + if constexpr (has_npy_type) { + auto *arr_heap = new std::array{arr}; + return make_numpy_proxy_from_heap_array(arr_heap); + } else { + auto *arr_heap = new std::array{}; + std::transform(begin(arr), end(arr), begin(*arr_heap), [](T const &x) { return py_converter>::c2py(x); }); + return make_numpy_proxy_from_heap_array(arr_heap); } + } + + // Make a new array from numpy view + template std::array make_array_from_numpy_proxy(numpy_proxy const &p) { + EXPECTS(p.extents.size() == 1); + EXPECTS(p.extents[0] == R); + + std::array arr; + + if (p.element_type == npy_type) { + auto *data = static_cast(p.data); + std::transform(data, data + R, begin(arr), [](PyObject *o) { return py_converter>::py2c(o); }); + } else { + EXPECTS(p.strides == std::vector{sizeof(T)}); + T *data = static_cast(p.data); + std::copy(data, data + R, begin(arr)); + } + + return arr; + } + + // -------------------------------------- + + template struct py_converter> { + + static PyObject *c2py(std::array const &a) { return make_numpy_proxy_from_array(a).to_python(); } // -------------------------------------- static bool is_convertible(PyObject *ob, bool raise_exception) { - if (!PySequence_Check(ob)) goto _false; - { - pyref seq = PySequence_Fast(ob, "expected a sequence"); - int len = PySequence_Size(ob); - if (len != R) { - if (raise_exception) { - auto s = std::string{"Convertion to std::array failed : the length of the sequence ( = "} + std::to_string(len) - + " does not match R = " + std::to_string(R); - PyErr_SetString(PyExc_TypeError, s.c_str()); - } + _import_array(); + + // Special case: 1-d ndarray of builtin type + if (PyArray_Check(ob)) { + PyArrayObject *arr = (PyArrayObject *)(ob); +#ifdef PYTHON_NUMPY_VERSION_LT_17 + int rank = arr->nd; +#else + int rank = PyArray_NDIM(arr); +#endif + if (PyArray_TYPE(arr) == npy_type and rank == 1) return true; + } + + if (!PySequence_Check(ob)) { + if (raise_exception) { PyErr_SetString(PyExc_TypeError, "Cannot convert a non-sequence to std::array"); } + return false; + } + + pyref seq = PySequence_Fast(ob, "expected a sequence"); + int len = PySequence_Size(ob); + if (len != R) { + if (raise_exception) { + auto s = std::string{"Convertion to std::array failed : the length of the sequence ( = "} + std::to_string(len) + + " does not match R = " + std::to_string(R); + PyErr_SetString(PyExc_TypeError, s.c_str()); + } + return false; + } + for (int i = 0; i < len; i++) { + if (!py_converter>::is_convertible(PySequence_Fast_GET_ITEM((PyObject *)seq, i), raise_exception)) { + if (PyErr_Occurred()) PyErr_Print(); return false; } - for (int i = 0; i < len; i++) - if (!py_converter::is_convertible(PySequence_Fast_GET_ITEM((PyObject *)seq, i), raise_exception)) goto _false; // borrowed ref - - return true; } - _false: - if (raise_exception) { PyErr_SetString(PyExc_TypeError, "Cannot convert to std::array"); } - return false; + return true; } // -------------------------------------- static std::array py2c(PyObject *ob) { - pyref seq = PySequence_Fast(ob, "expected a sequence"); + _import_array(); + + // Special case: 1-d ndarray of builtin type + if (PyArray_Check(ob)) { + PyArrayObject *arr = (PyArrayObject *)(ob); +#ifdef PYTHON_NUMPY_VERSION_LT_17 + int rank = arr->nd; +#else + int rank = PyArray_NDIM(arr); +#endif + if (rank == 1) return make_array_from_numpy_proxy(make_numpy_proxy(ob)); + } + + ASSERT(PySequence_Check(ob)); std::array res; - for (int i = 0; i < R; i++) res[i] = py_converter::py2c(PySequence_Fast_GET_ITEM((PyObject *)seq, i)); // borrowed ref + pyref seq = PySequence_Fast(ob, "expected a sequence"); + for (int i = 0; i < R; i++) res[i] = py_converter>::py2c(PySequence_Fast_GET_ITEM((PyObject *)seq, i)); // borrowed ref return res; } }; diff --git a/c++/cpp2py/converters/vector.hpp b/c++/cpp2py/converters/vector.hpp index 1829867..97f6b77 100644 --- a/c++/cpp2py/converters/vector.hpp +++ b/c++/cpp2py/converters/vector.hpp @@ -6,6 +6,7 @@ #include "../traits.hpp" #include "../macros.hpp" #include "../numpy_proxy.hpp" +#include "../py_converter.hpp" namespace cpp2py { @@ -14,35 +15,54 @@ namespace cpp2py { static_assert(is_instantiation_of_v>, "Logic error"); using value_type = typename std::remove_reference_t::value_type; - auto *vec_heap = new std::vector{std::forward(v)}; - auto delete_pycapsule = [](PyObject *capsule) { - auto *ptr = static_cast *>(PyCapsule_GetPointer(capsule, "guard")); - delete ptr; - }; - PyObject *capsule = PyCapsule_New(vec_heap, "guard", delete_pycapsule); - - return {1, // rank - npy_type, - (void *)vec_heap->data(), - std::is_const_v, - std::vector{long(vec_heap->size())}, // extents - std::vector{sizeof(value_type)}, // strides - capsule}; + if constexpr (has_npy_type) { + auto *vec_heap = new std::vector{std::forward(v)}; + auto delete_pycapsule = [](PyObject *capsule) { + auto *ptr = static_cast *>(PyCapsule_GetPointer(capsule, "guard")); + delete ptr; + }; + PyObject *capsule = PyCapsule_New(vec_heap, "guard", delete_pycapsule); + + return {1, // rank + npy_type, + (void *)vec_heap->data(), + std::is_const_v, + std::vector{long(vec_heap->size())}, // extents + std::vector{sizeof(value_type)}, // strides + capsule}; + } else { + std::vector vobj(v.size()); + std::transform(begin(v), end(v), begin(vobj), [](auto &&x) { + if constexpr (std::is_reference_v) { + return convert_to_python(x); + } else { // vector passed as rvalue + return convert_to_python(std::move(x)); + } + }); + return make_numpy_proxy_from_vector(std::move(vobj)); + } } // Make a new vector from numpy view template std::vector make_vector_from_numpy_proxy(numpy_proxy const &p) { EXPECTS(p.extents.size() == 1); - EXPECTS(p.strides[0] % sizeof(T) == 0); long size = p.extents[0]; - long step = p.strides[0] / sizeof(T); std::vector v(size); - T *data = static_cast(p.data); - for(long i = 0; i < size; ++i) - v[i] = *(data + i * step); + if (p.element_type == npy_type) { + long step = p.strides[0] / sizeof(pyref); + auto **data = static_cast(p.data); + for(long i = 0; i < size; ++i) + v[i] = py_converter>::py2c(data[i * step]); + } else { + EXPECTS(p.strides[0] % sizeof(T) == 0); + long step = p.strides[0] / sizeof(T); + T *data = static_cast(p.data); + for(long i = 0; i < size; ++i) + v[i] = data[i * step]; + } return v; } @@ -53,26 +73,7 @@ namespace cpp2py { template static PyObject *c2py(V &&v) { static_assert(is_instantiation_of_v>, "Logic error"); - using value_type = typename std::remove_reference_t::value_type; - - if constexpr (has_npy_type) { - return make_numpy_proxy_from_vector(std::forward(v)).to_python(); - } else { // Convert to Python List - PyObject *list = PyList_New(0); - for (auto &x : v) { - pyref y; - if constexpr(std::is_reference_v){ - y = py_converter::c2py(x); - } else { // Vector passed as rvalue - y = py_converter::c2py(std::move(x)); - } - if (y.is_null() or (PyList_Append(list, y) == -1)) { - Py_DECREF(list); - return NULL; - } // error - } - return list; - } + return make_numpy_proxy_from_vector(std::forward(v)).to_python(); } // -------------------------------------- @@ -99,11 +100,8 @@ namespace cpp2py { pyref seq = PySequence_Fast(ob, "expected a sequence"); int len = PySequence_Size(ob); for (int i = 0; i < len; i++) { - if (!py_converter::is_convertible(PySequence_Fast_GET_ITEM((PyObject *)seq, i), false)) { // borrowed ref - if (raise_exception) { - auto err = std::string{"Cannot convert sequence to std::vector due to element at position "} + std::to_string(i); - PyErr_SetString(PyExc_TypeError, err.c_str()); - } + if (!py_converter>::is_convertible(PySequence_Fast_GET_ITEM((PyObject *)seq, i), raise_exception)) { // borrowed ref + if (PyErr_Occurred()) PyErr_Print(); return false; } }