From 1bcf5727804caf346bb1e3ac84f0f59281cef58c Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Wed, 10 Jul 2024 15:20:31 -0700 Subject: [PATCH] First pass adapting all `property_cpp_function<>` implementations. Still needs debugging and more testing: ``` ========================================================= short test summary info ========================================================== SKIPPED [2] test_class_sh_property.py:19: BAKEIN_BREAK: Failed: DID NOT RAISE SKIPPED [1] test_class_sh_property.py:87: BAKEIN_BREAK: m_uqcp_readwrite does not build SKIPPED [1] test_class_sh_property.py:140: BAKEIN_BREAK: m_uqmp_readwrite does not build SKIPPED [1] test_class_sh_property.py:140: BAKEIN_BREAK: m_uqcp_readwrite does not build SKIPPED [1] test_class_sh_basic.py:156: unconditional skip SKIPPED [1] test_class_sh_property.py:87: BAKEIN_BREAK: m_uqmp_readwrite does not build SKIPPED [1] test_stl.py:149: no SKIPPED [1] test_smart_ptr.py:301: BAKEIN_EXPECTED: Failed: DID NOT RAISE FAILED test_class_sh_property_non_owning.py::test_core_fld_common[core_fld_value_rw-expected1-True] - RuntimeError: Non-owning holder (loaded_as_shared_ptr). FAILED test_class_sh_property_non_owning.py::test_core_fld_common[core_fld_value_rw-expected1-False] - RuntimeError: Non-owning holder (loaded_as_shared_ptr). FAILED test_class_sh_property_non_owning.py::test_core_fld_common[core_fld_value_ro-expected0-True] - RuntimeError: Non-owning holder (loaded_as_shared_ptr). FAILED test_class_sh_property_non_owning.py::test_core_fld_common[core_fld_raw_ptr_ro-expected4-True] - RuntimeError: Non-owning holder (loaded_as_shared_ptr). FAILED test_class_sh_property_non_owning.py::test_core_fld_common[core_fld_raw_ptr_ro-expected4-False] - RuntimeError: Non-owning holder (loaded_as_shared_ptr). FAILED test_class_sh_property_non_owning.py::test_core_fld_common[core_fld_raw_ptr_rw-expected5-True] - RuntimeError: Non-owning holder (loaded_as_shared_ptr). FAILED test_class_sh_property_non_owning.py::test_core_fld_common[core_fld_value_ro-expected0-False] - RuntimeError: Non-owning holder (loaded_as_shared_ptr). FAILED test_class_sh_property_non_owning.py::test_core_fld_common[core_fld_raw_ptr_rw-expected5-False] - RuntimeError: Non-owning holder (loaded_as_shared_ptr). ================================================ 8 failed, 1056 passed, 9 skipped in 3.96s ================================================= ``` --- include/pybind11/pybind11.h | 121 +++++++++++++++++++++++------------- 1 file changed, 78 insertions(+), 43 deletions(-) diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index 34fdcfc0d..e94979492 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -1599,6 +1599,7 @@ using must_be_member_function_pointer // Note that property_cpp_function is intentionally in the main pybind11 namespace, // because user-defined specializations could be useful. +// BAKEIN_WIP: Rewrite comment. // Classic (non-smart_holder) implementations for .def_readonly and .def_readwrite // getter and setter functions. // WARNING: This classic implementation can lead to dangling pointers for raw pointer members. @@ -1622,7 +1623,7 @@ struct property_cpp_function { } }; -#ifdef BAKEIN_WIP +// BAKEIN_WIP: Rewrite comment. // smart_holder specializations for raw pointer members. // WARNING: Like the classic implementation, this implementation can lead to dangling pointers. // See test_ptr() in tests/test_class_sh_property.py @@ -1633,21 +1634,26 @@ template struct property_cpp_function< T, D, - detail::enable_if_t, - detail::type_uses_smart_holder_type_caster, - std::is_pointer>::value>> { + detail::enable_if_t< + detail::all_of, detail::type_caster>, + std::is_base_of, detail::type_caster>, + std::is_pointer>::value>> { using drp = typename std::remove_pointer::type; template = 0> static cpp_function readonly(PM pm, const handle &hdl) { - return cpp_function( - [pm](handle c_hdl) -> std::shared_ptr { - std::shared_ptr c_sp = detail::type_caster::shared_ptr_from_python(c_hdl); - D ptr = (*c_sp).*pm; - return std::shared_ptr(c_sp, ptr); - }, - is_method(hdl)); + detail::type_info *tinfo = detail::get_type_info(typeid(T), /*throw_if_missing=*/true); + if (tinfo->default_holder) { + return cpp_function( + [pm](handle c_hdl) -> std::shared_ptr { + auto c_sp = cast>(c_hdl); + D ptr = (*c_sp).*pm; + return std::shared_ptr(c_sp, ptr); + }, + is_method(hdl)); + } + return cpp_function([pm](const T &c) -> const D & { return c.*pm; }, is_method(hdl)); } template = 0> @@ -1657,11 +1663,16 @@ struct property_cpp_function< template = 0> static cpp_function write(PM pm, const handle &hdl) { - return cpp_function([pm](T &c, D value) { c.*pm = std::forward(value); }, - is_method(hdl)); + detail::type_info *tinfo = detail::get_type_info(typeid(T), /*throw_if_missing=*/true); + if (tinfo->default_holder) { + return cpp_function([pm](T &c, D value) { c.*pm = std::forward(value); }, + is_method(hdl)); + } + return cpp_function([pm](T &c, const D &value) { c.*pm = value; }, is_method(hdl)); } }; +// BAKEIN_WIP: Rewrite comment. // smart_holder specializations for members held by-value. // The read functions return a shared_ptr to the member, emulating the PyCLIF approach: // https://github.com/google/clif/blob/c371a6d4b28d25d53a16e6d2a6d97305fb1be25a/clif/python/instance.h#L233 @@ -1670,38 +1681,53 @@ template struct property_cpp_function< T, D, - detail::enable_if_t, - detail::type_uses_smart_holder_type_caster, - detail::none_of, - detail::is_std_unique_ptr, - detail::is_std_shared_ptr>>::value>> { + detail::enable_if_t< + detail::all_of, detail::type_caster>, + std::is_base_of, detail::type_caster>, + detail::none_of, + detail::is_instantiation, + detail::is_instantiation>>::value>> { template = 0> static cpp_function readonly(PM pm, const handle &hdl) { - return cpp_function( - [pm](handle c_hdl) -> std::shared_ptr::type> { - std::shared_ptr c_sp = detail::type_caster::shared_ptr_from_python(c_hdl); - return std::shared_ptr::type>(c_sp, &(c_sp.get()->*pm)); - }, - is_method(hdl)); + detail::type_info *tinfo = detail::get_type_info(typeid(T), /*throw_if_missing=*/true); + if (tinfo->default_holder) { + return cpp_function( + [pm](handle c_hdl) -> std::shared_ptr::type> { + auto c_sp = cast>(c_hdl); + return std::shared_ptr::type>(c_sp, + &(c_sp.get()->*pm)); + }, + is_method(hdl)); + } + return cpp_function([pm](const T &c) -> const D & { return c.*pm; }, is_method(hdl)); } template = 0> static cpp_function read(PM pm, const handle &hdl) { - return cpp_function( - [pm](handle c_hdl) -> std::shared_ptr { - std::shared_ptr c_sp = detail::type_caster::shared_ptr_from_python(c_hdl); - return std::shared_ptr(c_sp, &(c_sp.get()->*pm)); - }, - is_method(hdl)); + detail::type_info *tinfo = detail::get_type_info(typeid(T), /*throw_if_missing=*/true); + if (tinfo->default_holder) { + return cpp_function( + [pm](handle c_hdl) -> std::shared_ptr { + auto c_sp = cast>(c_hdl); + return std::shared_ptr(c_sp, &(c_sp.get()->*pm)); + }, + is_method(hdl)); + } + return cpp_function([pm](const T &c) -> const D & { return c.*pm; }, is_method(hdl)); } template = 0> static cpp_function write(PM pm, const handle &hdl) { + detail::type_info *tinfo = detail::get_type_info(typeid(T), /*throw_if_missing=*/true); + if (tinfo->default_holder) { + return cpp_function([pm](T &c, const D &value) { c.*pm = value; }, is_method(hdl)); + } return cpp_function([pm](T &c, const D &value) { c.*pm = value; }, is_method(hdl)); } }; +// BAKEIN_WIP: Rewrite comment. // smart_holder specializations for std::unique_ptr members. // read disowns the member unique_ptr. // write disowns the passed Python object. @@ -1712,34 +1738,43 @@ template struct property_cpp_function< T, D, - detail::enable_if_t, - detail::is_std_unique_ptr, - detail::type_uses_smart_holder_type_caster>::value>> { + detail::enable_if_t< + detail::all_of, detail::type_caster>, + detail::is_instantiation, + std::is_base_of, + detail::type_caster>>::value>> { template = 0> static cpp_function readonly(PM, const handle &) { - static_assert(!detail::is_std_unique_ptr::value, + static_assert(!detail::is_instantiation::value, "def_readonly cannot be used for std::unique_ptr members."); return cpp_function{}; // Unreachable. } template = 0> static cpp_function read(PM pm, const handle &hdl) { - return cpp_function( - [pm](handle c_hdl) -> D { - std::shared_ptr c_sp = detail::type_caster::shared_ptr_from_python(c_hdl); - return D{std::move(c_sp.get()->*pm)}; - }, - is_method(hdl)); + detail::type_info *tinfo = detail::get_type_info(typeid(T), /*throw_if_missing=*/true); + if (tinfo->default_holder) { + return cpp_function( + [pm](handle c_hdl) -> D { + auto c_sp = cast>(c_hdl); + return D{std::move(c_sp.get()->*pm)}; + }, + is_method(hdl)); + } + return cpp_function([pm](const T &c) -> const D & { return c.*pm; }, is_method(hdl)); } template = 0> static cpp_function write(PM pm, const handle &hdl) { - return cpp_function([pm](T &c, D &&value) { c.*pm = std::move(value); }, is_method(hdl)); + detail::type_info *tinfo = detail::get_type_info(typeid(T), /*throw_if_missing=*/true); + if (tinfo->default_holder) { + return cpp_function([pm](T &c, D &&value) { c.*pm = std::move(value); }, + is_method(hdl)); + } + return cpp_function([pm](T &c, const D &value) { c.*pm = value; }, is_method(hdl)); } }; -#endif template class class_ : public detail::generic_type {