Skip to content

Commit c03b578

Browse files
committed
Fix smart_holder def_readwrite for non-smart-holder member types
1 parent cd538ed commit c03b578

File tree

3 files changed

+72
-2
lines changed

3 files changed

+72
-2
lines changed

include/pybind11/pybind11.h

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2013,10 +2013,17 @@ struct property_cpp_function_sh_raw_ptr_member {
20132013
// This prevents disowning of the Python object owning the member.
20142014
template <typename T, typename D>
20152015
struct property_cpp_function_sh_member_held_by_value {
2016+
static bool use_smart_holder_member_aliasing() {
2017+
type_info *member_tinfo = get_type_info(typeid(intrinsic_t<D>), /*throw_if_missing=*/false);
2018+
return member_tinfo != nullptr
2019+
&& member_tinfo->holder_enum_v == holder_enum_t::smart_holder;
2020+
}
2021+
20162022
template <typename PM, must_be_member_function_pointer<PM> = 0>
20172023
static cpp_function readonly(PM pm, const handle &hdl) {
20182024
type_info *tinfo = get_type_info(typeid(T), /*throw_if_missing=*/true);
2019-
if (tinfo->holder_enum_v == holder_enum_t::smart_holder) {
2025+
if (tinfo->holder_enum_v == holder_enum_t::smart_holder
2026+
&& use_smart_holder_member_aliasing()) {
20202027
return cpp_function(
20212028
[pm](handle c_hdl) -> std::shared_ptr<typename std::add_const<D>::type> {
20222029
std::shared_ptr<T> c_sp
@@ -2033,7 +2040,8 @@ struct property_cpp_function_sh_member_held_by_value {
20332040
template <typename PM, must_be_member_function_pointer<PM> = 0>
20342041
static cpp_function read(PM pm, const handle &hdl) {
20352042
type_info *tinfo = get_type_info(typeid(T), /*throw_if_missing=*/true);
2036-
if (tinfo->holder_enum_v == holder_enum_t::smart_holder) {
2043+
if (tinfo->holder_enum_v == holder_enum_t::smart_holder
2044+
&& use_smart_holder_member_aliasing()) {
20372045
return cpp_function(
20382046
[pm](handle c_hdl) -> std::shared_ptr<D> {
20392047
std::shared_ptr<T> c_sp

tests/test_class_sh_property.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,23 @@ struct WithConstCharPtrMember {
4343
const char *const_char_ptr_member = "ConstChar*";
4444
};
4545

46+
enum class TinyLevel {
47+
A = 0,
48+
B = 1,
49+
};
50+
51+
struct HolderWithEnum {
52+
TinyLevel level = TinyLevel::A;
53+
};
54+
55+
struct LegacyThing {
56+
int value = 7;
57+
};
58+
59+
struct HolderWithLegacyMember {
60+
LegacyThing legacy;
61+
};
62+
4663
} // namespace test_class_sh_property
4764

4865
TEST_SUBMODULE(class_sh_property, m) {
@@ -91,4 +108,16 @@ TEST_SUBMODULE(class_sh_property, m) {
91108
py::classh<WithConstCharPtrMember>(m, "WithConstCharPtrMember")
92109
.def(py::init<>())
93110
.def_readonly("const_char_ptr_member", &WithConstCharPtrMember::const_char_ptr_member);
111+
112+
py::enum_<TinyLevel>(m, "TinyLevel").value("A", TinyLevel::A).value("B", TinyLevel::B);
113+
114+
py::classh<HolderWithEnum>(m, "HolderWithEnum")
115+
.def(py::init<>())
116+
.def_readwrite("level", &HolderWithEnum::level);
117+
118+
py::class_<LegacyThing>(m, "LegacyThing").def(py::init<>()).def_readwrite("value", &LegacyThing::value);
119+
120+
py::classh<HolderWithLegacyMember>(m, "HolderWithLegacyMember")
121+
.def(py::init<>())
122+
.def_readwrite("legacy", &HolderWithLegacyMember::legacy);
94123
}

tests/test_class_sh_property.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,3 +164,36 @@ def test_readonly_char6_member():
164164
def test_readonly_const_char_ptr_member():
165165
obj = m.WithConstCharPtrMember()
166166
assert obj.const_char_ptr_member == "ConstChar*"
167+
168+
169+
def test_enum_member_with_smart_holder_def_readwrite():
170+
env.check_script_success_in_subprocess(
171+
"""
172+
import gc
173+
from pybind11_tests import class_sh_property as m
174+
175+
obj = m.HolderWithEnum()
176+
assert obj.level == m.TinyLevel.A
177+
for _ in range(100):
178+
v = obj.level
179+
assert v == m.TinyLevel.A
180+
del v
181+
gc.collect()
182+
"""
183+
)
184+
185+
186+
def test_non_smart_holder_member_type_with_smart_holder_owner():
187+
env.check_script_success_in_subprocess(
188+
"""
189+
import gc
190+
from pybind11_tests import class_sh_property as m
191+
192+
obj = m.HolderWithLegacyMember()
193+
for _ in range(1000):
194+
v = obj.legacy
195+
assert v.value == 7
196+
del v
197+
gc.collect()
198+
"""
199+
)

0 commit comments

Comments
 (0)