Merge pull request #308 from aldanor/recarray

py::dtype, buffer protocol improvements, structured types support
This commit is contained in:
Wenzel Jakob 2016-08-13 22:21:46 +02:00 committed by GitHub
commit 3c3533b4bc
19 changed files with 1081 additions and 189 deletions

3
.gitignore vendored
View File

@ -3,7 +3,7 @@ CMakeFiles
Makefile Makefile
cmake_install.cmake cmake_install.cmake
.DS_Store .DS_Store
/example/example.so /example/example*.so
/example/example.cpython*.so /example/example.cpython*.so
/example/example.pyd /example/example.pyd
/example/example*.dll /example/example*.dll
@ -31,3 +31,4 @@ MANIFEST
.DS_Store .DS_Store
/dist /dist
/build /build
/cmake/

View File

@ -106,8 +106,9 @@ Tomasz Miąsko,
Dean Moldovan, Dean Moldovan,
Ben Pritchard, Ben Pritchard,
Jason Rhinelander, Jason Rhinelander,
Boris Schäling, and Boris Schäling,
Pim Schellart. Pim Schellart,
Ivan Smirnov.
### License ### License

View File

@ -1224,12 +1224,12 @@ completely avoid copy operations with Python expressions like
py::class_<Matrix>(m, "Matrix") py::class_<Matrix>(m, "Matrix")
.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(float), /* Size of one scalar */ sizeof(float), /* Size of one scalar */
py::format_descriptor<float>::value, /* 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 */
{ sizeof(float) * m.rows(), /* Strides (in bytes) for each index */ { sizeof(float) * m.rows(), /* Strides (in bytes) for each index */
sizeof(float) } sizeof(float) }
); );
}); });
@ -1273,7 +1273,7 @@ buffer objects (e.g. a NumPy matrix).
py::buffer_info info = b.request(); py::buffer_info info = b.request();
/* Some sanity checks ... */ /* Some sanity checks ... */
if (info.format != py::format_descriptor<Scalar>::value) if (info.format != py::format_descriptor<Scalar>::format())
throw std::runtime_error("Incompatible format: expected a double array!"); throw std::runtime_error("Incompatible format: expected a double array!");
if (info.ndim != 2) if (info.ndim != 2)
@ -1299,7 +1299,7 @@ as follows:
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 */ /* Python struct-style format descriptor */
py::format_descriptor<Scalar>::value, py::format_descriptor<Scalar>::format(),
/* Number of dimensions */ /* Number of dimensions */
2, 2,
/* Buffer dimensions */ /* Buffer dimensions */
@ -1358,6 +1358,30 @@ template paramenter, and it ensures that non-conforming arguments are converted
into an array satisfying the specified requirements instead of trying the next into an array satisfying the specified requirements instead of trying the next
function overload. function overload.
NumPy structured types
======================
In order for ``py::array_t`` to work with structured (record) types, we first need
to register the memory layout of the type. This can be done via ``PYBIND11_NUMPY_DTYPE``
macro which expects the type followed by field names:
.. code-block:: cpp
struct A {
int x;
double y;
};
struct B {
int z;
A a;
};
PYBIND11_NUMPY_DTYPE(A, x, y);
PYBIND11_NUMPY_DTYPE(B, z, a);
/* now both A and B can be used as template arguments to py::array_t */
Vectorizing functions Vectorizing functions
===================== =====================
@ -1433,17 +1457,11 @@ simply using ``vectorize``).
if (buf1.ndim != 1 || buf2.ndim != 1) if (buf1.ndim != 1 || buf2.ndim != 1)
throw std::runtime_error("Number of dimensions must be one"); throw std::runtime_error("Number of dimensions must be one");
if (buf1.shape[0] != buf2.shape[0]) if (buf1.size != buf2.size)
throw std::runtime_error("Input shapes must match"); throw std::runtime_error("Input shapes must match");
auto result = py::array(py::buffer_info( /* No pointer is passed, so NumPy will allocate the buffer */
nullptr, /* Pointer to data (nullptr -> ask NumPy to allocate!) */ auto result = py::array_t<double>(buf1.size);
sizeof(double), /* Size of one item */
py::format_descriptor<double>::value, /* Buffer format */
buf1.ndim, /* How many dimensions? */
{ buf1.shape[0] }, /* Number of elements for each dimension */
{ sizeof(double) } /* Strides for each dimension */
));
auto buf3 = result.request(); auto buf3 = result.request();
@ -1830,4 +1848,3 @@ is always ``none``).
// Evaluate the statements in an separate Python file on disk // Evaluate the statements in an separate Python file on disk
py::eval_file("script.py", scope); py::eval_file("script.py", scope);

View File

@ -27,6 +27,23 @@ Breaking changes queued for v2.0.0 (Not yet released)
* ``make_iterator()`` improvements for better compatibility with various types * ``make_iterator()`` improvements for better compatibility with various types
(now uses prefix increment operator) (now uses prefix increment operator)
* ``arg()`` now accepts a wider range of argument types for default values * ``arg()`` now accepts a wider range of argument types for default values
* Added support for registering structured dtypes via ``PYBIND11_NUMPY_DTYPE()`` macro.
* Added ``PYBIND11_STR_TYPE`` macro which maps to the ``builtins.str`` type.
* Added a simplified ``buffer_info`` constructor for 1-dimensional buffers.
* Format descriptor strings should now be accessed via ``format_descriptor::format()``
(for compatibility purposes, the old syntax ``format_descriptor::value`` will still
work for non-structured data types).
* Added a class wrapping NumPy array descriptors: ``dtype``.
* Added buffer/NumPy support for ``char[N]`` and ``std::array<char, N>`` types.
* ``array`` gained new constructors accepting dtype objects.
* Added constructors for ``array`` and ``array_t`` explicitly accepting shape and
strides; if strides are not provided, they are deduced assuming C-contiguity.
Also added simplified constructors for 1-dimensional case.
* Added constructors for ``str`` from ``bytes`` and for ``bytes`` from ``str``.
This will do the UTF-8 decoding/encoding as required.
* Added constructors for ``str`` and ``bytes`` from zero-terminated char pointers,
and from char pointers and length.
* Added ``memoryview`` wrapper type which is constructible from ``buffer_info``.
* Various minor improvements of library internals (no user-visible changes) * Various minor improvements of library internals (no user-visible changes)
1.8.1 (July 12, 2016) 1.8.1 (July 12, 2016)

View File

@ -26,6 +26,7 @@ set(PYBIND11_EXAMPLES
example-stl-binder-vector.cpp example-stl-binder-vector.cpp
example-eval.cpp example-eval.cpp
example-custom-exceptions.cpp example-custom-exceptions.cpp
example-numpy-dtypes.cpp
issues.cpp issues.cpp
) )
@ -65,4 +66,3 @@ foreach(VALUE ${PYBIND11_EXAMPLES})
string(REGEX REPLACE "^(.+).cpp$" "\\1" EXAMPLE_NAME "${VALUE}") string(REGEX REPLACE "^(.+).cpp$" "\\1" EXAMPLE_NAME "${VALUE}")
add_test(NAME ${EXAMPLE_NAME} COMMAND ${RUN_TEST} ${EXAMPLE_NAME}) add_test(NAME ${EXAMPLE_NAME} COMMAND ${RUN_TEST} ${EXAMPLE_NAME})
endforeach() endforeach()

View File

@ -81,7 +81,7 @@ void init_ex_buffers(py::module &m) {
/// 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>::value || 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) * v.rows() * v.cols());
@ -104,12 +104,12 @@ void init_ex_buffers(py::module &m) {
/// Provide buffer access /// Provide buffer access
.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(float), /* Size of one scalar */ sizeof(float), /* Size of one scalar */
py::format_descriptor<float>::value, /* 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 */
{ sizeof(float) * m.rows(), /* Strides (in bytes) for each index */ { sizeof(float) * m.rows(), /* Strides (in bytes) for each index */
sizeof(float) } sizeof(float) }
); );
}) })

View File

