Skip to content

Commit 6a7e9f4

Browse files
authored
Changing all but one std::runtime_error to std::invalid_argument, which appears as ValueError in the Python interpreter. Adding test_cannot_disown_use_count_ne_1. (#2883)
1 parent 3a336a2 commit 6a7e9f4

File tree

3 files changed

+46
-17
lines changed

3 files changed

+46
-17
lines changed

include/pybind11/detail/smart_holder_poc.h

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ struct smart_holder {
104104
bool is_populated : 1;
105105

106106
// Design choice: smart_holder is movable but not copyable.
107-
smart_holder(smart_holder &&) = default;
107+
smart_holder(smart_holder &&) = default;
108108
smart_holder(const smart_holder &) = delete;
109109
smart_holder &operator=(smart_holder &&) = default;
110110
smart_holder &operator=(const smart_holder &) = delete;
@@ -124,8 +124,8 @@ struct smart_holder {
124124
template <typename T>
125125
static void ensure_pointee_is_destructible(const char *context) {
126126
if (!std::is_destructible<T>::value)
127-
throw std::runtime_error(std::string("Pointee is not destructible (") + context
128-
+ ").");
127+
throw std::invalid_argument(std::string("Pointee is not destructible (") + context
128+
+ ").");
129129
}
130130

131131
void ensure_is_populated(const char *context) const {
@@ -136,16 +136,16 @@ struct smart_holder {
136136

137137
void ensure_vptr_is_using_builtin_delete(const char *context) const {
138138
if (vptr_is_external_shared_ptr) {
139-
throw std::runtime_error(std::string("Cannot disown external shared_ptr (") + context
140-
+ ").");
139+
throw std::invalid_argument(std::string("Cannot disown external shared_ptr (")
140+
+ context + ").");
141141
}
142142
if (vptr_is_using_noop_deleter) {
143-
throw std::runtime_error(std::string("Cannot disown non-owning holder (") + context
144-
+ ").");
143+
throw std::invalid_argument(std::string("Cannot disown non-owning holder (") + context
144+
+ ").");
145145
}
146146
if (!vptr_is_using_builtin_delete) {
147-
throw std::runtime_error(std::string("Cannot disown custom deleter (") + context
148-
+ ").");
147+
throw std::invalid_argument(std::string("Cannot disown custom deleter (") + context
148+
+ ").");
149149
}
150150
}
151151

@@ -154,34 +154,34 @@ struct smart_holder {
154154
const std::type_info *rtti_requested = &typeid(D);
155155
if (!rtti_uqp_del) {
156156
if (!is_std_default_delete<T>(*rtti_requested)) {
157-
throw std::runtime_error(std::string("Missing unique_ptr deleter (") + context
158-
+ ").");
157+
throw std::invalid_argument(std::string("Missing unique_ptr deleter (") + context
158+
+ ").");
159159
}
160160
ensure_vptr_is_using_builtin_delete(context);
161161
} else if (!(*rtti_requested == *rtti_uqp_del)) {
162-
throw std::runtime_error(std::string("Incompatible unique_ptr deleter (") + context
163-
+ ").");
162+
throw std::invalid_argument(std::string("Incompatible unique_ptr deleter (") + context
163+
+ ").");
164164
}
165165
}
166166

167167
void ensure_has_pointee(const char *context) const {
168168
if (!has_pointee()) {
169-
throw std::runtime_error(std::string("Disowned holder (") + context + ").");
169+
throw std::invalid_argument(std::string("Disowned holder (") + context + ").");
170170
}
171171
}
172172

173173
void ensure_use_count_1(const char *context) const {
174174
if (vptr.get() == nullptr) {
175-
throw std::runtime_error(std::string("Cannot disown nullptr (") + context + ").");
175+
throw std::invalid_argument(std::string("Cannot disown nullptr (") + context + ").");
176176
}
177177
// In multithreaded environments accessing use_count can lead to
178178
// race conditions, but in the context of Python it is a bug (elsewhere)
179179
// if the Global Interpreter Lock (GIL) is not being held when this code
180180
// is reached.
181181
// SMART_HOLDER_WIP: IMPROVABLE: assert(GIL is held).
182182
if (vptr.use_count() != 1) {
183-
throw std::runtime_error(std::string("Cannot disown use_count != 1 (") + context
184-
+ ").");
183+
throw std::invalid_argument(std::string("Cannot disown use_count != 1 (") + context
184+
+ ").");
185185
}
186186
}
187187

tests/test_class_sh_basic.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
#include <memory>
66
#include <string>
7+
#include <vector>
78

89
namespace pybind11_tests {
910
namespace class_sh_basic {
@@ -57,11 +58,16 @@ std::string pass_udcp(std::unique_ptr<atyp const, sddc> obj) { return "pass_udcp
5758
// Helpers for testing.
5859
std::string get_mtxt(atyp const &obj) { return obj.mtxt; }
5960
std::unique_ptr<atyp> unique_ptr_roundtrip(std::unique_ptr<atyp> obj) { return obj; }
61+
struct SharedPtrStash {
62+
std::vector<std::shared_ptr<const atyp>> stash;
63+
void Add(std::shared_ptr<const atyp> obj) { stash.push_back(obj); }
64+
};
6065

6166
} // namespace class_sh_basic
6267
} // namespace pybind11_tests
6368

6469
PYBIND11_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::class_sh_basic::atyp)
70+
PYBIND11_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::class_sh_basic::SharedPtrStash)
6571

6672
namespace pybind11_tests {
6773
namespace class_sh_basic {
@@ -110,6 +116,9 @@ TEST_SUBMODULE(class_sh_basic, m) {
110116
// These require selected functions above to work first, as indicated:
111117
m.def("get_mtxt", get_mtxt); // pass_cref
112118
m.def("unique_ptr_roundtrip", unique_ptr_roundtrip); // pass_uqmp, rtrn_uqmp
119+
py::classh<SharedPtrStash>(m, "SharedPtrStash")
120+
.def(py::init<>())
121+
.def("Add", &SharedPtrStash::Add, py::arg("obj"));
113122

114123
m.def("py_type_handle_of_atyp", []() {
115124
return py::type::handle_of<atyp>(); // Exercises static_cast in this function.

tests/test_class_sh_basic.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,26 @@ def test_pass_unique_ptr_disowns(pass_f, rtrn_f, expected):
8585
)
8686

8787

88+
@pytest.mark.parametrize(
89+
"pass_f, rtrn_f",
90+
[
91+
(m.pass_uqmp, m.rtrn_uqmp),
92+
(m.pass_uqcp, m.rtrn_uqcp),
93+
(m.pass_udmp, m.rtrn_udmp),
94+
(m.pass_udcp, m.rtrn_udcp),
95+
],
96+
)
97+
def test_cannot_disown_use_count_ne_1(pass_f, rtrn_f):
98+
obj = rtrn_f()
99+
stash = m.SharedPtrStash()
100+
stash.Add(obj)
101+
with pytest.raises(ValueError) as exc_info:
102+
pass_f(obj)
103+
assert str(exc_info.value) == (
104+
"Cannot disown use_count != 1 (loaded_as_unique_ptr)."
105+
)
106+
107+
88108
def test_unique_ptr_roundtrip(num_round_trips=1000):
89109
# Multiple roundtrips to stress-test instance registration/deregistration.
90110
recycled = m.atyp("passenger")

0 commit comments

Comments
 (0)