Skip to content

Commit 3dcfcb0

Browse files
Merge pull request #11 from EricCousineau-TRI/issue/drake_8160
unique_ptr: Fix ownership issues for failed overloads (RobotLocomotion/drake#8160)
2 parents 060f8eb + fe60760 commit 3dcfcb0

File tree

3 files changed

+49
-0
lines changed

3 files changed

+49
-0
lines changed

include/pybind11/cast.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1630,6 +1630,22 @@ struct move_only_holder_caster : type_caster_base<type> {
16301630
using base::typeinfo;
16311631
using base::value;
16321632

1633+
// We must explicitly define the default constructor(s) since we define a
1634+
// destructor; otherwise, the compiler will incorrectly use the copy
1635+
// constructor.
1636+
move_only_holder_caster() = default;
1637+
move_only_holder_caster(move_only_holder_caster&&) = default;
1638+
move_only_holder_caster(const move_only_holder_caster&) = delete;
1639+
~move_only_holder_caster() {
1640+
if (holder) {
1641+
// If the argument was loaded into C++, but not transferred out,
1642+
// then this was most likely part of a failed overload in
1643+
// `argument_loader`. Transfer ownership back to Python.
1644+
move_only_holder_caster::cast(
1645+
std::move(holder), return_value_policy{}, handle{});
1646+
}
1647+
}
1648+
16331649
static_assert(std::is_base_of<type_caster_base<type>, type_caster<type>>::value,
16341650
"Holder classes are only supported for custom types");
16351651

tests/test_smart_ptr.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,28 @@ TEST_SUBMODULE(smart_ptr, m) {
335335
return py::cast(std::move(obj));
336336
});
337337

338+
class FirstT {};
339+
py::class_<FirstT>(m, "FirstT")
340+
.def(py::init());
341+
class SecondT {};
342+
py::class_<SecondT>(m, "SecondT")
343+
.def(py::init());
344+
345+
m.def("unique_ptr_overload",
346+
[](std::unique_ptr<UniquePtrHeld> obj, FirstT) {
347+
py::dict out;
348+
out["obj"] = py::cast(std::move(obj));
349+
out["overload"] = 1;
350+
return out;
351+
});
352+
m.def("unique_ptr_overload",
353+
[](std::unique_ptr<UniquePtrHeld> obj, SecondT) {
354+
py::dict out;
355+
out["obj"] = py::cast(std::move(obj));
356+
out["overload"] = 2;
357+
return out;
358+
});
359+
338360
// Ensure class is non-empty, so it's easier to detect double-free
339361
// corruption. (If empty, this may be harder to see easily.)
340362
struct SharedPtrHeld { int value = 10; };

tests/test_smart_ptr.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,3 +258,14 @@ def test_unique_ptr_arg():
258258
def test_unique_ptr_to_shared_ptr():
259259
obj = m.shared_ptr_held_in_unique_ptr()
260260
assert m.shared_ptr_held_func(obj)
261+
262+
263+
def test_unique_ptr_overload_fail():
264+
obj = m.UniquePtrHeld(1)
265+
# These overloads pass ownership back to Python.
266+
out = m.unique_ptr_overload(obj, m.FirstT())
267+
assert out["obj"] is obj
268+
assert out["overload"] == 1
269+
out = m.unique_ptr_overload(obj, m.SecondT())
270+
assert out["obj"] is obj
271+
assert out["overload"] == 2

0 commit comments

Comments
 (0)