@ -0,0 +1,280 @@
/*
example/example-numpy-dtypes.cpp -- Structured and compound NumPy dtypes
Copyright (c) 2016 Ivan Smirnov
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE file.
*/
#include "example.h"
#include <pybind11/numpy.h>
#include <cstdint>
#include <iostream>
#ifdef __GNUC__
#define PYBIND11_PACKED(cls) cls __attribute__((__packed__))
#else
#define PYBIND11_PACKED(cls) __pragma(pack(push, 1)) cls __pragma(pack(pop))
#endif
namespace py = pybind11;
struct SimpleStruct {
bool x;
uint32_t y;
float z;
};
std::ostream& operator<<(std::ostream& os, const SimpleStruct& v) {
return os << "s:" << v.x << "," << v.y << "," << v.z;
}
PYBIND11_PACKED(struct PackedStruct {
bool x;
uint32_t y;
float z;
});
std::ostream& operator<<(std::ostream& os, const PackedStruct& v) {
return os << "p:" << v.x << "," << v.y << "," << v.z;
}
PYBIND11_PACKED(struct NestedStruct {
SimpleStruct a;
PackedStruct b;
});
std::ostream& operator<<(std::ostream& os, const NestedStruct& v) {
return os << "n:a=" << v.a << ";b=" << v.b;
}
struct PartialStruct {
bool x;
uint32_t y;
float z;
uint64_t dummy2;
};
struct PartialNestedStruct {
uint64_t dummy1;
PartialStruct a;
uint64_t dummy2;
};
struct UnboundStruct { };
struct StringStruct {
char a[3];
std::array<char, 3> b;
};
std::ostream& operator<<(std::ostream& os, const StringStruct& v) {
os << "a='";
for (size_t i = 0; i < 3 && v.a[i]; i++) os << v.a[i];
os << "',b='";
for (size_t i = 0; i < 3 && v.b[i]; i++) os << v.b[i];
return os << "'";
}
template <typename T>
py::array mkarray_via_buffer(size_t n) {
return py::array(py::buffer_info(nullptr, sizeof(T),
py::format_descriptor<T>::format(),
1, { n }, { sizeof(T) }));
}
template <typename S>
py::array_t<S, 0> create_recarray(size_t n) {
auto arr = mkarray_via_buffer<S>(n);
auto req = arr.request();
auto ptr = static_cast<S*>(req.ptr);
for (size_t i = 0; i < n; i++) {
ptr[i].x = i % 2 != 0; ptr[i].y = (uint32_t) i; ptr[i].z = (float) i * 1.5f;
}
return arr;
}
std::string get_format_unbound() {
return py::format_descriptor<UnboundStruct>::format();
}
py::array_t<NestedStruct, 0> create_nested(size_t n) {
auto arr = mkarray_via_buffer<NestedStruct>(n);
auto req = arr.request();
auto ptr = static_cast<NestedStruct*>(req.ptr);
for (size_t i = 0; i < n; i++) {
ptr[i].a.x = i % 2 != 0; ptr[i].a.y = (uint32_t) i; ptr[i].a.z = (float) i * 1.5f;
ptr[i].b.x = (i + 1) % 2 != 0; ptr[i].b.y = (uint32_t) (i + 1); ptr[i].b.z = (float) (i + 1) * 1.5f;
}
return arr;
}
py::array_t<PartialNestedStruct, 0> create_partial_nested(size_t n) {
auto arr = mkarray_via_buffer<PartialNestedStruct>(n);
auto req = arr.request();
auto ptr = static_cast<PartialNestedStruct*>(req.ptr);
for (size_t i = 0; i < n; i++) {
ptr[i].a.x = i % 2 != 0; ptr[i].a.y = (uint32_t) i; ptr[i].a.z = (float) i * 1.5f;
}
return arr;
}
py::array_t<StringStruct, 0> create_string_array(bool non_empty) {
auto arr = mkarray_via_buffer<StringStruct>(non_empty ? 4 : 0);
if (non_empty) {
auto req = arr.request();
auto ptr = static_cast<StringStruct*>(req.ptr);
for (size_t i = 0; i < req.size * req.itemsize; i++)
static_cast<char*>(req.ptr)[i] = 0;
ptr[1].a[0] = 'a'; ptr[1].b[0] = 'a';
ptr[2].a[0] = 'a'; ptr[2].b[0] = 'a';
ptr[3].a[0] = 'a'; ptr[3].b[0] = 'a';
ptr[2].a[1] = 'b'; ptr[2].b[1] = 'b';
ptr[3].a[1] = 'b'; ptr[3].b[1] = 'b';
ptr[3].a[2] = 'c'; ptr[3].b[2] = 'c';
}
return arr;
}
template <typename S>
void print_recarray(py::array_t<S, 0> arr) {
auto req = arr.request();
auto ptr = static_cast<S*>(req.ptr);
for (size_t i = 0; i < req.size; i++)
std::cout << ptr[i] << std::endl;
}
void print_format_descriptors() {
std::cout << py::format_descriptor<SimpleStruct>::format() << std::endl;
std::cout << py::format_descriptor<PackedStruct>::format() << std::endl;
std::cout << py::format_descriptor<NestedStruct>::format() << std::endl;
std::cout << py::format_descriptor<PartialStruct>::format() << std::endl;
std::cout << py::format_descriptor<PartialNestedStruct>::format() << std::endl;
std::cout << py::format_descriptor<StringStruct>::format() << std::endl;
}
void print_dtypes() {
std::cout << (std::string) py::dtype::of<SimpleStruct>().str() << std::endl;
std::cout << (std::string) py::dtype::of<PackedStruct>().str() << std::endl;
std::cout << (std::string) py::dtype::of<NestedStruct>().str() << std::endl;
std::cout << (std::string) py::dtype::of<PartialStruct>().str() << std::endl;
std::cout << (std::string) py::dtype::of<PartialNestedStruct>().str() << std::endl;
std::cout << (std::string) py::dtype::of<StringStruct>().str() << std::endl;
}
py::array_t<int32_t, 0> test_array_ctors(int i) {
using arr_t = py::array_t<int32_t, 0>;
std::vector<int32_t> data { 1, 2, 3, 4, 5, 6 };
std::vector<size_t> shape { 3, 2 };
std::vector<size_t> strides { 8, 4 };
auto ptr = data.data();
auto vptr = (void *) ptr;
auto dtype = py::dtype("int32");
py::buffer_info buf_ndim1(vptr, 4, "i", 6);
py::buffer_info buf_ndim1_null(nullptr, 4, "i", 6);
py::buffer_info buf_ndim2(vptr, 4, "i", 2, shape, strides);
py::buffer_info buf_ndim2_null(nullptr, 4, "i", 2, shape, strides);
auto fill = [](py::array arr) {
auto req = arr.request();
for (int i = 0; i < 6; i++) ((int32_t *) req.ptr)[i] = i + 1;
return arr;
};
switch (i) {
// shape: (3, 2)
case 10: return arr_t(shape, strides, ptr);
case 11: return py::array(shape, strides, ptr);
case 12: return py::array(dtype, shape, strides, vptr);
case 13: return arr_t(shape, ptr);
case 14: return py::array(shape, ptr);
case 15: return py::array(dtype, shape, vptr);
case 16: return arr_t(buf_ndim2);
case 17: return py::array(buf_ndim2);
// shape: (3, 2) - post-fill
case 20: return fill(arr_t(shape, strides));
case 21: return py::array(shape, strides, ptr); // can't have nullptr due to templated ctor
case 22: return fill(py::array(dtype, shape, strides));
case 23: return fill(arr_t(shape));
case 24: return py::array(shape, ptr); // can't have nullptr due to templated ctor
case 25: return fill(py::array(dtype, shape));
case 26: return fill(arr_t(buf_ndim2_null));
case 27: return fill(py::array(buf_ndim2_null));
// shape: (6, )
case 30: return arr_t(6, ptr);
case 31: return py::array(6, ptr);
case 32: return py::array(dtype, 6, vptr);
case 33: return arr_t(buf_ndim1);
case 34: return py::array(buf_ndim1);
// shape: (6, )
case 40: return fill(arr_t(6));
case 41: return py::array(6, ptr); // can't have nullptr due to templated ctor
case 42: return fill(py::array(dtype, 6));
case 43: return fill(arr_t(buf_ndim1_null));
case 44: return fill(py::array(buf_ndim1_null));
}
return arr_t();
}
py::list test_dtype_ctors() {
py::list list;
list.append(py::dtype("int32"));
list.append(py::dtype(std::string("float64")));
list.append(py::dtype::from_args(py::str("bool")));
py::list names, offsets, formats;
py::dict dict;
names.append(py::str("a")); names.append(py::str("b")); dict["names"] = names;
offsets.append(py::int_(1)); offsets.append(py::int_(10)); dict["offsets"] = offsets;
formats.append(py::dtype("int32")); formats.append(py::dtype("float64")); dict["formats"] = formats;
dict["itemsize"] = py::int_(20);
list.append(py::dtype::from_args(dict));
list.append(py::dtype(names, formats, offsets, 20));
list.append(py::dtype(py::buffer_info((void *) 0, 1, "I", 1)));
list.append(py::dtype(py::buffer_info((void *) 0, 1, "T{i:a:f:b:}", 1)));
return list;
}
py::list test_dtype_methods() {
py::list list;
auto dt1 = py::dtype::of<int32_t>();
auto dt2 = py::dtype::of<SimpleStruct>();
list.append(dt1); list.append(dt2);
list.append(py::bool_(dt1.has_fields())); list.append(py::bool_(dt2.has_fields()));
list.append(py::int_(dt1.itemsize())); list.append(py::int_(dt2.itemsize()));
return list;
}
void init_ex_numpy_dtypes(py::module &m) {
PYBIND11_NUMPY_DTYPE(SimpleStruct, x, y, z);
PYBIND11_NUMPY_DTYPE(PackedStruct, x, y, z);
PYBIND11_NUMPY_DTYPE(NestedStruct, a, b);
PYBIND11_NUMPY_DTYPE(PartialStruct, x, y, z);
PYBIND11_NUMPY_DTYPE(PartialNestedStruct, a);
PYBIND11_NUMPY_DTYPE(StringStruct, a, b);
m.def("create_rec_simple", &create_recarray<SimpleStruct>);
m.def("create_rec_packed", &create_recarray<PackedStruct>);
m.def("create_rec_nested", &create_nested);
m.def("create_rec_partial", &create_recarray<PartialStruct>);
m.def("create_rec_partial_nested", &create_partial_nested);
m.def("print_format_descriptors", &print_format_descriptors);
m.def("print_rec_simple", &print_recarray<SimpleStruct>);
m.def("print_rec_packed", &print_recarray<PackedStruct>);
m.def("print_rec_nested", &print_recarray<NestedStruct>);
m.def("print_dtypes", &print_dtypes);
m.def("get_format_unbound", &get_format_unbound);
m.def("create_string_array", &create_string_array);
m.def("print_string_array", &print_recarray<StringStruct>);
m.def("test_array_ctors", &test_array_ctors);
m.def("test_dtype_ctors", &test_dtype_ctors);
m.def("test_dtype_methods", &test_dtype_methods);
}
#undef PYBIND11_PACKED

View File

