Make unique_ptr's with non-default deleters work

Currently pybind11 only supports std::unique_ptr<T> 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_<Type, std::unique_ptr<Type, py::nodelete>> 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.
This commit is contained in:
Jason Rhinelander 2016-09-04 18:23:55 -04:00
parent f3be07c661
commit a6495af87a
3 changed files with 28 additions and 2 deletions

View File

@ -498,9 +498,9 @@ protected:
bool success = false; bool success = false;
}; };
template <typename type> class type_caster<std::unique_ptr<type>> { template <typename type, typename deleter> class type_caster<std::unique_ptr<type, deleter>> {
public: public:
static handle cast(std::unique_ptr<type> &&src, return_value_policy policy, handle parent) { static handle cast(std::unique_ptr<type, deleter> &&src, return_value_policy policy, handle parent) {
handle result = type_caster_base<type>::cast(src.get(), policy, parent); handle result = type_caster_base<type>::cast(src.get(), policy, parent);
if (result) if (result)
src.release(); src.release();

View File

@ -69,6 +69,18 @@ private:
int value; 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) /// Make pybind aware of the ref-counted wrapper type (s)
PYBIND11_DECLARE_HOLDER_TYPE(T, ref<T>); PYBIND11_DECLARE_HOLDER_TYPE(T, ref<T>);
PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr<T>); PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr<T>);
@ -143,6 +155,10 @@ test_initializer smart_ptr([](py::module &m) {
m.def("print_myobject3_3", &print_myobject3_3); m.def("print_myobject3_3", &print_myobject3_3);
m.def("print_myobject3_4", &print_myobject3_4); m.def("print_myobject3_4", &print_myobject3_4);
py::class_<MyObject4, std::unique_ptr<MyObject4, py::nodelete>>(m, "MyObject4")
.def(py::init<int>())
.def_readwrite("value", &MyObject4::value);
py::implicitly_convertible<py::int_, MyObject1>(); py::implicitly_convertible<py::int_, MyObject1>();
// Expose constructor stats for the ref type // Expose constructor stats for the ref type

View File

@ -113,3 +113,13 @@ def test_smart_ptr(capture):
# assert cstats.move_constructions >= 0 # Doesn't invoke any # assert cstats.move_constructions >= 0 # Doesn't invoke any
assert cstats.copy_assignments == 30 assert cstats.copy_assignments == 30
assert cstats.move_assignments == 0 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