From a6495af87af57f148f7264b45d02d8b6eb48d5f2 Mon Sep 17 00:00:00 2001 From: Jason Rhinelander Date: Sun, 4 Sep 2016 18:23:55 -0400 Subject: [PATCH] Make unique_ptr's with non-default deleters work Currently pybind11 only supports std::unique_ptr holders by default (other holders can, of course, be declared using the macro). PR #368 added a `py::nodelete` that is intended to be used as: py::class_> c("Type"); but this doesn't work out of the box. (You could add an explicit holder type declaration, but this doesn't appear to have been the intention of the commit). This commit fixes it by generalizing the unique_ptr type_caster to take both the type and deleter as template arguments, so that *any* unique_ptr instances are now automatically handled by pybind. It also adds a test to test_smart_ptr, testing both that py::nodelete (now) works, and that the object is indeed not deleted as intended. --- include/pybind11/cast.h | 4 ++-- tests/test_smart_ptr.cpp | 16 ++++++++++++++++ tests/test_smart_ptr.py | 10 ++++++++++ 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index 185c94352..4e5b21a18 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -498,9 +498,9 @@ protected: bool success = false; }; -template class type_caster> { +template class type_caster> { public: - static handle cast(std::unique_ptr &&src, return_value_policy policy, handle parent) { + static handle cast(std::unique_ptr &&src, return_value_policy policy, handle parent) { handle result = type_caster_base::cast(src.get(), policy, parent); if (result) src.release(); diff --git a/tests/test_smart_ptr.cpp b/tests/test_smart_ptr.cpp index d42596942..62c39c5f3 100644 --- a/tests/test_smart_ptr.cpp +++ b/tests/test_smart_ptr.cpp @@ -69,6 +69,18 @@ private: int value; }; +class MyObject4 { +public: + MyObject4(int value) : value{value} { + print_created(this); + } + int value; +private: + ~MyObject4() { + print_destroyed(this); + } +}; + /// Make pybind aware of the ref-counted wrapper type (s) PYBIND11_DECLARE_HOLDER_TYPE(T, ref); PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr); @@ -143,6 +155,10 @@ test_initializer smart_ptr([](py::module &m) { m.def("print_myobject3_3", &print_myobject3_3); m.def("print_myobject3_4", &print_myobject3_4); + py::class_>(m, "MyObject4") + .def(py::init()) + .def_readwrite("value", &MyObject4::value); + py::implicitly_convertible(); // Expose constructor stats for the ref type diff --git a/tests/test_smart_ptr.py b/tests/test_smart_ptr.py index ffc407db5..acb0f6172 100644 --- a/tests/test_smart_ptr.py +++ b/tests/test_smart_ptr.py @@ -113,3 +113,13 @@ def test_smart_ptr(capture): # assert cstats.move_constructions >= 0 # Doesn't invoke any assert cstats.copy_assignments == 30 assert cstats.move_assignments == 0 + +def test_unique_nodelete(capture): + from pybind11_tests import MyObject4 + o = MyObject4(23) + assert o.value == 23 + cstats = ConstructorStats.get(MyObject4) + assert cstats.alive() == 1 + del o + cstats = ConstructorStats.get(MyObject4) + assert cstats.alive() == 1 # Leak, but that's intentional