Skip to content

Commit 63c2a97

Browse files
trelauwjakob
authored andcommitted
Enable unique_ptr holder with mixed Deleters between base and derived types (#1353)
* Check default holder -Recognize "std::unique_ptr<T, D>" as a default holder even if "D" doesn't match between base and derived holders * Add test for unique_ptr<T, D> change
1 parent cea4246 commit 63c2a97

File tree

3 files changed

+48
-1
lines changed

3 files changed

+48
-1
lines changed

include/pybind11/pybind11.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -1069,7 +1069,7 @@ class class_ : public detail::generic_type {
10691069
record.holder_size = sizeof(holder_type);
10701070
record.init_instance = init_instance;
10711071
record.dealloc = dealloc;
1072-
record.default_holder = std::is_same<holder_type, std::unique_ptr<type>>::value;
1072+
record.default_holder = detail::is_instantiation<std::unique_ptr, holder_type>::value;
10731073

10741074
set_operator_new<type>(&record);
10751075

tests/test_smart_ptr.cpp

+26
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,32 @@ TEST_SUBMODULE(smart_ptr, m) {
186186
.def(py::init<int>())
187187
.def_readwrite("value", &MyObject4::value);
188188

189+
// test_unique_deleter
190+
// Object with std::unique_ptr<T, D> where D is not matching the base class
191+
// Object with a protected destructor
192+
class MyObject4a {
193+
public:
194+
MyObject4a(int i) {
195+
value = i;
196+
print_created(this);
197+
};
198+
int value;
199+
protected:
200+
virtual ~MyObject4a() { print_destroyed(this); }
201+
};
202+
py::class_<MyObject4a, std::unique_ptr<MyObject4a, py::nodelete>>(m, "MyObject4a")
203+
.def(py::init<int>())
204+
.def_readwrite("value", &MyObject4a::value);
205+
206+
// Object derived but with public destructor and no Deleter in default holder
207+
class MyObject4b : public MyObject4a {
208+
public:
209+
MyObject4b(int i) : MyObject4a(i) { print_created(this); }
210+
~MyObject4b() { print_destroyed(this); }
211+
};
212+
py::class_<MyObject4b, MyObject4a>(m, "MyObject4b")
213+
.def(py::init<int>());
214+
189215
// test_large_holder
190216
class MyObject5 { // managed by huge_unique_ptr
191217
public:

tests/test_smart_ptr.py

+21
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,27 @@ def test_unique_nodelete():
115115
assert cstats.alive() == 1 # Leak, but that's intentional
116116

117117

118+
def test_unique_nodelete4a():
119+
o = m.MyObject4a(23)
120+
assert o.value == 23
121+
cstats = ConstructorStats.get(m.MyObject4a)
122+
assert cstats.alive() == 1
123+
del o
124+
assert cstats.alive() == 1 # Leak, but that's intentional
125+
126+
127+
def test_unique_deleter():
128+
o = m.MyObject4b(23)
129+
assert o.value == 23
130+
cstats4a = ConstructorStats.get(m.MyObject4a)
131+
assert cstats4a.alive() == 2 # Two becaue of previous test
132+
cstats4b = ConstructorStats.get(m.MyObject4b)
133+
assert cstats4b.alive() == 1
134+
del o
135+
assert cstats4a.alive() == 1 # Should now only be one leftover from previous test
136+
assert cstats4b.alive() == 0 # Should be deleted
137+
138+
118139
def test_large_holder():
119140
o = m.MyObject5(5)
120141
assert o.value == 5

0 commit comments

Comments
 (0)