Fix STL casters for containers with proxies (regression)
To avoid an ODR violation in the test suite while testing
both `stl.h` and `std_bind.h` with `std::vector<bool>`,
the `py::bind_vector<std::vector<bool>>` test is moved to
the secondary module (which does not include `stl.h`).
diff --git a/docs/changelog.rst b/docs/changelog.rst
index f03d4f4..dcab3b1 100644
--- a/docs/changelog.rst
+++ b/docs/changelog.rst
@@ -17,6 +17,11 @@
* Fixed compilation with Clang on host GCC < 5 (old libstdc++ which isn't fully
C++11 compliant). `#1062 <https://github.com/pybind/pybind11/pull/1062>`_.
+* Fixed a regression where the automatic ``std::vector<bool>`` caster would
+ fail to compile. The same fix also applies to any container which returns
+ element proxies instead of references.
+ `#1053 <https://github.com/pybind/pybind11/pull/1053>`_.
+
* Fixed a regression where the ``py::keep_alive`` policy could not be applied
to constructors. `#1065 <https://github.com/pybind/pybind11/pull/1065>`_.
diff --git a/include/pybind11/stl.h b/include/pybind11/stl.h
index ac212bf..db900e6 100644
--- a/include/pybind11/stl.h
+++ b/include/pybind11/stl.h
@@ -83,7 +83,7 @@
template <typename T>
static handle cast(T &&src, return_value_policy policy, handle parent) {
pybind11::set s;
- for (auto &value: src) {
+ for (auto &&value : src) {
auto value_ = reinterpret_steal<object>(key_conv::cast(forward_like<T>(value), policy, parent));
if (!value_ || !s.add(value_))
return handle();
@@ -117,7 +117,7 @@
template <typename T>
static handle cast(T &&src, return_value_policy policy, handle parent) {
dict d;
- for (auto &kv: src) {
+ for (auto &&kv : src) {
auto key = reinterpret_steal<object>(key_conv::cast(forward_like<T>(kv.first), policy, parent));
auto value = reinterpret_steal<object>(value_conv::cast(forward_like<T>(kv.second), policy, parent));
if (!key || !value)
@@ -159,7 +159,7 @@
static handle cast(T &&src, return_value_policy policy, handle parent) {
list l(src.size());
size_t index = 0;
- for (auto &value: src) {
+ for (auto &&value : src) {
auto value_ = reinterpret_steal<object>(value_conv::cast(forward_like<T>(value), policy, parent));
if (!value_)
return handle();
@@ -213,7 +213,7 @@
static handle cast(T &&src, return_value_policy policy, handle parent) {
list l(src.size());
size_t index = 0;
- for (auto &value: src) {
+ for (auto &&value : src) {
auto value_ = reinterpret_steal<object>(value_conv::cast(forward_like<T>(value), policy, parent));
if (!value_)
return handle();
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 5232629..d8c53c2 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -76,6 +76,7 @@
set(PYBIND11_CROSS_MODULE_TESTS
test_exceptions.py
test_local_bindings.py
+ test_stl_binders.py
)
# Check if Eigen is available; if not, remove from PYBIND11_TEST_FILES (but
diff --git a/tests/pybind11_cross_module_tests.cpp b/tests/pybind11_cross_module_tests.cpp
index 2091624..7dd70e0 100644
--- a/tests/pybind11_cross_module_tests.cpp
+++ b/tests/pybind11_cross_module_tests.cpp
@@ -104,4 +104,10 @@
m.def("get_gl_value", [](MixGL &o) { return o.i + 100; });
py::class_<MixGL2>(m, "MixGL2", py::module_local()).def(py::init<int>());
+
+ // test_vector_bool
+ // We can't test both stl.h and stl_bind.h conversions of `std::vector<bool>` within
+ // the same module (it would be an ODR violation). Therefore `bind_vector` of `bool`
+ // is defined here and tested in `test_stl_binders.py`.
+ py::bind_vector<std::vector<bool>>(m, "VectorBool");
}
diff --git a/tests/test_stl.cpp b/tests/test_stl.cpp
index 0746fb4..7d53e9c 100644
--- a/tests/test_stl.cpp
+++ b/tests/test_stl.cpp
@@ -48,6 +48,11 @@
// test_vector
m.def("cast_vector", []() { return std::vector<int>{1}; });
m.def("load_vector", [](const std::vector<int> &v) { return v.at(0) == 1 && v.at(1) == 2; });
+ // `std::vector<bool>` is special because it returns proxy objects instead of references
+ m.def("cast_bool_vector", []() { return std::vector<bool>{true, false}; });
+ m.def("load_bool_vector", [](const std::vector<bool> &v) {
+ return v.at(0) == true && v.at(1) == false;
+ });
// Unnumbered regression (caused by #936): pointers to stl containers aren't castable
static std::vector<RValueCaster> lvv{2};
m.def("cast_ptr_vector", []() { return &lvv; });
diff --git a/tests/test_stl.py b/tests/test_stl.py
index c2fa7db..f04eaeb 100644
--- a/tests/test_stl.py
+++ b/tests/test_stl.py
@@ -12,6 +12,9 @@
assert m.load_vector(l)
assert m.load_vector(tuple(l))
+ assert m.cast_bool_vector() == [True, False]
+ assert m.load_bool_vector([True, False])
+
assert doc(m.cast_vector) == "cast_vector() -> List[int]"
assert doc(m.load_vector) == "load_vector(arg0: List[int]) -> bool"
diff --git a/tests/test_stl_binders.cpp b/tests/test_stl_binders.cpp
index 2df6ca0..a88b589 100644
--- a/tests/test_stl_binders.cpp
+++ b/tests/test_stl_binders.cpp
@@ -55,13 +55,9 @@
}
TEST_SUBMODULE(stl_binders, m) {
-
// test_vector_int
py::bind_vector<std::vector<unsigned int>>(m, "VectorInt", py::buffer_protocol());
- // test_vector_bool
- py::bind_vector<std::vector<bool>>(m, "VectorBool");
-
// test_vector_custom
py::class_<El>(m, "El")
.def(py::init<int>());
diff --git a/tests/test_stl_binders.py b/tests/test_stl_binders.py
index 7496d05..bf1aa67 100644
--- a/tests/test_stl_binders.py
+++ b/tests/test_stl_binders.py
@@ -86,7 +86,9 @@
def test_vector_bool():
- vv_c = m.VectorBool()
+ import pybind11_cross_module_tests as cm
+
+ vv_c = cm.VectorBool()
for i in range(10):
vv_c.append(i % 2 == 0)
for i in range(10):