Skip to content

Commit 060f8eb

Browse files
Merge pull request #10 from EricCousineau-TRI/feature/unique_ptr_cast_error
unique_ptr: Use original type caster, otherwise, will get faulty overloads!
2 parents e190864 + daf6298 commit 060f8eb

File tree

5 files changed

+17
-51
lines changed

5 files changed

+17
-51
lines changed

include/pybind11/cast.h

Lines changed: 7 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1653,49 +1653,8 @@ struct move_only_holder_caster : type_caster_base<type> {
16531653

16541654
explicit operator holder_type&&() { return std::move(holder); }
16551655

1656-
object extract_obj(handle src) {
1657-
// See if this is a supported `move` container.
1658-
bool is_move_container = false;
1659-
object obj = none();
1660-
// TODO(eric.cousineau): See if we might need to safeguard against objects that are
1661-
// implicitly convertible from `list`.
1662-
// Can try to cast to T* first, and if that fails, assume it's a move container.
1663-
if (isinstance(src, (PyObject*)&PyList_Type) && PyList_Size(src.ptr()) == 1) {
1664-
// Extract the object from a single-item list, and remove the existing reference so we have exclusive control.
1665-
// @note This will break implicit casting when constructing from vectors, but eh, who cares.
1666-
// Swap.
1667-
list li = src.cast<list>();
1668-
obj = li[0];
1669-
li[0] = none();
1670-
is_move_container = true;
1671-
} else if (hasattr(src, "_is_move_container")) {
1672-
// Try to extract the value with `release()`.
1673-
obj = src.attr("release")();
1674-
is_move_container = true;
1675-
} else {
1676-
obj = reinterpret_borrow<object>(src);
1677-
}
1678-
if (is_move_container && obj.ref_count() != 1) {
1679-
throw std::runtime_error("Non-unique reference from a move-container, cannot cast to unique_ptr.");
1680-
}
1681-
return obj;
1682-
}
1683-
1684-
bool load(handle src, bool /*convert*/) {
1685-
if (src.is(none())) {
1686-
holder.reset();
1687-
return true;
1688-
}
1689-
// Allow loose reference management (if it's just a plain object) or require tighter reference
1690-
// management if it's a move container.
1691-
object obj = extract_obj(src);
1692-
// Do not use `load_impl`, as it's not structured conveniently for `unique_ptr`.
1693-
// Specifically, trying to delegate to resolving to conversion.
1694-
// return base::template load_impl<move_only_holder_caster<type, holder_type>>(src, convert);
1695-
check_holder_compat();
1696-
auto v_h = reinterpret_cast<instance*>(obj.ptr())->get_value_and_holder();
1697-
LoadType load_type = determine_load_type(obj, typeinfo);
1698-
return load_value(std::move(obj), std::move(v_h), load_type);
1656+
bool load(handle src, bool convert) {
1657+
return base::template load_impl<move_only_holder_caster>(src, convert);
16991658
}
17001659

17011660
static constexpr auto name = type_caster_base<type>::name;
@@ -1707,7 +1666,7 @@ struct move_only_holder_caster : type_caster_base<type> {
17071666
throw cast_error("Unable to load a non-default holder type (unique_ptr)");
17081667
}
17091668

1710-
bool load_value(object obj_exclusive, value_and_holder &&v_h, LoadType load_type) {
1669+
bool load_value(value_and_holder &&v_h, LoadType load_type) {
17111670
// TODO(eric.cousineau): This should try and find the downcast-lowest
17121671
// level (closest to child) `release_to_cpp` method that is derived-releasable
17131672
// (which derives from `wrapper<type>`).
@@ -1720,7 +1679,8 @@ struct move_only_holder_caster : type_caster_base<type> {
17201679
// NOT try to release using `PyBase`s mechanism.
17211680
// Additionally, if `Child` does not have a wrapper (for whatever reason) and is extended,
17221681
// then we still can NOT use `PyBase` since it's not part of the hierachy.
1723-
1682+
handle src = (PyObject*)v_h.inst;
1683+
object obj = reinterpret_borrow<object>(src);
17241684
// Try to get the lowest-hierarchy level of the type.
17251685
// This requires that we are single-inheritance at most.
17261686
const detail::type_info* lowest_type = nullptr;
@@ -1738,7 +1698,7 @@ struct move_only_holder_caster : type_caster_base<type> {
17381698
case LoadType::ConversionNeeded: {
17391699
// Try to get the lowest-hierarchy (closets to child class) of the type.
17401700
// The usage of `get_type_info` implicitly requires single inheritance.
1741-
auto* py_type = (PyTypeObject*)obj_exclusive.get_type().ptr();
1701+
auto* py_type = (PyTypeObject*)obj.get_type().ptr();
17421702
lowest_type = detail::get_type_info(py_type);
17431703
break;
17441704
}
@@ -1756,7 +1716,7 @@ struct move_only_holder_caster : type_caster_base<type> {
17561716
auto& release_info = lowest_type->release_info;
17571717
if (!release_info.release_to_cpp)
17581718
throw std::runtime_error("No release mechanism in lowest type?");
1759-
release_info.release_to_cpp(v_h.inst, &holder, std::move(obj_exclusive));
1719+
release_info.release_to_cpp(v_h.inst, &holder, std::move(obj));
17601720
return true;
17611721
}
17621722

include/pybind11/detail/class.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ inline bool deregister_instance_impl(void *ptr, instance *self) {
222222
auto &registered_instances = get_internals().registered_instances;
223223
auto range = registered_instances.equal_range(ptr);
224224
for (auto it = range.first; it != range.second; ++it) {
225-
if (Py_TYPE(self) == Py_TYPE(it->second)) {
225+
if (self == it->second && Py_TYPE(self) == Py_TYPE(it->second)) {
226226
registered_instances.erase(it);
227227
return true;
228228
}

tests/test_multiple_inheritance.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -354,9 +354,8 @@ def test_mi_ownership_constraint():
354354
# See `test_ownership_transfer` for positive tests.
355355

356356
# unique_ptr
357-
with pytest.raises(RuntimeError) as excinfo:
358-
c = m.ContainerBase1(m.MIType(10, 100))
359-
assert "multiple inheritance" in str(excinfo.value)
357+
c = m.ContainerBase1(m.MIType(10, 100))
358+
assert c is not None
360359

361360
# shared_ptr
362361
# Should not throw an error.

tests/test_smart_ptr.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,10 @@ TEST_SUBMODULE(smart_ptr, m) {
294294
.def(py::init<int>())
295295
.def("value", &UniquePtrHeld::value);
296296

297+
class UniquePtrOther {};
298+
py::class_<UniquePtrOther>(m, "UniquePtrOther")
299+
.def(py::init<>());
300+
297301
m.def("unique_ptr_pass_through",
298302
[](std::unique_ptr<UniquePtrHeld> obj) {
299303
return obj;

tests/test_smart_ptr.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,9 @@ def test_unique_ptr_arg():
251251
assert m.unique_ptr_pass_through(None) is None
252252
m.unique_ptr_terminal(None)
253253

254+
with pytest.raises(TypeError):
255+
m.unique_ptr_terminal(m.UniquePtrOther())
256+
254257

255258
def test_unique_ptr_to_shared_ptr():
256259
obj = m.shared_ptr_held_in_unique_ptr()

0 commit comments

Comments
 (0)