Skip to content

Commit ff0792e

Browse files
committed
Generalize test_potentially_slicing_shared_ptr.cpp,py for testing with smart_holder and std::shared_ptr as holder.
1 parent 56d23dc commit ff0792e

File tree

2 files changed

+130
-49
lines changed

2 files changed

+130
-49
lines changed

tests/test_potentially_slicing_shared_ptr.cpp

+64-28
Original file line numberDiff line numberDiff line change
@@ -9,26 +9,38 @@
99
namespace pybind11_tests {
1010
namespace potentially_slicing_shared_ptr {
1111

12+
template <int> // Using int as a trick to easily generate multiple types.
1213
struct VirtBase {
1314
virtual ~VirtBase() = default;
1415
virtual int get_code() { return 100; }
1516
};
1617

17-
struct PyVirtBase : VirtBase, py::trampoline_self_life_support {
18-
using VirtBase::VirtBase;
19-
int get_code() override { PYBIND11_OVERRIDE(int, VirtBase, get_code); }
18+
using VirtBaseSH = VirtBase<0>; // for testing with py::smart_holder
19+
using VirtBaseSP = VirtBase<1>; // for testing with std::shared_ptr as holder
20+
21+
struct PyVirtBaseSH : VirtBaseSH, py::trampoline_self_life_support {
22+
using VirtBaseSH::VirtBaseSH;
23+
int get_code() override { PYBIND11_OVERRIDE(int, VirtBaseSH, get_code); }
24+
};
25+
26+
struct PyVirtBaseSP : VirtBaseSP { // self-life-support not available
27+
using VirtBaseSP::VirtBaseSP;
28+
int get_code() override { PYBIND11_OVERRIDE(int, VirtBaseSP, get_code); }
2029
};
2130

22-
std::shared_ptr<VirtBase> rtrn_obj_cast_shared_ptr(py::handle obj) {
23-
return obj.cast<std::shared_ptr<VirtBase>>();
31+
template <typename VB>
32+
std::shared_ptr<VB> rtrn_obj_cast_shared_ptr(py::handle obj) {
33+
return obj.cast<std::shared_ptr<VB>>();
2434
}
2535

26-
std::shared_ptr<VirtBase> rtrn_potentially_slicing_shared_ptr(py::handle obj) {
27-
return py::potentially_slicing_shared_ptr<VirtBase>(obj);
36+
template <typename VB>
37+
std::shared_ptr<VB> rtrn_potentially_slicing_shared_ptr(py::handle obj) {
38+
return py::potentially_slicing_shared_ptr<VB>(obj);
2839
}
2940

41+
template <typename VB>
3042
struct SpOwner {
31-
void set_sp(const std::shared_ptr<VirtBase> &sp_) { sp = sp_; }
43+
void set_sp(const std::shared_ptr<VB> &sp_) { sp = sp_; }
3244

3345
int get_code() {
3446
if (!sp) {
@@ -38,11 +50,12 @@ struct SpOwner {
3850
}
3951

4052
private:
41-
std::shared_ptr<VirtBase> sp;
53+
std::shared_ptr<VB> sp;
4254
};
4355

56+
template <typename VB>
4457
struct WpOwner {
45-
void set_wp(const std::shared_ptr<VirtBase> &sp) { wp = sp; }
58+
void set_wp(const std::shared_ptr<VB> &sp) { wp = sp; }
4659

4760
int get_code() {
4861
auto sp = wp.lock();
@@ -53,33 +66,56 @@ struct WpOwner {
5366
}
5467

5568
private:
56-
std::weak_ptr<VirtBase> wp;
69+
std::weak_ptr<VB> wp;
5770
};
5871

72+
template <typename VB>
73+
void wrap(py::module_ &m,
74+
const char *roc_pyname,
75+
const char *rps_pyname,
76+
const char *spo_pyname,
77+
const char *wpo_pyname) {
78+
m.def(roc_pyname, rtrn_obj_cast_shared_ptr<VB>);
79+
m.def(rps_pyname, rtrn_potentially_slicing_shared_ptr<VB>);
80+
81+
py::classh<SpOwner<VB>>(m, spo_pyname)
82+
.def(py::init<>())
83+
.def("set_sp", &SpOwner<VB>::set_sp)
84+
.def("get_code", &SpOwner<VB>::get_code);
85+
86+
py::classh<WpOwner<VB>>(m, wpo_pyname)
87+
.def(py::init<>())
88+
.def("set_wp", &WpOwner<VB>::set_wp)
89+
.def("set_wp_potentially_slicing",
90+
[](WpOwner<VB> &self, py::handle obj) {
91+
self.set_wp(py::potentially_slicing_shared_ptr<VB>(obj));
92+
})
93+
.def("get_code", &WpOwner<VB>::get_code);
94+
}
95+
5996
} // namespace potentially_slicing_shared_ptr
6097
} // namespace pybind11_tests
6198

6299
using namespace pybind11_tests::potentially_slicing_shared_ptr;
63100

64101
TEST_SUBMODULE(potentially_slicing_shared_ptr, m) {
65-
py::classh<VirtBase, PyVirtBase>(m, "VirtBase")
66-
.def(py::init<>())
67-
.def("get_code", &VirtBase::get_code);
68-
69-
m.def("rtrn_obj_cast_shared_ptr", rtrn_obj_cast_shared_ptr);
70-
m.def("rtrn_potentially_slicing_shared_ptr", rtrn_potentially_slicing_shared_ptr);
71-
72-
py::classh<SpOwner>(m, "SpOwner")
102+
py::classh<VirtBaseSH, PyVirtBaseSH>(m, "VirtBaseSH")
73103
.def(py::init<>())
74-
.def("set_sp", &SpOwner::set_sp)
75-
.def("get_code", &SpOwner::get_code);
104+
.def("get_code", &VirtBaseSH::get_code);
76105

77-
py::classh<WpOwner>(m, "WpOwner")
106+
py::class_<VirtBaseSP, std::shared_ptr<VirtBaseSP>, PyVirtBaseSP>(m, "VirtBaseSP")
78107
.def(py::init<>())
79-
.def("set_wp", &WpOwner::set_wp)
80-
.def("set_wp_potentially_slicing",
81-
[](WpOwner &self, py::handle obj) {
82-
self.set_wp(py::potentially_slicing_shared_ptr<VirtBase>(obj));
83-
})
84-
.def("get_code", &WpOwner::get_code);
108+
.def("get_code", &VirtBaseSP::get_code);
109+
110+
wrap<VirtBaseSH>(m,
111+
"SH_rtrn_obj_cast_shared_ptr",
112+
"SH_rtrn_potentially_slicing_shared_ptr",
113+
"SH_SpOwner",
114+
"SH_WpOwner");
115+
116+
wrap<VirtBaseSP>(m,
117+
"SP_rtrn_obj_cast_shared_ptr",
118+
"SP_rtrn_potentially_slicing_shared_ptr",
119+
"SP_SpOwner",
120+
"SP_WpOwner");
85121
}

tests/test_potentially_slicing_shared_ptr.py

+66-21
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,51 @@
99
import pybind11_tests.potentially_slicing_shared_ptr as m
1010

1111

12-
class PyDrvd(m.VirtBase):
12+
class PyDrvdSH(m.VirtBaseSH):
1313
def get_code(self):
1414
return 200
1515

1616

17-
@pytest.mark.parametrize(("vtype", "expected_code"), [(m.VirtBase, 100), (PyDrvd, 200)])
18-
@pytest.mark.parametrize(
19-
"rtrn_meth", ["rtrn_obj_cast_shared_ptr", "rtrn_potentially_slicing_shared_ptr"]
20-
)
21-
def test_rtrn_obj_cast_shared_ptr(vtype, rtrn_meth, expected_code):
22-
obj = vtype()
23-
ptr = getattr(m, rtrn_meth)(obj)
17+
class PyDrvdSP(m.VirtBaseSP):
18+
def get_code(self):
19+
return 200
20+
21+
22+
VIRT_BASE_TYPES = {
23+
"SH": {100: m.VirtBaseSH, 200: PyDrvdSH},
24+
"SP": {100: m.VirtBaseSP, 200: PyDrvdSP},
25+
}
26+
27+
RTRN_FUNCS = {
28+
"SH": {
29+
"oc": m.SH_rtrn_obj_cast_shared_ptr,
30+
"ps": m.SH_rtrn_potentially_slicing_shared_ptr,
31+
},
32+
"SP": {
33+
"oc": m.SP_rtrn_obj_cast_shared_ptr,
34+
"ps": m.SP_rtrn_potentially_slicing_shared_ptr,
35+
},
36+
}
37+
38+
SP_OWNER_TYPES = {
39+
"SH": m.SH_SpOwner,
40+
"SP": m.SP_SpOwner,
41+
}
42+
43+
WP_OWNER_TYPES = {
44+
"SH": m.SH_WpOwner,
45+
"SP": m.SP_WpOwner,
46+
}
47+
48+
GC_IS_RELIABLE = not (env.PYPY or env.GRAALPY)
49+
50+
51+
@pytest.mark.parametrize("expected_code", [100, 200])
52+
@pytest.mark.parametrize("rtrn_kind", ["oc", "ps"])
53+
@pytest.mark.parametrize("holder_kind", ["SH", "SP"])
54+
def test_rtrn_obj_cast_shared_ptr(holder_kind, rtrn_kind, expected_code):
55+
obj = VIRT_BASE_TYPES[holder_kind][expected_code]()
56+
ptr = RTRN_FUNCS[holder_kind][rtrn_kind](obj)
2457
assert ptr.get_code() == expected_code
2558
objref = weakref.ref(obj)
2659
del obj
@@ -29,41 +62,53 @@ def test_rtrn_obj_cast_shared_ptr(vtype, rtrn_meth, expected_code):
2962
assert objref() is not None
3063
del ptr
3164
gc.collect()
32-
assert objref() is None
65+
if GC_IS_RELIABLE:
66+
assert objref() is None
3367

3468

35-
@pytest.mark.parametrize(("vtype", "expected_code"), [(m.VirtBase, 100), (PyDrvd, 200)])
36-
def test_with_sp_owner(vtype, expected_code):
37-
spo = m.SpOwner()
69+
@pytest.mark.parametrize("expected_code", [100, 200])
70+
@pytest.mark.parametrize("holder_kind", ["SH", "SP"])
71+
def test_with_sp_owner(holder_kind, expected_code):
72+
spo = SP_OWNER_TYPES[holder_kind]()
3873
assert spo.get_code() == -888
3974

40-
obj = vtype()
75+
obj = VIRT_BASE_TYPES[holder_kind][expected_code]()
4176
assert obj.get_code() == expected_code
4277

4378
spo.set_sp(obj)
4479
assert spo.get_code() == expected_code
4580

4681
del obj
4782
gc.collect()
48-
assert spo.get_code() == expected_code
83+
if holder_kind == "SH":
84+
assert spo.get_code() == expected_code
85+
elif GC_IS_RELIABLE:
86+
assert (
87+
spo.get_code() == 100
88+
) # see issue #1333 (inheritance slicing) and PR #5624
4989

5090

51-
@pytest.mark.parametrize(("vtype", "expected_code"), [(m.VirtBase, 100), (PyDrvd, 200)])
91+
@pytest.mark.parametrize("expected_code", [100, 200])
5292
@pytest.mark.parametrize("set_meth", ["set_wp", "set_wp_potentially_slicing"])
53-
def test_with_wp_owner(vtype, set_meth, expected_code):
54-
wpo = m.WpOwner()
93+
@pytest.mark.parametrize("holder_kind", ["SH", "SP"])
94+
def test_with_wp_owner(holder_kind, set_meth, expected_code):
95+
wpo = WP_OWNER_TYPES[holder_kind]()
5596
assert wpo.get_code() == -999
5697

57-
obj = vtype()
98+
obj = VIRT_BASE_TYPES[holder_kind][expected_code]()
5899
assert obj.get_code() == expected_code
59100

60101
getattr(wpo, set_meth)(obj)
61-
if vtype is m.VirtBase or set_meth == "set_wp_potentially_slicing":
102+
if (
103+
holder_kind == "SP"
104+
or expected_code == 100
105+
or set_meth == "set_wp_potentially_slicing"
106+
):
62107
assert wpo.get_code() == expected_code
63108
else:
64-
assert wpo.get_code() == -999 # see PR #5624
109+
assert wpo.get_code() == -999 # see issue #5623 (weak_ptr expired) and PR #5624
65110

66111
del obj
67112
gc.collect()
68-
if not (env.PYPY or env.GRAALPY):
113+
if GC_IS_RELIABLE:
69114
assert wpo.get_code() == -999

0 commit comments

Comments
 (0)