@ -0,0 +1,102 @@
#!/usr/bin/env python
from __future__ import print_function
import numpy as np
from example import (
create_rec_simple, create_rec_packed, create_rec_nested, print_format_descriptors,
print_rec_simple, print_rec_packed, print_rec_nested, print_dtypes, get_format_unbound,
create_rec_partial, create_rec_partial_nested, create_string_array, print_string_array,
test_array_ctors, test_dtype_ctors, test_dtype_methods
)
def check_eq(arr, data, dtype):
np.testing.assert_equal(arr, np.array(data, dtype=dtype))
try:
get_format_unbound()
raise Exception
except RuntimeError as e:
assert 'unsupported buffer format' in str(e)
print_format_descriptors()
print_dtypes()
simple_dtype = np.dtype({'names': ['x', 'y', 'z'],
'formats': ['?', 'u4', 'f4'],
'offsets': [0, 4, 8]})
packed_dtype = np.dtype([('x', '?'), ('y', 'u4'), ('z', 'f4')])
elements = [(False, 0, 0.0), (True, 1, 1.5), (False, 2, 3.0)]
for func, dtype in [(create_rec_simple, simple_dtype), (create_rec_packed, packed_dtype)]:
arr = func(0)
assert arr.dtype == dtype
check_eq(arr, [], simple_dtype)
check_eq(arr, [], packed_dtype)
arr = func(3)
assert arr.dtype == dtype
check_eq(arr, elements, simple_dtype)
check_eq(arr, elements, packed_dtype)
if dtype == simple_dtype:
print_rec_simple(arr)
else:
print_rec_packed(arr)
arr = create_rec_partial(3)
print(arr.dtype)
partial_dtype = arr.dtype
assert '' not in arr.dtype.fields
assert partial_dtype.itemsize > simple_dtype.itemsize
check_eq(arr, elements, simple_dtype)
check_eq(arr, elements, packed_dtype)
arr = create_rec_partial_nested(3)
print(arr.dtype)
assert '' not in arr.dtype.fields
assert '' not in arr.dtype.fields['a'][0].fields
assert arr.dtype.itemsize > partial_dtype.itemsize
np.testing.assert_equal(arr['a'], create_rec_partial(3))
nested_dtype = np.dtype([('a', simple_dtype), ('b', packed_dtype)])
arr = create_rec_nested(0)
assert arr.dtype == nested_dtype
check_eq(arr, [], nested_dtype)
arr = create_rec_nested(3)
assert arr.dtype == nested_dtype
check_eq(arr, [((False, 0, 0.0), (True, 1, 1.5)),
((True, 1, 1.5), (False, 2, 3.0)),
((False, 2, 3.0), (True, 3, 4.5))], nested_dtype)
print_rec_nested(arr)
assert create_rec_nested.__doc__.strip().endswith('numpy.ndarray[NestedStruct]')
arr = create_string_array(True)
print(arr.dtype)
print_string_array(arr)
dtype = arr.dtype
assert arr['a'].tolist() == [b'', b'a', b'ab', b'abc']
assert arr['b'].tolist() == [b'', b'a', b'ab', b'abc']
arr = create_string_array(False)
assert dtype == arr.dtype
data = np.arange(1, 7, dtype='int32')
for i in range(8):
np.testing.assert_array_equal(test_array_ctors(10 + i), data.reshape((3, 2)))
np.testing.assert_array_equal(test_array_ctors(20 + i), data.reshape((3, 2)))
for i in range(5):
np.testing.assert_array_equal(test_array_ctors(30 + i), data)
np.testing.assert_array_equal(test_array_ctors(40 + i), data)
d1 = np.dtype({'names': ['a', 'b'], 'formats': ['int32', 'float64'],
'offsets': [1, 10], 'itemsize': 20})
d2 = np.dtype([('a', 'i4'), ('b', 'f4')])
assert test_dtype_ctors() == [np.dtype('int32'), np.dtype('float64'),
np.dtype('bool'), d1, d1, np.dtype('uint32'), d2]
assert test_dtype_methods() == [np.dtype('int32'), simple_dtype, False, True,
np.dtype('int32').itemsize, simple_dtype.itemsize]

View File

@ -0,0 +1,28 @@
T{=?:x:3x=I:y:=f:z:}
T{=?:x:=I:y:=f:z:}
T{=T{=?:x:3x=I:y:=f:z:}:a:=T{=?:x:=I:y:=f:z:}:b:}
T{=?:x:3x=I:y:=f:z:12x}
T{8x=T{=?:x:3x=I:y:=f:z:12x}:a:8x}
T{=3s:a:=3s:b:}
{'names':['x','y','z'], 'formats':['?','<u4','<f4'], 'offsets':[0,4,8], 'itemsize':12}
[('x', '?'), ('y', '<u4'), ('z', '<f4')]
[('a', {'names':['x','y','z'], 'formats':['?','<u4','<f4'], 'offsets':[0,4,8], 'itemsize':12}), ('b', [('x', '?'), ('y', '<u4'), ('z', '<f4')])]
{'names':['x','y','z'], 'formats':['?','<u4','<f4'], 'offsets':[0,4,8], 'itemsize':24}
{'names':['a'], 'formats':[{'names':['x','y','z'], 'formats':['?','<u4','<f4'], 'offsets':[0,4,8], 'itemsize':24}], 'offsets':[8], 'itemsize':40}
[('a', 'S3'), ('b', 'S3')]
s:0,0,0
s:1,1,1.5
s:0,2,3
p:0,0,0
p:1,1,1.5
p:0,2,3
{'names':['x','y','z'], 'formats':['?','<u4','<f4'], 'offsets':[0,4,8], 'itemsize':24}
{'names':['a'], 'formats':[{'names':['x','y','z'], 'formats':['?','<u4','<f4'], 'offsets':[0,4,8], 'itemsize':24}], 'offsets':[8], 'itemsize':40}
n:a=s:0,0,0;b=p:1,1,1.5
n:a=s:1,1,1.5;b=p:0,2,3
n:a=s:0,2,3;b=p:1,3,4.5
[('a', 'S3'), ('b', 'S3')]
a='',b=''
a='a',b='a'
a='ab',b='ab'
a='abc',b='abc'

View File

