Skip to content

Commit 43130fd

Browse files
clrusbywjakob
authored andcommitted
Support more natural syntax for vector extend
1 parent 8462dd7 commit 43130fd

File tree

4 files changed

+63
-1
lines changed

4 files changed

+63
-1
lines changed

docs/changelog.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,11 @@ v2.2.4 (September 11, 2018)
133133
* A few minor typo fixes and improvements to the test suite, and
134134
patches that silence compiler warnings.
135135

136+
* Vectors now support construction from generators, as well as ``extend()`` from a
137+
list or generator.
138+
`#1496 <https://github.com/pybind/pybind11/pull/1496>`_.
139+
140+
136141
v2.2.3 (April 29, 2018)
137142
-----------------------------------------------------
138143

include/pybind11/pytypes.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1346,6 +1346,21 @@ inline size_t len(handle h) {
13461346
return (size_t) result;
13471347
}
13481348

1349+
inline size_t len_hint(handle h) {
1350+
#if PY_VERSION_HEX >= 0x03040000
1351+
ssize_t result = PyObject_LengthHint(h.ptr(), 0);
1352+
#else
1353+
ssize_t result = PyObject_Length(h.ptr());
1354+
#endif
1355+
if (result < 0) {
1356+
// Sometimes a length can't be determined at all (eg generators)
1357+
// In which case simply return 0
1358+
PyErr_Clear();
1359+
return 0;
1360+
}
1361+
return (size_t) result;
1362+
}
1363+
13491364
inline str repr(handle h) {
13501365
PyObject *str_value = PyObject_Repr(h.ptr());
13511366
if (!str_value) throw error_already_set();

include/pybind11/stl_bind.h

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ void vector_modifiers(enable_if_t<is_copy_constructible<typename Vector::value_t
122122

123123
cl.def(init([](iterable it) {
124124
auto v = std::unique_ptr<Vector>(new Vector());
125-
v->reserve(len(it));
125+
v->reserve(len_hint(it));
126126
for (handle h : it)
127127
v->push_back(h.cast<T>());
128128
return v.release();
@@ -136,6 +136,28 @@ void vector_modifiers(enable_if_t<is_copy_constructible<typename Vector::value_t
136136
"Extend the list by appending all the items in the given list"
137137
);
138138

139+
cl.def("extend",
140+
[](Vector &v, iterable it) {
141+
const size_t old_size = v.size();
142+
v.reserve(old_size + len_hint(it));
143+
try {
144+
for (handle h : it) {
145+
v.push_back(h.cast<T>());
146+
}
147+
} catch (const cast_error &) {
148+
v.erase(v.begin() + static_cast<typename Vector::difference_type>(old_size), v.end());
149+
try {
150+
v.shrink_to_fit();
151+
} catch (const std::exception &) {
152+
// Do nothing
153+
}
154+
throw;
155+
}
156+
},
157+
arg("L"),
158+
"Extend the list by appending all the items in the given list"
159+
);
160+
139161
cl.def("insert",
140162
[](Vector &v, SizeType i, const T &x) {
141163
if (i > v.size())

tests/test_stl_binders.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ def test_vector_int():
1111
assert len(v_int) == 2
1212
assert bool(v_int) is True
1313

14+
# test construction from a generator
15+
v_int1 = m.VectorInt(x for x in range(5))
16+
assert v_int1 == m.VectorInt([0, 1, 2, 3, 4])
17+
1418
v_int2 = m.VectorInt([0, 0])
1519
assert v_int == v_int2
1620
v_int2[1] = 1
@@ -33,6 +37,22 @@ def test_vector_int():
3337
del v_int2[0]
3438
assert v_int2 == m.VectorInt([0, 99, 2, 3])
3539

40+
v_int2.extend(m.VectorInt([4, 5]))
41+
assert v_int2 == m.VectorInt([0, 99, 2, 3, 4, 5])
42+
43+
v_int2.extend([6, 7])
44+
assert v_int2 == m.VectorInt([0, 99, 2, 3, 4, 5, 6, 7])
45+
46+
# test error handling, and that the vector is unchanged
47+
with pytest.raises(RuntimeError):
48+
v_int2.extend([8, 'a'])
49+
50+
assert v_int2 == m.VectorInt([0, 99, 2, 3, 4, 5, 6, 7])
51+
52+
# test extending from a generator
53+
v_int2.extend(x for x in range(5))
54+
assert v_int2 == m.VectorInt([0, 99, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4])
55+
3656

3757
# related to the PyPy's buffer protocol.
3858
@pytest.unsupported_on_pypy

0 commit comments

Comments
 (0)