Skip to content

Commit ceab4ce

Browse files
rwgkrhaschke
authored andcommitted
demonstrate premature delete using current pybind#2687
1 parent 6b3c9b5 commit ceab4ce

File tree

3 files changed

+104
-0
lines changed

3 files changed

+104
-0
lines changed

tests/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ set(PYBIND11_TEST_FILES
115115
test_opaque_types.cpp
116116
test_operator_overloading.cpp
117117
test_pickling.cpp
118+
test_premature_delete.cpp
118119
test_pytypes.cpp
119120
test_sequences_and_iterators.cpp
120121
test_smart_ptr.cpp

tests/test_premature_delete.cpp

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
#include "pybind11_tests.h"
2+
3+
#include <iostream>
4+
#include <memory>
5+
6+
namespace pybind11_tests {
7+
8+
inline void to_cout(std::string text) { std::cout << text << std::endl; }
9+
10+
class pointee { // NOT copyable.
11+
public:
12+
pointee() {
13+
std::cout << "pointee(): " << this << std::endl;
14+
}
15+
16+
int get_int() const {
17+
to_cout("pointee::get_int()");
18+
return 213;
19+
}
20+
21+
~pointee() { std::cout << "~pointee(): " << this << std::endl; }
22+
23+
private:
24+
pointee(const pointee &) = delete;
25+
pointee(pointee &&) = delete;
26+
pointee &operator=(const pointee &) = delete;
27+
pointee &operator=(pointee &&) = delete;
28+
};
29+
30+
inline std::unique_ptr<pointee> make_unique_pointee() {
31+
return std::unique_ptr<pointee>(new pointee);
32+
}
33+
34+
class ptr_owner {
35+
public:
36+
explicit ptr_owner(std::unique_ptr<pointee> ptr) : ptr_(std::move(ptr)) {}
37+
38+
bool is_owner() const { return bool(ptr_); }
39+
40+
std::unique_ptr<pointee> give_up_ownership_via_unique_ptr() {
41+
return std::move(ptr_);
42+
}
43+
std::shared_ptr<pointee> give_up_ownership_via_shared_ptr() {
44+
return std::move(ptr_);
45+
}
46+
47+
private:
48+
std::unique_ptr<pointee> ptr_;
49+
};
50+
51+
TEST_SUBMODULE(premature_delete, m) {
52+
m.def("to_cout", to_cout);
53+
54+
py::class_<pointee>(m, "pointee")
55+
.def(py::init<>())
56+
.def("get_int", &pointee::get_int);
57+
58+
m.def("make_unique_pointee", make_unique_pointee);
59+
60+
py::class_<ptr_owner>(m, "ptr_owner")
61+
.def(py::init<std::unique_ptr<pointee>>(), py::arg("ptr"))
62+
.def("is_owner", &ptr_owner::is_owner)
63+
.def("give_up_ownership_via_unique_ptr",
64+
&ptr_owner::give_up_ownership_via_unique_ptr)
65+
.def("give_up_ownership_via_shared_ptr",
66+
&ptr_owner::give_up_ownership_via_shared_ptr);
67+
}
68+
69+
} // namespace pybind11_tests

tests/test_premature_delete.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# -*- coding: utf-8 -*-
2+
import pytest
3+
4+
from pybind11_tests import premature_delete as m
5+
6+
7+
def test_pointee_and_ptr_owner():
8+
m.to_cout("")
9+
obj = m.pointee()
10+
assert obj.get_int() == 213
11+
owner = m.ptr_owner(obj)
12+
with pytest.raises(RuntimeError) as exc_info:
13+
obj.get_int()
14+
assert str(exc_info.value) == "Invalid object instance"
15+
assert owner.is_owner()
16+
m.to_cout("before give up")
17+
if 1: # Behavior is the same with 0 or 1 here.
18+
reclaimed = owner.give_up_ownership_via_unique_ptr()
19+
else:
20+
reclaimed = owner.give_up_ownership_via_shared_ptr()
21+
m.to_cout("after give up")
22+
assert not owner.is_owner()
23+
if 0:
24+
# This is desired.
25+
assert reclaimed.get_int() == 213
26+
else:
27+
# But obj is actually disowned.
28+
with pytest.raises(RuntimeError) as exc_info:
29+
obj.get_int()
30+
assert str(exc_info.value) == "Invalid object instance"
31+
m.to_cout("before del")
32+
del reclaimed
33+
m.to_cout("after del")
34+
m.to_cout("")

0 commit comments

Comments
 (0)