Now shape, size, ndims and itemsize are also signed integers.

This commit is contained in:
Cris Luengo 2017-04-14 14:33:44 -06:00 committed by Dean Moldovan
parent b68959e822
commit 30d43c4992
13 changed files with 207 additions and 205 deletions

View File

@ -57,10 +57,10 @@ specification.
struct buffer_info { struct buffer_info {
void *ptr; void *ptr;
size_t itemsize; ssize_t itemsize;
std::string format; std::string format;
int ndim; ssize_t ndim;
std::vector<size_t> shape; std::vector<ssize_t> shape;
std::vector<ssize_t> strides; std::vector<ssize_t> strides;
}; };
@ -95,8 +95,8 @@ buffer objects (e.g. a NumPy matrix).
throw std::runtime_error("Incompatible buffer dimension!"); throw std::runtime_error("Incompatible buffer dimension!");
auto strides = Strides( auto strides = Strides(
info.strides[rowMajor ? 0 : 1] / sizeof(Scalar), info.strides[rowMajor ? 0 : 1] / (py::ssize_t)sizeof(Scalar),
info.strides[rowMajor ? 1 : 0] / sizeof(Scalar)); info.strides[rowMajor ? 1 : 0] / (py::ssize_t)sizeof(Scalar));
auto map = Eigen::Map<Matrix, 0, Strides>( auto map = Eigen::Map<Matrix, 0, Strides>(
static_cat<Scalar *>(info.ptr), info.shape[0], info.shape[1], strides); static_cat<Scalar *>(info.ptr), info.shape[0], info.shape[1], strides);
@ -111,17 +111,14 @@ as follows:
.def_buffer([](Matrix &m) -> py::buffer_info { .def_buffer([](Matrix &m) -> py::buffer_info {
return py::buffer_info( return py::buffer_info(
m.data(), /* Pointer to buffer */ m.data(), /* Pointer to buffer */
sizeof(Scalar), /* Size of one scalar */ sizeof(Scalar), /* Size of one scalar */
/* Python struct-style format descriptor */ py::format_descriptor<Scalar>::format(), /* Python struct-style format descriptor */
py::format_descriptor<Scalar>::format(), 2, /* Number of dimensions */
/* Number of dimensions */ { m.rows(), m.cols() }, /* Buffer dimensions */
2,
/* Buffer dimensions */
{ m.rows(), m.cols() },
/* Strides (in bytes) for each index */
{ sizeof(Scalar) * (rowMajor ? m.cols() : 1), { sizeof(Scalar) * (rowMajor ? m.cols() : 1),
sizeof(Scalar) * (rowMajor ? 1 : m.rows()) } sizeof(Scalar) * (rowMajor ? 1 : m.rows()) }
/* Strides (in bytes) for each index */
); );
}) })
@ -321,17 +318,17 @@ where ``N`` gives the required dimensionality of the array:
m.def("sum_3d", [](py::array_t<double> x) { m.def("sum_3d", [](py::array_t<double> x) {
auto r = x.unchecked<3>(); // x must have ndim = 3; can be non-writeable auto r = x.unchecked<3>(); // x must have ndim = 3; can be non-writeable
double sum = 0; double sum = 0;
for (size_t i = 0; i < r.shape(0); i++) for (ssize_t i = 0; i < r.shape(0); i++)
for (size_t j = 0; j < r.shape(1); j++) for (ssize_t j = 0; j < r.shape(1); j++)
for (size_t k = 0; k < r.shape(2); k++) for (ssize_t k = 0; k < r.shape(2); k++)
sum += r(i, j, k); sum += r(i, j, k);
return sum; return sum;
}); });
m.def("increment_3d", [](py::array_t<double> x) { m.def("increment_3d", [](py::array_t<double> x) {
auto r = x.mutable_unchecked<3>(); // Will throw if ndim != 3 or flags.writeable is false auto r = x.mutable_unchecked<3>(); // Will throw if ndim != 3 or flags.writeable is false
for (size_t i = 0; i < r.shape(0); i++) for (ssize_t i = 0; i < r.shape(0); i++)
for (size_t j = 0; j < r.shape(1); j++) for (ssize_t j = 0; j < r.shape(1); j++)
for (size_t k = 0; k < r.shape(2); k++) for (ssize_t k = 0; k < r.shape(2); k++)
r(i, j, k) += 1.0; r(i, j, k) += 1.0;
}, py::arg().noconvert()); }, py::arg().noconvert());

View File

