Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions tests/test_smart_ptr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,22 @@ class custom_unique_ptr {
};
PYBIND11_DECLARE_HOLDER_TYPE(T, custom_unique_ptr<T>);

class NontrivialDeleter {
int value{1};
public:
NontrivialDeleter() = default;
NontrivialDeleter(int value_in) : value(value_in) {}

template <typename T>
void operator()(T* p) const {
sum += value;
py::print(py::str("NontrivialDeleter: value={}, sum={}").format(value, sum));
delete p;
}

static int sum;
};
int NontrivialDeleter::sum = 0;

TEST_SUBMODULE(smart_ptr, m) {

Expand Down Expand Up @@ -183,6 +199,34 @@ TEST_SUBMODULE(smart_ptr, m) {
py::class_<MyObject4b, MyObject4a>(m, "MyObject4b")
.def(py::init<int>());

// Object derived but with a unique_ptr with a nontrivial deleter.
class MyObject4c : public MyObject4b {
public:
MyObject4c(int i) : MyObject4b(i) { print_created(this); }
~MyObject4c() { print_destroyed(this); }

using holder_type = std::unique_ptr<MyObject4c, NontrivialDeleter>;
};
py::class_<MyObject4c, MyObject4b, MyObject4c::holder_type>(m, "MyObject4c")
.def(py::init<int>())
.def(py::init(
[](int i, int deleter_value) {
return MyObject4c::holder_type(new MyObject4c(i), deleter_value);
}))
// Upcast to 4b, but with nontrivial holder.
.def_static("create_as_4b_nontrivial_deleter",
[](int i, int deleter_value) {
return std::unique_ptr<MyObject4b, NontrivialDeleter>(new MyObject4c(i), deleter_value);
})
.def_static("create_as_4b_trivial_deleter",
[](int i) {
// Causes a segfault.
return std::unique_ptr<MyObject4b>(new MyObject4c(i));
});
m.def("get_nontrivial_deleter_sum", []() {
return NontrivialDeleter::sum;
});

// test_large_holder
class MyObject5 { // managed by huge_unique_ptr
public:
Expand Down
26 changes: 26 additions & 0 deletions tests/test_smart_ptr.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,32 @@ def test_unique_deleter():
assert cstats4a.alive() == 1 # Should now only be one leftover from previous test
assert cstats4b.alive() == 0 # Should be deleted

# Nontrivial deleter, accidentally mixed with parent classes that do not have deleters
# of the same memory layout.
cstats4c = ConstructorStats.get(m.MyObject4c)
o = m.MyObject4c(23, 10)
assert cstats4c.alive() == 1
assert m.get_nontrivial_deleter_sum() == 0
del o
assert cstats4c.alive() == 0
assert m.get_nontrivial_deleter_sum() == 10
o = m.MyObject4c.create_as_4b_nontrivial_deleter(23, 100)
assert cstats4c.alive() == 1
del o
assert cstats4c.alive() == 0
# This is correct, because `pybind11` will still use the correct holder due
# to dynamic downcasting (`type_caster_base<>::src_and_type`) type erasure.
assert m.get_nontrivial_deleter_sum() == 110

o = m.MyObject4c.create_as_4b_trivial_deleter(23)
# Does not correctly count the instances? Possibly due to early memory leaks?
# assert cstats4c.alive() == 1
# Starts to invoke the nontrivial deleter with junk memory.
del o
assert cstats4c.alive() == 0
# Assuming memory is not garbage???
assert m.get_nontrivial_deleter_sum() == 110


def test_large_holder():
o = m.MyObject5(5)
Expand Down