Skip to content

Commit 524c2b7

Browse files
Sync with WIP branch: Limit feature to C++14, disable tests on C++11, fix CI failures
1 parent 31fe60a commit 524c2b7

File tree

6 files changed

+76
-49
lines changed

6 files changed

+76
-49
lines changed

include/pybind11/detail/inference.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ struct type_pack {
4848
using type_at = typename type_at_internal<N>::type;
4949
};
5050

51+
52+
#if defined(PYBIND11_CPP14)
53+
5154
template <typename... A, typename... B>
5255
auto type_pack_concat(type_pack<A...> = {}, type_pack<B...> = {}) {
5356
return type_pack<A..., B...>{};
@@ -136,6 +139,8 @@ struct function_inference {
136139
}
137140
};
138141

142+
#endif // defined(PYBIND_CPP14)
143+
139144
NAMESPACE_END(detail)
140145

141146
NAMESPACE_END(PYBIND11_NAMESPACE)

include/pybind11/detail/numpy_ufunc.h

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
#include "../numpy.h"
1313
#include "inference.h"
1414

15+
#if defined(PYBIND11_CPP14)
16+
1517
NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
1618
NAMESPACE_BEGIN(detail)
1719

@@ -31,11 +33,11 @@ auto ufunc_to_ptr(Func func, type_pack<Arg0, Out>) {
3133
auto ufunc = [](
3234
char** args, npy_intp* dimensions, npy_intp* steps, void* data) {
3335
Func& func = *(Func*)data;
34-
int step_0 = steps[0];
35-
int step_out = steps[1];
36-
int n = *dimensions;
36+
npy_intp step_0 = steps[0];
37+
npy_intp step_out = steps[1];
38+
npy_intp n = *dimensions;
3739
char *in_0 = args[0], *out = args[1];
38-
for (int k = 0; k < n; k++) {
40+
for (npy_intp k = 0; k < n; k++) {
3941
// TODO(eric.cousineau): Support pointers being changed.
4042
*(Out*)out = func(*(Arg0*)in_0);
4143
in_0 += step_0;
@@ -51,12 +53,12 @@ template <typename Arg0, typename Arg1, typename Out, typename Func = void>
5153
auto ufunc_to_ptr(Func func, type_pack<Arg0, Arg1, Out>) {
5254
auto ufunc = [](char** args, npy_intp* dimensions, npy_intp* steps, void* data) {
5355
Func& func = *(Func*)data;
54-
int step_0 = steps[0];
55-
int step_1 = steps[1];
56-
int step_out = steps[2];
57-
int n = *dimensions;
56+
npy_intp step_0 = steps[0];
57+
npy_intp step_1 = steps[1];
58+
npy_intp step_out = steps[2];
59+
npy_intp n = *dimensions;
5860
char *in_0 = args[0], *in_1 = args[1], *out = args[2];
59-
for (int k = 0; k < n; k++) {
61+
for (npy_intp k = 0; k < n; k++) {
6062
// TODO(eric.cousineau): Support pointers being fed in.
6163
*(Out*)out = func(*(Arg0*)in_0, *(Arg1*)in_1);
6264
in_0 += step_0;
@@ -86,7 +88,7 @@ void ufunc_register_cast(
8688
static auto cast_lambda = detail::function_inference::run(func).func;
8789
auto cast_func = +[](
8890
void* from_, void* to_, npy_intp n,
89-
void* fromarr, void* toarr) {
91+
void* /*fromarr*/, void* /*toarr*/) {
9092
const From* from = (From*)from_;
9193
To* to = (To*)to_;
9294
for (npy_intp i = 0; i < n; i++)
@@ -156,6 +158,7 @@ class ufunc : public object {
156158
pybind11_fail("dtype: unspecified name");
157159
// TODO(eric.cousineau): Fix unfreed memory with `name`.
158160
auto leak = new std::string(name_);
161+
char* name = (char*)&(*leak)[0];
159162
// The following dummy stuff is to allow monkey-patching existing ufuncs.
160163
// This is a bit sketchy, as calling the wrong thing may cause a segfault.
161164
// TODO(eric.cousineau): Figure out how to more elegantly specify preallocation...
@@ -179,7 +182,7 @@ class ufunc : public object {
179182
}
180183
auto h = api.PyUFunc_FromFuncAndData_(
181184
dummy_funcs, dummy_data, dummy_types, ntypes,
182-
nin, nout, constants::PyUFunc_None_, &(*leak)[0], "", 0);
185+
nin, nout, constants::PyUFunc_None_, name, nullptr, 0);
183186
self() = reinterpret_borrow<object>((PyObject*)h);
184187
scope_.attr(name_) = self();
185188
}
@@ -209,8 +212,10 @@ class ufunc : public object {
209212
}
210213

211214
// These are only used if we have something new.
212-
const char* name_{};
213215
handle scope_{};
216+
const char* name_{};
214217
};
215218

216219
NAMESPACE_END(PYBIND11_NAMESPACE)
220+
221+
#endif // defined(PYBIND11_CPP14)

include/pybind11/numpy_dtype_user.h

Lines changed: 14 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
#include <utility>
1919
#include <typeindex>
2020

21+
#if defined(PYBIND11_CPP14)
22+
2123
// N.B. For NumPy dtypes, `custom` tends to mean record-like structures, while
2224
// `user-defined` means teaching NumPy about previously opaque C structures.
2325

@@ -109,7 +111,7 @@ struct dtype_user_instance {
109111
}
110112

111113
// Implementation for `tp_new` slot.
112-
static PyObject* tp_new(PyTypeObject* type, PyObject* args, PyObject* kwds) {
114+
static PyObject* tp_new(PyTypeObject* /*type*/, PyObject* /*args*/, PyObject* /*kwds*/) {
113115
// N.B. `__init__` should call the in-place constructor.
114116
auto obj = alloc_py();
115117
// // Register.
@@ -308,7 +310,7 @@ class dtype_user : public class_<Class_> {
308310
}
309311

310312
template <typename ... Args, typename... Extra>
311-
dtype_user& def(detail::initimpl::constructor<Args...>&& init, const Extra&... extra) {
313+
dtype_user& def(detail::initimpl::constructor<Args...>&&, const Extra&... extra) {
312314
// See notes in `add_init`.
313315
// N.B. Do NOT use `Class*` as the argument, since that may incur recursion.
314316
add_init([](object py_self, Args... args) {
@@ -322,7 +324,7 @@ class dtype_user : public class_<Class_> {
322324
template <detail::op_id id, detail::op_type ot,
323325
typename L, typename R, typename... Extra>
324326
dtype_user& def_ufunc(
325-
const detail::op_<id, ot, L, R>& op, const Extra&... extra) {
327+
const detail::op_<id, ot, L, R>&, const Extra&... extra) {
326328
using op_ = detail::op_<id, ot, L, R>;
327329
using op_impl = typename op_::template info<dtype_user>::op;
328330
auto func = &op_impl::execute;
@@ -433,7 +435,6 @@ class dtype_user : public class_<Class_> {
433435
'V', /* kind (V = arbitrary) */
434436
'r', /* type */
435437
'=', /* byteorder */
436-
// TODO(eric.cousineau): NPY_NEEDS_INIT?
437438
npy_api::NPY_NEEDS_PYAPI_ | npy_api::NPY_USE_GETITEM_ |
438439
npy_api::NPY_USE_SETITEM_ |
439440
npy_api::NPY_NEEDS_INIT_, /* flags */
@@ -454,11 +455,11 @@ class dtype_user : public class_<Class_> {
454455
using detail::npy_intp;
455456

456457
// https://docs.scipy.org/doc/numpy/reference/c-api.types-and-structures.html
457-
arrfuncs.getitem = (void*)+[](void* in, void* arr) -> PyObject* {
458+
arrfuncs.getitem = (void*)+[](void* in, void* /*arr*/) -> PyObject* {
458459
auto item = (const Class*)in;
459460
return cast(*item).release().ptr();
460461
};
461-
arrfuncs.setitem = (void*)+[](PyObject* in, void* out, void* arr) {
462+
arrfuncs.setitem = (void*)+[](PyObject* in, void* out, void* /*arr*/) {
462463
detail::dtype_user_caster<Class> caster;
463464
if (!caster.load(in, true)) {
464465
PyErr_SetString(
@@ -470,7 +471,7 @@ class dtype_user : public class_<Class_> {
470471
print("setitem: in = ", handle(in));
471472
return 0;
472473
};
473-
arrfuncs.copyswap = (void*)+[](void* dst, void* src, int swap, void* arr) {
474+
arrfuncs.copyswap = (void*)+[](void* dst, void* src, int swap, void* /*arr*/) {
474475
// TODO(eric.cousineau): Figure out actual purpose of this.
475476
if (!src) return;
476477
Class* r_dst = (Class*)dst;
@@ -484,23 +485,12 @@ class dtype_user : public class_<Class_> {
484485
*r_dst = *r_src;
485486
}
486487
};
487-
// - Test and ensure this doesn't overwrite our `equal` unfunc.
488-
arrfuncs.compare = (void*)+[](const void* d1, const void* d2, void* arr) {
489-
return 0;
488+
// - Ensure this doesn't overwrite our `equal` unfunc.
489+
arrfuncs.compare = (void*)+[](const void* /*d1*/, const void* /*d2*/, void* /*arr*/) {
490+
pybind11_fail("dtype: `compare` should not be called for pybind11 custom dtype");
490491
};
491-
// arrfuncs.fill = (void*)+[](void* data_, npy_intp length, void* arr) {
492-
// Class* data = (Class*)data_;
493-
// Class delta = data[1] - data[0];
494-
// Class r = data[1];
495-
// npy_intp i;
496-
// for (i = 2; i < length; i++) {
497-
// r += delta;
498-
// data[i] = r;
499-
// }
500-
// return 0;
501-
// };
502492
arrfuncs.fillwithscalar = (void*)+[](
503-
void* buffer_raw, npy_intp length, void* value_raw, void* arr) {
493+
void* buffer_raw, npy_intp length, void* value_raw, void* /*arr*/) {
504494
const Class* value = (const Class*)value_raw;
505495
Class* buffer = (Class*)buffer_raw;
506496
for (int k = 0; k < length; k++) {
@@ -529,3 +519,5 @@ NAMESPACE_END(PYBIND11_NAMESPACE)
529519
struct npy_format_descriptor<Type> \
530520
: public dtype_user_npy_format_descriptor<Type> {}; \
531521
}}
522+
523+
#endif // defined(PYBIND11_CPP14)

tests/pybind11_tests.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
#pragma once
2+
3+
#include <functional>
4+
25
#include <pybind11/pybind11.h>
36

47
#if defined(_MSC_VER) && _MSC_VER < 1910

tests/test_numpy_dtype_user.cpp

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,14 @@
1414
#include <pybind11/stl.h>
1515
#include <pybind11/operators.h>
1616
#include <pybind11/numpy_dtype_user.h>
17-
#include <pybind11/embed.h>
1817

19-
using std::make_unique;
2018
using std::string;
2119
using std::unique_ptr;
2220

2321
namespace py = pybind11;
2422

23+
namespace {
24+
2525
// Trivial string class.
2626
class CustomStr {
2727
public:
@@ -43,8 +43,6 @@ class CustomStr {
4343
char buffer[len];
4444
};
4545

46-
PYBIND11_NUMPY_DTYPE_USER(CustomStr);
47-
4846
// Basic structure, meant to be an implicitly convertible value for `Custom`.
4947
// Would have used a struct type, but the scalars are only tuples.
5048
struct SimpleStruct {
@@ -53,14 +51,12 @@ struct SimpleStruct {
5351
SimpleStruct(double value_in) : value(value_in) {}
5452
};
5553

56-
PYBIND11_NUMPY_DTYPE_USER(SimpleStruct);
57-
5854
template <typename T>
5955
void clone(const unique_ptr<T>& src, unique_ptr<T>& dst) {
6056
if (!src)
6157
dst.reset();
6258
else
63-
dst = make_unique<T>(*src);
59+
dst.reset(new T(*src));
6460
}
6561

6662
class Custom {
@@ -75,7 +71,7 @@ class Custom {
7571
print_created(this, value);
7672
}
7773
Custom(double value, string str)
78-
: value_{value}, str_{make_unique<string>(str)} {
74+
: value_{value}, str_{unique_ptr<string>(new string(str))} {
7975
print_created(this, value, str);
8076
}
8177
Custom(const Custom& other) {
@@ -150,6 +146,12 @@ class Custom {
150146
std::unique_ptr<string> str_;
151147
};
152148

149+
} // namespace
150+
151+
#if defined(PYBIND11_CPP14)
152+
153+
PYBIND11_NUMPY_DTYPE_USER(CustomStr);
154+
PYBIND11_NUMPY_DTYPE_USER(SimpleStruct);
153155
PYBIND11_NUMPY_DTYPE_USER(Custom);
154156

155157
TEST_SUBMODULE(numpy_dtype_user, m) {
@@ -165,7 +167,7 @@ TEST_SUBMODULE(numpy_dtype_user, m) {
165167
.def(py::init<const char*>())
166168
.def("__str__", &CustomStr::str)
167169
.def("__repr__", &CustomStr::str)
168-
.def_ufunc_cast([](const CustomStr& in) -> double {
170+
.def_ufunc_cast([](const CustomStr&) -> double {
169171
py::pybind11_fail("Cannot cast");
170172
})
171173
.def(py::self == py::self);
@@ -231,3 +233,11 @@ TEST_SUBMODULE(numpy_dtype_user, m) {
231233
// Define this for checking logicals.
232234
.def_loop<bool>([](bool a, bool b) { return a == b; });
233235
}
236+
237+
#else // defined(PYBIND11_CPP14)
238+
239+
TEST_SUBMODULE(numpy_dtype_user, m) {
240+
m.attr("DISABLED") = true;
241+
}
242+
243+
#endif // defined(PYBIND11_CPP14)

tests/test_numpy_dtype_user.py

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,25 @@
11
import pytest
22

33
from pybind11_tests import ConstructorStats
4+
from pybind11_tests import numpy_dtype_user as m
45

5-
pytestmark = pytest.requires_numpy
6-
6+
np = None
77
with pytest.suppress(ImportError):
88
import numpy as np
9+
import sys
10+
sys.stderr.write("numpy version: {} {}\n".format(
11+
np.version.full_version, np.version.git_revision))
912

10-
from pybind11_tests import numpy_dtype_user as m
11-
stats = ConstructorStats.get(m.Custom)
13+
pytestmark = pytest.mark.skipif(
14+
not np or hasattr(m, "DISABLED"), reason="requires numpy and C++ >= 14")
1215

1316

1417
def test_scalar_meta():
1518
"""Tests basic metadata."""
1619
assert issubclass(m.Custom, np.generic)
1720
assert isinstance(np.dtype(m.Custom), np.dtype)
1821

22+
1923
def test_scalar_ctor():
2024
"""Tests instance lifetime management since we had to redo the instance
2125
registry to inherit from `np.generic` :( """
@@ -26,8 +30,10 @@ def test_scalar_ctor():
2630
del c
2731
del c1
2832
pytest.gc_collect()
33+
stats = ConstructorStats.get(m.Custom)
2934
assert stats.alive() == 0
3035

36+
3137
def test_scalar_op():
3238
"""Tests scalar operators."""
3339
a = m.Custom(1)
@@ -56,6 +62,7 @@ def test_scalar_op():
5662
assert m.same(a == b, m.CustomStr("6 == 2 && '' == ''"))
5763
assert m.same(a < b, False)
5864

65+
5966
def test_array_creation():
6067
# Zeros.
6168
x = np.zeros((2, 2), dtype=m.Custom)
@@ -68,8 +75,10 @@ def test_array_creation():
6875
x = np.array([m.Custom(10), 1.])
6976
assert x.dtype == object
7077
# - At present, we will be leaking memory. This doesn't show up in instance
71-
# count, since these items are only mutated via `operator=`; however, we're
72-
# gonna be leaking.
78+
# count, since these items are only mutated via `operator=`; however, it will
79+
# still be the case for resizing.
80+
# See https://github.com/numpy/numpy/issues/10721 for more information.
81+
7382

7483
def check_array(actual, expected):
7584
"""Checks if two arrays are exactly similar (shape, type, and data)."""
@@ -82,6 +91,7 @@ def check_array(actual, expected):
8291
return False
8392
return True
8493

94+
8595
def test_array_cast():
8696
def check(x, dtype):
8797
dx = x.astype(dtype)
@@ -103,6 +113,7 @@ def check(x, dtype):
103113
dx = check(x, m.Custom)
104114
assert check_array(dx, [m.Custom(1), m.Custom(10), m.Custom(100)])
105115

116+
106117
def test_array_cast_implicit():
107118
a = np.array([1.]).astype(m.Custom)
108119
# - We registered `Custom{} + double{}`.
@@ -139,6 +150,7 @@ def test_array_cast_implicit():
139150
assert not check_array(d, [ds])
140151
assert check_array(d, [m.Custom(100)])
141152

153+
142154
def test_array_ufunc():
143155
x = np.array([m.Custom(4)])
144156
y = np.array([m.Custom(2, "World")])

0 commit comments

Comments
 (0)