From 9cca56fbedb69b218b97cefc3822a5e8f21eb5b7 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Sun, 17 May 2026 03:51:06 +0000 Subject: [PATCH 1/8] test: reproduce custom holder shared_ptr cast regression --- tests/test_smart_ptr.cpp | 52 ++++++++++++++++++++++++++++++++++++++++ tests/test_smart_ptr.py | 10 ++++++++ 2 files changed, 62 insertions(+) diff --git a/tests/test_smart_ptr.cpp b/tests/test_smart_ptr.cpp index 3617fa3e85..ba1da574a6 100644 --- a/tests/test_smart_ptr.cpp +++ b/tests/test_smart_ptr.cpp @@ -83,6 +83,26 @@ class const_only_shared_ptr { // for demonstration purpose only, this imitates smart pointer with a const-only pointer }; +template +class shared_ptr_as_custom_holder { + std::shared_ptr ptr_; + +public: + using element_type = T; + + shared_ptr_as_custom_holder() = default; + + explicit shared_ptr_as_custom_holder(T *) { + throw std::runtime_error("invalid shared_ptr_as_custom_holder constructor call"); + } + + explicit shared_ptr_as_custom_holder(std::shared_ptr ptr) : ptr_(std::move(ptr)) {} + + T *get() const { return ptr_.get(); } + + operator std::shared_ptr() const { return ptr_; } +}; + // Custom object with builtin reference counting (see 'object.h' for the implementation) class MyObject1 : public Object { public: @@ -239,6 +259,25 @@ struct SharedFromThisRef { std::shared_ptr shared = std::make_shared(); }; +class PrivateDtorWithCustomHolder { +public: + static std::shared_ptr create(int value) { + return {new PrivateDtorWithCustomHolder(value), + [](PrivateDtorWithCustomHolder *ptr) { delete ptr; }}; + } + + int value = 0; + +private: + explicit PrivateDtorWithCustomHolder(int value_) : value(value_) {} + ~PrivateDtorWithCustomHolder() = default; +}; + +std::shared_ptr &private_dtor_with_custom_holder_singleton() { + static auto singleton = PrivateDtorWithCustomHolder::create(17); + return singleton; +} + // Issue #865: shared_from_this doesn't work with virtual inheritance struct SharedFromThisVBase : std::enable_shared_from_this { SharedFromThisVBase() = default; @@ -341,6 +380,7 @@ struct holder_helper> { // Make pybind aware of the ref-counted wrapper type (s): PYBIND11_DECLARE_HOLDER_TYPE(T, ref, true) PYBIND11_DECLARE_HOLDER_TYPE(T, const_only_shared_ptr, true) +PYBIND11_DECLARE_HOLDER_TYPE(T, shared_ptr_as_custom_holder) // The following is not required anymore for std::shared_ptr, but it should compile without error: PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr) PYBIND11_DECLARE_HOLDER_TYPE(T, huge_unique_ptr) @@ -501,6 +541,18 @@ TEST_SUBMODULE(smart_ptr, m) { .def(py::init([](const std::string &value) { return MyObject6::createObject(value); })) .def_property_readonly("value", &MyObject6::value); + py::class_>( + m, "PrivateDtorWithCustomHolder") + .def_property("value", + [](const PrivateDtorWithCustomHolder &self) { return self.value; }, + [](PrivateDtorWithCustomHolder &self, int value) { self.value = value; }) + .def_static("get_singleton_holder", []() { + return shared_ptr_as_custom_holder( + private_dtor_with_custom_holder_singleton()); + }); + m.def("get_private_dtor_with_custom_holder_shared_ptr", + []() { return private_dtor_with_custom_holder_singleton(); }); + // test_shared_ptr_and_references using A = SharedPtrRef::A; py::class_>(m, "A"); diff --git a/tests/test_smart_ptr.py b/tests/test_smart_ptr.py index 76ebd8cf20..9c34a1b9ed 100644 --- a/tests/test_smart_ptr.py +++ b/tests/test_smart_ptr.py @@ -368,3 +368,13 @@ def test_move_only_holder_caster_shared_ptr_with_smart_holder_support_enabled(): def test_const_only_holder(): o = m.MyObject6("my_data") assert o.value == "my_data" + + +def test_shared_ptr_cast_for_custom_holder_with_private_dtor(): + holder_obj = m.PrivateDtorWithCustomHolder.get_singleton_holder() + shared_ptr_obj = m.get_private_dtor_with_custom_holder_shared_ptr() + + holder_obj.value = 23 + + assert shared_ptr_obj.value == 23 + assert m.get_private_dtor_with_custom_holder_shared_ptr().value == 23 From fc535cc0b3b68cccd53c26870996f4bf605950ad Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Sun, 17 May 2026 05:38:14 +0000 Subject: [PATCH 2/8] fix: support shared_ptr casts for compatible custom holders Add internal callback plumbing so class_ registrations can expose when a custom holder is constructible from std::shared_ptr. When py::cast() sees a returned std::shared_ptr for such a bound type, it now creates the Python instance by reconstructing the bound holder from an erased aliasing shared_ptr instead of rejecting the conversion. This preserves the #6008 safety check for incompatible holders while restoring support for private-destructor/shared_ptr patterns that use a custom holder wrapper. --- include/pybind11/attr.h | 3 ++ include/pybind11/cast.h | 4 ++ include/pybind11/detail/internals.h | 1 + include/pybind11/detail/type_caster_base.h | 57 ++++++++++++++++++++++ include/pybind11/pybind11.h | 33 +++++++++++++ 5 files changed, 98 insertions(+) diff --git a/include/pybind11/attr.h b/include/pybind11/attr.h index d337595a0b..e00704f546 100644 --- a/include/pybind11/attr.h +++ b/include/pybind11/attr.h @@ -313,6 +313,9 @@ struct type_record { /// Function pointer to class_<..>::dealloc void (*dealloc)(detail::value_and_holder &) = nullptr; + /// Function pointer to construct a bound holder from an erased std::shared_ptr. + void (*init_instance_from_shared_ptr)(instance *, const std::shared_ptr *) = nullptr; + /// Function pointer for casting alias class (aka trampoline) pointer to /// trampoline_self_life_support pointer. Sidesteps cross-DSO RTTI issues /// on platforms like macOS (see PR #5728 for details). diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index b7a4c2b0ce..86b9682045 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -1025,6 +1025,10 @@ struct copyable_holder_caster< if (tinfo != nullptr && tinfo->holder_enum_v == holder_enum_t::std_shared_ptr) { return type_caster_base::cast_holder(srcs, &src); } + if (tinfo != nullptr && tinfo->init_instance_from_shared_ptr != nullptr) { + return smart_holder_type_caster_support::custom_holder_from_shared_ptr( + src, policy, parent, srcs.result); + } if (parent) { return type_caster_generic::cast_non_owning( diff --git a/include/pybind11/detail/internals.h b/include/pybind11/detail/internals.h index 4175d20b21..e3865f17a8 100644 --- a/include/pybind11/detail/internals.h +++ b/include/pybind11/detail/internals.h @@ -403,6 +403,7 @@ struct type_info { void *(*operator_new)(size_t); void (*init_instance)(instance *, const void *); void (*dealloc)(value_and_holder &v_h); + void (*init_instance_from_shared_ptr)(instance *, const std::shared_ptr *) = nullptr; // Cross-DSO-safe function pointers, to sidestep cross-DSO RTTI issues // on platforms like macOS (see PR #5728 for details): diff --git a/include/pybind11/detail/type_caster_base.h b/include/pybind11/detail/type_caster_base.h index 8fbf700e12..b95b0bdb0e 100644 --- a/include/pybind11/detail/type_caster_base.h +++ b/include/pybind11/detail/type_caster_base.h @@ -803,6 +803,63 @@ handle smart_holder_from_shared_ptr(const std::shared_ptr &src, cs); } +template +handle custom_holder_from_shared_ptr(const std::shared_ptr &src, + return_value_policy policy, + handle parent, + const cast_sources::resolved_source &cs) { + switch (policy) { + case return_value_policy::automatic: + case return_value_policy::automatic_reference: + break; + case return_value_policy::take_ownership: + throw cast_error("Invalid return_value_policy for shared_ptr (take_ownership)."); + case return_value_policy::copy: + case return_value_policy::move: + break; + case return_value_policy::reference: + throw cast_error("Invalid return_value_policy for shared_ptr (reference)."); + case return_value_policy::reference_internal: + break; + } + if (!src) { + return none().release(); + } + + void *src_raw_void_ptr = const_cast(cs.cppobj); + assert(cs.tinfo != nullptr); + const detail::type_info *tinfo = cs.tinfo; + if (handle existing_inst = find_registered_python_instance(src_raw_void_ptr, tinfo)) { + return existing_inst; + } + + auto inst = reinterpret_steal(make_new_instance(tinfo->type)); + auto *inst_raw_ptr = reinterpret_cast(inst.ptr()); + inst_raw_ptr->owned = true; + void *&valueptr = values_and_holders(inst_raw_ptr).begin()->value_ptr(); + valueptr = src_raw_void_ptr; + + auto erased_shared_ptr = std::shared_ptr(src, src_raw_void_ptr); + tinfo->init_instance_from_shared_ptr(inst_raw_ptr, &erased_shared_ptr); + + if (policy == return_value_policy::reference_internal) { + keep_alive_impl(inst, parent); + } + + return inst.release(); +} + +template +handle custom_holder_from_shared_ptr(const std::shared_ptr &src, + return_value_policy policy, + handle parent, + const cast_sources::resolved_source &cs) { + return custom_holder_from_shared_ptr(std::const_pointer_cast(src), // Const2Mutbl + policy, + parent, + cs); +} + struct shared_ptr_parent_life_support { PyObject *parent; explicit shared_ptr_parent_life_support(PyObject *parent) : parent{parent} { diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index 9cc45bdbdc..c0e6f554de 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -1825,6 +1825,7 @@ class generic_type : public object { tinfo->holder_size_in_ptrs = size_in_ptrs(rec.holder_size); tinfo->init_instance = rec.init_instance; tinfo->dealloc = rec.dealloc; + tinfo->init_instance_from_shared_ptr = rec.init_instance_from_shared_ptr; tinfo->get_trampoline_self_life_support = rec.get_trampoline_self_life_support; tinfo->simple_type = true; tinfo->simple_ancestors = true; @@ -2361,6 +2362,7 @@ class class_ : public detail::generic_type { record.type_align = alignof(conditional_t &); record.holder_size = sizeof(holder_type); record.init_instance = init_instance; + record.init_instance_from_shared_ptr = get_init_instance_from_shared_ptr(); if (detail::is_instantiation::value) { record.holder_enum_v = detail::holder_enum_t::std_unique_ptr; @@ -2783,6 +2785,37 @@ class class_ : public detail::generic_type { } } + template >::value, int> + = 0> + static void init_instance_from_shared_ptr(detail::instance *inst, + const std::shared_ptr *shared_ptr_void_ptr) { + auto v_h = inst->get_value_and_holder(detail::get_type_info(typeid(type))); + if (!v_h.instance_registered()) { + register_instance(inst, v_h.value_ptr(), v_h.type); + v_h.set_instance_registered(); + } + auto shared_ptr = std::shared_ptr(*shared_ptr_void_ptr, v_h.value_ptr()); + new (std::addressof(v_h.holder())) holder_type(std::move(shared_ptr)); + v_h.set_holder_constructed(); + } + + template >::value, int> + = 0> + static constexpr auto get_init_instance_from_shared_ptr() + -> void (*)(detail::instance *, const std::shared_ptr *) { + return &init_instance_from_shared_ptr<>; + } + + template >::value, int> + = 0> + static constexpr auto get_init_instance_from_shared_ptr() + -> void (*)(detail::instance *, const std::shared_ptr *) { + return nullptr; + } + /// Performs instance initialization including constructing a holder and registering the known /// instance. Should be called as soon as the `type` value_ptr is set for an instance. Takes /// an optional pointer to an existing holder to use; if not specified and the instance is From e56dc0318c827c6fd23459bbade22419292ea7ff Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 17 May 2026 05:44:42 +0000 Subject: [PATCH 3/8] style: pre-commit fixes --- include/pybind11/pybind11.h | 6 ++---- tests/test_smart_ptr.cpp | 10 ++++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index c0e6f554de..0f4a7e69c8 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -2786,8 +2786,7 @@ class class_ : public detail::generic_type { } template >::value, int> - = 0> + detail::enable_if_t>::value, int> = 0> static void init_instance_from_shared_ptr(detail::instance *inst, const std::shared_ptr *shared_ptr_void_ptr) { auto v_h = inst->get_value_and_holder(detail::get_type_info(typeid(type))); @@ -2801,8 +2800,7 @@ class class_ : public detail::generic_type { } template >::value, int> - = 0> + detail::enable_if_t>::value, int> = 0> static constexpr auto get_init_instance_from_shared_ptr() -> void (*)(detail::instance *, const std::shared_ptr *) { return &init_instance_from_shared_ptr<>; diff --git a/tests/test_smart_ptr.cpp b/tests/test_smart_ptr.cpp index ba1da574a6..68110f77fe 100644 --- a/tests/test_smart_ptr.cpp +++ b/tests/test_smart_ptr.cpp @@ -541,11 +541,13 @@ TEST_SUBMODULE(smart_ptr, m) { .def(py::init([](const std::string &value) { return MyObject6::createObject(value); })) .def_property_readonly("value", &MyObject6::value); - py::class_>( + py::class_>( m, "PrivateDtorWithCustomHolder") - .def_property("value", - [](const PrivateDtorWithCustomHolder &self) { return self.value; }, - [](PrivateDtorWithCustomHolder &self, int value) { self.value = value; }) + .def_property( + "value", + [](const PrivateDtorWithCustomHolder &self) { return self.value; }, + [](PrivateDtorWithCustomHolder &self, int value) { self.value = value; }) .def_static("get_singleton_holder", []() { return shared_ptr_as_custom_holder( private_dtor_with_custom_holder_singleton()); From 5d7e40598f0488a192ed6356f617a16b89f86e42 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Sun, 17 May 2026 16:16:49 -0700 Subject: [PATCH 4/8] test: mark custom holder conversion explicit --- tests/test_smart_ptr.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_smart_ptr.cpp b/tests/test_smart_ptr.cpp index 68110f77fe..aef7923831 100644 --- a/tests/test_smart_ptr.cpp +++ b/tests/test_smart_ptr.cpp @@ -100,7 +100,7 @@ class shared_ptr_as_custom_holder { T *get() const { return ptr_.get(); } - operator std::shared_ptr() const { return ptr_; } + explicit operator std::shared_ptr() const { return ptr_; } }; // Custom object with builtin reference counting (see 'object.h' for the implementation) From 61135f5e3d61287211fb8c173cac892e759fa0c7 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Sun, 17 May 2026 16:23:24 -0700 Subject: [PATCH 5/8] test: cover incompatible custom holder shared_ptr casts --- tests/test_smart_ptr.cpp | 1 + tests/test_smart_ptr.py | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/tests/test_smart_ptr.cpp b/tests/test_smart_ptr.cpp index aef7923831..f771d4ae07 100644 --- a/tests/test_smart_ptr.cpp +++ b/tests/test_smart_ptr.cpp @@ -613,6 +613,7 @@ TEST_SUBMODULE(smart_ptr, m) { py::class_>(m, "TypeWithMoveOnlyHolder") .def_static("make", []() { return custom_unique_ptr(new C); }) .def_static("make_as_object", []() { return py::cast(custom_unique_ptr(new C)); }); + m.def("get_type_with_move_only_holder_shared_ptr", []() { return std::make_shared(); }); // test_holder_with_addressof_operator using HolderWithAddressOf = shared_ptr_with_addressof_operator; diff --git a/tests/test_smart_ptr.py b/tests/test_smart_ptr.py index 9c34a1b9ed..35b6a79e7d 100644 --- a/tests/test_smart_ptr.py +++ b/tests/test_smart_ptr.py @@ -378,3 +378,14 @@ def test_shared_ptr_cast_for_custom_holder_with_private_dtor(): assert shared_ptr_obj.value == 23 assert m.get_private_dtor_with_custom_holder_shared_ptr().value == 23 + + +def test_shared_ptr_cast_for_incompatible_custom_holder_throws(): + with pytest.raises( + RuntimeError, + match=( + "Unable to convert std::shared_ptr to Python when the bound type does not use" + " std::shared_ptr or py::smart_holder as its holder type" + ), + ): + m.get_type_with_move_only_holder_shared_ptr() From 60a0fc71b0570042bf108a5c31e87a8c29099c79 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Sun, 17 May 2026 16:31:57 -0700 Subject: [PATCH 6/8] refactor: share std::shared_ptr cast setup --- include/pybind11/detail/type_caster_base.h | 98 +++++++++++----------- 1 file changed, 51 insertions(+), 47 deletions(-) diff --git a/include/pybind11/detail/type_caster_base.h b/include/pybind11/detail/type_caster_base.h index b95b0bdb0e..0fe699d47e 100644 --- a/include/pybind11/detail/type_caster_base.h +++ b/include/pybind11/detail/type_caster_base.h @@ -742,11 +742,29 @@ handle smart_holder_from_unique_ptr(std::unique_ptr &&src, cs); } +struct shared_ptr_cast_data { + handle result; + object inst; + instance *inst_raw_ptr; + void *src_raw_void_ptr; + const detail::type_info *tinfo; + + explicit shared_ptr_cast_data(handle result_) + : result(result_), inst(), inst_raw_ptr(nullptr), src_raw_void_ptr(nullptr), + tinfo(nullptr) {} + + shared_ptr_cast_data(object &&inst_, + instance *inst_raw_ptr_, + void *src_raw_void_ptr_, + const detail::type_info *tinfo_) + : result(), inst(std::move(inst_)), inst_raw_ptr(inst_raw_ptr_), + src_raw_void_ptr(src_raw_void_ptr_), tinfo(tinfo_) {} +}; + template -handle smart_holder_from_shared_ptr(const std::shared_ptr &src, - return_value_policy policy, - handle parent, - const cast_sources::resolved_source &cs) { +shared_ptr_cast_data prepare_shared_ptr_cast(const std::shared_ptr &src, + return_value_policy policy, + const cast_sources::resolved_source &cs) { switch (policy) { case return_value_policy::automatic: case return_value_policy::automatic_reference: @@ -762,7 +780,7 @@ handle smart_holder_from_shared_ptr(const std::shared_ptr &src, break; } if (!src) { - return none().release(); + return shared_ptr_cast_data(none().release()); } // cs.cppobj is the subobject pointer appropriate for tinfo (may differ from src.get() @@ -771,9 +789,9 @@ handle smart_holder_from_shared_ptr(const std::shared_ptr &src, assert(cs.tinfo != nullptr); const detail::type_info *tinfo = cs.tinfo; if (handle existing_inst = find_registered_python_instance(src_raw_void_ptr, tinfo)) { - // PYBIND11:REMINDER: MISSING: Enforcement of consistency with existing smart_holder. + // PYBIND11:REMINDER: MISSING: Enforcement of consistency with existing holder. // PYBIND11:REMINDER: MISSING: keep_alive. - return existing_inst; + return shared_ptr_cast_data(existing_inst); } auto inst = reinterpret_steal(make_new_instance(tinfo->type)); @@ -781,17 +799,33 @@ handle smart_holder_from_shared_ptr(const std::shared_ptr &src, inst_raw_ptr->owned = true; void *&valueptr = values_and_holders(inst_raw_ptr).begin()->value_ptr(); valueptr = src_raw_void_ptr; + return shared_ptr_cast_data(std::move(inst), inst_raw_ptr, src_raw_void_ptr, tinfo); +} - auto smhldr = smart_holder::from_shared_ptr(std::shared_ptr(src, src_raw_void_ptr)); - tinfo->init_instance(inst_raw_ptr, static_cast(&smhldr)); - +inline handle finish_shared_ptr_cast(object &&inst, return_value_policy policy, handle parent) { if (policy == return_value_policy::reference_internal) { keep_alive_impl(inst, parent); } - return inst.release(); } +template +handle smart_holder_from_shared_ptr(const std::shared_ptr &src, + return_value_policy policy, + handle parent, + const cast_sources::resolved_source &cs) { + auto cast_data = prepare_shared_ptr_cast(src, policy, cs); + if (cast_data.result) { + return cast_data.result; + } + + auto smhldr + = smart_holder::from_shared_ptr(std::shared_ptr(src, cast_data.src_raw_void_ptr)); + cast_data.tinfo->init_instance(cast_data.inst_raw_ptr, static_cast(&smhldr)); + + return finish_shared_ptr_cast(std::move(cast_data.inst), policy, parent); +} + template handle smart_holder_from_shared_ptr(const std::shared_ptr &src, return_value_policy policy, @@ -808,45 +842,15 @@ handle custom_holder_from_shared_ptr(const std::shared_ptr &src, return_value_policy policy, handle parent, const cast_sources::resolved_source &cs) { - switch (policy) { - case return_value_policy::automatic: - case return_value_policy::automatic_reference: - break; - case return_value_policy::take_ownership: - throw cast_error("Invalid return_value_policy for shared_ptr (take_ownership)."); - case return_value_policy::copy: - case return_value_policy::move: - break; - case return_value_policy::reference: - throw cast_error("Invalid return_value_policy for shared_ptr (reference)."); - case return_value_policy::reference_internal: - break; - } - if (!src) { - return none().release(); + auto cast_data = prepare_shared_ptr_cast(src, policy, cs); + if (cast_data.result) { + return cast_data.result; } - void *src_raw_void_ptr = const_cast(cs.cppobj); - assert(cs.tinfo != nullptr); - const detail::type_info *tinfo = cs.tinfo; - if (handle existing_inst = find_registered_python_instance(src_raw_void_ptr, tinfo)) { - return existing_inst; - } - - auto inst = reinterpret_steal(make_new_instance(tinfo->type)); - auto *inst_raw_ptr = reinterpret_cast(inst.ptr()); - inst_raw_ptr->owned = true; - void *&valueptr = values_and_holders(inst_raw_ptr).begin()->value_ptr(); - valueptr = src_raw_void_ptr; + auto erased_shared_ptr = std::shared_ptr(src, cast_data.src_raw_void_ptr); + cast_data.tinfo->init_instance_from_shared_ptr(cast_data.inst_raw_ptr, &erased_shared_ptr); - auto erased_shared_ptr = std::shared_ptr(src, src_raw_void_ptr); - tinfo->init_instance_from_shared_ptr(inst_raw_ptr, &erased_shared_ptr); - - if (policy == return_value_policy::reference_internal) { - keep_alive_impl(inst, parent); - } - - return inst.release(); + return finish_shared_ptr_cast(std::move(cast_data.inst), policy, parent); } template From 3682e9de2a49044259221b9cb5227f72ac185e5c Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Mon, 18 May 2026 05:17:12 +0000 Subject: [PATCH 7/8] refactor: unify shared_ptr cast flow --- include/pybind11/detail/type_caster_base.h | 74 ++++++++-------------- 1 file changed, 27 insertions(+), 47 deletions(-) diff --git a/include/pybind11/detail/type_caster_base.h b/include/pybind11/detail/type_caster_base.h index 0fe699d47e..e15dff762e 100644 --- a/include/pybind11/detail/type_caster_base.h +++ b/include/pybind11/detail/type_caster_base.h @@ -742,29 +742,12 @@ handle smart_holder_from_unique_ptr(std::unique_ptr &&src, cs); } -struct shared_ptr_cast_data { - handle result; - object inst; - instance *inst_raw_ptr; - void *src_raw_void_ptr; - const detail::type_info *tinfo; - - explicit shared_ptr_cast_data(handle result_) - : result(result_), inst(), inst_raw_ptr(nullptr), src_raw_void_ptr(nullptr), - tinfo(nullptr) {} - - shared_ptr_cast_data(object &&inst_, - instance *inst_raw_ptr_, - void *src_raw_void_ptr_, - const detail::type_info *tinfo_) - : result(), inst(std::move(inst_)), inst_raw_ptr(inst_raw_ptr_), - src_raw_void_ptr(src_raw_void_ptr_), tinfo(tinfo_) {} -}; - -template -shared_ptr_cast_data prepare_shared_ptr_cast(const std::shared_ptr &src, - return_value_policy policy, - const cast_sources::resolved_source &cs) { +template +handle cast_shared_ptr_with_holder(const std::shared_ptr &src, + return_value_policy policy, + handle parent, + const cast_sources::resolved_source &cs, + InitHolder &&init_holder) { switch (policy) { case return_value_policy::automatic: case return_value_policy::automatic_reference: @@ -780,7 +763,7 @@ shared_ptr_cast_data prepare_shared_ptr_cast(const std::shared_ptr &src, break; } if (!src) { - return shared_ptr_cast_data(none().release()); + return none().release(); } // cs.cppobj is the subobject pointer appropriate for tinfo (may differ from src.get() @@ -791,7 +774,7 @@ shared_ptr_cast_data prepare_shared_ptr_cast(const std::shared_ptr &src, if (handle existing_inst = find_registered_python_instance(src_raw_void_ptr, tinfo)) { // PYBIND11:REMINDER: MISSING: Enforcement of consistency with existing holder. // PYBIND11:REMINDER: MISSING: keep_alive. - return shared_ptr_cast_data(existing_inst); + return existing_inst; } auto inst = reinterpret_steal(make_new_instance(tinfo->type)); @@ -799,10 +782,9 @@ shared_ptr_cast_data prepare_shared_ptr_cast(const std::shared_ptr &src, inst_raw_ptr->owned = true; void *&valueptr = values_and_holders(inst_raw_ptr).begin()->value_ptr(); valueptr = src_raw_void_ptr; - return shared_ptr_cast_data(std::move(inst), inst_raw_ptr, src_raw_void_ptr, tinfo); -} -inline handle finish_shared_ptr_cast(object &&inst, return_value_policy policy, handle parent) { + init_holder(tinfo, inst_raw_ptr, src, src_raw_void_ptr); + if (policy == return_value_policy::reference_internal) { keep_alive_impl(inst, parent); } @@ -814,16 +796,15 @@ handle smart_holder_from_shared_ptr(const std::shared_ptr &src, return_value_policy policy, handle parent, const cast_sources::resolved_source &cs) { - auto cast_data = prepare_shared_ptr_cast(src, policy, cs); - if (cast_data.result) { - return cast_data.result; - } - - auto smhldr - = smart_holder::from_shared_ptr(std::shared_ptr(src, cast_data.src_raw_void_ptr)); - cast_data.tinfo->init_instance(cast_data.inst_raw_ptr, static_cast(&smhldr)); - - return finish_shared_ptr_cast(std::move(cast_data.inst), policy, parent); + return cast_shared_ptr_with_holder( + src, policy, parent, cs, [](const detail::type_info *tinfo, + instance *inst_raw_ptr, + const std::shared_ptr &shared_ptr, + void *src_raw_void_ptr) { + auto smhldr = smart_holder::from_shared_ptr( + std::shared_ptr(shared_ptr, src_raw_void_ptr)); + tinfo->init_instance(inst_raw_ptr, static_cast(&smhldr)); + }); } template @@ -842,15 +823,14 @@ handle custom_holder_from_shared_ptr(const std::shared_ptr &src, return_value_policy policy, handle parent, const cast_sources::resolved_source &cs) { - auto cast_data = prepare_shared_ptr_cast(src, policy, cs); - if (cast_data.result) { - return cast_data.result; - } - - auto erased_shared_ptr = std::shared_ptr(src, cast_data.src_raw_void_ptr); - cast_data.tinfo->init_instance_from_shared_ptr(cast_data.inst_raw_ptr, &erased_shared_ptr); - - return finish_shared_ptr_cast(std::move(cast_data.inst), policy, parent); + return cast_shared_ptr_with_holder( + src, policy, parent, cs, [](const detail::type_info *tinfo, + instance *inst_raw_ptr, + const std::shared_ptr &shared_ptr, + void *src_raw_void_ptr) { + auto erased_shared_ptr = std::shared_ptr(shared_ptr, src_raw_void_ptr); + tinfo->init_instance_from_shared_ptr(inst_raw_ptr, &erased_shared_ptr); + }); } template From 72f7d7ec7f4fa40e84b7149265fb9b2b16f750e1 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 18 May 2026 05:19:38 +0000 Subject: [PATCH 8/8] style: pre-commit fixes --- include/pybind11/detail/type_caster_base.h | 24 ++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/include/pybind11/detail/type_caster_base.h b/include/pybind11/detail/type_caster_base.h index e15dff762e..44deaeb998 100644 --- a/include/pybind11/detail/type_caster_base.h +++ b/include/pybind11/detail/type_caster_base.h @@ -797,10 +797,14 @@ handle smart_holder_from_shared_ptr(const std::shared_ptr &src, handle parent, const cast_sources::resolved_source &cs) { return cast_shared_ptr_with_holder( - src, policy, parent, cs, [](const detail::type_info *tinfo, - instance *inst_raw_ptr, - const std::shared_ptr &shared_ptr, - void *src_raw_void_ptr) { + src, + policy, + parent, + cs, + [](const detail::type_info *tinfo, + instance *inst_raw_ptr, + const std::shared_ptr &shared_ptr, + void *src_raw_void_ptr) { auto smhldr = smart_holder::from_shared_ptr( std::shared_ptr(shared_ptr, src_raw_void_ptr)); tinfo->init_instance(inst_raw_ptr, static_cast(&smhldr)); @@ -824,10 +828,14 @@ handle custom_holder_from_shared_ptr(const std::shared_ptr &src, handle parent, const cast_sources::resolved_source &cs) { return cast_shared_ptr_with_holder( - src, policy, parent, cs, [](const detail::type_info *tinfo, - instance *inst_raw_ptr, - const std::shared_ptr &shared_ptr, - void *src_raw_void_ptr) { + src, + policy, + parent, + cs, + [](const detail::type_info *tinfo, + instance *inst_raw_ptr, + const std::shared_ptr &shared_ptr, + void *src_raw_void_ptr) { auto erased_shared_ptr = std::shared_ptr(shared_ptr, src_raw_void_ptr); tinfo->init_instance_from_shared_ptr(inst_raw_ptr, &erased_shared_ptr); });