Skip to content

Commit f33da18

Browse files
committed
Merge branch 'master' into sh_merge_master
2 parents c8953eb + 777352f commit f33da18

File tree

5 files changed

+158
-28
lines changed

5 files changed

+158
-28
lines changed

include/pybind11/detail/common.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,12 @@ PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
410410
using ssize_t = Py_ssize_t;
411411
using size_t = std::size_t;
412412

413+
template <typename IntType>
414+
inline ssize_t ssize_t_cast(const IntType &val) {
415+
static_assert(sizeof(IntType) <= sizeof(ssize_t), "Implicit narrowing is not permitted.");
416+
return static_cast<ssize_t>(val);
417+
}
418+
413419
/// Approach used to cast a previously unknown C++ instance into a Python object
414420
enum class return_value_policy : uint8_t {
415421
/** This is the default return value policy, which falls back to the policy

include/pybind11/pytypes.h

Lines changed: 42 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -661,15 +661,17 @@ struct generic_item {
661661
struct sequence_item {
662662
using key_type = size_t;
663663

664-
static object get(handle obj, size_t index) {
665-
PyObject *result = PySequence_GetItem(obj.ptr(), static_cast<ssize_t>(index));
664+
template <typename IdxType, detail::enable_if_t<std::is_integral<IdxType>::value, int> = 0>
665+
static object get(handle obj, const IdxType &index) {
666+
PyObject *result = PySequence_GetItem(obj.ptr(), ssize_t_cast(index));
666667
if (!result) { throw error_already_set(); }
667668
return reinterpret_steal<object>(result);
668669
}
669670

670-
static void set(handle obj, size_t index, handle val) {
671+
template <typename IdxType, detail::enable_if_t<std::is_integral<IdxType>::value, int> = 0>
672+
static void set(handle obj, const IdxType &index, handle val) {
671673
// PySequence_SetItem does not steal a reference to 'val'
672-
if (PySequence_SetItem(obj.ptr(), static_cast<ssize_t>(index), val.ptr()) != 0) {
674+
if (PySequence_SetItem(obj.ptr(), ssize_t_cast(index), val.ptr()) != 0) {
673675
throw error_already_set();
674676
}
675677
}
@@ -678,15 +680,17 @@ struct sequence_item {
678680
struct list_item {
679681
using key_type = size_t;
680682

681-
static object get(handle obj, size_t index) {
682-
PyObject *result = PyList_GetItem(obj.ptr(), static_cast<ssize_t>(index));
683+
template <typename IdxType, detail::enable_if_t<std::is_integral<IdxType>::value, int> = 0>
684+
static object get(handle obj, const IdxType &index) {
685+
PyObject *result = PyList_GetItem(obj.ptr(), ssize_t_cast(index));
683686
if (!result) { throw error_already_set(); }
684687
return reinterpret_borrow<object>(result);
685688
}
686689

687-
static void set(handle obj, size_t index, handle val) {
690+
template <typename IdxType, detail::enable_if_t<std::is_integral<IdxType>::value, int> = 0>
691+
static void set(handle obj, const IdxType &index, handle val) {
688692
// PyList_SetItem steals a reference to 'val'
689-
if (PyList_SetItem(obj.ptr(), static_cast<ssize_t>(index), val.inc_ref().ptr()) != 0) {
693+
if (PyList_SetItem(obj.ptr(), ssize_t_cast(index), val.inc_ref().ptr()) != 0) {
690694
throw error_already_set();
691695
}
692696
}
@@ -695,15 +699,17 @@ struct list_item {
695699
struct tuple_item {
696700
using key_type = size_t;
697701

698-
static object get(handle obj, size_t index) {
699-
PyObject *result = PyTuple_GetItem(obj.ptr(), static_cast<ssize_t>(index));
702+
template <typename IdxType, detail::enable_if_t<std::is_integral<IdxType>::value, int> = 0>
703+
static object get(handle obj, const IdxType &index) {
704+
PyObject *result = PyTuple_GetItem(obj.ptr(), ssize_t_cast(index));
700705
if (!result) { throw error_already_set(); }
701706
return reinterpret_borrow<object>(result);
702707
}
703708

704-
static void set(handle obj, size_t index, handle val) {
709+
template <typename IdxType, detail::enable_if_t<std::is_integral<IdxType>::value, int> = 0>
710+
static void set(handle obj, const IdxType &index, handle val) {
705711
// PyTuple_SetItem steals a reference to 'val'
706-
if (PyTuple_SetItem(obj.ptr(), static_cast<ssize_t>(index), val.inc_ref().ptr()) != 0) {
712+
if (PyTuple_SetItem(obj.ptr(), ssize_t_cast(index), val.inc_ref().ptr()) != 0) {
707713
throw error_already_set();
708714
}
709715
}
@@ -1043,8 +1049,9 @@ class str : public object {
10431049
public:
10441050
PYBIND11_OBJECT_CVT(str, object, PYBIND11_STR_CHECK_FUN, raw_str)
10451051

1046-
str(const char *c, size_t n)
1047-
: object(PyUnicode_FromStringAndSize(c, (ssize_t) n), stolen_t{}) {
1052+
template <typename SzType, detail::enable_if_t<std::is_integral<SzType>::value, int> = 0>
1053+
str(const char *c, const SzType &n)
1054+
: object(PyUnicode_FromStringAndSize(c, ssize_t_cast(n)), stolen_t{}) {
10481055
if (!m_ptr) pybind11_fail("Could not allocate string object!");
10491056
}
10501057

@@ -1116,8 +1123,9 @@ class bytes : public object {
11161123
if (!m_ptr) pybind11_fail("Could not allocate bytes object!");
11171124
}
11181125

1119-
bytes(const char *c, size_t n)
1120-
: object(PYBIND11_BYTES_FROM_STRING_AND_SIZE(c, (ssize_t) n), stolen_t{}) {
1126+
template <typename SzType, detail::enable_if_t<std::is_integral<SzType>::value, int> = 0>
1127+
bytes(const char *c, const SzType &n)
1128+
: object(PYBIND11_BYTES_FROM_STRING_AND_SIZE(c, ssize_t_cast(n)), stolen_t{}) {
11211129
if (!m_ptr) pybind11_fail("Could not allocate bytes object!");
11221130
}
11231131

@@ -1160,7 +1168,7 @@ inline str::str(const bytes& b) {
11601168
ssize_t length = 0;
11611169
if (PYBIND11_BYTES_AS_STRING_AND_SIZE(b.ptr(), &buffer, &length))
11621170
pybind11_fail("Unable to extract bytes contents!");
1163-
auto obj = reinterpret_steal<object>(PyUnicode_FromStringAndSize(buffer, (ssize_t) length));
1171+
auto obj = reinterpret_steal<object>(PyUnicode_FromStringAndSize(buffer, length));
11641172
if (!obj)
11651173
pybind11_fail("Could not allocate string object!");
11661174
m_ptr = obj.release().ptr();
@@ -1172,8 +1180,9 @@ class bytearray : public object {
11721180
public:
11731181
PYBIND11_OBJECT_CVT(bytearray, object, PyByteArray_Check, PyByteArray_FromObject)
11741182

1175-
bytearray(const char *c, size_t n)
1176-
: object(PyByteArray_FromStringAndSize(c, (ssize_t) n), stolen_t{}) {
1183+
template <typename SzType, detail::enable_if_t<std::is_integral<SzType>::value, int> = 0>
1184+
bytearray(const char *c, const SzType &n)
1185+
: object(PyByteArray_FromStringAndSize(c, ssize_t_cast(n)), stolen_t{}) {
11771186
if (!m_ptr) pybind11_fail("Could not allocate bytearray object!");
11781187
}
11791188

@@ -1398,7 +1407,10 @@ class capsule : public object {
13981407
class tuple : public object {
13991408
public:
14001409
PYBIND11_OBJECT_CVT(tuple, object, PyTuple_Check, PySequence_Tuple)
1401-
explicit tuple(size_t size = 0) : object(PyTuple_New((ssize_t) size), stolen_t{}) {
1410+
template <typename SzType = ssize_t,
1411+
detail::enable_if_t<std::is_integral<SzType>::value, int> = 0>
1412+
// Some compilers generate link errors when using `const SzType &` here:
1413+
explicit tuple(SzType size = 0) : object(PyTuple_New(ssize_t_cast(size)), stolen_t{}) {
14021414
if (!m_ptr) pybind11_fail("Could not allocate tuple object!");
14031415
}
14041416
size_t size() const { return (size_t) PyTuple_Size(m_ptr); }
@@ -1467,7 +1479,10 @@ class sequence : public object {
14671479
class list : public object {
14681480
public:
14691481
PYBIND11_OBJECT_CVT(list, object, PyList_Check, PySequence_List)
1470-
explicit list(size_t size = 0) : object(PyList_New((ssize_t) size), stolen_t{}) {
1482+
template <typename SzType = ssize_t,
1483+
detail::enable_if_t<std::is_integral<SzType>::value, int> = 0>
1484+
// Some compilers generate link errors when using `const SzType &` here:
1485+
explicit list(SzType size = 0) : object(PyList_New(ssize_t_cast(size)), stolen_t{}) {
14711486
if (!m_ptr) pybind11_fail("Could not allocate list object!");
14721487
}
14731488
size_t size() const { return (size_t) PyList_Size(m_ptr); }
@@ -1479,9 +1494,12 @@ class list : public object {
14791494
template <typename T> void append(T &&val) /* py-non-const */ {
14801495
PyList_Append(m_ptr, detail::object_or_cast(std::forward<T>(val)).ptr());
14811496
}
1482-
template <typename T> void insert(size_t index, T &&val) /* py-non-const */ {
1483-
PyList_Insert(m_ptr, static_cast<ssize_t>(index),
1484-
detail::object_or_cast(std::forward<T>(val)).ptr());
1497+
template <typename IdxType,
1498+
typename ValType,
1499+
detail::enable_if_t<std::is_integral<IdxType>::value, int> = 0>
1500+
void insert(const IdxType &index, ValType &&val) /* py-non-const */ {
1501+
PyList_Insert(
1502+
m_ptr, ssize_t_cast(index), detail::object_or_cast(std::forward<ValType>(val)).ptr());
14851503
}
14861504
};
14871505

include/pybind11/stl.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -168,12 +168,12 @@ template <typename Type, typename Value> struct list_caster {
168168
if (!std::is_lvalue_reference<T>::value)
169169
policy = return_value_policy_override<Value>::policy(policy);
170170
list l(src.size());
171-
size_t index = 0;
171+
ssize_t index = 0;
172172
for (auto &&value : src) {
173173
auto value_ = reinterpret_steal<object>(value_conv::cast(forward_like<T>(value), policy, parent));
174174
if (!value_)
175175
return handle();
176-
PyList_SET_ITEM(l.ptr(), (ssize_t) index++, value_.release().ptr()); // steals a reference
176+
PyList_SET_ITEM(l.ptr(), index++, value_.release().ptr()); // steals a reference
177177
}
178178
return l.release();
179179
}
@@ -225,12 +225,12 @@ template <typename ArrayType, typename Value, bool Resizable, size_t Size = 0> s
225225
template <typename T>
226226
static handle cast(T &&src, return_value_policy policy, handle parent) {
227227
list l(src.size());
228-
size_t index = 0;
228+
ssize_t index = 0;
229229
for (auto &&value : src) {
230230
auto value_ = reinterpret_steal<object>(value_conv::cast(forward_like<T>(value), policy, parent));
231231
if (!value_)
232232
return handle();
233-
PyList_SET_ITEM(l.ptr(), (ssize_t) index++, value_.release().ptr()); // steals a reference
233+
PyList_SET_ITEM(l.ptr(), index++, value_.release().ptr()); // steals a reference
234234
}
235235
return l.release();
236236
}

tests/test_pytypes.cpp

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@ TEST_SUBMODULE(pytypes, m) {
2020
// test_iterable
2121
m.def("get_iterable", []{return py::iterable();});
2222
// test_list
23+
m.def("list_no_args", []() { return py::list{}; });
24+
m.def("list_ssize_t", []() { return py::list{(py::ssize_t) 0}; });
25+
m.def("list_size_t", []() { return py::list{(py::size_t) 0}; });
26+
m.def("list_insert_ssize_t", [](py::list *l) { return l->insert((py::ssize_t) 1, 83); });
27+
m.def("list_insert_size_t", [](py::list *l) { return l->insert((py::size_t) 3, 57); });
2328
m.def("get_list", []() {
2429
py::list list;
2530
list.append("value");
@@ -71,6 +76,9 @@ TEST_SUBMODULE(pytypes, m) {
7176
[](const py::dict &dict, const char *val) { return dict.contains(val); });
7277

7378
// test_tuple
79+
m.def("tuple_no_args", []() { return py::tuple{}; });
80+
m.def("tuple_ssize_t", []() { return py::tuple{(py::ssize_t) 0}; });
81+
m.def("tuple_size_t", []() { return py::tuple{(py::size_t) 0}; });
7482
m.def("get_tuple", []() { return py::make_tuple(42, py::none(), "spam"); });
7583

7684
#if PY_VERSION_HEX >= 0x03030000
@@ -84,6 +92,8 @@ TEST_SUBMODULE(pytypes, m) {
8492
#endif
8593

8694
// test_str
95+
m.def("str_from_char_ssize_t", []() { return py::str{"red", (py::ssize_t) 3}; });
96+
m.def("str_from_char_size_t", []() { return py::str{"blue", (py::size_t) 4}; });
8797
m.def("str_from_string", []() { return py::str(std::string("baz")); });
8898
m.def("str_from_bytes", []() { return py::str(py::bytes("boo", 3)); });
8999
m.def("str_from_object", [](const py::object& obj) { return py::str(obj); });
@@ -100,10 +110,14 @@ TEST_SUBMODULE(pytypes, m) {
100110
});
101111

102112
// test_bytes
113+
m.def("bytes_from_char_ssize_t", []() { return py::bytes{"green", (py::ssize_t) 5}; });
114+
m.def("bytes_from_char_size_t", []() { return py::bytes{"purple", (py::size_t) 6}; });
103115
m.def("bytes_from_string", []() { return py::bytes(std::string("foo")); });
104116
m.def("bytes_from_str", []() { return py::bytes(py::str("bar", 3)); });
105117

106118
// test bytearray
119+
m.def("bytearray_from_char_ssize_t", []() { return py::bytearray{"$%", (py::ssize_t) 2}; });
120+
m.def("bytearray_from_char_size_t", []() { return py::bytearray{"@$!", (py::size_t) 3}; });
107121
m.def("bytearray_from_string", []() { return py::bytearray(std::string("foo")); });
108122
m.def("bytearray_size", []() { return py::bytearray("foo").size(); });
109123

@@ -447,4 +461,57 @@ TEST_SUBMODULE(pytypes, m) {
447461
m.def("weakref_from_object", [](const py::object &o) { return py::weakref(o); });
448462
m.def("weakref_from_object_and_function",
449463
[](py::object o, py::function f) { return py::weakref(std::move(o), std::move(f)); });
464+
465+
// Tests below this line are for pybind11 IMPLEMENTATION DETAILS:
466+
467+
m.def("sequence_item_get_ssize_t", [](const py::object &o) {
468+
return py::detail::accessor_policies::sequence_item::get(o, (py::ssize_t) 1);
469+
});
470+
m.def("sequence_item_set_ssize_t", [](const py::object &o) {
471+
auto s = py::str{"peppa", 5};
472+
py::detail::accessor_policies::sequence_item::set(o, (py::ssize_t) 1, s);
473+
});
474+
m.def("sequence_item_get_size_t", [](const py::object &o) {
475+
return py::detail::accessor_policies::sequence_item::get(o, (py::size_t) 2);
476+
});
477+
m.def("sequence_item_set_size_t", [](const py::object &o) {
478+
auto s = py::str{"george", 6};
479+
py::detail::accessor_policies::sequence_item::set(o, (py::size_t) 2, s);
480+
});
481+
m.def("list_item_get_ssize_t", [](const py::object &o) {
482+
return py::detail::accessor_policies::list_item::get(o, (py::ssize_t) 3);
483+
});
484+
m.def("list_item_set_ssize_t", [](const py::object &o) {
485+
auto s = py::str{"rebecca", 7};
486+
py::detail::accessor_policies::list_item::set(o, (py::ssize_t) 3, s);
487+
});
488+
m.def("list_item_get_size_t", [](const py::object &o) {
489+
return py::detail::accessor_policies::list_item::get(o, (py::size_t) 4);
490+
});
491+
m.def("list_item_set_size_t", [](const py::object &o) {
492+
auto s = py::str{"richard", 7};
493+
py::detail::accessor_policies::list_item::set(o, (py::size_t) 4, s);
494+
});
495+
m.def("tuple_item_get_ssize_t", [](const py::object &o) {
496+
return py::detail::accessor_policies::tuple_item::get(o, (py::ssize_t) 5);
497+
});
498+
m.def("tuple_item_set_ssize_t", []() {
499+
auto s0 = py::str{"emely", 5};
500+
auto s1 = py::str{"edmond", 6};
501+
auto o = py::tuple{2};
502+
py::detail::accessor_policies::tuple_item::set(o, (py::ssize_t) 0, s0);
503+
py::detail::accessor_policies::tuple_item::set(o, (py::ssize_t) 1, s1);
504+
return o;
505+
});
506+
m.def("tuple_item_get_size_t", [](const py::object &o) {
507+
return py::detail::accessor_policies::tuple_item::get(o, (py::size_t) 6);
508+
});
509+
m.def("tuple_item_set_size_t", []() {
510+
auto s0 = py::str{"candy", 5};
511+
auto s1 = py::str{"cat", 3};
512+
auto o = py::tuple{2};
513+
py::detail::accessor_policies::tuple_item::set(o, (py::size_t) 1, s1);
514+
py::detail::accessor_policies::tuple_item::set(o, (py::size_t) 0, s0);
515+
return o;
516+
});
450517
}

tests/test_pytypes.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,15 @@ def test_iterable(doc):
2323

2424

2525
def test_list(capture, doc):
26+
assert m.list_no_args() == []
27+
assert m.list_ssize_t() == []
28+
assert m.list_size_t() == []
29+
lins = [1, 2]
30+
m.list_insert_ssize_t(lins)
31+
assert lins == [1, 83, 2]
32+
m.list_insert_size_t(lins)
33+
assert lins == [1, 83, 2, 57]
34+
2635
with capture:
2736
lst = m.get_list()
2837
assert lst == ["inserted-0", "overwritten", "inserted-2"]
@@ -100,6 +109,9 @@ def test_dict(capture, doc):
100109

101110

102111
def test_tuple():
112+
assert m.tuple_no_args() == ()
113+
assert m.tuple_ssize_t() == ()
114+
assert m.tuple_size_t() == ()
103115
assert m.get_tuple() == (42, None, "spam")
104116

105117

@@ -113,6 +125,8 @@ def test_simple_namespace():
113125

114126

115127
def test_str(doc):
128+
assert m.str_from_char_ssize_t().encode().decode() == "red"
129+
assert m.str_from_char_size_t().encode().decode() == "blue"
116130
assert m.str_from_string().encode().decode() == "baz"
117131
assert m.str_from_bytes().encode().decode() == "boo"
118132

@@ -157,6 +171,8 @@ def __repr__(self):
157171

158172

159173
def test_bytes(doc):
174+
assert m.bytes_from_char_ssize_t().decode() == "green"
175+
assert m.bytes_from_char_size_t().decode() == "purple"
160176
assert m.bytes_from_string().decode() == "foo"
161177
assert m.bytes_from_str().decode() == "bar"
162178

@@ -166,6 +182,8 @@ def test_bytes(doc):
166182

167183

168184
def test_bytearray(doc):
185+
assert m.bytearray_from_char_ssize_t().decode() == "$%"
186+
assert m.bytearray_from_char_size_t().decode() == "@$!"
169187
assert m.bytearray_from_string().decode() == "foo"
170188
assert m.bytearray_size() == len("foo")
171189

@@ -603,3 +621,24 @@ def callback(wr):
603621
del obj
604622
pytest.gc_collect()
605623
assert callback.called
624+
625+
626+
def test_implementation_details():
627+
lst = [39, 43, 92, 49, 22, 29, 93, 98, 26, 57, 8]
628+
tup = tuple(lst)
629+
assert m.sequence_item_get_ssize_t(lst) == 43
630+
assert m.sequence_item_set_ssize_t(lst) is None
631+
assert lst[1] == "peppa"
632+
assert m.sequence_item_get_size_t(lst) == 92
633+
assert m.sequence_item_set_size_t(lst) is None
634+
assert lst[2] == "george"
635+
assert m.list_item_get_ssize_t(lst) == 49
636+
assert m.list_item_set_ssize_t(lst) is None
637+
assert lst[3] == "rebecca"
638+
assert m.list_item_get_size_t(lst) == 22
639+
assert m.list_item_set_size_t(lst) is None
640+
assert lst[4] == "richard"
641+
assert m.tuple_item_get_ssize_t(tup) == 29
642+
assert m.tuple_item_set_ssize_t() == ("emely", "edmond")
643+
assert m.tuple_item_get_size_t(tup) == 93
644+
assert m.tuple_item_set_size_t() == ("candy", "cat")

0 commit comments

Comments
 (0)