diff options
Diffstat (limited to '')
-rw-r--r-- | src/boost/libs/python/src/numpy/dtype.cpp | 220 |
1 files changed, 220 insertions, 0 deletions
diff --git a/src/boost/libs/python/src/numpy/dtype.cpp b/src/boost/libs/python/src/numpy/dtype.cpp new file mode 100644 index 000000000..88a20a27b --- /dev/null +++ b/src/boost/libs/python/src/numpy/dtype.cpp @@ -0,0 +1,220 @@ +// Copyright Jim Bosch 2010-2012. +// Copyright Stefan Seefeld 2016. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifdef _MSC_VER +#include <boost/cstdint.hpp> +#endif +#define BOOST_PYTHON_NUMPY_INTERNAL +#include <boost/python/numpy/internal.hpp> + +#define DTYPE_FROM_CODE(code) \ + dtype(python::detail::new_reference(reinterpret_cast<PyObject*>(PyArray_DescrFromType(code)))) + +#define BUILTIN_INT_DTYPE(bits) \ + template <> struct builtin_int_dtype<bits, false> \ + { \ + static dtype get() { return DTYPE_FROM_CODE(NPY_INT ## bits);} \ + }; \ + template <> struct builtin_int_dtype<bits, true> \ + { \ + static dtype get() { return DTYPE_FROM_CODE(NPY_UINT ## bits);} \ + }; \ + template BOOST_NUMPY_DECL dtype get_int_dtype<bits, false>(); \ + template BOOST_NUMPY_DECL dtype get_int_dtype<bits, true>() + +#define BUILTIN_FLOAT_DTYPE(bits) \ + template <> struct builtin_float_dtype<bits> \ + { \ + static dtype get() { return DTYPE_FROM_CODE(NPY_FLOAT ## bits);} \ + }; \ + template BOOST_NUMPY_DECL dtype get_float_dtype<bits>() + +#define BUILTIN_COMPLEX_DTYPE(bits) \ + template <> struct builtin_complex_dtype<bits> \ + { \ + static dtype get() { return DTYPE_FROM_CODE(NPY_COMPLEX ## bits);} \ + }; \ + template BOOST_NUMPY_DECL dtype get_complex_dtype<bits>() + +namespace boost { namespace python { namespace converter { +NUMPY_OBJECT_MANAGER_TRAITS_IMPL(PyArrayDescr_Type, numpy::dtype) +} // namespace boost::python::converter + +namespace numpy { +namespace detail { + +dtype builtin_dtype<bool,true>::get() { return DTYPE_FROM_CODE(NPY_BOOL); } + +template <int bits, bool isUnsigned> struct builtin_int_dtype; +template <int bits> struct builtin_float_dtype; +template <int bits> struct builtin_complex_dtype; + +template <int bits, bool isUnsigned> dtype get_int_dtype() { + return builtin_int_dtype<bits,isUnsigned>::get(); +} +template <int bits> dtype get_float_dtype() { return builtin_float_dtype<bits>::get(); } +template <int bits> dtype get_complex_dtype() { return builtin_complex_dtype<bits>::get(); } + +BUILTIN_INT_DTYPE(8); +BUILTIN_INT_DTYPE(16); +BUILTIN_INT_DTYPE(32); +BUILTIN_INT_DTYPE(64); +#ifdef NPY_FLOAT16 +BUILTIN_FLOAT_DTYPE(16); +#endif +BUILTIN_FLOAT_DTYPE(32); +BUILTIN_FLOAT_DTYPE(64); +BUILTIN_COMPLEX_DTYPE(64); +BUILTIN_COMPLEX_DTYPE(128); +#if NPY_BITSOF_LONGDOUBLE > NPY_BITSOF_DOUBLE +template <> struct builtin_float_dtype< NPY_BITSOF_LONGDOUBLE > { + static dtype get() { return DTYPE_FROM_CODE(NPY_LONGDOUBLE); } +}; +template dtype get_float_dtype< NPY_BITSOF_LONGDOUBLE >(); +template <> struct builtin_complex_dtype< 2 * NPY_BITSOF_LONGDOUBLE > { + static dtype get() { return DTYPE_FROM_CODE(NPY_CLONGDOUBLE); } +}; +template dtype get_complex_dtype< 2 * NPY_BITSOF_LONGDOUBLE >(); +#endif + +} // namespace detail + +python::detail::new_reference dtype::convert(object const & arg, bool align) +{ + PyArray_Descr* obj=NULL; + if (align) + { + if (PyArray_DescrAlignConverter(arg.ptr(), &obj) < 0) + throw_error_already_set(); + } + else + { + if (PyArray_DescrConverter(arg.ptr(), &obj) < 0) + throw_error_already_set(); + } + return python::detail::new_reference(reinterpret_cast<PyObject*>(obj)); +} + +int dtype::get_itemsize() const { return reinterpret_cast<PyArray_Descr*>(ptr())->elsize;} + +bool equivalent(dtype const & a, dtype const & b) { + // On Windows x64, the behaviour described on + // http://docs.scipy.org/doc/numpy/reference/c-api.array.html for + // PyArray_EquivTypes unfortunately does not extend as expected: + // "For example, on 32-bit platforms, NPY_LONG and NPY_INT are equivalent". + // This should also hold for 64-bit platforms (and does on Linux), but not + // on Windows. Implement an alternative: +#ifdef _MSC_VER + if (sizeof(long) == sizeof(int) && + // Manually take care of the type equivalence. + ((a == dtype::get_builtin<long>() || a == dtype::get_builtin<int>()) && + (b == dtype::get_builtin<long>() || b == dtype::get_builtin<int>()) || + (a == dtype::get_builtin<unsigned int>() || a == dtype::get_builtin<unsigned long>()) && + (b == dtype::get_builtin<unsigned int>() || b == dtype::get_builtin<unsigned long>()))) { + return true; + } else { + return PyArray_EquivTypes( + reinterpret_cast<PyArray_Descr*>(a.ptr()), + reinterpret_cast<PyArray_Descr*>(b.ptr()) + ); + } +#else + return PyArray_EquivTypes( + reinterpret_cast<PyArray_Descr*>(a.ptr()), + reinterpret_cast<PyArray_Descr*>(b.ptr()) + ); +#endif +} + +namespace +{ + +namespace pyconv = boost::python::converter; + +template <typename T> +class array_scalar_converter +{ +public: + + static PyTypeObject const * get_pytype() + { + // This implementation depends on the fact that get_builtin returns pointers to objects + // NumPy has declared statically, and that the typeobj member also refers to a static + // object. That means we don't need to do any reference counting. + // In fact, I'm somewhat concerned that increasing the reference count of any of these + // might cause leaks, because I don't think Boost.Python ever decrements it, but it's + // probably a moot point if everything is actually static. + return reinterpret_cast<PyArray_Descr*>(dtype::get_builtin<T>().ptr())->typeobj; + } + + static void * convertible(PyObject * obj) + { + if (obj->ob_type == get_pytype()) + { + return obj; + } + else + { + dtype dt(python::detail::borrowed_reference(obj->ob_type)); + if (equivalent(dt, dtype::get_builtin<T>())) + { + return obj; + } + } + return 0; + } + + static void convert(PyObject * obj, pyconv::rvalue_from_python_stage1_data* data) + { + void * storage = reinterpret_cast<pyconv::rvalue_from_python_storage<T>*>(data)->storage.bytes; + // We assume std::complex is a "standard layout" here and elsewhere; not guaranteed by + // C++03 standard, but true in every known implementation (and guaranteed by C++11). + PyArray_ScalarAsCtype(obj, reinterpret_cast<T*>(storage)); + data->convertible = storage; + } + + static void declare() + { + pyconv::registry::push_back(&convertible, &convert, python::type_id<T>() +#ifndef BOOST_PYTHON_NO_PY_SIGNATURES + , &get_pytype +#endif + ); + } + +}; + +} // anonymous + +void dtype::register_scalar_converters() +{ + array_scalar_converter<bool>::declare(); + array_scalar_converter<npy_uint8>::declare(); + array_scalar_converter<npy_int8>::declare(); + array_scalar_converter<npy_uint16>::declare(); + array_scalar_converter<npy_int16>::declare(); + array_scalar_converter<npy_uint32>::declare(); + array_scalar_converter<npy_int32>::declare(); +#ifdef _MSC_VER + // Since the npy_(u)int32 types are defined as long types and treated + // as being different from the int32 types, these converters must be declared + // explicitely. + array_scalar_converter<boost::uint32_t>::declare(); + array_scalar_converter<boost::int32_t>::declare(); +#endif + array_scalar_converter<npy_uint64>::declare(); + array_scalar_converter<npy_int64>::declare(); + array_scalar_converter<float>::declare(); + array_scalar_converter<double>::declare(); + array_scalar_converter< std::complex<float> >::declare(); + array_scalar_converter< std::complex<double> >::declare(); +#if NPY_BITSOF_LONGDOUBLE > NPY_BITSOF_DOUBLE + array_scalar_converter<long double>::declare(); + array_scalar_converter< std::complex<long double> >::declare(); +#endif +} + +}}} // namespace boost::python::numpy |