#ifndef NUMPY_WRAPPERS_H #define NUMPY_WRAPPERS_H #include #include #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION #if defined(slots) && (defined(__GNUC__) || defined(_MSC_VER) || defined(__clang__)) #pragma push_macro("slots") #undef slots extern "C" { /* * Python 2 uses the "register" keyword, which is deprecated in C++ 11 * and forbidden in C++17. */ #if defined(__clang__) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-register" #endif #include #include #if defined(__clang__) #pragma clang diagnostic pop #endif } #else #include #include #endif #include #include #include inline int init_numpy() { DISABLE_WARNING_PUSH DISABLE_WARNING_CONVERSION_NULL import_array(); // PyError if not successful DISABLE_WARNING_POP return 0; } const static int numpy_initialized = init_numpy(); template struct PyObjectWrapper { private: PyObject* _py_obj = nullptr; void inc_refcount() { Py_XINCREF(_py_obj); } void dec_refcount() { Py_XDECREF(_py_obj); _py_obj = nullptr; } public: PyObjectWrapper() : _py_obj { nullptr } {} PyObjectWrapper(const PyObjectWrapper& other) : _py_obj { other._py_obj } { inc_refcount(); } PyObjectWrapper(PyObjectWrapper&& other) : _py_obj { other._py_obj } { inc_refcount(); } explicit PyObjectWrapper(PyObject* obj) : _py_obj { obj } { inc_refcount(); } ~PyObjectWrapper() { dec_refcount(); } PyObjectWrapper& operator=(PyObjectWrapper&& other) { dec_refcount(); this->_py_obj = other._py_obj; inc_refcount(); return *this; } PyObjectWrapper& operator=(const PyObjectWrapper& other) { dec_refcount(); this->_py_obj = other._py_obj; inc_refcount(); return *this; } PyObject* py_object() { return _py_obj; } inline dest_type* get() { return reinterpret_cast(_py_obj); } inline bool is_null() { return _py_obj == nullptr; } }; struct NpArray_view { private: PyObjectWrapper _py_obj; NpArray_view(const NpArray_view&& other) = delete; public: static bool isNpArray(PyObject* obj) { auto arr = reinterpret_cast(obj); auto is_c_aray = obj && PyArray_Check(arr) && PyArray_ISCARRAY(arr); return is_c_aray; } NpArray_view() : _py_obj { nullptr } {} NpArray_view(const NpArray_view& other) : _py_obj { other._py_obj } {} NpArray_view(NpArray_view&& other) : _py_obj { other._py_obj } {} explicit NpArray_view(PyObject* obj) : _py_obj { obj } { assert(isNpArray(obj)); assert(PyArray_ISFLOAT(_py_obj.get())); } NpArray_view& operator=(const NpArray_view& other) { this->_py_obj = other._py_obj; return *this; } NpArray_view& operator=(NpArray_view&& other) { this->_py_obj = other._py_obj; return *this; } std::vector shape() { std::vector shape; if (!_py_obj.is_null()) { if (int ndim = PyArray_NDIM(_py_obj.get()); ndim > 0) { if (ndim < 10) { shape.resize(ndim); std::copy_n(PyArray_SHAPE(_py_obj.get()), ndim, std::begin(shape)); } } } return shape; } std::size_t ndim() { if (!_py_obj.is_null()) { return static_cast(PyArray_NDIM(_py_obj.get())); } return 0; } std::size_t size(std::size_t index = 0) { if (!_py_obj.is_null()) { if (index < static_cast(PyArray_NDIM(_py_obj.get()))) { return PyArray_SHAPE(_py_obj.get())[index]; } } return 0; } std::size_t flat_size() { auto s = this->shape(); return std::accumulate( std::cbegin(s), std::cend(s), 1, [](const auto& a, const auto& b) { return a * b; }); } double data(std::size_t pos) { if (!_py_obj.is_null()) { return reinterpret_cast(PyArray_DATA(_py_obj.get()))[pos]; } return nan("NAN"); } std::vector to_std_vect() { assert(!this->_py_obj.is_null()); auto sz = flat_size(); std::vector v(sz); auto d_ptr = reinterpret_cast(PyArray_DATA(_py_obj.get())); std::copy(d_ptr, d_ptr + sz, std::begin(v)); return v; } std::vector to_std_vect_vect() { auto sz = size(0); std::vector v(sz); if (sz) { assert(ndim() == 2); assert(size(1) == 3); auto d_ptr = reinterpret_cast(PyArray_DATA(_py_obj.get())); std::copy(d_ptr, d_ptr + sz, std::begin(v)); } return v; } PyObject* py_object() { return _py_obj.py_object(); } }; struct NpArray { std::vector shape; std::vector data; static bool isNpArray(PyObject* obj) { return NpArray_view::isNpArray(obj); } NpArray() = default; explicit NpArray(PyObject* obj) { if (obj) { NpArray_view view { obj }; shape = view.shape(); data = view.to_std_vect(); } } inline std::size_t ndim() { return shape.size(); } std::size_t size(std::size_t index = 0) { if (index < shape.size()) return shape[index]; return 0; } std::size_t flat_size() { return std::accumulate(std::cbegin(shape), std::cend(shape), 1, [](const auto& a, const auto& b) { return a * b; }); } // TODO temporary hack should find a way to avoid this copy std::vector to_std_vect_vect() { auto sz = size(0); std::vector v(sz); if (sz) { assert(ndim() == 2); assert(size(1) == 3); auto d_ptr = reinterpret_cast(data.data()); std::copy(d_ptr, d_ptr + sz, std::begin(v)); } return v; } // TODO maybe ;) PyObject* py_object() { return nullptr; } }; inline int test_np_array(NpArray& arr) { auto shape = arr.shape; std::cout << "len(shape)=" << shape.size() << std::endl; std::for_each(std::cbegin(shape), std::cend(shape), [](auto sz) { static int i = 0; std::cout << "shape[" << i++ << "]=" << sz << std::endl; }); auto flatsize = std::accumulate(std::cbegin(shape), std::cend(shape), 0); for (auto i = 0; i < flatsize; i++) { std::cout << "data[" << i << "]=" << arr.data[i] << std::endl; } return 1; } #endif //#ifndef NUMPY_WRAPPERS_H