@ -15,31 +15,31 @@ NAMESPACE_BEGIN(pybind11)
/// Information record describing a Python buffer object /// Information record describing a Python buffer object
struct buffer_info { struct buffer_info {
void *ptr = nullptr; // Pointer to the underlying storage void *ptr = nullptr; // Pointer to the underlying storage
size_t itemsize = 0; // Size of individual items in bytes ssize_t itemsize = 0; // Size of individual items in bytes
size_t size = 0; // Total number of entries ssize_t size = 0; // Total number of entries
std::string format; // For homogeneous buffers, this should be set to format_descriptor<T>::format() std::string format; // For homogeneous buffers, this should be set to format_descriptor<T>::format()
size_t ndim = 0; // Number of dimensions ssize_t ndim = 0; // Number of dimensions
std::vector<size_t> shape; // Shape of the tensor (1 entry per dimension) std::vector<ssize_t> shape; // Shape of the tensor (1 entry per dimension)
std::vector<ssize_t> strides; // Number of entries between adjacent entries (for each per dimension) std::vector<ssize_t> strides; // Number of entries between adjacent entries (for each per dimension)
buffer_info() { } buffer_info() { }
buffer_info(void *ptr, size_t itemsize, const std::string &format, size_t ndim, buffer_info(void *ptr, ssize_t itemsize, const std::string &format, ssize_t ndim,
detail::any_container<size_t> shape_in, detail::any_container<ssize_t> strides_in) detail::any_container<ssize_t> shape_in, detail::any_container<ssize_t> strides_in)
: ptr(ptr), itemsize(itemsize), size(1), format(format), ndim(ndim), : ptr(ptr), itemsize(itemsize), size(1), format(format), ndim(ndim),
shape(std::move(shape_in)), strides(std::move(strides_in)) { shape(std::move(shape_in)), strides(std::move(strides_in)) {
if (ndim != shape.size() || ndim != strides.size()) if (ndim != (ssize_t) shape.size() || ndim != (ssize_t) strides.size())
pybind11_fail("buffer_info: ndim doesn't match shape and/or strides length"); pybind11_fail("buffer_info: ndim doesn't match shape and/or strides length");
for (size_t i = 0; i < ndim; ++i) for (size_t i = 0; i < (size_t) ndim; ++i)
size *= shape[i]; size *= shape[i];
} }
buffer_info(void *ptr, size_t itemsize, const std::string &format, size_t size) buffer_info(void *ptr, ssize_t itemsize, const std::string &format, ssize_t size)
: buffer_info(ptr, itemsize, format, 1, {size}, {itemsize}) { } : buffer_info(ptr, itemsize, format, 1, {size}, {itemsize}) { }
explicit buffer_info(Py_buffer *view, bool ownview = true) explicit buffer_info(Py_buffer *view, bool ownview = true)
: buffer_info(view->buf, (size_t) view->itemsize, view->format, (size_t) view->ndim, : buffer_info(view->buf, view->itemsize, view->format, view->ndim,
{view->shape, view->shape + view->ndim}, {view->strides, view->strides + view->ndim}) { {view->shape, view->shape + view->ndim}, {view->strides, view->strides + view->ndim}) {
this->view = view; this->view = view;
this->ownview = ownview; this->ownview = ownview;
@ -78,13 +78,13 @@ NAMESPACE_BEGIN(detail)
template <typename T, typename SFINAE = void> struct compare_buffer_info { template <typename T, typename SFINAE = void> struct compare_buffer_info {
static bool compare(const buffer_info& b) { static bool compare(const buffer_info& b) {
return b.format == format_descriptor<T>::format() && b.itemsize == sizeof(T); return b.format == format_descriptor<T>::format() && b.itemsize == (ssize_t) sizeof(T);
} }
}; };
template <typename T> struct compare_buffer_info<T, detail::enable_if_t<std::is_integral<T>::value>> { template <typename T> struct compare_buffer_info<T, detail::enable_if_t<std::is_integral<T>::value>> {
static bool compare(const buffer_info& b) { static bool compare(const buffer_info& b) {
return b.itemsize == sizeof(T) && (b.format == format_descriptor<T>::value || return (size_t) b.itemsize == sizeof(T) && (b.format == format_descriptor<T>::value ||
((sizeof(T) == sizeof(long)) && b.format == (std::is_unsigned<T>::value ? "L" : "l")) || ((sizeof(T) == sizeof(long)) && b.format == (std::is_unsigned<T>::value ? "L" : "l")) ||
((sizeof(T) == sizeof(size_t)) && b.format == (std::is_unsigned<T>::value ? "N" : "n"))); ((sizeof(T) == sizeof(size_t)) && b.format == (std::is_unsigned<T>::value ? "N" : "n")));
} }

View File

@ -433,7 +433,7 @@ inline void enable_dynamic_attributes(PyHeapTypeObject *heap_type) {
#endif #endif
type->tp_flags |= Py_TPFLAGS_HAVE_GC; type->tp_flags |= Py_TPFLAGS_HAVE_GC;
type->tp_dictoffset = type->tp_basicsize; // place dict at the end type->tp_dictoffset = type->tp_basicsize; // place dict at the end
type->tp_basicsize += (Py_ssize_t)sizeof(PyObject *); // and allocate enough space for it type->tp_basicsize += (ssize_t)sizeof(PyObject *); // and allocate enough space for it
type->tp_traverse = pybind11_traverse; type->tp_traverse = pybind11_traverse;
type->tp_clear = pybind11_clear; type->tp_clear = pybind11_clear;
@ -459,18 +459,16 @@ extern "C" inline int pybind11_getbuffer(PyObject *obj, Py_buffer *view, int fla
view->ndim = 1; view->ndim = 1;
view->internal = info; view->internal = info;
view->buf = info->ptr; view->buf = info->ptr;
view->itemsize = (Py_ssize_t) info->itemsize; view->itemsize = info->itemsize;
view->len = view->itemsize; view->len = view->itemsize;
for (auto s : info->shape) for (auto s : info->shape)
view->len *= (Py_ssize_t) s; view->len *= s;
if ((flags & PyBUF_FORMAT) == PyBUF_FORMAT) if ((flags & PyBUF_FORMAT) == PyBUF_FORMAT)
view->format = const_cast<char *>(info->format.c_str()); view->format = const_cast<char *>(info->format.c_str());
if ((flags & PyBUF_STRIDES) == PyBUF_STRIDES) { if ((flags & PyBUF_STRIDES) == PyBUF_STRIDES) {
view->ndim = (int) info->ndim; view->ndim = (int) info->ndim;
view->strides = &info->strides[0]; view->strides = &info->strides[0];
// Next is a pointer cast, let's make sure it's safe. view->shape = &info->shape[0];
static_assert(sizeof(Py_ssize_t)==sizeof(info->shape[0]), "sizeof(Py_ssize_t) != sizeof(size_t)");
view->shape = (Py_ssize_t *) &info->shape[0];
} }
Py_INCREF(view->obj); Py_INCREF(view->obj);
return 0; return 0;

View File

@ -68,7 +68,7 @@ template <typename T> using is_eigen_other = all_of<
template <bool EigenRowMajor> struct EigenConformable { template <bool EigenRowMajor> struct EigenConformable {
bool conformable = false; bool conformable = false;
EigenIndex rows = 0, cols = 0; EigenIndex rows = 0, cols = 0;
EigenDStride stride{0, 0}; // Only valid if negativestridees is false! EigenDStride stride{0, 0}; // Only valid if negativestrides is false!
bool negativestrides = false; // If true, do not use stride! bool negativestrides = false; // If true, do not use stride!
EigenConformable(bool fits = false) : conformable{fits} {} EigenConformable(bool fits = false) : conformable{fits} {}
@ -207,7 +207,7 @@ template <typename Type_> struct EigenProps {
// Casts an Eigen type to numpy array. If given a base, the numpy array references the src data, // Casts an Eigen type to numpy array. If given a base, the numpy array references the src data,
// otherwise it'll make a copy. writeable lets you turn off the writeable flag for the array. // otherwise it'll make a copy. writeable lets you turn off the writeable flag for the array.
template <typename props> handle eigen_array_cast(typename props::Type const &src, handle base = handle(), bool writeable = true) { template <typename props> handle eigen_array_cast(typename props::Type const &src, handle base = handle(), bool writeable = true) {
constexpr size_t elem_size = sizeof(typename props::Scalar); constexpr ssize_t elem_size = sizeof(typename props::Scalar);
array a; array a;
if (props::vector) if (props::vector)
a = array({ src.size() }, { elem_size * src.innerStride() }, src.data(), base); a = array({ src.size() }, { elem_size * src.innerStride() }, src.data(), base);
@ -581,9 +581,9 @@ struct type_caster<Type, enable_if_t<is_eigen_sparse<Type>::value>> {
object matrix_type = module::import("scipy.sparse").attr( object matrix_type = module::import("scipy.sparse").attr(
rowMajor ? "csr_matrix" : "csc_matrix"); rowMajor ? "csr_matrix" : "csc_matrix");
array data((size_t) src.nonZeros(), src.valuePtr()); array data(src.nonZeros(), src.valuePtr());
array outerIndices((size_t) (rowMajor ? src.rows() : src.cols()) + 1, src.outerIndexPtr()); array outerIndices((rowMajor ? src.rows() : src.cols()) + 1, src.outerIndexPtr());
array innerIndices((size_t) src.nonZeros(), src.innerIndexPtr()); array innerIndices(src.nonZeros(), src.innerIndexPtr());
return matrix_type( return matrix_type(
std::make_tuple(data, innerIndices, outerIndices), std::make_tuple(data, innerIndices, outerIndices),

View File

@ -251,10 +251,10 @@ template <typename T> using is_pod_struct = all_of<
satisfies_none_of<T, std::is_reference, std::is_array, is_std_array, std::is_arithmetic, is_complex, std::is_enum> satisfies_none_of<T, std::is_reference, std::is_array, is_std_array, std::is_arithmetic, is_complex, std::is_enum>
>; >;
template <size_t Dim = 0, typename Strides> ssize_t byte_offset_unsafe(const Strides &) { return 0; } template <ssize_t Dim = 0, typename Strides> ssize_t byte_offset_unsafe(const Strides &) { return 0; }
template <size_t Dim = 0, typename Strides, typename... Ix> template <ssize_t Dim = 0, typename Strides, typename... Ix>
ssize_t byte_offset_unsafe(const Strides &strides, size_t i, Ix... index) { ssize_t byte_offset_unsafe(const Strides &strides, ssize_t i, Ix... index) {
return static_cast<ssize_t>(i) * strides[Dim] + byte_offset_unsafe<Dim + 1>(strides, index...); return i * strides[Dim] + byte_offset_unsafe<Dim + 1>(strides, index...);
} }
/** Proxy class providing unsafe, unchecked const access to array data. This is constructed through /** Proxy class providing unsafe, unchecked const access to array data. This is constructed through
@ -268,23 +268,23 @@ protected:
const unsigned char *data_; const unsigned char *data_;
// Storing the shape & strides in local variables (i.e. these arrays) allows the compiler to // Storing the shape & strides in local variables (i.e. these arrays) allows the compiler to
// make large performance gains on big, nested loops, but requires compile-time dimensions // make large performance gains on big, nested loops, but requires compile-time dimensions
conditional_t<Dynamic, const size_t *, std::array<size_t, (size_t) Dims>> shape_; conditional_t<Dynamic, const ssize_t *, std::array<ssize_t, (size_t) Dims>>
conditional_t<Dynamic, const ssize_t *, std::array<ssize_t, (size_t) Dims>> strides_; shape_, strides_;
const size_t dims_; const ssize_t dims_;
friend class pybind11::array; friend class pybind11::array;
// Constructor for compile-time dimensions: // Constructor for compile-time dimensions:
template <bool Dyn = Dynamic> template <bool Dyn = Dynamic>
unchecked_reference(const void *data, const size_t *shape, const ssize_t *strides, enable_if_t<!Dyn, size_t>) unchecked_reference(const void *data, const ssize_t *shape, const ssize_t *strides, enable_if_t<!Dyn, ssize_t>)
: data_{reinterpret_cast<const unsigned char *>(data)}, dims_{Dims} { : data_{reinterpret_cast<const unsigned char *>(data)}, dims_{Dims} {
for (size_t i = 0; i < dims_; i++) { for (size_t i = 0; i < (size_t) dims_; i++) {
shape_[i] = shape[i]; shape_[i] = shape[i];
strides_[i] = strides[i]; strides_[i] = strides[i];
} }
} }
// Constructor for runtime dimensions: // Constructor for runtime dimensions:
template <bool Dyn = Dynamic> template <bool Dyn = Dynamic>
unchecked_reference(const void *data, const size_t *shape, const ssize_t *strides, enable_if_t<Dyn, size_t> dims) unchecked_reference(const void *data, const ssize_t *shape, const ssize_t *strides, enable_if_t<Dyn, ssize_t> dims)
: data_{reinterpret_cast<const unsigned char *>(data)}, shape_{shape}, strides_{strides}, dims_{dims} {} : data_{reinterpret_cast<const unsigned char *>(data)}, shape_{shape}, strides_{strides}, dims_{dims} {}
public: public:
@ -295,39 +295,39 @@ public:
template <typename... Ix> const T &operator()(Ix... index) const { template <typename... Ix> const T &operator()(Ix... index) const {
static_assert(sizeof...(Ix) == Dims || Dynamic, static_assert(sizeof...(Ix) == Dims || Dynamic,
"Invalid number of indices for unchecked array reference"); "Invalid number of indices for unchecked array reference");
return *reinterpret_cast<const T *>(data_ + byte_offset_unsafe(strides_, size_t(index)...)); return *reinterpret_cast<const T *>(data_ + byte_offset_unsafe(strides_, ssize_t(index)...));
} }
/** Unchecked const reference access to data; this operator only participates if the reference /** Unchecked const reference access to data; this operator only participates if the reference
* is to a 1-dimensional array. When present, this is exactly equivalent to `obj(index)`. * is to a 1-dimensional array. When present, this is exactly equivalent to `obj(index)`.
*/ */
template <size_t D = Dims, typename = enable_if_t<D == 1 || Dynamic>> template <ssize_t D = Dims, typename = enable_if_t<D == 1 || Dynamic>>
const T &operator[](size_t index) const { return operator()(index); } const T &operator[](ssize_t index) const { return operator()(index); }
/// Pointer access to the data at the given indices. /// Pointer access to the data at the given indices.
template <typename... Ix> const T *data(Ix... ix) const { return &operator()(size_t(ix)...); } template <typename... Ix> const T *data(Ix... ix) const { return &operator()(ssize_t(ix)...); }
/// Returns the item size, i.e. sizeof(T) /// Returns the item size, i.e. sizeof(T)
constexpr static size_t itemsize() { return sizeof(T); } constexpr static ssize_t itemsize() { return sizeof(T); }
/// Returns the shape (i.e. size) of dimension `dim` /// Returns the shape (i.e. size) of dimension `dim`
size_t shape(size_t dim) const { return shape_[dim]; } ssize_t shape(ssize_t dim) const { return shape_[(size_t) dim]; }
/// Returns the number of dimensions of the array /// Returns the number of dimensions of the array
size_t ndim() const { return dims_; } ssize_t ndim() const { return dims_; }
/// Returns the total number of elements in the referenced array, i.e. the product of the shapes /// Returns the total number of elements in the referenced array, i.e. the product of the shapes
template <bool Dyn = Dynamic> template <bool Dyn = Dynamic>
enable_if_t<!Dyn, size_t> size() const { enable_if_t<!Dyn, ssize_t> size() const {
return std::accumulate(shape_.begin(), shape_.end(), (size_t) 1, std::multiplies<size_t>()); return std::accumulate(shape_.begin(), shape_.end(), (ssize_t) 1, std::multiplies<ssize_t>());
} }
template <bool Dyn = Dynamic> template <bool Dyn = Dynamic>
enable_if_t<Dyn, size_t> size() const { enable_if_t<Dyn, ssize_t> size() const {
return std::accumulate(shape_, shape_ + ndim(), (size_t) 1, std::multiplies<size_t>()); return std::accumulate(shape_, shape_ + ndim(), (ssize_t) 1, std::multiplies<ssize_t>());
} }
/// Returns the total number of bytes used by the referenced data. Note that the actual span in /// Returns the total number of bytes used by the referenced data. Note that the actual span in
/// memory may be larger if the referenced array has non-contiguous strides (e.g. for a slice). /// memory may be larger if the referenced array has non-contiguous strides (e.g. for a slice).
size_t nbytes() const { ssize_t nbytes() const {
return size() * itemsize(); return size() * itemsize();
} }
}; };
@ -349,11 +349,11 @@ public:
* reference is to a 1-dimensional array (or has runtime dimensions). When present, this is * reference is to a 1-dimensional array (or has runtime dimensions). When present, this is
* exactly equivalent to `obj(index)`. * exactly equivalent to `obj(index)`.
*/ */
template <size_t D = Dims, typename = enable_if_t<D == 1 || Dynamic>> template <ssize_t D = Dims, typename = enable_if_t<D == 1 || Dynamic>>
T &operator[](size_t index) { return operator()(index); } T &operator[](ssize_t index) { return operator()(index); }
/// Mutable pointer access to the data at the given indices. /// Mutable pointer access to the data at the given indices.
template <typename... Ix> T *mutable_data(Ix... ix) { return &operator()(size_t(ix)...); } template <typename... Ix> T *mutable_data(Ix... ix) { return &operator()(ssize_t(ix)...); }
}; };
template <typename T, ssize_t Dim> template <typename T, ssize_t Dim>
@ -381,7 +381,7 @@ public:
dtype(const char *format) : dtype(std::string(format)) { } dtype(const char *format) : dtype(std::string(format)) { }
dtype(list names, list formats, list offsets, size_t itemsize) { dtype(list names, list formats, list offsets, ssize_t itemsize) {
dict args; dict args;
args["names"] = names; args["names"] = names;
args["formats"] = formats; args["formats"] = formats;
@ -404,8 +404,8 @@ public:
} }
/// Size of the data type in bytes. /// Size of the data type in bytes.
size_t itemsize() const { ssize_t itemsize() const {
return (size_t) detail::array_descriptor_proxy(m_ptr)->elsize; return detail::array_descriptor_proxy(m_ptr)->elsize;
} }
/// Returns true for structured data types. /// Returns true for structured data types.
@ -425,7 +425,7 @@ private:
return reinterpret_borrow<object>(obj); return reinterpret_borrow<object>(obj);
} }
dtype strip_padding(size_t itemsize) { dtype strip_padding(ssize_t itemsize) {
// Recursively strip all void fields with empty names that are generated for // Recursively strip all void fields with empty names that are generated for
// padding fields (as of NumPy v1.11). // padding fields (as of NumPy v1.11).
if (!has_fields()) if (!has_fields())
@ -501,7 +501,7 @@ public:
api.PyArray_Type_, descr.release().ptr(), (int) ndim, shape->data(), strides->data(), api.PyArray_Type_, descr.release().ptr(), (int) ndim, shape->data(), strides->data(),
const_cast<void *>(ptr), flags, nullptr)); const_cast<void *>(ptr), flags, nullptr));
if (!tmp) if (!tmp)
pybind11_fail("NumPy: unable to create array!"); throw error_already_set();
if (ptr) { if (ptr) {
if (base) { if (base) {
api.PyArray_SetBaseObject_(tmp.ptr(), base.inc_ref().ptr()); api.PyArray_SetBaseObject_(tmp.ptr(), base.inc_ref().ptr());
@ -528,7 +528,7 @@ public:
: array(std::move(shape), {}, ptr, base) { } : array(std::move(shape), {}, ptr, base) { }
template <typename T> template <typename T>
explicit array(size_t count, const T *ptr, handle base = handle()) : array({count}, {}, ptr, base) { } explicit array(ssize_t count, const T *ptr, handle base = handle()) : array({count}, {}, ptr, base) { }
explicit array(const buffer_info &info) explicit array(const buffer_info &info)
: array(pybind11::dtype(info), info.shape, info.strides, info.ptr) { } : array(pybind11::dtype(info), info.shape, info.strides, info.ptr) { }
@ -539,23 +539,23 @@ public:
} }
/// Total number of elements /// Total number of elements
size_t size() const { ssize_t size() const {
return std::accumulate(shape(), shape() + ndim(), (size_t) 1, std::multiplies<size_t>()); return std::accumulate(shape(), shape() + ndim(), (ssize_t) 1, std::multiplies<ssize_t>());
} }
/// Byte size of a single element /// Byte size of a single element
size_t itemsize() const { ssize_t itemsize() const {
return (size_t) detail::array_descriptor_proxy(detail::array_proxy(m_ptr)->descr)->elsize; return detail::array_descriptor_proxy(detail::array_proxy(m_ptr)->descr)->elsize;
} }
/// Total number of bytes /// Total number of bytes
size_t nbytes() const { ssize_t nbytes() const {
return size() * itemsize(); return size() * itemsize();
} }
/// Number of dimensions /// Number of dimensions
size_t ndim() const { ssize_t ndim() const {
return (size_t) detail::array_proxy(m_ptr)->nd; return detail::array_proxy(m_ptr)->nd;
} }
/// Base object /// Base object
@ -564,12 +564,12 @@ public:
} }
/// Dimensions of the array /// Dimensions of the array
const size_t* shape() const { const ssize_t* shape() const {
return reinterpret_cast<const size_t *>(detail::array_proxy(m_ptr)->dimensions); return detail::array_proxy(m_ptr)->dimensions;
} }
/// Dimension along a given axis /// Dimension along a given axis
size_t shape(size_t dim) const { ssize_t shape(ssize_t dim) const {
if (dim >= ndim()) if (dim >= ndim())
fail_dim_check(dim, "invalid axis"); fail_dim_check(dim, "invalid axis");
return shape()[dim]; return shape()[dim];
@ -577,11 +577,11 @@ public:
/// Strides of the array /// Strides of the array
const ssize_t* strides() const { const ssize_t* strides() const {
return reinterpret_cast<const ssize_t *>(detail::array_proxy(m_ptr)->strides); return detail::array_proxy(m_ptr)->strides;
} }
/// Stride along a given axis /// Stride along a given axis
ssize_t strides(size_t dim) const { ssize_t strides(ssize_t dim) const {
if (dim >= ndim()) if (dim >= ndim())
fail_dim_check(dim, "invalid axis"); fail_dim_check(dim, "invalid axis");
return strides()[dim]; return strides()[dim];
@ -619,9 +619,9 @@ public:
/// Byte offset from beginning of the array to a given index (full or partial). /// Byte offset from beginning of the array to a given index (full or partial).
/// May throw if the index would lead to out of bounds access. /// May throw if the index would lead to out of bounds access.
template<typename... Ix> ssize_t offset_at(Ix... index) const { template<typename... Ix> ssize_t offset_at(Ix... index) const {
if (sizeof...(index) > ndim()) if ((ssize_t) sizeof...(index) > ndim())
fail_dim_check(sizeof...(index), "too many indices for an array"); fail_dim_check(sizeof...(index), "too many indices for an array");
return byte_offset(size_t(index)...); return byte_offset(ssize_t(index)...);
} }
ssize_t offset_at() const { return 0; } ssize_t offset_at() const { return 0; }
@ -629,7 +629,7 @@ public:
/// Item count from beginning of the array to a given index (full or partial). /// Item count from beginning of the array to a given index (full or partial).
/// May throw if the index would lead to out of bounds access. /// May throw if the index would lead to out of bounds access.
template<typename... Ix> ssize_t index_at(Ix... index) const { template<typename... Ix> ssize_t index_at(Ix... index) const {
return offset_at(index...) / static_cast<ssize_t>(itemsize()); return offset_at(index...) / itemsize();
} }
/** Returns a proxy object that provides access to the array's data without bounds or /** Returns a proxy object that provides access to the array's data without bounds or
@ -638,7 +638,7 @@ public:
* and the caller must take care not to access invalid dimensions or dimension indices. * and the caller must take care not to access invalid dimensions or dimension indices.
*/ */
template <typename T, ssize_t Dims = -1> detail::unchecked_mutable_reference<T, Dims> mutable_unchecked() { template <typename T, ssize_t Dims = -1> detail::unchecked_mutable_reference<T, Dims> mutable_unchecked() {
if (Dims >= 0 && ndim() != (size_t) Dims) if (Dims >= 0 && ndim() != Dims)
throw std::domain_error("array has incorrect number of dimensions: " + std::to_string(ndim()) + throw std::domain_error("array has incorrect number of dimensions: " + std::to_string(ndim()) +
"; expected " + std::to_string(Dims)); "; expected " + std::to_string(Dims));
return detail::unchecked_mutable_reference<T, Dims>(mutable_data(), shape(), strides(), ndim()); return detail::unchecked_mutable_reference<T, Dims>(mutable_data(), shape(), strides(), ndim());
@ -651,7 +651,7 @@ public:
* invalid dimensions or dimension indices. * invalid dimensions or dimension indices.
*/ */
template <typename T, ssize_t Dims = -1> detail::unchecked_reference<T, Dims> unchecked() const { template <typename T, ssize_t Dims = -1> detail::unchecked_reference<T, Dims> unchecked() const {
if (Dims >= 0 && ndim() != (size_t) Dims) if (Dims >= 0 && ndim() != Dims)
throw std::domain_error("array has incorrect number of dimensions: " + std::to_string(ndim()) + throw std::domain_error("array has incorrect number of dimensions: " + std::to_string(ndim()) +
"; expected " + std::to_string(Dims)); "; expected " + std::to_string(Dims));
return detail::unchecked_reference<T, Dims>(data(), shape(), strides(), ndim()); return detail::unchecked_reference<T, Dims>(data(), shape(), strides(), ndim());
@ -690,14 +690,14 @@ public:
protected: protected:
template<typename, typename> friend struct detail::npy_format_descriptor; template<typename, typename> friend struct detail::npy_format_descriptor;
void fail_dim_check(size_t dim, const std::string& msg) const { void fail_dim_check(ssize_t dim, const std::string& msg) const {
throw index_error(msg + ": " + std::to_string(dim) + throw index_error(msg + ": " + std::to_string(dim) +
" (ndim = " + std::to_string(ndim()) + ")"); " (ndim = " + std::to_string(ndim()) + ")");
} }
template<typename... Ix> ssize_t byte_offset(Ix... index) const { template<typename... Ix> ssize_t byte_offset(Ix... index) const {
check_dimensions(index...); check_dimensions(index...);
return detail::byte_offset_unsafe(strides(), size_t(index)...); return detail::byte_offset_unsafe(strides(), ssize_t(index)...);
} }
void check_writeable() const { void check_writeable() const {
@ -705,7 +705,7 @@ protected:
throw std::domain_error("array is not writeable"); throw std::domain_error("array is not writeable");
} }
static std::vector<ssize_t> default_strides(const std::vector<size_t>& shape, size_t itemsize) { static std::vector<Py_intptr_t> default_strides(const std::vector<Py_intptr_t>& shape, ssize_t itemsize) {
auto ndim = shape.size(); auto ndim = shape.size();
std::vector<ssize_t> strides(ndim); std::vector<ssize_t> strides(ndim);
if (ndim) { if (ndim) {
@ -718,12 +718,12 @@ protected:
} }
template<typename... Ix> void check_dimensions(Ix... index) const { template<typename... Ix> void check_dimensions(Ix... index) const {
check_dimensions_impl(size_t(0), shape(), size_t(index)...); check_dimensions_impl(ssize_t(0), shape(), ssize_t(index)...);
} }
void check_dimensions_impl(size_t, const size_t*) const { } void check_dimensions_impl(ssize_t, const ssize_t*) const { }
template<typename... Ix> void check_dimensions_impl(size_t axis, const size_t* shape, size_t i, Ix... index) const { template<typename... Ix> void check_dimensions_impl(ssize_t axis, const ssize_t* shape, ssize_t i, Ix... index) const {
if (i >= *shape) { if (i >= *shape) {
throw index_error(std::string("index ") + std::to_string(i) + throw index_error(std::string("index ") + std::to_string(i) +
" is out of bounds for axis " + std::to_string(axis) + " is out of bounds for axis " + std::to_string(axis) +
@ -772,12 +772,12 @@ public:
explicit array_t(size_t count, const T *ptr = nullptr, handle base = handle()) explicit array_t(size_t count, const T *ptr = nullptr, handle base = handle())
: array({count}, {}, ptr, base) { } : array({count}, {}, ptr, base) { }
constexpr size_t itemsize() const { constexpr ssize_t itemsize() const {
return sizeof(T); return sizeof(T);
} }
template<typename... Ix> ssize_t index_at(Ix... index) const { template<typename... Ix> ssize_t index_at(Ix... index) const {
return offset_at(index...) / static_cast<ssize_t>(itemsize()); return offset_at(index...) / itemsize();
} }
template<typename... Ix> const T* data(Ix... index) const { template<typename... Ix> const T* data(Ix... index) const {
@ -792,14 +792,14 @@ public:
template<typename... Ix> const T& at(Ix... index) const { template<typename... Ix> const T& at(Ix... index) const {
if (sizeof...(index) != ndim()) if (sizeof...(index) != ndim())
fail_dim_check(sizeof...(index), "index dimension mismatch"); fail_dim_check(sizeof...(index), "index dimension mismatch");
return *(static_cast<const T*>(array::data()) + byte_offset(size_t(index)...) / static_cast<ssize_t>(itemsize())); return *(static_cast<const T*>(array::data()) + byte_offset(ssize_t(index)...) / itemsize());
} }
// Mutable reference to element at a given index // Mutable reference to element at a given index
template<typename... Ix> T& mutable_at(Ix... index) { template<typename... Ix> T& mutable_at(Ix... index) {
if (sizeof...(index) != ndim()) if (sizeof...(index) != ndim())
fail_dim_check(sizeof...(index), "index dimension mismatch"); fail_dim_check(sizeof...(index), "index dimension mismatch");
return *(static_cast<T*>(array::mutable_data()) + byte_offset(size_t(index)...) / static_cast<ssize_t>(itemsize())); return *(static_cast<T*>(array::mutable_data()) + byte_offset(ssize_t(index)...) / itemsize());
} }
/** Returns a proxy object that provides access to the array's data without bounds or /** Returns a proxy object that provides access to the array's data without bounds or
@ -949,16 +949,16 @@ public:
struct field_descriptor { struct field_descriptor {
const char *name; const char *name;
size_t offset; ssize_t offset;
size_t size; ssize_t size;
size_t alignment; ssize_t alignment;
std::string format; std::string format;
dtype descr; dtype descr;
}; };
inline PYBIND11_NOINLINE void register_structured_dtype( inline PYBIND11_NOINLINE void register_structured_dtype(
const std::initializer_list<field_descriptor>& fields, const std::initializer_list<field_descriptor>& fields,
const std::type_info& tinfo, size_t itemsize, const std::type_info& tinfo, ssize_t itemsize,
bool (*direct_converter)(PyObject *, void *&)) { bool (*direct_converter)(PyObject *, void *&)) {
auto& numpy_internals = get_numpy_internals(); auto& numpy_internals = get_numpy_internals();
@ -986,7 +986,7 @@ inline PYBIND11_NOINLINE void register_structured_dtype(
std::vector<field_descriptor> ordered_fields(fields); std::vector<field_descriptor> ordered_fields(fields);
std::sort(ordered_fields.begin(), ordered_fields.end(), std::sort(ordered_fields.begin(), ordered_fields.end(),
[](const field_descriptor &a, const field_descriptor &b) { return a.offset < b.offset; }); [](const field_descriptor &a, const field_descriptor &b) { return a.offset < b.offset; });
size_t offset = 0; ssize_t offset = 0;
std::ostringstream oss; std::ostringstream oss;
oss << "T{"; oss << "T{";
for (auto& field : ordered_fields) { for (auto& field : ordered_fields) {
@ -1142,7 +1142,7 @@ public:
common_iterator() : p_ptr(0), m_strides() {} common_iterator() : p_ptr(0), m_strides() {}
common_iterator(void* ptr, const container_type& strides, const std::vector<size_t>& shape) common_iterator(void* ptr, const container_type& strides, const container_type& shape)
: p_ptr(reinterpret_cast<char*>(ptr)), m_strides(strides.size()) { : p_ptr(reinterpret_cast<char*>(ptr)), m_strides(strides.size()) {
m_strides.back() = static_cast<value_type>(strides.back()); m_strides.back() = static_cast<value_type>(strides.back());
for (size_type i = m_strides.size() - 1; i != 0; --i) { for (size_type i = m_strides.size() - 1; i != 0; --i) {
@ -1167,18 +1167,18 @@ private:
template <size_t N> class multi_array_iterator { template <size_t N> class multi_array_iterator {
public: public:
using container_type = std::vector<size_t>; using container_type = std::vector<ssize_t>;
multi_array_iterator(const std::array<buffer_info, N> &buffers, multi_array_iterator(const std::array<buffer_info, N> &buffers,
const std::vector<size_t> &shape) const container_type &shape)
: m_shape(shape.size()), m_index(shape.size(), 0), : m_shape(shape.size()), m_index(shape.size(), 0),
m_common_iterator() { m_common_iterator() {
// Manual copy to avoid conversion warning if using std::copy // Manual copy to avoid conversion warning if using std::copy
for (size_t i = 0; i < shape.size(); ++i) for (size_t i = 0; i < shape.size(); ++i)
m_shape[i] = static_cast<container_type::value_type>(shape[i]); m_shape[i] = shape[i];
std::vector<ssize_t> strides(shape.size()); container_type strides(shape.size());
for (size_t i = 0; i < N; ++i) for (size_t i = 0; i < N; ++i)
init_common_iterator(buffers[i], shape, m_common_iterator[i], strides); init_common_iterator(buffers[i], shape, m_common_iterator[i], strides);
} }
@ -1205,8 +1205,9 @@ private:
using common_iter = common_iterator; using common_iter = common_iterator;
void init_common_iterator(const buffer_info &buffer, void init_common_iterator(const buffer_info &buffer,
const std::vector<size_t> &shape, const container_type &shape,
common_iter &iterator, std::vector<ssize_t> &strides) { common_iter &iterator,
container_type &strides) {
auto buffer_shape_iter = buffer.shape.rbegin(); auto buffer_shape_iter = buffer.shape.rbegin();
auto buffer_strides_iter = buffer.strides.rbegin(); auto buffer_strides_iter = buffer.strides.rbegin();
auto shape_iter = shape.rbegin(); auto shape_iter = shape.rbegin();
@ -1245,13 +1246,13 @@ enum class broadcast_trivial { non_trivial, c_trivial, f_trivial };
// singleton or a full-size, C-contiguous (`c_trivial`) or Fortran-contiguous (`f_trivial`) storage // singleton or a full-size, C-contiguous (`c_trivial`) or Fortran-contiguous (`f_trivial`) storage
// buffer; returns `non_trivial` otherwise. // buffer; returns `non_trivial` otherwise.
template <size_t N> template <size_t N>
broadcast_trivial broadcast(const std::array<buffer_info, N> &buffers, size_t &ndim, std::vector<size_t> &shape) { broadcast_trivial broadcast(const std::array<buffer_info, N> &buffers, ssize_t &ndim, std::vector<ssize_t> &shape) {
ndim = std::accumulate(buffers.begin(), buffers.end(), size_t(0), [](size_t res, const buffer_info& buf) { ndim = std::accumulate(buffers.begin(), buffers.end(), ssize_t(0), [](ssize_t res, const buffer_info& buf) {
return std::max(res, buf.ndim); return std::max(res, buf.ndim);
}); });
shape.clear(); shape.clear();
shape.resize(ndim, 1); shape.resize((size_t) ndim, 1);
// Figure out the output size, and make sure all input arrays conform (i.e. are either size 1 or // Figure out the output size, and make sure all input arrays conform (i.e. are either size 1 or
// the full size). // the full size).
@ -1286,11 +1287,10 @@ broadcast_trivial broadcast(const std::array<buffer_info, N> &buffers, size_t &n
// Check for C contiguity (but only if previous inputs were also C contiguous) // Check for C contiguity (but only if previous inputs were also C contiguous)
if (trivial_broadcast_c) { if (trivial_broadcast_c) {
ssize_t expect_stride = static_cast<ssize_t>(buffers[i].itemsize); ssize_t expect_stride = buffers[i].itemsize;
auto end = buffers[i].shape.crend(); auto end = buffers[i].shape.crend();
auto shape_iter = buffers[i].shape.crbegin(); for (auto shape_iter = buffers[i].shape.crbegin(), stride_iter = buffers[i].strides.crbegin();
auto stride_iter = buffers[i].strides.crbegin(); trivial_broadcast_c && shape_iter != end; ++shape_iter, ++stride_iter) {
for (; trivial_broadcast_c && shape_iter != end; ++shape_iter, ++stride_iter) {
if (expect_stride == *stride_iter) if (expect_stride == *stride_iter)
expect_stride *= *shape_iter; expect_stride *= *shape_iter;
else else
@ -1300,11 +1300,10 @@ broadcast_trivial broadcast(const std::array<buffer_info, N> &buffers, size_t &n
// Check for Fortran contiguity (if previous inputs were also F contiguous) // Check for Fortran contiguity (if previous inputs were also F contiguous)
if (trivial_broadcast_f) { if (trivial_broadcast_f) {
ssize_t expect_stride = static_cast<ssize_t>(buffers[i].itemsize); ssize_t expect_stride = buffers[i].itemsize;
auto end = buffers[i].shape.cend(); auto end = buffers[i].shape.cend();
auto shape_iter = buffers[i].shape.cbegin(); for (auto shape_iter = buffers[i].shape.cbegin(), stride_iter = buffers[i].strides.cbegin();
auto stride_iter = buffers[i].strides.cbegin(); trivial_broadcast_f && shape_iter != end; ++shape_iter, ++stride_iter) {
for (; trivial_broadcast_f && shape_iter != end; ++shape_iter, ++stride_iter) {
if (expect_stride == *stride_iter) if (expect_stride == *stride_iter)
expect_stride *= *shape_iter; expect_stride *= *shape_iter;
else else
@ -1336,25 +1335,26 @@ struct vectorize_helper {
std::array<buffer_info, N> buffers {{ args.request()... }}; std::array<buffer_info, N> buffers {{ args.request()... }};
/* Determine dimensions parameters of output array */ /* Determine dimensions parameters of output array */
size_t ndim = 0; ssize_t nd = 0;
std::vector<size_t> shape(0); std::vector<ssize_t> shape(0);
auto trivial = broadcast(buffers, ndim, shape); auto trivial = broadcast(buffers, nd, shape);
size_t ndim = (size_t) nd;
size_t size = 1; ssize_t size = 1;
std::vector<ssize_t> strides(ndim); std::vector<ssize_t> strides(ndim);
if (ndim > 0) { if (ndim > 0) {
if (trivial == broadcast_trivial::f_trivial) { if (trivial == broadcast_trivial::f_trivial) {
strides[0] = static_cast<ssize_t>(sizeof(Return)); strides[0] = sizeof(Return);
for (size_t i = 1; i < ndim; ++i) { for (size_t i = 1; i < ndim; ++i) {
strides[i] = strides[i - 1] * static_cast<ssize_t>(shape[i - 1]); strides[i] = strides[i - 1] * shape[i - 1];
size *= shape[i - 1]; size *= shape[i - 1];
} }
size *= shape[ndim - 1]; size *= shape[ndim - 1];
} }
else { else {
strides[ndim-1] = static_cast<ssize_t>(sizeof(Return)); strides[ndim-1] = sizeof(Return);
for (size_t i = ndim - 1; i > 0; --i) { for (size_t i = ndim - 1; i > 0; --i) {
strides[i - 1] = strides[i] * static_cast<ssize_t>(shape[i]); strides[i - 1] = strides[i] * shape[i];
size *= shape[i]; size *= shape[i];
} }
size *= shape[0]; size *= shape[0];
@ -1372,7 +1372,7 @@ struct vectorize_helper {
if (trivial == broadcast_trivial::non_trivial) { if (trivial == broadcast_trivial::non_trivial) {
apply_broadcast<Index...>(buffers, buf, index); apply_broadcast<Index...>(buffers, buf, index);
} else { } else {
for (size_t i = 0; i < size; ++i) for (ssize_t i = 0; i < size; ++i)
output[i] = f((reinterpret_cast<Args *>(buffers[Index].ptr)[buffers[Index].size == 1 ? 0 : i])...); output[i] = f((reinterpret_cast<Args *>(buffers[Index].ptr)[buffers[Index].size == 1 ? 0 : i])...);
} }

View File

@ -1165,15 +1165,15 @@ public:
static std::vector<Py_ssize_t> py_strides { }; static std::vector<Py_ssize_t> py_strides { };
static std::vector<Py_ssize_t> py_shape { }; static std::vector<Py_ssize_t> py_shape { };
buf.buf = info.ptr; buf.buf = info.ptr;
buf.itemsize = (Py_ssize_t) info.itemsize; buf.itemsize = info.itemsize;
buf.format = const_cast<char *>(info.format.c_str()); buf.format = const_cast<char *>(info.format.c_str());
buf.ndim = (int) info.ndim; buf.ndim = (int) info.ndim;
buf.len = (Py_ssize_t) info.size; buf.len = info.size;
py_strides.clear(); py_strides.clear();
py_shape.clear(); py_shape.clear();
for (size_t i = 0; i < info.ndim; ++i) { for (size_t i = 0; i < (size_t) info.ndim; ++i) {
py_strides.push_back((Py_ssize_t) info.strides[i]); py_strides.push_back(info.strides[i]);
py_shape.push_back((Py_ssize_t) info.shape[i]); py_shape.push_back(info.shape[i]);
} }
buf.strides = py_strides.data(); buf.strides = py_strides.data();
buf.shape = py_shape.data(); buf.shape = py_shape.data();

View File

@ -345,21 +345,21 @@ vector_buffer(Class_& cl) {
format_descriptor<T>::format(); format_descriptor<T>::format();
cl.def_buffer([](Vector& v) -> buffer_info { cl.def_buffer([](Vector& v) -> buffer_info {
return buffer_info(v.data(), sizeof(T), format_descriptor<T>::format(), 1, {v.size()}, {sizeof(T)}); return buffer_info(v.data(), static_cast<ssize_t>(sizeof(T)), format_descriptor<T>::format(), 1, {v.size()}, {sizeof(T)});
}); });
cl.def("__init__", [](Vector& vec, buffer buf) { cl.def("__init__", [](Vector& vec, buffer buf) {
auto info = buf.request(); auto info = buf.request();
if (info.ndim != 1 || info.strides[0] <= 0 || info.strides[0] % static_cast<ssize_t>(sizeof(T))) if (info.ndim != 1 || info.strides[0] % static_cast<ssize_t>(sizeof(T)))
throw type_error("Only valid 1D buffers can be copied to a vector"); throw type_error("Only valid 1D buffers can be copied to a vector");
if (!detail::compare_buffer_info<T>::compare(info) || sizeof(T) != info.itemsize) if (!detail::compare_buffer_info<T>::compare(info) || (ssize_t) sizeof(T) != info.itemsize)
throw type_error("Format mismatch (Python: " + info.format + " C++: " + format_descriptor<T>::format() + ")"); throw type_error("Format mismatch (Python: " + info.format + " C++: " + format_descriptor<T>::format() + ")");
new (&vec) Vector(); new (&vec) Vector();
vec.reserve(info.shape[0]); vec.reserve((size_t) info.shape[0]);
T *p = static_cast<T*>(info.ptr); T *p = static_cast<T*>(info.ptr);
auto step = info.strides[0] / static_cast<ssize_t>(sizeof(T)); ssize_t step = info.strides[0] / static_cast<ssize_t>(sizeof(T));
T *end = p + static_cast<ssize_t>(info.shape[0]) * step; T *end = p + info.shape[0] * step;
for (; p < end; p += step) for (; p != end; p += step)
vec.push_back(*p); vec.push_back(*p);
}); });

View File

@ -12,16 +12,16 @@
class Matrix { class Matrix {
public: public:
Matrix(size_t rows, size_t cols) : m_rows(rows), m_cols(cols) { Matrix(ssize_t rows, ssize_t cols) : m_rows(rows), m_cols(cols) {
print_created(this, std::to_string(m_rows) + "x" + std::to_string(m_cols) + " matrix"); print_created(this, std::to_string(m_rows) + "x" + std::to_string(m_cols) + " matrix");
m_data = new float[rows*cols]; m_data = new float[(size_t) (rows*cols)];
memset(m_data, 0, sizeof(float) * rows * cols); memset(m_data, 0, sizeof(float) * (size_t) (rows * cols));
} }
Matrix(const Matrix &s) : m_rows(s.m_rows), m_cols(s.m_cols) { Matrix(const Matrix &s) : m_rows(s.m_rows), m_cols(s.m_cols) {
print_copy_created(this, std::to_string(m_rows) + "x" + std::to_string(m_cols) + " matrix"); print_copy_created(this, std::to_string(m_rows) + "x" + std::to_string(m_cols) + " matrix");
m_data = new float[m_rows * m_cols]; m_data = new float[(size_t) (m_rows * m_cols)];
memcpy(m_data, s.m_data, sizeof(float) * m_rows * m_cols); memcpy(m_data, s.m_data, sizeof(float) * (size_t) (m_rows * m_cols));
} }
Matrix(Matrix &&s) : m_rows(s.m_rows), m_cols(s.m_cols), m_data(s.m_data) { Matrix(Matrix &&s) : m_rows(s.m_rows), m_cols(s.m_cols), m_data(s.m_data) {
@ -41,8 +41,8 @@ public:
delete[] m_data; delete[] m_data;
m_rows = s.m_rows; m_rows = s.m_rows;
m_cols = s.m_cols; m_cols = s.m_cols;
m_data = new float[m_rows * m_cols]; m_data = new float[(size_t) (m_rows * m_cols)];
memcpy(m_data, s.m_data, sizeof(float) * m_rows * m_cols); memcpy(m_data, s.m_data, sizeof(float) * (size_t) (m_rows * m_cols));
return *this; return *this;
} }
@ -56,47 +56,47 @@ public:
return *this; return *this;
} }
float operator()(size_t i, size_t j) const { float operator()(ssize_t i, ssize_t j) const {
return m_data[i*m_cols + j]; return m_data[(size_t) (i*m_cols + j)];
} }
float &operator()(size_t i, size_t j) { float &operator()(ssize_t i, ssize_t j) {
return m_data[i*m_cols + j]; return m_data[(size_t) (i*m_cols + j)];
} }
float *data() { return m_data; } float *data() { return m_data; }
size_t rows() const { return m_rows; } ssize_t rows() const { return m_rows; }
size_t cols() const { return m_cols; } ssize_t cols() const { return m_cols; }
private: private:
size_t m_rows; ssize_t m_rows;
size_t m_cols; ssize_t m_cols;
float *m_data; float *m_data;
}; };
test_initializer buffers([](py::module &m) { test_initializer buffers([](py::module &m) {
py::class_<Matrix> mtx(m, "Matrix", py::buffer_protocol()); py::class_<Matrix> mtx(m, "Matrix", py::buffer_protocol());
mtx.def(py::init<size_t, size_t>()) mtx.def(py::init<ssize_t, ssize_t>())
/// Construct from a buffer /// Construct from a buffer
.def("__init__", [](Matrix &v, py::buffer b) { .def("__init__", [](Matrix &v, py::buffer b) {
py::buffer_info info = b.request(); py::buffer_info info = b.request();
if (info.format != py::format_descriptor<float>::format() || info.ndim != 2) if (info.format != py::format_descriptor<float>::format() || info.ndim != 2)
throw std::runtime_error("Incompatible buffer format!"); throw std::runtime_error("Incompatible buffer format!");
new (&v) Matrix(info.shape[0], info.shape[1]); new (&v) Matrix(info.shape[0], info.shape[1]);
memcpy(v.data(), info.ptr, sizeof(float) * v.rows() * v.cols()); memcpy(v.data(), info.ptr, sizeof(float) * (size_t) (v.rows() * v.cols()));
}) })
.def("rows", &Matrix::rows) .def("rows", &Matrix::rows)
.def("cols", &Matrix::cols) .def("cols", &Matrix::cols)
/// Bare bones interface /// Bare bones interface
.def("__getitem__", [](const Matrix &m, std::pair<size_t, size_t> i) { .def("__getitem__", [](const Matrix &m, std::pair<ssize_t, ssize_t> i) {
if (i.first >= m.rows() || i.second >= m.cols()) if (i.first >= m.rows() || i.second >= m.cols())
throw py::index_error(); throw py::index_error();
return m(i.first, i.second); return m(i.first, i.second);
}) })
.def("__setitem__", [](Matrix &m, std::pair<size_t, size_t> i, float v) { .def("__setitem__", [](Matrix &m, std::pair<ssize_t, ssize_t> i, float v) {
if (i.first >= m.rows() || i.second >= m.cols()) if (i.first >= m.rows() || i.second >= m.cols())
throw py::index_error(); throw py::index_error();
m(i.first, i.second) = v; m(i.first, i.second) = v;
@ -109,8 +109,8 @@ test_initializer buffers([](py::module &m) {
py::format_descriptor<float>::format(), /* Python struct-style format descriptor */ py::format_descriptor<float>::format(), /* Python struct-style format descriptor */
2, /* Number of dimensions */ 2, /* Number of dimensions */
{ m.rows(), m.cols() }, /* Buffer dimensions */ { m.rows(), m.cols() }, /* Buffer dimensions */
{ static_cast<ssize_t>(sizeof(float) * m.rows()), /* Strides (in bytes) for each index */ { sizeof(float) * size_t(m.rows()), /* Strides (in bytes) for each index */
static_cast<ssize_t>(sizeof(float)) } sizeof(float) }
); );
}) })
; ;

View File

@ -186,7 +186,7 @@ def test_negative_stride_from_python(msg):
double_threer(second_row) double_threer(second_row)
assert msg(excinfo.value) == """ assert msg(excinfo.value) == """
double_threer(): incompatible function arguments. The following argument types are supported: double_threer(): incompatible function arguments. The following argument types are supported:
1. (numpy.ndarray[float32[1, 3], flags.writeable]) -> arg0: None 1. (arg0: numpy.ndarray[float32[1, 3], flags.writeable]) -> None
Invoked with: array([ 5., 4., 3.], dtype=float32) Invoked with: array([ 5., 4., 3.], dtype=float32)
""" """
@ -195,7 +195,7 @@ def test_negative_stride_from_python(msg):
double_threec(second_col) double_threec(second_col)
assert msg(excinfo.value) == """ assert msg(excinfo.value) == """
double_threec(): incompatible function arguments. The following argument types are supported: double_threec(): incompatible function arguments. The following argument types are supported:
1. (numpy.ndarray[float32[3, 1], flags.writeable]) -> arg0: None 1. (arg0: numpy.ndarray[float32[3, 1], flags.writeable]) -> None
Invoked with: array([ 7., 4., 1.], dtype=float32) Invoked with: array([ 7., 4., 1.], dtype=float32)
""" """

View File

@ -13,53 +13,52 @@
#include <pybind11/stl.h> #include <pybind11/stl.h>
#include <cstdint> #include <cstdint>
#include <vector>
using arr = py::array; using arr = py::array;
using arr_t = py::array_t<uint16_t, 0>; using arr_t = py::array_t<uint16_t, 0>;
static_assert(std::is_same<arr_t::value_type, uint16_t>::value, ""); static_assert(std::is_same<arr_t::value_type, uint16_t>::value, "");
template<typename... Ix> arr data(const arr& a, Ix... index) { template<typename... Ix> arr data(const arr& a, Ix... index) {
return arr(a.nbytes() - size_t(a.offset_at(index...)), (const uint8_t *) a.data(index...)); return arr(a.nbytes() - a.offset_at(index...), (const uint8_t *) a.data(index...));
} }
template<typename... Ix> arr data_t(const arr_t& a, Ix... index) { template<typename... Ix> arr data_t(const arr_t& a, Ix... index) {
return arr(a.size() - size_t(a.index_at(index...)), a.data(index...)); return arr(a.size() - a.index_at(index...), a.data(index...));
} }
arr& mutate_data(arr& a) { arr& mutate_data(arr& a) {
auto ptr = (uint8_t *) a.mutable_data(); auto ptr = (uint8_t *) a.mutable_data();
for (size_t i = 0; i < a.nbytes(); i++) for (ssize_t i = 0; i < a.nbytes(); i++)
ptr[i] = (uint8_t) (ptr[i] * 2); ptr[i] = (uint8_t) (ptr[i] * 2);
return a; return a;
} }
arr_t& mutate_data_t(arr_t& a) { arr_t& mutate_data_t(arr_t& a) {
auto ptr = a.mutable_data(); auto ptr = a.mutable_data();
for (size_t i = 0; i < a.size(); i++) for (ssize_t i = 0; i < a.size(); i++)
ptr[i]++; ptr[i]++;
return a; return a;
} }
template<typename... Ix> arr& mutate_data(arr& a, Ix... index) { template<typename... Ix> arr& mutate_data(arr& a, Ix... index) {
auto ptr = (uint8_t *) a.mutable_data(index...); auto ptr = (uint8_t *) a.mutable_data(index...);
for (size_t i = 0; i < a.nbytes() - size_t(a.offset_at(index...)); i++) for (ssize_t i = 0; i < a.nbytes() - a.offset_at(index...); i++)
ptr[i] = (uint8_t) (ptr[i] * 2); ptr[i] = (uint8_t) (ptr[i] * 2);
return a; return a;
} }
template<typename... Ix> arr_t& mutate_data_t(arr_t& a, Ix... index) { template<typename... Ix> arr_t& mutate_data_t(arr_t& a, Ix... index) {
auto ptr = a.mutable_data(index...); auto ptr = a.mutable_data(index...);
for (size_t i = 0; i < a.size() - size_t(a.index_at(index...)); i++) for (ssize_t i = 0; i < a.size() - a.index_at(index...); i++)
ptr[i]++; ptr[i]++;
return a; return a;
} }
template<typename... Ix> py::ssize_t index_at(const arr& a, Ix... idx) { return a.index_at(idx...); } template<typename... Ix> ssize_t index_at(const arr& a, Ix... idx) { return a.index_at(idx...); }
template<typename... Ix> py::ssize_t index_at_t(const arr_t& a, Ix... idx) { return a.index_at(idx...); } template<typename... Ix> ssize_t index_at_t(const arr_t& a, Ix... idx) { return a.index_at(idx...); }
template<typename... Ix> py::ssize_t offset_at(const arr& a, Ix... idx) { return a.offset_at(idx...); } template<typename... Ix> ssize_t offset_at(const arr& a, Ix... idx) { return a.offset_at(idx...); }
template<typename... Ix> py::ssize_t offset_at_t(const arr_t& a, Ix... idx) { return a.offset_at(idx...); } template<typename... Ix> ssize_t offset_at_t(const arr_t& a, Ix... idx) { return a.offset_at(idx...); }
template<typename... Ix> py::ssize_t at_t(const arr_t& a, Ix... idx) { return a.at(idx...); } template<typename... Ix> ssize_t at_t(const arr_t& a, Ix... idx) { return a.at(idx...); }
template<typename... Ix> arr_t& mutate_at_t(arr_t& a, Ix... idx) { a.mutable_at(idx...)++; return a; } template<typename... Ix> arr_t& mutate_at_t(arr_t& a, Ix... idx) { a.mutable_at(idx...)++; return a; }
#define def_index_fn(name, type) \ #define def_index_fn(name, type) \
@ -88,9 +87,9 @@ test_initializer numpy_array([](py::module &m) {
sm.def("ndim", [](const arr& a) { return a.ndim(); }); sm.def("ndim", [](const arr& a) { return a.ndim(); });
sm.def("shape", [](const arr& a) { return arr(a.ndim(), a.shape()); }); sm.def("shape", [](const arr& a) { return arr(a.ndim(), a.shape()); });
sm.def("shape", [](const arr& a, size_t dim) { return a.shape(dim); }); sm.def("shape", [](const arr& a, ssize_t dim) { return a.shape(dim); });
sm.def("strides", [](const arr& a) { return arr(a.ndim(), a.strides()); }); sm.def("strides", [](const arr& a) { return arr(a.ndim(), a.strides()); });
sm.def("strides", [](const arr& a, size_t dim) { return a.strides(dim); }); sm.def("strides", [](const arr& a, ssize_t dim) { return a.strides(dim); });
sm.def("writeable", [](const arr& a) { return a.writeable(); }); sm.def("writeable", [](const arr& a) { return a.writeable(); });
sm.def("size", [](const arr& a) { return a.size(); }); sm.def("size", [](const arr& a) { return a.size(); });
sm.def("itemsize", [](const arr& a) { return a.itemsize(); }); sm.def("itemsize", [](const arr& a) { return a.itemsize(); });
@ -202,33 +201,33 @@ test_initializer numpy_array([](py::module &m) {
sm.def("proxy_add2", [](py::array_t<double> a, double v) { sm.def("proxy_add2", [](py::array_t<double> a, double v) {
auto r = a.mutable_unchecked<2>(); auto r = a.mutable_unchecked<2>();
for (size_t i = 0; i < r.shape(0); i++) for (ssize_t i = 0; i < r.shape(0); i++)
for (size_t j = 0; j < r.shape(1); j++) for (ssize_t j = 0; j < r.shape(1); j++)
r(i, j) += v; r(i, j) += v;
}, py::arg().noconvert(), py::arg()); }, py::arg().noconvert(), py::arg());
sm.def("proxy_init3", [](double start) { sm.def("proxy_init3", [](double start) {
py::array_t<double, py::array::c_style> a({ 3, 3, 3 }); py::array_t<double, py::array::c_style> a({ 3, 3, 3 });
auto r = a.mutable_unchecked<3>(); auto r = a.mutable_unchecked<3>();
for (size_t i = 0; i < r.shape(0); i++) for (ssize_t i = 0; i < r.shape(0); i++)
for (size_t j = 0; j < r.shape(1); j++) for (ssize_t j = 0; j < r.shape(1); j++)
for (size_t k = 0; k < r.shape(2); k++) for (ssize_t k = 0; k < r.shape(2); k++)
r(i, j, k) = start++; r(i, j, k) = start++;
return a; return a;
}); });
sm.def("proxy_init3F", [](double start) { sm.def("proxy_init3F", [](double start) {
py::array_t<double, py::array::f_style> a({ 3, 3, 3 }); py::array_t<double, py::array::f_style> a({ 3, 3, 3 });
auto r = a.mutable_unchecked<3>(); auto r = a.mutable_unchecked<3>();
for (size_t k = 0; k < r.shape(2); k++) for (ssize_t k = 0; k < r.shape(2); k++)
for (size_t j = 0; j < r.shape(1); j++) for (ssize_t j = 0; j < r.shape(1); j++)
for (size_t i = 0; i < r.shape(0); i++) for (ssize_t i = 0; i < r.shape(0); i++)
r(i, j, k) = start++; r(i, j, k) = start++;
return a; return a;
}); });
sm.def("proxy_squared_L2_norm", [](py::array_t<double> a) { sm.def("proxy_squared_L2_norm", [](py::array_t<double> a) {
auto r = a.unchecked<1>(); auto r = a.unchecked<1>();
double sumsq = 0; double sumsq = 0;
for (size_t i = 0; i < r.shape(0); i++) for (ssize_t i = 0; i < r.shape(0); i++)
sumsq += r[i] * r(i); // Either notation works for a 1D array sumsq += r[i] * r(i); // Either notation works for a 1D array
return sumsq; return sumsq;
}); });
@ -243,17 +242,17 @@ test_initializer numpy_array([](py::module &m) {
sm.def("proxy_add2_dyn", [](py::array_t<double> a, double v) { sm.def("proxy_add2_dyn", [](py::array_t<double> a, double v) {
auto r = a.mutable_unchecked(); auto r = a.mutable_unchecked();
if (r.ndim() != 2) throw std::domain_error("error: ndim != 2"); if (r.ndim() != 2) throw std::domain_error("error: ndim != 2");
for (size_t i = 0; i < r.shape(0); i++) for (ssize_t i = 0; i < r.shape(0); i++)
for (size_t j = 0; j < r.shape(1); j++) for (ssize_t j = 0; j < r.shape(1); j++)
r(i, j) += v; r(i, j) += v;
}, py::arg().noconvert(), py::arg()); }, py::arg().noconvert(), py::arg());
sm.def("proxy_init3_dyn", [](double start) { sm.def("proxy_init3_dyn", [](double start) {
py::array_t<double, py::array::c_style> a({ 3, 3, 3 }); py::array_t<double, py::array::c_style> a({ 3, 3, 3 });
auto r = a.mutable_unchecked(); auto r = a.mutable_unchecked();
if (r.ndim() != 3) throw std::domain_error("error: ndim != 3"); if (r.ndim() != 3) throw std::domain_error("error: ndim != 3");
for (size_t i = 0; i < r.shape(0); i++) for (ssize_t i = 0; i < r.shape(0); i++)
for (size_t j = 0; j < r.shape(1); j++) for (ssize_t j = 0; j < r.shape(1); j++)
for (size_t k = 0; k < r.shape(2); k++) for (ssize_t k = 0; k < r.shape(2); k++)
r(i, j, k) = start++; r(i, j, k) = start++;
return a; return a;
}); });
@ -269,6 +268,9 @@ test_initializer numpy_array([](py::module &m) {
sm.def("array_fail_test", []() { return py::array(py::object()); }); sm.def("array_fail_test", []() { return py::array(py::object()); });
sm.def("array_t_fail_test", []() { return py::array_t<double>(py::object()); }); sm.def("array_t_fail_test", []() { return py::array_t<double>(py::object()); });
// Make sure the error from numpy is being passed through:
sm.def("array_fail_test_negative_size", []() { int c = 0; return py::array(-1, &c); });
// Issue (unnumbered; reported in #788): regression: initializer lists can be ambiguous // Issue (unnumbered; reported in #788): regression: initializer lists can be ambiguous
sm.def("array_initializer_list", []() { return py::array_t<float>(1); }); // { 1 } also works, but clang warns about it sm.def("array_initializer_list", []() { return py::array_t<float>(1); }); // { 1 } also works, but clang warns about it
sm.def("array_initializer_list", []() { return py::array_t<float>({ 1, 2 }); }); sm.def("array_initializer_list", []() { return py::array_t<float>({ 1, 2 }); });
@ -277,7 +279,7 @@ test_initializer numpy_array([](py::module &m) {
// reshape array to 2D without changing size // reshape array to 2D without changing size
sm.def("array_reshape2", [](py::array_t<double> a) { sm.def("array_reshape2", [](py::array_t<double> a) {
const size_t dim_sz = (size_t)std::sqrt(a.size()); const ssize_t dim_sz = (ssize_t)std::sqrt(a.size());
if (dim_sz * dim_sz != a.size()) if (dim_sz * dim_sz != a.size())
throw std::domain_error("array_reshape2: input array total size is not a squared integer"); throw std::domain_error("array_reshape2: input array total size is not a squared integer");
a.resize({dim_sz, dim_sz}); a.resize({dim_sz, dim_sz});

View File

@ -384,7 +384,8 @@ def test_array_unchecked_dyn_dims(msg):
def test_array_failure(): def test_array_failure():
from pybind11_tests.array import array_fail_test, array_t_fail_test from pybind11_tests.array import (array_fail_test, array_t_fail_test,
array_fail_test_negative_size)
with pytest.raises(ValueError) as excinfo: with pytest.raises(ValueError) as excinfo:
array_fail_test() array_fail_test()
@ -394,6 +395,10 @@ def test_array_failure():
array_t_fail_test() array_t_fail_test()
assert str(excinfo.value) == 'cannot create a pybind11::array_t from a nullptr' assert str(excinfo.value) == 'cannot create a pybind11::array_t from a nullptr'
with pytest.raises(ValueError) as excinfo:
array_fail_test_negative_size()
assert str(excinfo.value) == 'negative dimensions are not allowed'
def test_array_resize(msg): def test_array_resize(msg):
from pybind11_tests.array import (array_reshape2, array_resize3) from pybind11_tests.array import (array_reshape2, array_resize3)

View File

@ -149,7 +149,7 @@ py::array_t<StringStruct, 0> create_string_array(bool non_empty) {
if (non_empty) { if (non_empty) {
auto req = arr.request(); auto req = arr.request();
auto ptr = static_cast<StringStruct*>(req.ptr); auto ptr = static_cast<StringStruct*>(req.ptr);
for (size_t i = 0; i < req.size * req.itemsize; i++) for (ssize_t i = 0; i < req.size * req.itemsize; i++)
static_cast<char*>(req.ptr)[i] = 0; static_cast<char*>(req.ptr)[i] = 0;
ptr[1].a[0] = 'a'; ptr[1].b[0] = 'a'; ptr[1].a[0] = 'a'; ptr[1].b[0] = 'a';
ptr[2].a[0] = 'a'; ptr[2].b[0] = 'a'; ptr[2].a[0] = 'a'; ptr[2].b[0] = 'a';
@ -178,7 +178,7 @@ py::list print_recarray(py::array_t<S, 0> arr) {
const auto req = arr.request(); const auto req = arr.request();
const auto ptr = static_cast<S*>(req.ptr); const auto ptr = static_cast<S*>(req.ptr);
auto l = py::list(); auto l = py::list();
for (size_t i = 0; i < req.size; i++) { for (ssize_t i = 0; i < req.size; i++) {
std::stringstream ss; std::stringstream ss;
ss << ptr[i]; ss << ptr[i];
l.append(py::str(ss.str())); l.append(py::str(ss.str()));
@ -225,7 +225,7 @@ py::array_t<int32_t, 0> test_array_ctors(int i) {
using arr_t = py::array_t<int32_t, 0>; using arr_t = py::array_t<int32_t, 0>;
std::vector<int32_t> data { 1, 2, 3, 4, 5, 6 }; std::vector<int32_t> data { 1, 2, 3, 4, 5, 6 };
std::vector<size_t> shape { 3, 2 }; std::vector<ssize_t> shape { 3, 2 };
std::vector<ssize_t> strides { 8, 4 }; std::vector<ssize_t> strides { 8, 4 };
auto ptr = data.data(); auto ptr = data.data();

View File

@ -50,8 +50,8 @@ test_initializer numpy_vectorize([](py::module &m) {
py::array_t<float, py::array::forcecast> arg2, py::array_t<float, py::array::forcecast> arg2,
py::array_t<double, py::array::forcecast> arg3 py::array_t<double, py::array::forcecast> arg3
) { ) {
size_t ndim; ssize_t ndim;
std::vector<size_t> shape; std::vector<ssize_t> shape;
std::array<py::buffer_info, 3> buffers {{ arg1.request(), arg2.request(), arg3.request() }}; std::array<py::buffer_info, 3> buffers {{ arg1.request(), arg2.request(), arg3.request() }};
return py::detail::broadcast(buffers, ndim, shape); return py::detail::broadcast(buffers, ndim, shape);
}); });