diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 3ced297bd..f88cc54ad 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -126,6 +126,7 @@ set(PYBIND11_TEST_FILES test_class_sh_inheritance test_class_sh_mi_thunks test_class_sh_property + test_class_sh_property_non_owning test_class_sh_shared_ptr_copy_move test_class_sh_trampoline_basic test_class_sh_trampoline_self_life_support diff --git a/tests/test_class_sh_property_non_owning.cpp b/tests/test_class_sh_property_non_owning.cpp new file mode 100644 index 000000000..cd7fc5c60 --- /dev/null +++ b/tests/test_class_sh_property_non_owning.cpp @@ -0,0 +1,68 @@ +#include "pybind11/smart_holder.h" +#include "pybind11_tests.h" + +#include +#include + +namespace test_class_sh_property_non_owning { + +struct CoreField { + explicit CoreField(int int_value = -99) : int_value{int_value} {} + int int_value; +}; + +struct DataField { + DataField(int i_value, int i_shared, int i_unique) + : core_fld_value{i_value}, core_fld_shared_ptr{new CoreField{i_shared}}, + core_fld_raw_ptr{core_fld_shared_ptr.get()}, + core_fld_unique_ptr{new CoreField{i_unique}} {} + CoreField core_fld_value; + std::shared_ptr core_fld_shared_ptr; + CoreField *core_fld_raw_ptr; + std::unique_ptr core_fld_unique_ptr; +}; + +struct DataFieldsHolder { +private: + std::vector vec; + +public: + explicit DataFieldsHolder(std::size_t vec_size) { + for (std::size_t i = 0; i < vec_size; i++) { + int i11 = static_cast(i) * 11; + vec.emplace_back(13 + i11, 14 + i11, 15 + i11); + } + } + + DataField *vec_at(std::size_t index) { + if (index >= vec.size()) { + return nullptr; + } + return &vec[index]; + } +}; + +} // namespace test_class_sh_property_non_owning + +using namespace test_class_sh_property_non_owning; + +PYBIND11_SMART_HOLDER_TYPE_CASTERS(CoreField) +PYBIND11_SMART_HOLDER_TYPE_CASTERS(DataField) +PYBIND11_SMART_HOLDER_TYPE_CASTERS(DataFieldsHolder) + +TEST_SUBMODULE(class_sh_property_non_owning, m) { + py::classh(m, "CoreField").def_readwrite("int_value", &CoreField::int_value); + + py::classh(m, "DataField") + .def_readonly("core_fld_value_ro", &DataField::core_fld_value) + .def_readwrite("core_fld_value_rw", &DataField::core_fld_value) + .def_readonly("core_fld_shared_ptr_ro", &DataField::core_fld_shared_ptr) + .def_readwrite("core_fld_shared_ptr_rw", &DataField::core_fld_shared_ptr) + .def_readonly("core_fld_raw_ptr_ro", &DataField::core_fld_raw_ptr) + .def_readwrite("core_fld_raw_ptr_rw", &DataField::core_fld_raw_ptr) + .def_readwrite("core_fld_unique_ptr_rw", &DataField::core_fld_unique_ptr); + + py::classh(m, "DataFieldsHolder") + .def(py::init()) + .def("vec_at", &DataFieldsHolder::vec_at, py::return_value_policy::reference_internal); +} diff --git a/tests/test_class_sh_property_non_owning.py b/tests/test_class_sh_property_non_owning.py new file mode 100644 index 000000000..33a9d4503 --- /dev/null +++ b/tests/test_class_sh_property_non_owning.py @@ -0,0 +1,30 @@ +from __future__ import annotations + +import pytest + +from pybind11_tests import class_sh_property_non_owning as m + + +@pytest.mark.parametrize("persistent_holder", [True, False]) +@pytest.mark.parametrize( + ("core_fld", "expected"), + [ + ("core_fld_value_ro", (13, 24)), + ("core_fld_value_rw", (13, 24)), + ("core_fld_shared_ptr_ro", (14, 25)), + ("core_fld_shared_ptr_rw", (14, 25)), + ("core_fld_raw_ptr_ro", (14, 25)), + ("core_fld_raw_ptr_rw", (14, 25)), + ("core_fld_unique_ptr_rw", (15, 26)), + ], +) +def test_core_fld_common(core_fld, expected, persistent_holder): + if persistent_holder: + h = m.DataFieldsHolder(2) + for i, exp in enumerate(expected): + c = getattr(h.vec_at(i), core_fld) + assert c.int_value == exp + else: + for i, exp in enumerate(expected): + c = getattr(m.DataFieldsHolder(2).vec_at(i), core_fld) + assert c.int_value == exp