@ -139,6 +139,22 @@ public:
throw std::runtime_error("This exception was intentionally thrown."); throw std::runtime_error("This exception was intentionally thrown.");
} }
py::bytes get_bytes_from_string() {
return (py::bytes) std::string("foo");
}
py::bytes get_bytes_from_str() {
return (py::bytes) py::str("bar", 3);
}
py::str get_str_from_string() {
return (py::str) std::string("baz");
}
py::str get_str_from_bytes() {
return (py::str) py::bytes("boo", 3);
}
static int value; static int value;
static const int value2; static const int value2;
}; };
@ -167,6 +183,10 @@ void init_ex_python_types(py::module &m) {
.def("pair_passthrough", &ExamplePythonTypes::pair_passthrough, "Return a pair in reversed order") .def("pair_passthrough", &ExamplePythonTypes::pair_passthrough, "Return a pair in reversed order")
.def("tuple_passthrough", &ExamplePythonTypes::tuple_passthrough, "Return a triple in reversed order") .def("tuple_passthrough", &ExamplePythonTypes::tuple_passthrough, "Return a triple in reversed order")
.def("throw_exception", &ExamplePythonTypes::throw_exception, "Throw an exception") .def("throw_exception", &ExamplePythonTypes::throw_exception, "Throw an exception")
.def("get_bytes_from_string", &ExamplePythonTypes::get_bytes_from_string, "py::bytes from std::string")
.def("get_bytes_from_str", &ExamplePythonTypes::get_bytes_from_str, "py::bytes from py::str")
.def("get_str_from_string", &ExamplePythonTypes::get_str_from_string, "py::str from std::string")
.def("get_str_from_bytes", &ExamplePythonTypes::get_str_from_bytes, "py::str from py::bytes")
.def_static("new_instance", &ExamplePythonTypes::new_instance, "Return an instance") .def_static("new_instance", &ExamplePythonTypes::new_instance, "Return an instance")
.def_readwrite_static("value", &ExamplePythonTypes::value, "Static value member") .def_readwrite_static("value", &ExamplePythonTypes::value, "Static value member")
.def_readonly_static("value2", &ExamplePythonTypes::value2, "Static value member (readonly)") .def_readonly_static("value2", &ExamplePythonTypes::value2, "Static value member (readonly)")

View File

@ -66,6 +66,11 @@ print("__module__(example.ExamplePythonTypes) = %s" % ExamplePythonTypes.__modul
print("__name__(example.ExamplePythonTypes.get_set) = %s" % ExamplePythonTypes.get_set.__name__) print("__name__(example.ExamplePythonTypes.get_set) = %s" % ExamplePythonTypes.get_set.__name__)
print("__module__(example.ExamplePythonTypes.get_set) = %s" % ExamplePythonTypes.get_set.__module__) print("__module__(example.ExamplePythonTypes.get_set) = %s" % ExamplePythonTypes.get_set.__module__)
print(instance.get_bytes_from_string().decode())
print(instance.get_bytes_from_str().decode())
print(instance.get_str_from_string().encode().decode())
print(instance.get_str_from_bytes().encode().decode())
from example import ConstructorStats from example import ConstructorStats
cstats = ConstructorStats.get(ExamplePythonTypes) cstats = ConstructorStats.get(ExamplePythonTypes)

View File

@ -28,106 +28,106 @@ Help on class ExamplePythonTypes in module example
class EExxaammpplleePPyytthhoonnTTyyppeess(__builtin__.object) class EExxaammpplleePPyytthhoonnTTyyppeess(__builtin__.object)
| Example 2 documentation | Example 2 documentation
| |
| Methods defined here: | Methods defined here:
| |
| ____iinniitt____(...) | ____iinniitt____(...)
| x.__init__(...) initializes x; see help(type(x)) for signature | x.__init__(...) initializes x; see help(type(x)) for signature
| |
| ggeett__aarrrraayy(...) | ggeett__aarrrraayy(...)
| |
| Signature : (example.ExamplePythonTypes) -> List[unicode[2]] | Signature : (example.ExamplePythonTypes) -> List[unicode[2]]
| Return a C++ array | Return a C++ array
| |
| ggeett__ddiicctt(...) | ggeett__ddiicctt(...)
| Signature : (example.ExamplePythonTypes) -> dict | Signature : (example.ExamplePythonTypes) -> dict
| |
| Return a Python dictionary | Return a Python dictionary
| |
| ggeett__ddiicctt__22(...) | ggeett__ddiicctt__22(...)
| |
| Signature : (example.ExamplePythonTypes) -> Dict[unicode, unicode] | Signature : (example.ExamplePythonTypes) -> Dict[unicode, unicode]
| Return a C++ dictionary | Return a C++ dictionary
| |
| ggeett__lliisstt(...) | ggeett__lliisstt(...)
| Signature : (example.ExamplePythonTypes) -> list | Signature : (example.ExamplePythonTypes) -> list
| |
| Return a Python list | Return a Python list
| |
| ggeett__lliisstt__22(...) | ggeett__lliisstt__22(...)
| |
| Signature : (example.ExamplePythonTypes) -> List[unicode] | Signature : (example.ExamplePythonTypes) -> List[unicode]
| Return a C++ list | Return a C++ list
| |
| ggeett__sseett(...) | ggeett__sseett(...)
| Signature : (example.ExamplePythonTypes) -> set | Signature : (example.ExamplePythonTypes) -> set
| |
| Return a Python set | Return a Python set
| |
| ggeett__sseett22(...) | ggeett__sseett22(...)
| Signature : (example.ExamplePythonTypes) -> set | Signature : (example.ExamplePythonTypes) -> set
| |
| Return a C++ set | Return a C++ set
| |
| ppaaiirr__ppaasssstthhrroouugghh(...) | ppaaiirr__ppaasssstthhrroouugghh(...)
| |
| Signature : (example.ExamplePythonTypes, Tuple[bool, unicode]) -> Tuple[unicode, bool] | Signature : (example.ExamplePythonTypes, Tuple[bool, unicode]) -> Tuple[unicode, bool]
| Return a pair in reversed order | Return a pair in reversed order
| |
| pprriinntt__aarrrraayy(...) | pprriinntt__aarrrraayy(...)
| |
| Signature : (example.ExamplePythonTypes, List[unicode[2]]) -> None | Signature : (example.ExamplePythonTypes, List[unicode[2]]) -> None
| Print entries of a C++ array | Print entries of a C++ array
| |
| pprriinntt__ddiicctt(...) | pprriinntt__ddiicctt(...)
| |
| Signature : (example.ExamplePythonTypes, dict) -> None | Signature : (example.ExamplePythonTypes, dict) -> None
| Print entries of a Python dictionary | Print entries of a Python dictionary
| |
| pprriinntt__ddiicctt__22(...) | pprriinntt__ddiicctt__22(...)
| |
| Signature : (example.ExamplePythonTypes, Dict[unicode, unicode]) -> None | Signature : (example.ExamplePythonTypes, Dict[unicode, unicode]) -> None
| Print entries of a C++ dictionary | Print entries of a C++ dictionary
| |
| pprriinntt__lliisstt(...) | pprriinntt__lliisstt(...)
| |
| Signature : (example.ExamplePythonTypes, list) -> None | Signature : (example.ExamplePythonTypes, list) -> None
| Print entries of a Python list | Print entries of a Python list
| |
| pprriinntt__lliisstt__22(...) | pprriinntt__lliisstt__22(...)
| |
| Signature : (example.ExamplePythonTypes, List[unicode]) -> None | Signature : (example.ExamplePythonTypes, List[unicode]) -> None
| Print entries of a C++ list | Print entries of a C++ list
| |
| pprriinntt__sseett(...) | pprriinntt__sseett(...)
| |
| Signature : (example.ExamplePythonTypes, set) -> None | Signature : (example.ExamplePythonTypes, set) -> None
| Print entries of a Python set | Print entries of a Python set
| |
| pprriinntt__sseett__22(...) | pprriinntt__sseett__22(...)
| |
| Signature : (example.ExamplePythonTypes, Set[unicode]) -> None | Signature : (example.ExamplePythonTypes, Set[unicode]) -> None
| Print entries of a C++ set | Print entries of a C++ set
| |
| tthhrrooww__eexxcceeppttiioonn(...) | tthhrrooww__eexxcceeppttiioonn(...)
| |
| Signature : (example.ExamplePythonTypes) -> None | Signature : (example.ExamplePythonTypes) -> None
| Throw an exception | Throw an exception
| |
| ttuuppllee__ppaasssstthhrroouugghh(...) | ttuuppllee__ppaasssstthhrroouugghh(...)
| |
| Signature : (example.ExamplePythonTypes, Tuple[bool, unicode, int]) -> Tuple[int, unicode, bool] | Signature : (example.ExamplePythonTypes, Tuple[bool, unicode, int]) -> Tuple[int, unicode, bool]
| Return a triple in reversed order | Return a triple in reversed order
| |
| ---------------------------------------------------------------------- | ----------------------------------------------------------------------
| Data and other attributes defined here: | Data and other attributes defined here:
| |
| ____nneeww____ = <built-in method __new__ of example.ExamplePythonTypes__Meta object> | ____nneeww____ = <built-in method __new__ of example.ExamplePythonTypes__Meta object>
| T.__new__(S, ...) -> a new object with type S, a subtype of T | T.__new__(S, ...) -> a new object with type S, a subtype of T
| |
| nneeww__iinnssttaannccee = <built-in method new_instance of PyCapsule object> | nneeww__iinnssttaannccee = <built-in method new_instance of PyCapsule object>
| Signature : () -> example.ExamplePythonTypes | Signature : () -> example.ExamplePythonTypes
| |
| Return an instance | Return an instance
__name__(example) = example __name__(example) = example
@ -135,6 +135,10 @@ __name__(example.ExamplePythonTypes) = ExamplePythonTypes
__module__(example.ExamplePythonTypes) = example __module__(example.ExamplePythonTypes) = example
__name__(example.ExamplePythonTypes.get_set) = get_set __name__(example.ExamplePythonTypes.get_set) = get_set
__module__(example.ExamplePythonTypes.get_set) = example __module__(example.ExamplePythonTypes.get_set) = example
foo
bar
baz
boo
Instances not destroyed: 1 Instances not destroyed: 1
### ExamplePythonTypes @ 0x1045b80 destroyed ### ExamplePythonTypes @ 0x1045b80 destroyed
Instances not destroyed: 0 Instances not destroyed: 0

View File

@ -29,6 +29,7 @@ void init_ex_inheritance(py::module &);
void init_ex_stl_binder_vector(py::module &); void init_ex_stl_binder_vector(py::module &);
void init_ex_eval(py::module &); void init_ex_eval(py::module &);
void init_ex_custom_exceptions(py::module &); void init_ex_custom_exceptions(py::module &);
void init_ex_numpy_dtypes(py::module &);
void init_issues(py::module &); void init_issues(py::module &);
#if defined(PYBIND11_TEST_EIGEN) #if defined(PYBIND11_TEST_EIGEN)
@ -72,6 +73,7 @@ PYBIND11_PLUGIN(example) {
init_ex_stl_binder_vector(m); init_ex_stl_binder_vector(m);
init_ex_eval(m); init_ex_eval(m);
init_ex_custom_exceptions(m); init_ex_custom_exceptions(m);
init_ex_numpy_dtypes(m);
init_issues(m); init_issues(m);
#if defined(PYBIND11_TEST_EIGEN) #if defined(PYBIND11_TEST_EIGEN)

View File

@ -46,12 +46,11 @@ name = sys.argv[1]
try: try:
output_bytes = subprocess.check_output([sys.executable, "-u", name + ".py"], output_bytes = subprocess.check_output([sys.executable, "-u", name + ".py"],
stderr=subprocess.STDOUT) stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError as exc:
if e.returncode == 99: print('Test `{}` failed:\n{}\n'.format(name, '-' * 50))
print('Test "%s" could not be run.' % name) print(exc.output.decode())
exit(0) print('-' * 50)
else: sys.exit(1)
raise
output = sanitize(output_bytes.decode('utf-8')) output = sanitize(output_bytes.decode('utf-8'))
reference = sanitize(open(name + '.ref', 'r').read()) reference = sanitize(open(name + '.ref', 'r').read())

View File

@ -95,6 +95,7 @@
#define PYBIND11_STRING_NAME "str" #define PYBIND11_STRING_NAME "str"
#define PYBIND11_SLICE_OBJECT PyObject #define PYBIND11_SLICE_OBJECT PyObject
#define PYBIND11_FROM_STRING PyUnicode_FromString #define PYBIND11_FROM_STRING PyUnicode_FromString
#define PYBIND11_STR_TYPE ::pybind11::str
#define PYBIND11_OB_TYPE(ht_type) (ht_type).ob_base.ob_base.ob_type #define PYBIND11_OB_TYPE(ht_type) (ht_type).ob_base.ob_base.ob_type
#define PYBIND11_PLUGIN_IMPL(name) \ #define PYBIND11_PLUGIN_IMPL(name) \
extern "C" PYBIND11_EXPORT PyObject *PyInit_##name() extern "C" PYBIND11_EXPORT PyObject *PyInit_##name()
@ -113,6 +114,7 @@
#define PYBIND11_STRING_NAME "unicode" #define PYBIND11_STRING_NAME "unicode"
#define PYBIND11_SLICE_OBJECT PySliceObject #define PYBIND11_SLICE_OBJECT PySliceObject
#define PYBIND11_FROM_STRING PyString_FromString #define PYBIND11_FROM_STRING PyString_FromString
#define PYBIND11_STR_TYPE ::pybind11::bytes
#define PYBIND11_OB_TYPE(ht_type) (ht_type).ob_type #define PYBIND11_OB_TYPE(ht_type) (ht_type).ob_type
#define PYBIND11_PLUGIN_IMPL(name) \ #define PYBIND11_PLUGIN_IMPL(name) \
extern "C" PYBIND11_EXPORT PyObject *init##name() extern "C" PYBIND11_EXPORT PyObject *init##name()
@ -204,12 +206,13 @@ struct buffer_info {
void *ptr; // Pointer to the underlying storage void *ptr; // Pointer to the underlying storage
size_t itemsize; // Size of individual items in bytes size_t itemsize; // Size of individual items in bytes
size_t size; // Total number of entries size_t size; // Total number of entries
std::string format; // For homogeneous buffers, this should be set to format_descriptor<T>::value std::string format; // For homogeneous buffers, this should be set to format_descriptor<T>::format()
size_t ndim; // Number of dimensions size_t ndim; // Number of dimensions
std::vector<size_t> shape; // Shape of the tensor (1 entry per dimension) std::vector<size_t> shape; // Shape of the tensor (1 entry per dimension)
std::vector<size_t> strides; // Number of entries between adjacent entries (for each per dimension) std::vector<size_t> strides; // Number of entries between adjacent entries (for each per dimension)
buffer_info() : ptr(nullptr), view(nullptr) {} buffer_info() : ptr(nullptr), view(nullptr) {}
buffer_info(void *ptr, size_t itemsize, const std::string &format, size_t ndim, buffer_info(void *ptr, size_t itemsize, const std::string &format, size_t ndim,
const std::vector<size_t> &shape, const std::vector<size_t> &strides) const std::vector<size_t> &shape, const std::vector<size_t> &strides)
: ptr(ptr), itemsize(itemsize), size(1), format(format), : ptr(ptr), itemsize(itemsize), size(1), format(format),
@ -218,6 +221,10 @@ struct buffer_info {
size *= shape[i]; size *= shape[i];
} }
buffer_info(void *ptr, size_t itemsize, const std::string &format, size_t size)
: buffer_info(ptr, itemsize, format, 1, std::vector<size_t> { size },
std::vector<size_t> { itemsize }) { }
buffer_info(Py_buffer *view) buffer_info(Py_buffer *view)
: ptr(view->buf), itemsize((size_t) view->itemsize), size(1), format(view->format), : ptr(view->buf), itemsize((size_t) view->itemsize), size(1), format(view->format),
ndim((size_t) view->ndim), shape((size_t) view->ndim), strides((size_t) view->ndim), view(view) { ndim((size_t) view->ndim), shape((size_t) view->ndim), strides((size_t) view->ndim), view(view) {
@ -231,6 +238,7 @@ struct buffer_info {
~buffer_info() { ~buffer_info() {
if (view) { PyBuffer_Release(view); delete view; } if (view) { PyBuffer_Release(view); delete view; }
} }
private: private:
Py_buffer *view = nullptr; Py_buffer *view = nullptr;
}; };
@ -323,14 +331,23 @@ PYBIND11_RUNTIME_EXCEPTION(reference_cast_error) /// Used internally
[[noreturn]] PYBIND11_NOINLINE inline void pybind11_fail(const std::string &reason) { throw std::runtime_error(reason); } [[noreturn]] PYBIND11_NOINLINE inline void pybind11_fail(const std::string &reason) { throw std::runtime_error(reason); }
/// Format strings for basic number types /// Format strings for basic number types
#define PYBIND11_DECL_FMT(t, v) template<> struct format_descriptor<t> { static constexpr const char *value = v; } #define PYBIND11_DECL_FMT(t, v) template<> struct format_descriptor<t> \
{ static constexpr const char* value = v; /* for backwards compatibility */ \
static constexpr const char* format() { return value; } }
template <typename T, typename SFINAE = void> struct format_descriptor { }; template <typename T, typename SFINAE = void> struct format_descriptor { };
template <typename T> struct format_descriptor<T, typename std::enable_if<std::is_integral<T>::value>::type> { template <typename T> struct format_descriptor<T, typename std::enable_if<std::is_integral<T>::value>::type> {
static constexpr const char value[2] = static constexpr const char value[2] =
{ "bBhHiIqQ"[detail::log2(sizeof(T))*2 + (std::is_unsigned<T>::value ? 1 : 0)], '\0' }; { "bBhHiIqQ"[detail::log2(sizeof(T))*2 + (std::is_unsigned<T>::value ? 1 : 0)], '\0' };
static constexpr const char* format() { return value; }
}; };
template <typename T> constexpr const char format_descriptor< template <typename T> constexpr const char format_descriptor<
T, typename std::enable_if<std::is_integral<T>::value>::type>::value[2]; T, typename std::enable_if<std::is_integral<T>::value>::type>::value[2];
PYBIND11_DECL_FMT(float, "f"); PYBIND11_DECL_FMT(double, "d"); PYBIND11_DECL_FMT(bool, "?");
PYBIND11_DECL_FMT(float, "f");
PYBIND11_DECL_FMT(double, "d");
PYBIND11_DECL_FMT(bool, "?");
NAMESPACE_END(pybind11) NAMESPACE_END(pybind11)

View File

@ -181,7 +181,7 @@ PYBIND11_NOINLINE inline descr concat(descr &&d) { return d; }
template <typename... Args> PYBIND11_NOINLINE descr concat(descr &&d, Args&&... args) { return std::move(d) + _(", ") + concat(std::forward<Args>(args)...); } template <typename... Args> PYBIND11_NOINLINE descr concat(descr &&d, Args&&... args) { return std::move(d) + _(", ") + concat(std::forward<Args>(args)...); }
PYBIND11_NOINLINE inline descr type_descr(descr&& d) { return _("{") + std::move(d) + _("}"); } PYBIND11_NOINLINE inline descr type_descr(descr&& d) { return _("{") + std::move(d) + _("}"); }
#define PYBIND11_DESCR descr #define PYBIND11_DESCR ::pybind11::detail::descr
#endif #endif
NAMESPACE_END(detail) NAMESPACE_END(detail)

View File

@ -133,7 +133,7 @@ struct type_caster<Type, typename std::enable_if<is_eigen_dense<Type>::value &&
/* Size of one scalar */ /* Size of one scalar */
sizeof(Scalar), sizeof(Scalar),
/* Python struct-style format descriptor */ /* Python struct-style format descriptor */
format_descriptor<Scalar>::value, format_descriptor<Scalar>::format(),
/* Number of dimensions */ /* Number of dimensions */
1, 1,
/* Buffer dimensions */ /* Buffer dimensions */
@ -148,7 +148,7 @@ struct type_caster<Type, typename std::enable_if<is_eigen_dense<Type>::value &&
/* Size of one scalar */ /* Size of one scalar */
sizeof(Scalar), sizeof(Scalar),
/* Python struct-style format descriptor */ /* Python struct-style format descriptor */
format_descriptor<Scalar>::value, format_descriptor<Scalar>::format(),
/* Number of dimensions */ /* Number of dimensions */
isVector ? 1 : 2, isVector ? 1 : 2,
/* Buffer dimensions */ /* Buffer dimensions */
@ -233,7 +233,7 @@ struct type_caster<Type, typename std::enable_if<is_eigen_sparse<Type>::value>::
try { try {
obj = matrix_type(obj); obj = matrix_type(obj);
} catch (const error_already_set &) { } catch (const error_already_set &) {
PyErr_Clear(); PyErr_Clear();
return false; return false;
} }
} }
@ -276,7 +276,7 @@ struct type_caster<Type, typename std::enable_if<is_eigen_sparse<Type>::value>::
// Size of one scalar // Size of one scalar
sizeof(Scalar), sizeof(Scalar),
// Python struct-style format descriptor // Python struct-style format descriptor
format_descriptor<Scalar>::value, format_descriptor<Scalar>::format(),
// Number of dimensions // Number of dimensions
1, 1,
// Buffer dimensions // Buffer dimensions
@ -291,7 +291,7 @@ struct type_caster<Type, typename std::enable_if<is_eigen_sparse<Type>::value>::
// Size of one scalar // Size of one scalar
sizeof(StorageIndex), sizeof(StorageIndex),
// Python struct-style format descriptor // Python struct-style format descriptor
format_descriptor<StorageIndex>::value, format_descriptor<StorageIndex>::format(),
// Number of dimensions // Number of dimensions
1, 1,
// Buffer dimensions // Buffer dimensions
@ -306,7 +306,7 @@ struct type_caster<Type, typename std::enable_if<is_eigen_sparse<Type>::value>::
// Size of one scalar // Size of one scalar
sizeof(StorageIndex), sizeof(StorageIndex),
// Python struct-style format descriptor // Python struct-style format descriptor
format_descriptor<StorageIndex>::value, format_descriptor<StorageIndex>::format(),
// Number of dimensions // Number of dimensions
1, 1,
// Buffer dimensions // Buffer dimensions

View File

@ -13,6 +13,11 @@
#include "complex.h" #include "complex.h"
#include <numeric> #include <numeric>
#include <algorithm> #include <algorithm>
#include <array>
#include <cstdlib>
#include <cstring>
#include <sstream>
#include <initializer_list>
#if defined(_MSC_VER) #if defined(_MSC_VER)
#pragma warning(push) #pragma warning(push)
@ -20,123 +25,275 @@
#endif #endif
NAMESPACE_BEGIN(pybind11) NAMESPACE_BEGIN(pybind11)
namespace detail { template <typename type, typename SFINAE = void> struct npy_format_descriptor { }; } namespace detail {
template <typename type, typename SFINAE = void> struct npy_format_descriptor { };
template <typename type> struct is_pod_struct;
struct npy_api {
enum constants {
NPY_C_CONTIGUOUS_ = 0x0001,
NPY_F_CONTIGUOUS_ = 0x0002,
NPY_ARRAY_FORCECAST_ = 0x0010,
NPY_ENSURE_ARRAY_ = 0x0040,
NPY_BOOL_ = 0,
NPY_BYTE_, NPY_UBYTE_,
NPY_SHORT_, NPY_USHORT_,
NPY_INT_, NPY_UINT_,
NPY_LONG_, NPY_ULONG_,
NPY_LONGLONG_, NPY_ULONGLONG_,
NPY_FLOAT_, NPY_DOUBLE_, NPY_LONGDOUBLE_,
NPY_CFLOAT_, NPY_CDOUBLE_, NPY_CLONGDOUBLE_,
NPY_OBJECT_ = 17,
NPY_STRING_, NPY_UNICODE_, NPY_VOID_
};
static npy_api& get() {
static npy_api api = lookup();
return api;
}
bool PyArray_Check_(PyObject *obj) const {
return (bool) PyObject_TypeCheck(obj, PyArray_Type_);
}
bool PyArrayDescr_Check_(PyObject *obj) const {
return (bool) PyObject_TypeCheck(obj, PyArrayDescr_Type_);
}
PyObject *(*PyArray_DescrFromType_)(int);
PyObject *(*PyArray_NewFromDescr_)
(PyTypeObject *, PyObject *, int, Py_intptr_t *,
Py_intptr_t *, void *, int, PyObject *);
PyObject *(*PyArray_DescrNewFromType_)(int);
PyObject *(*PyArray_NewCopy_)(PyObject *, int);
PyTypeObject *PyArray_Type_;
PyTypeObject *PyArrayDescr_Type_;
PyObject *(*PyArray_FromAny_) (PyObject *, PyObject *, int, int, int, PyObject *);
int (*PyArray_DescrConverter_) (PyObject *, PyObject **);
bool (*PyArray_EquivTypes_) (PyObject *, PyObject *);
int (*PyArray_GetArrayParamsFromObject_)(PyObject *, PyObject *, char, PyObject **, int *,
Py_ssize_t *, PyObject **, PyObject *);
private:
enum functions {
API_PyArray_Type = 2,
API_PyArrayDescr_Type = 3,
API_PyArray_DescrFromType = 45,
API_PyArray_FromAny = 69,
API_PyArray_NewCopy = 85,
API_PyArray_NewFromDescr = 94,
API_PyArray_DescrNewFromType = 9,
API_PyArray_DescrConverter = 174,
API_PyArray_EquivTypes = 182,
API_PyArray_GetArrayParamsFromObject = 278,
};
static npy_api lookup() {
module m = module::import("numpy.core.multiarray");
object c = (object) m.attr("_ARRAY_API");
#if PY_MAJOR_VERSION >= 3
void **api_ptr = (void **) (c ? PyCapsule_GetPointer(c.ptr(), NULL) : nullptr);
#else
void **api_ptr = (void **) (c ? PyCObject_AsVoidPtr(c.ptr()) : nullptr);
#endif
npy_api api;
#define DECL_NPY_API(Func) api.Func##_ = (decltype(api.Func##_)) api_ptr[API_##Func];
DECL_NPY_API(PyArray_Type);
DECL_NPY_API(PyArrayDescr_Type);
DECL_NPY_API(PyArray_DescrFromType);
DECL_NPY_API(PyArray_FromAny);
DECL_NPY_API(PyArray_NewCopy);
DECL_NPY_API(PyArray_NewFromDescr);
DECL_NPY_API(PyArray_DescrNewFromType);
DECL_NPY_API(PyArray_DescrConverter);
DECL_NPY_API(PyArray_EquivTypes);
DECL_NPY_API(PyArray_GetArrayParamsFromObject);
#undef DECL_NPY_API
return api;
}
};
}
class dtype : public object {
public:
PYBIND11_OBJECT_DEFAULT(dtype, object, detail::npy_api::get().PyArrayDescr_Check_);
dtype(const buffer_info &info) {
dtype descr(_dtype_from_pep3118()(PYBIND11_STR_TYPE(info.format)));
m_ptr = descr.strip_padding().release().ptr();
}
dtype(std::string format) {
m_ptr = from_args(pybind11::str(format)).release().ptr();
}
dtype(const char *format) : dtype(std::string(format)) { }
dtype(list names, list formats, list offsets, size_t itemsize) {
dict args;
args["names"] = names;
args["formats"] = formats;
args["offsets"] = offsets;
args["itemsize"] = int_(itemsize);
m_ptr = from_args(args).release().ptr();
}
static dtype from_args(object args) {
// This is essentially the same as calling np.dtype() constructor in Python
PyObject *ptr = nullptr;
if (!detail::npy_api::get().PyArray_DescrConverter_(args.release().ptr(), &ptr) || !ptr)
pybind11_fail("NumPy: failed to create structured dtype");
return object(ptr, false);
}
template <typename T> static dtype of() {
return detail::npy_format_descriptor<T>::dtype();
}
size_t itemsize() const {
return (size_t) attr("itemsize").cast<int_>();
}
bool has_fields() const {
return attr("fields").cast<object>().ptr() != Py_None;
}
std::string kind() const {
return (std::string) attr("kind").cast<pybind11::str>();
}
private:
static object _dtype_from_pep3118() {
static PyObject *obj = module::import("numpy.core._internal")
.attr("_dtype_from_pep3118").cast<object>().release().ptr();
return object(obj, true);
}
dtype strip_padding() {
// Recursively strip all void fields with empty names that are generated for
// padding fields (as of NumPy v1.11).
auto fields = attr("fields").cast<object>();
if (fields.ptr() == Py_None)
return *this;
struct field_descr { PYBIND11_STR_TYPE name; object format; int_ offset; };
std::vector<field_descr> field_descriptors;
auto items = fields.attr("items").cast<object>();
for (auto field : items()) {
auto spec = object(field, true).cast<tuple>();
auto name = spec[0].cast<pybind11::str>();
auto format = spec[1].cast<tuple>()[0].cast<dtype>();
auto offset = spec[1].cast<tuple>()[1].cast<int_>();
if (!len(name) && format.kind() == "V")
continue;
field_descriptors.push_back({(PYBIND11_STR_TYPE) name, format.strip_padding(), offset});
}
std::sort(field_descriptors.begin(), field_descriptors.end(),
[](const field_descr& a, const field_descr& b) {
return (int) a.offset < (int) b.offset;
});
list names, formats, offsets;
for (auto& descr : field_descriptors) {
names.append(descr.name);
formats.append(descr.format);
offsets.append(descr.offset);
}
return dtype(names, formats, offsets, itemsize());
}
};
class array : public buffer { class array : public buffer {
public: public:
struct API { PYBIND11_OBJECT_DEFAULT(array, buffer, detail::npy_api::get().PyArray_Check_)
enum Entries {
API_PyArray_Type = 2,
API_PyArray_DescrFromType = 45,
API_PyArray_FromAny = 69,
API_PyArray_NewCopy = 85,
API_PyArray_NewFromDescr = 94,
NPY_C_CONTIGUOUS_ = 0x0001,
NPY_F_CONTIGUOUS_ = 0x0002,
NPY_ARRAY_FORCECAST_ = 0x0010,
NPY_ENSURE_ARRAY_ = 0x0040,
NPY_BOOL_ = 0,
NPY_BYTE_, NPY_UBYTE_,
NPY_SHORT_, NPY_USHORT_,
NPY_INT_, NPY_UINT_,
NPY_LONG_, NPY_ULONG_,
NPY_LONGLONG_, NPY_ULONGLONG_,
NPY_FLOAT_, NPY_DOUBLE_, NPY_LONGDOUBLE_,
NPY_CFLOAT_, NPY_CDOUBLE_, NPY_CLONGDOUBLE_
};
static API lookup() {
module m = module::import("numpy.core.multiarray");
object c = (object) m.attr("_ARRAY_API");
#if PY_MAJOR_VERSION >= 3
void **api_ptr = (void **) (c ? PyCapsule_GetPointer(c.ptr(), NULL) : nullptr);
#else
void **api_ptr = (void **) (c ? PyCObject_AsVoidPtr(c.ptr()) : nullptr);
#endif
API api;
api.PyArray_Type_ = (decltype(api.PyArray_Type_)) api_ptr[API_PyArray_Type];
api.PyArray_DescrFromType_ = (decltype(api.PyArray_DescrFromType_)) api_ptr[API_PyArray_DescrFromType];
api.PyArray_FromAny_ = (decltype(api.PyArray_FromAny_)) api_ptr[API_PyArray_FromAny];
api.PyArray_NewCopy_ = (decltype(api.PyArray_NewCopy_)) api_ptr[API_PyArray_NewCopy];
api.PyArray_NewFromDescr_ = (decltype(api.PyArray_NewFromDescr_)) api_ptr[API_PyArray_NewFromDescr];
return api;
}
bool PyArray_Check_(PyObject *obj) const { return (bool) PyObject_TypeCheck(obj, PyArray_Type_); }
PyObject *(*PyArray_DescrFromType_)(int);
PyObject *(*PyArray_NewFromDescr_)
(PyTypeObject *, PyObject *, int, Py_intptr_t *,
Py_intptr_t *, void *, int, PyObject *);
PyObject *(*PyArray_NewCopy_)(PyObject *, int);
PyTypeObject *PyArray_Type_;
PyObject *(*PyArray_FromAny_) (PyObject *, PyObject *, int, int, int, PyObject *);
};
PYBIND11_OBJECT_DEFAULT(array, buffer, lookup_api().PyArray_Check_)
enum { enum {
c_style = API::NPY_C_CONTIGUOUS_, c_style = detail::npy_api::NPY_C_CONTIGUOUS_,
f_style = API::NPY_F_CONTIGUOUS_, f_style = detail::npy_api::NPY_F_CONTIGUOUS_,
forcecast = API::NPY_ARRAY_FORCECAST_ forcecast = detail::npy_api::NPY_ARRAY_FORCECAST_
}; };
template <typename Type> array(size_t size, const Type *ptr) { array(const pybind11::dtype& dt, const std::vector<size_t>& shape,
API& api = lookup_api(); const std::vector<size_t>& strides, void *ptr = nullptr) {
PyObject *descr = api.PyArray_DescrFromType_(detail::npy_format_descriptor<Type>::value); auto& api = detail::npy_api::get();
if (descr == nullptr) auto ndim = shape.size();
pybind11_fail("NumPy: unsupported buffer format!"); if (shape.size() != strides.size())
Py_intptr_t shape = (Py_intptr_t) size; pybind11_fail("NumPy: shape ndim doesn't match strides ndim");
object tmp = object(api.PyArray_NewFromDescr_( auto descr = dt;
api.PyArray_Type_, descr, 1, &shape, nullptr, (void *) ptr, 0, nullptr), false); object tmp(api.PyArray_NewFromDescr_(
if (ptr && tmp) api.PyArray_Type_, descr.release().ptr(), (int) ndim, (Py_intptr_t *) shape.data(),
tmp = object(api.PyArray_NewCopy_(tmp.ptr(), -1 /* any order */), false); (Py_intptr_t *) strides.data(), ptr, 0, nullptr), false);
if (!tmp) if (!tmp)
pybind11_fail("NumPy: unable to create array!"); pybind11_fail("NumPy: unable to create array!");
if (ptr)
tmp = object(api.PyArray_NewCopy_(tmp.ptr(), -1 /* any order */), false);
m_ptr = tmp.release().ptr(); m_ptr = tmp.release().ptr();
} }
array(const buffer_info &info) { array(const pybind11::dtype& dt, const std::vector<size_t>& shape, void *ptr = nullptr)
API& api = lookup_api(); : array(dt, shape, default_strides(shape, dt.itemsize()), ptr) { }
if ((info.format.size() < 1) || (info.format.size() > 2))
pybind11_fail("Unsupported buffer format!");
int fmt = (int) info.format[0];
if (info.format == "Zd") fmt = API::NPY_CDOUBLE_;
else if (info.format == "Zf") fmt = API::NPY_CFLOAT_;
PyObject *descr = api.PyArray_DescrFromType_(fmt); array(const pybind11::dtype& dt, size_t size, void *ptr = nullptr)
if (descr == nullptr) : array(dt, std::vector<size_t> { size }, ptr) { }
pybind11_fail("NumPy: unsupported buffer format '" + info.format + "'!");
object tmp(api.PyArray_NewFromDescr_( template<typename T> array(const std::vector<size_t>& shape,
api.PyArray_Type_, descr, (int) info.ndim, (Py_intptr_t *) &info.shape[0], const std::vector<size_t>& strides, T* ptr)
(Py_intptr_t *) &info.strides[0], info.ptr, 0, nullptr), false); : array(pybind11::dtype::of<T>(), shape, strides, (void *) ptr) { }
if (info.ptr && tmp)
tmp = object(api.PyArray_NewCopy_(tmp.ptr(), -1 /* any order */), false); template<typename T> array(const std::vector<size_t>& shape, T* ptr)
if (!tmp) : array(shape, default_strides(shape, sizeof(T)), ptr) { }
pybind11_fail("NumPy: unable to create array!");
m_ptr = tmp.release().ptr(); template<typename T> array(size_t size, T* ptr)
: array(std::vector<size_t> { size }, ptr) { }
array(const buffer_info &info)
: array(pybind11::dtype(info), info.shape, info.strides, info.ptr) { }
pybind11::dtype dtype() {
return attr("dtype").cast<pybind11::dtype>();
} }
protected: protected:
static API &lookup_api() { template <typename T, typename SFINAE> friend struct detail::npy_format_descriptor;
static API api = API::lookup();
return api; static std::vector<size_t> default_strides(const std::vector<size_t>& shape, size_t itemsize) {
auto ndim = shape.size();
std::vector<size_t> strides(ndim);
if (ndim) {
std::fill(strides.begin(), strides.end(), itemsize);
for (size_t i = 0; i < ndim - 1; i++)
for (size_t j = 0; j < ndim - 1 - i; j++)
strides[j] *= shape[ndim - 1 - i];
}
return strides;
} }
}; };
template <typename T, int ExtraFlags = array::forcecast> class array_t : public array { template <typename T, int ExtraFlags = array::forcecast> class array_t : public array {
public: public:
PYBIND11_OBJECT_CVT(array_t, array, is_non_null, m_ptr = ensure(m_ptr)); PYBIND11_OBJECT_CVT(array_t, array, is_non_null, m_ptr = ensure(m_ptr));
array_t() : array() { } array_t() : array() { }
array_t(const buffer_info& info) : array(info) {}
array_t(const buffer_info& info) : array(info) { }
array_t(const std::vector<size_t>& shape, const std::vector<size_t>& strides, T* ptr = nullptr)
: array(shape, strides, ptr) { }
array_t(const std::vector<size_t>& shape, T* ptr = nullptr)
: array(shape, ptr) { }
array_t(size_t size, T* ptr = nullptr)
: array(size, ptr) { }
static bool is_non_null(PyObject *ptr) { return ptr != nullptr; } static bool is_non_null(PyObject *ptr) { return ptr != nullptr; }
static PyObject *ensure(PyObject *ptr) { static PyObject *ensure(PyObject *ptr) {
if (ptr == nullptr) if (ptr == nullptr)
return nullptr; return nullptr;
API &api = lookup_api(); auto& api = detail::npy_api::get();
PyObject *descr = api.PyArray_DescrFromType_(detail::npy_format_descriptor<T>::value); PyObject *result = api.PyArray_FromAny_(ptr, pybind11::dtype::of<T>().release().ptr(), 0, 0,
PyObject *result = api.PyArray_FromAny_(ptr, descr, 0, 0, API::NPY_ENSURE_ARRAY_ | ExtraFlags, nullptr); detail::npy_api::NPY_ENSURE_ARRAY_ | ExtraFlags, nullptr);
if (!result) if (!result)
PyErr_Clear(); PyErr_Clear();
Py_DECREF(ptr); Py_DECREF(ptr);
@ -144,15 +301,47 @@ public:
} }
}; };
template <typename T>
struct format_descriptor<T, typename std::enable_if<detail::is_pod_struct<T>::value>::type> {
static const char *format() { return detail::npy_format_descriptor<T>::format(); }
};
template <size_t N> struct format_descriptor<char[N]> {
static const char *format() { PYBIND11_DESCR s = detail::_<N>() + detail::_("s"); return s.text(); }
};
template <size_t N> struct format_descriptor<std::array<char, N>> {
static const char *format() { PYBIND11_DESCR s = detail::_<N>() + detail::_("s"); return s.text(); }
};
NAMESPACE_BEGIN(detail) NAMESPACE_BEGIN(detail)
template <typename T> struct is_std_array : std::false_type { };
template <typename T, size_t N> struct is_std_array<std::array<T, N>> : std::true_type { };
template <typename T>
struct is_pod_struct {
enum { value = std::is_pod<T>::value && // offsetof only works correctly for POD types
!std::is_array<T>::value &&
!is_std_array<T>::value &&
!std::is_integral<T>::value &&
!std::is_same<T, float>::value &&
!std::is_same<T, double>::value &&
!std::is_same<T, bool>::value &&
!std::is_same<T, std::complex<float>>::value &&
!std::is_same<T, std::complex<double>>::value };
};
template <typename T> struct npy_format_descriptor<T, typename std::enable_if<std::is_integral<T>::value>::type> { template <typename T> struct npy_format_descriptor<T, typename std::enable_if<std::is_integral<T>::value>::type> {
private: private:
constexpr static const int values[8] = { constexpr static const int values[8] = {
array::API::NPY_BYTE_, array::API::NPY_UBYTE_, array::API::NPY_SHORT_, array::API::NPY_USHORT_, npy_api::NPY_BYTE_, npy_api::NPY_UBYTE_, npy_api::NPY_SHORT_, npy_api::NPY_USHORT_,
array::API::NPY_INT_, array::API::NPY_UINT_, array::API::NPY_LONGLONG_, array::API::NPY_ULONGLONG_ }; npy_api::NPY_INT_, npy_api::NPY_UINT_, npy_api::NPY_LONGLONG_, npy_api::NPY_ULONGLONG_ };
public: public:
enum { value = values[detail::log2(sizeof(T)) * 2 + (std::is_unsigned<T>::value ? 1 : 0)] }; enum { value = values[detail::log2(sizeof(T)) * 2 + (std::is_unsigned<T>::value ? 1 : 0)] };
static pybind11::dtype dtype() {
if (auto ptr = npy_api::get().PyArray_DescrFromType_(value))
return object(ptr, true);
pybind11_fail("Unsupported buffer format!");
}
template <typename T2 = T, typename std::enable_if<std::is_signed<T2>::value, int>::type = 0> template <typename T2 = T, typename std::enable_if<std::is_signed<T2>::value, int>::type = 0>
static PYBIND11_DESCR name() { return _("int") + _<sizeof(T)*8>(); } static PYBIND11_DESCR name() { return _("int") + _<sizeof(T)*8>(); }
template <typename T2 = T, typename std::enable_if<!std::is_signed<T2>::value, int>::type = 0> template <typename T2 = T, typename std::enable_if<!std::is_signed<T2>::value, int>::type = 0>
@ -162,12 +351,149 @@ template <typename T> constexpr const int npy_format_descriptor<
T, typename std::enable_if<std::is_integral<T>::value>::type>::values[8]; T, typename std::enable_if<std::is_integral<T>::value>::type>::values[8];
#define DECL_FMT(Type, NumPyName, Name) template<> struct npy_format_descriptor<Type> { \ #define DECL_FMT(Type, NumPyName, Name) template<> struct npy_format_descriptor<Type> { \
enum { value = array::API::NumPyName }; \ enum { value = npy_api::NumPyName }; \
static pybind11::dtype dtype() { \
if (auto ptr = npy_api::get().PyArray_DescrFromType_(value)) \
return object(ptr, true); \
pybind11_fail("Unsupported buffer format!"); \
} \
static PYBIND11_DESCR name() { return _(Name); } } static PYBIND11_DESCR name() { return _(Name); } }
DECL_FMT(float, NPY_FLOAT_, "float32"); DECL_FMT(double, NPY_DOUBLE_, "float64"); DECL_FMT(bool, NPY_BOOL_, "bool"); DECL_FMT(float, NPY_FLOAT_, "float32");
DECL_FMT(std::complex<float>, NPY_CFLOAT_, "complex64"); DECL_FMT(std::complex<double>, NPY_CDOUBLE_, "complex128"); DECL_FMT(double, NPY_DOUBLE_, "float64");
DECL_FMT(bool, NPY_BOOL_, "bool");
DECL_FMT(std::complex<float>, NPY_CFLOAT_, "complex64");
DECL_FMT(std::complex<double>, NPY_CDOUBLE_, "complex128");
#undef DECL_FMT #undef DECL_FMT
#define DECL_CHAR_FMT \
static PYBIND11_DESCR name() { return _("S") + _<N>(); } \
static pybind11::dtype dtype() { \
PYBIND11_DESCR fmt = _("S") + _<N>(); \
return pybind11::dtype(fmt.text()); \
} \
static const char *format() { PYBIND11_DESCR s = _<N>() + _("s"); return s.text(); }
template <size_t N> struct npy_format_descriptor<char[N]> { DECL_CHAR_FMT };
template <size_t N> struct npy_format_descriptor<std::array<char, N>> { DECL_CHAR_FMT };
#undef DECL_CHAR_FMT
struct field_descriptor {
const char *name;
size_t offset;
size_t size;
const char *format;
dtype descr;
};
template <typename T>
struct npy_format_descriptor<T, typename std::enable_if<is_pod_struct<T>::value>::type> {
static PYBIND11_DESCR name() { return _("struct"); }
static pybind11::dtype dtype() {
if (!dtype_())
pybind11_fail("NumPy: unsupported buffer format!");
return object(dtype_(), true);
}
static const char* format() {
if (!dtype_())
pybind11_fail("NumPy: unsupported buffer format!");
return format_().c_str();
}
static void register_dtype(std::initializer_list<field_descriptor> fields) {
list names, formats, offsets;
for (auto field : fields) {
if (!field.descr)
pybind11_fail("NumPy: unsupported field dtype");
names.append(PYBIND11_STR_TYPE(field.name));
formats.append(field.descr);
offsets.append(int_(field.offset));
}
dtype_() = pybind11::dtype(names, formats, offsets, sizeof(T)).release().ptr();
// There is an existing bug in NumPy (as of v1.11): trailing bytes are
// not encoded explicitly into the format string. This will supposedly
// get fixed in v1.12; for further details, see these:
// - https://github.com/numpy/numpy/issues/7797
// - https://github.com/numpy/numpy/pull/7798
// Because of this, we won't use numpy's logic to generate buffer format
// strings and will just do it ourselves.
std::vector<field_descriptor> ordered_fields(fields);
std::sort(ordered_fields.begin(), ordered_fields.end(),
[](const field_descriptor& a, const field_descriptor &b) {
return a.offset < b.offset;
});
size_t offset = 0;
std::ostringstream oss;
oss << "T{";
for (auto& field : ordered_fields) {
if (field.offset > offset)
oss << (field.offset - offset) << 'x';
// note that '=' is required to cover the case of unaligned fields
oss << '=' << field.format << ':' << field.name << ':';
offset = field.offset + field.size;
}
if (sizeof(T) > offset)
oss << (sizeof(T) - offset) << 'x';
oss << '}';
format_() = oss.str();
// Sanity check: verify that NumPy properly parses our buffer format string
auto& api = npy_api::get();
auto arr = array(buffer_info(nullptr, sizeof(T), format(), 1, { 0 }, { sizeof(T) }));
if (!api.PyArray_EquivTypes_(dtype_(), arr.dtype().ptr()))
pybind11_fail("NumPy: invalid buffer descriptor!");
}
private:
static inline PyObject*& dtype_() { static PyObject *ptr = nullptr; return ptr; }
static inline std::string& format_() { static std::string s; return s; }
};
// Extract name, offset and format descriptor for a struct field
#define PYBIND11_FIELD_DESCRIPTOR(Type, Field) \
::pybind11::detail::field_descriptor { \
#Field, offsetof(Type, Field), sizeof(decltype(static_cast<Type*>(0)->Field)), \
::pybind11::format_descriptor<decltype(static_cast<Type*>(0)->Field)>::format(), \
::pybind11::detail::npy_format_descriptor<decltype(static_cast<Type*>(0)->Field)>::dtype() \
}
// The main idea of this macro is borrowed from https://github.com/swansontec/map-macro
// (C) William Swanson, Paul Fultz
#define PYBIND11_EVAL0(...) __VA_ARGS__
#define PYBIND11_EVAL1(...) PYBIND11_EVAL0 (PYBIND11_EVAL0 (PYBIND11_EVAL0 (__VA_ARGS__)))
#define PYBIND11_EVAL2(...) PYBIND11_EVAL1 (PYBIND11_EVAL1 (PYBIND11_EVAL1 (__VA_ARGS__)))
#define PYBIND11_EVAL3(...) PYBIND11_EVAL2 (PYBIND11_EVAL2 (PYBIND11_EVAL2 (__VA_ARGS__)))
#define PYBIND11_EVAL4(...) PYBIND11_EVAL3 (PYBIND11_EVAL3 (PYBIND11_EVAL3 (__VA_ARGS__)))
#define PYBIND11_EVAL(...) PYBIND11_EVAL4 (PYBIND11_EVAL4 (PYBIND11_EVAL4 (__VA_ARGS__)))
#define PYBIND11_MAP_END(...)
#define PYBIND11_MAP_OUT
#define PYBIND11_MAP_COMMA ,
#define PYBIND11_MAP_GET_END() 0, PYBIND11_MAP_END
#define PYBIND11_MAP_NEXT0(test, next, ...) next PYBIND11_MAP_OUT
#define PYBIND11_MAP_NEXT1(test, next) PYBIND11_MAP_NEXT0 (test, next, 0)
#define PYBIND11_MAP_NEXT(test, next) PYBIND11_MAP_NEXT1 (PYBIND11_MAP_GET_END test, next)
#ifdef _MSC_VER // MSVC is not as eager to expand macros, hence this workaround
#define PYBIND11_MAP_LIST_NEXT1(test, next) \
PYBIND11_EVAL0 (PYBIND11_MAP_NEXT0 (test, PYBIND11_MAP_COMMA next, 0))
#else
#define PYBIND11_MAP_LIST_NEXT1(test, next) \
PYBIND11_MAP_NEXT0 (test, PYBIND11_MAP_COMMA next, 0)
#endif
#define PYBIND11_MAP_LIST_NEXT(test, next) \
PYBIND11_MAP_LIST_NEXT1 (PYBIND11_MAP_GET_END test, next)
#define PYBIND11_MAP_LIST0(f, t, x, peek, ...) \
f(t, x) PYBIND11_MAP_LIST_NEXT (peek, PYBIND11_MAP_LIST1) (f, t, peek, __VA_ARGS__)
#define PYBIND11_MAP_LIST1(f, t, x, peek, ...) \
f(t, x) PYBIND11_MAP_LIST_NEXT (peek, PYBIND11_MAP_LIST0) (f, t, peek, __VA_ARGS__)
// PYBIND11_MAP_LIST(f, t, a1, a2, ...) expands to f(t, a1), f(t, a2), ...
#define PYBIND11_MAP_LIST(f, t, ...) \
PYBIND11_EVAL (PYBIND11_MAP_LIST1 (f, t, __VA_ARGS__, (), 0))
#define PYBIND11_NUMPY_DTYPE(Type, ...) \
::pybind11::detail::npy_format_descriptor<Type>::register_dtype \
({PYBIND11_MAP_LIST (PYBIND11_FIELD_DESCRIPTOR, Type, __VA_ARGS__)})
template <class T> template <class T>
using array_iterator = typename std::add_pointer<T>::type; using array_iterator = typename std::add_pointer<T>::type;
@ -348,7 +674,7 @@ struct vectorize_helper {
return cast(f(*((Args *) buffers[Index].ptr)...)); return cast(f(*((Args *) buffers[Index].ptr)...));
array result(buffer_info(nullptr, sizeof(Return), array result(buffer_info(nullptr, sizeof(Return),
format_descriptor<Return>::value, format_descriptor<Return>::format(),
ndim, shape, strides)); ndim, shape, strides));
buffer_info buf = result.request(); buffer_info buf = result.request();

View File

@ -339,12 +339,14 @@ inline iterator handle::begin() const { return iterator(PyObject_GetIter(ptr()),
inline iterator handle::end() const { return iterator(nullptr, false); } inline iterator handle::end() const { return iterator(nullptr, false); }
inline detail::args_proxy handle::operator*() const { return detail::args_proxy(*this); } inline detail::args_proxy handle::operator*() const { return detail::args_proxy(*this); }
class bytes;
class str : public object { class str : public object {
public: public:
PYBIND11_OBJECT_DEFAULT(str, object, detail::PyUnicode_Check_Permissive) PYBIND11_OBJECT_DEFAULT(str, object, detail::PyUnicode_Check_Permissive)
str(const std::string &s) str(const char *c, size_t n)
: object(PyUnicode_FromStringAndSize(s.c_str(), (ssize_t) s.length()), false) { : object(PyUnicode_FromStringAndSize(c, (ssize_t) n), false) {
if (!m_ptr) pybind11_fail("Could not allocate string object!"); if (!m_ptr) pybind11_fail("Could not allocate string object!");
} }
@ -353,6 +355,10 @@ public:
if (!m_ptr) pybind11_fail("Could not allocate string object!"); if (!m_ptr) pybind11_fail("Could not allocate string object!");
} }
str(const std::string &s) : str(s.data(), s.size()) { }
str(const bytes &b);
operator std::string() const { operator std::string() const {
object temp = *this; object temp = *this;
if (PyUnicode_Check(m_ptr)) { if (PyUnicode_Check(m_ptr)) {
@ -362,8 +368,7 @@ public:
} }
char *buffer; char *buffer;
ssize_t length; ssize_t length;
int err = PYBIND11_BYTES_AS_STRING_AND_SIZE(temp.ptr(), &buffer, &length); if (PYBIND11_BYTES_AS_STRING_AND_SIZE(temp.ptr(), &buffer, &length))
if (err == -1)
pybind11_fail("Unable to extract string contents! (invalid type)"); pybind11_fail("Unable to extract string contents! (invalid type)");
return std::string(buffer, (size_t) length); return std::string(buffer, (size_t) length);
} }
@ -382,21 +387,57 @@ class bytes : public object {
public: public:
PYBIND11_OBJECT_DEFAULT(bytes, object, PYBIND11_BYTES_CHECK) PYBIND11_OBJECT_DEFAULT(bytes, object, PYBIND11_BYTES_CHECK)
bytes(const std::string &s) bytes(const char *c)
: object(PYBIND11_BYTES_FROM_STRING_AND_SIZE(s.data(), (ssize_t) s.size()), false) { : object(PYBIND11_BYTES_FROM_STRING(c), false) {
if (!m_ptr) pybind11_fail("Could not allocate bytes object!"); if (!m_ptr) pybind11_fail("Could not allocate bytes object!");
} }
bytes(const char *c, size_t n)
: object(PYBIND11_BYTES_FROM_STRING_AND_SIZE(c, (ssize_t) n), false) {
if (!m_ptr) pybind11_fail("Could not allocate bytes object!");
}
bytes(const std::string &s) : bytes(s.data(), s.size()) { }
bytes(const pybind11::str &s);
operator std::string() const { operator std::string() const {
char *buffer; char *buffer;
ssize_t length; ssize_t length;
int err = PYBIND11_BYTES_AS_STRING_AND_SIZE(m_ptr, &buffer, &length); if (PYBIND11_BYTES_AS_STRING_AND_SIZE(m_ptr, &buffer, &length))
if (err == -1)
pybind11_fail("Unable to extract bytes contents!"); pybind11_fail("Unable to extract bytes contents!");
return std::string(buffer, (size_t) length); return std::string(buffer, (size_t) length);
} }
}; };
inline bytes::bytes(const pybind11::str &s) {
object temp = s;
if (PyUnicode_Check(s.ptr())) {
temp = object(PyUnicode_AsUTF8String(s.ptr()), false);
if (!temp)
pybind11_fail("Unable to extract string contents! (encoding issue)");
}
char *buffer;
ssize_t length;
if (PYBIND11_BYTES_AS_STRING_AND_SIZE(temp.ptr(), &buffer, &length))
pybind11_fail("Unable to extract string contents! (invalid type)");
auto obj = object(PYBIND11_BYTES_FROM_STRING_AND_SIZE(buffer, length), false);
if (!obj)
pybind11_fail("Could not allocate bytes object!");
m_ptr = obj.release().ptr();
}
inline str::str(const bytes& b) {
char *buffer;
ssize_t length;
if (PYBIND11_BYTES_AS_STRING_AND_SIZE(b.ptr(), &buffer, &length))
pybind11_fail("Unable to extract bytes contents!");
auto obj = object(PyUnicode_FromStringAndSize(buffer, (ssize_t) length), false);
if (!obj)
pybind11_fail("Could not allocate string object!");
m_ptr = obj.release().ptr();
}
class none : public object { class none : public object {
public: public:
PYBIND11_OBJECT(none, object, detail::PyNone_Check) PYBIND11_OBJECT(none, object, detail::PyNone_Check)
@ -570,6 +611,38 @@ public:
} }
}; };
class memoryview : public object {
public:
memoryview(const buffer_info& info) {
static Py_buffer buf { };
// Py_buffer uses signed sizes, strides and shape!..
static std::vector<Py_ssize_t> py_strides { };
static std::vector<Py_ssize_t> py_shape { };
buf.buf = info.ptr;
buf.itemsize = (Py_ssize_t) info.itemsize;
buf.format = const_cast<char *>(info.format.c_str());
buf.ndim = (int) info.ndim;
buf.len = (Py_ssize_t) info.size;
py_strides.clear();
py_shape.clear();
for (size_t i = 0; i < info.ndim; ++i) {
py_strides.push_back((Py_ssize_t) info.strides[i]);
py_shape.push_back((Py_ssize_t) info.shape[i]);
}
buf.strides = py_strides.data();
buf.shape = py_shape.data();
buf.suboffsets = nullptr;
buf.readonly = false;
buf.internal = nullptr;
m_ptr = PyMemoryView_FromBuffer(&buf);
if (!m_ptr)
pybind11_fail("Unable to create memoryview from buffer descriptor");
}
PYBIND11_OBJECT_DEFAULT(memoryview, object, PyMemoryView_Check)
};
inline size_t len(handle h) { inline size_t len(handle h) {
ssize_t result = PyObject_Length(h.ptr()); ssize_t result = PyObject_Length(h.ptr());
if (result < 0) if (result < 0)