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`).
This commit is contained in:
Dean Moldovan 2017-09-01 21:42:20 +02:00
parent 43126201a6
commit 3c4933cb50
8 changed files with 27 additions and 9 deletions

View File

@ -17,6 +17,11 @@ v2.2.1 (Not yet released)
* Fixed compilation with Clang on host GCC < 5 (old libstdc++ which isn't fully * 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>`_. 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 * Fixed a regression where the ``py::keep_alive`` policy could not be applied
to constructors. `#1065 <https://github.com/pybind/pybind11/pull/1065>`_. to constructors. `#1065 <https://github.com/pybind/pybind11/pull/1065>`_.

View File

@ -83,7 +83,7 @@ template <typename Type, typename Key> struct set_caster {
template <typename T> template <typename T>
static handle cast(T &&src, return_value_policy policy, handle parent) { static handle cast(T &&src, return_value_policy policy, handle parent) {
pybind11::set s; 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)); auto value_ = reinterpret_steal<object>(key_conv::cast(forward_like<T>(value), policy, parent));
if (!value_ || !s.add(value_)) if (!value_ || !s.add(value_))
return handle(); return handle();
@ -117,7 +117,7 @@ template <typename Type, typename Key, typename Value> struct map_caster {
template <typename T> template <typename T>
static handle cast(T &&src, return_value_policy policy, handle parent) { static handle cast(T &&src, return_value_policy policy, handle parent) {
dict d; 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 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)); auto value = reinterpret_steal<object>(value_conv::cast(forward_like<T>(kv.second), policy, parent));
if (!key || !value) if (!key || !value)
@ -159,7 +159,7 @@ public:
static handle cast(T &&src, return_value_policy policy, handle parent) { static handle cast(T &&src, return_value_policy policy, handle parent) {
list l(src.size()); list l(src.size());
size_t index = 0; 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)); auto value_ = reinterpret_steal<object>(value_conv::cast(forward_like<T>(value), policy, parent));
if (!value_) if (!value_)
return handle(); return handle();
@ -213,7 +213,7 @@ public:
static handle cast(T &&src, return_value_policy policy, handle parent) { static handle cast(T &&src, return_value_policy policy, handle parent) {
list l(src.size()); list l(src.size());
size_t index = 0; 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)); auto value_ = reinterpret_steal<object>(value_conv::cast(forward_like<T>(value), policy, parent));
if (!value_) if (!value_)
return handle(); return handle();

View File

@ -76,6 +76,7 @@ string(REPLACE ".cpp" ".py" PYBIND11_PYTEST_FILES "${PYBIND11_TEST_FILES}")
set(PYBIND11_CROSS_MODULE_TESTS set(PYBIND11_CROSS_MODULE_TESTS
test_exceptions.py test_exceptions.py
test_local_bindings.py test_local_bindings.py
test_stl_binders.py
) )
# Check if Eigen is available; if not, remove from PYBIND11_TEST_FILES (but # Check if Eigen is available; if not, remove from PYBIND11_TEST_FILES (but

View File

@ -104,4 +104,10 @@ PYBIND11_MODULE(pybind11_cross_module_tests, m) {
m.def("get_gl_value", [](MixGL &o) { return o.i + 100; }); m.def("get_gl_value", [](MixGL &o) { return o.i + 100; });
py::class_<MixGL2>(m, "MixGL2", py::module_local()).def(py::init<int>()); 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");
} }

View File

@ -48,6 +48,11 @@ TEST_SUBMODULE(stl, m) {
// test_vector // test_vector
m.def("cast_vector", []() { return std::vector<int>{1}; }); 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; }); 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 // Unnumbered regression (caused by #936): pointers to stl containers aren't castable
static std::vector<RValueCaster> lvv{2}; static std::vector<RValueCaster> lvv{2};
m.def("cast_ptr_vector", []() { return &lvv; }); m.def("cast_ptr_vector", []() { return &lvv; });

View File

@ -12,6 +12,9 @@ def test_vector(doc):
assert m.load_vector(l) assert m.load_vector(l)
assert m.load_vector(tuple(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.cast_vector) == "cast_vector() -> List[int]"
assert doc(m.load_vector) == "load_vector(arg0: List[int]) -> bool" assert doc(m.load_vector) == "load_vector(arg0: List[int]) -> bool"

View File

@ -55,13 +55,9 @@ template <class Map> Map *times_ten(int n) {
} }
TEST_SUBMODULE(stl_binders, m) { TEST_SUBMODULE(stl_binders, m) {
// test_vector_int // test_vector_int
py::bind_vector<std::vector<unsigned int>>(m, "VectorInt", py::buffer_protocol()); 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 // test_vector_custom
py::class_<El>(m, "El") py::class_<El>(m, "El")
.def(py::init<int>()); .def(py::init<int>());

View File

@ -86,7 +86,9 @@ def test_vector_buffer_numpy():
def test_vector_bool(): def test_vector_bool():
vv_c = m.VectorBool() import pybind11_cross_module_tests as cm
vv_c = cm.VectorBool()
for i in range(10): for i in range(10):
vv_c.append(i % 2 == 0) vv_c.append(i % 2 == 0)
for i in range(10): for i in range(10):