From b2c2c79240679463dd231c5430d8626bca3b596a Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Sun, 17 Jan 2016 22:36:40 +0100 Subject: [PATCH] improved handling of shared/smart pointers Previously, pybind11 required classes using std::shared_ptr<> to derive from std::enable_shared_from_this<> (or compilation failures would ensue). Everything now also works for classes that don't do this, assuming that some basic rules are followed (e.g. never passing "raw" pointers of instances manged by shared pointers). The safer std::enable_shared_from_this<> approach continues to be supported. --- docs/advanced.rst | 81 ++++-- example/example8.cpp | 88 +++++-- example/example8.py | 49 ++-- example/example8.ref | 510 +++++++++++++++++------------------- include/pybind11/cast.h | 59 +++-- include/pybind11/common.h | 2 +- include/pybind11/pybind11.h | 95 ++++--- include/pybind11/pytypes.h | 6 + 8 files changed, 493 insertions(+), 397 deletions(-) diff --git a/docs/advanced.rst b/docs/advanced.rst index 84ca73cc6..4c2b23bb2 100644 --- a/docs/advanced.rst +++ b/docs/advanced.rst @@ -521,13 +521,9 @@ following snippet causes ``std::shared_ptr`` to be used instead. .. code-block:: cpp - /// Type declaration - class Example : public std::enable_shared_from_this /* <- important, see below */ { - // ... - }; + py::class_ /* <- holder type */> obj(m, "Example"); - /// .... code within PYBIND11_PLUGIN declaration ..... - py::class_ /* <- important */> obj(m, "Example"); +Note that any particular class can only be associated with a single holder type. To enable transparent conversions for functions that take shared pointers as an argument or that return them, a macro invocation similar to the following must @@ -537,7 +533,7 @@ be declared at the top level before any binding code: PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr); -.. warning:: +.. note:: The first argument of :func:`PYBIND11_DECLARE_HOLDER_TYPE` should be a placeholder name that is used as a template parameter of the second @@ -545,25 +541,74 @@ be declared at the top level before any binding code: both sides; also, don't use the name of a type that already exists in your codebase. -.. warning:: +One potential stumbling block when using holder types is that they need to be +applied consistently. Can you guess what's broken about the following binding +code? - To ensure correct reference counting among Python and C++, the use of - ``std::shared_ptr`` as a holder type requires that ``T`` inherits from - ``std::enable_shared_from_this`` (see cppreference_ for details). +.. code-block:: cpp -If you encounter issues (failure to compile, ``bad_weak_ptr`` exceptions), -please check that you really did all three steps: + class Child { }; -1. invoking the ``PYBIND11_DECLARE_HOLDER_TYPE`` macro in every file that - contains pybind11 code and uses your chosen smart pointer type. + class Parent { + public: + Parent() : child(std::make_shared()) { } + Child *get_child() { return child.get(); } /* Hint: ** DON'T DO THIS ** */ + private: + std::shared_ptr child; + }; -2. specifying the holder types to ``class_``. + PYBIND11_PLUGIN(example) { + py::module m("example"); -3. extending from ``std::enable_shared_from_this`` when using - ``std::shared_ptr``. + py::class_>(m, "Child"); + + py::class_>(m, "Parent") + .def(py::init<>()) + .def("get_child", &Parent::get_child); + + return m.ptr(); + } + +The following Python code will cause undefined behavior (and likely a +segmentation fault). + +.. code-block:: python + + from example import Parent + print(Parent().get_child()) + +The problem is that ``Parent::get_child()`` returns a pointer to an instance of +``Child``, but the fact that this instance is already managed by +``std::shared_ptr<...>`` is lost when passing raw pointers. In this case, +pybind11 will create a second independent ``std::shared_ptr<...>`` that also +claims ownership of the pointer. In the end, the object will be freed **twice** +since these shared pointers have no way of knowing about each other. + +There are two ways to resolve this issue: + +1. For types that are managed by a smart pointer class, never use raw pointers + in function arguments or return values. In other words: always consistently + wrap pointers into their designated holder types (such as + ``std::shared_ptr<...>``). In this case, the signature of ``get_child()`` + should be modified as follows: + +.. code-block:: cpp + + std::shared_ptr get_child() { return child; } + +2. Adjust the definition of ``Child`` by specifying + ``std::enable_shared_from_this`` (see cppreference_ for details) as a + base class. This adds a small bit of information to ``Child`` that allows + pybind11 to realize that there is already an existing + ``std::shared_ptr<...>`` and communicate with it. In this case, the + declaration of ``Child`` should look as follows: .. _cppreference: http://en.cppreference.com/w/cpp/memory/enable_shared_from_this +.. code-block:: cpp + + class Child : public std::enable_shared_from_this { }; + .. seealso:: The file :file:`example/example8.cpp` contains a complete example that diff --git a/example/example8.cpp b/example/example8.cpp index 17ca98c90..f492588bd 100644 --- a/example/example8.cpp +++ b/example/example8.cpp @@ -11,19 +11,19 @@ #include "example.h" #include "object.h" -/// Object subclass -class MyObject : public Object { +/// Custom object with builtin reference counting (see 'object.h' for the implementation) +class MyObject1 : public Object { public: - MyObject(int value) : value(value) { + MyObject1(int value) : value(value) { std::cout << toString() << " constructor" << std::endl; } std::string toString() const { - return "MyObject[" + std::to_string(value) + "]"; + return "MyObject1[" + std::to_string(value) + "]"; } protected: - virtual ~MyObject() { + virtual ~MyObject1() { std::cout << toString() << " destructor" << std::endl; } @@ -31,7 +31,8 @@ private: int value; }; -class MyObject2 : public std::enable_shared_from_this { +/// Object managed by a std::shared_ptr<> +class MyObject2 { public: MyObject2(int value) : value(value) { std::cout << toString() << " constructor" << std::endl; @@ -49,52 +50,80 @@ private: int value; }; -/// Make pybind aware of the ref-counted wrapper type +/// Object managed by a std::shared_ptr<>, additionally derives from std::enable_shared_from_this<> +class MyObject3 : public std::enable_shared_from_this { +public: + MyObject3(int value) : value(value) { + std::cout << toString() << " constructor" << std::endl; + } + + std::string toString() const { + return "MyObject3[" + std::to_string(value) + "]"; + } + + virtual ~MyObject3() { + std::cout << toString() << " destructor" << std::endl; + } + +private: + int value; +}; + +/// Make pybind aware of the ref-counted wrapper type (s) PYBIND11_DECLARE_HOLDER_TYPE(T, ref); PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr); -Object *make_object_1() { return new MyObject(1); } -ref make_object_2() { return new MyObject(2); } -MyObject *make_myobject_4() { return new MyObject(4); } -ref make_myobject_5() { return new MyObject(5); } +Object *make_object_1() { return new MyObject1(1); } +ref make_object_2() { return new MyObject1(2); } -MyObject2 *make_myobject2_1() { return new MyObject2(1); } -std::shared_ptr make_myobject2_2() { return std::make_shared(2); } +MyObject1 *make_myobject1_1() { return new MyObject1(4); } +ref make_myobject1_2() { return new MyObject1(5); } + +MyObject2 *make_myobject2_1() { return new MyObject2(6); } +std::shared_ptr make_myobject2_2() { return std::make_shared(7); } + +MyObject3 *make_myobject3_1() { return new MyObject3(8); } +std::shared_ptr make_myobject3_2() { return std::make_shared(9); } void print_object_1(const Object *obj) { std::cout << obj->toString() << std::endl; } void print_object_2(ref obj) { std::cout << obj->toString() << std::endl; } void print_object_3(const ref &obj) { std::cout << obj->toString() << std::endl; } void print_object_4(const ref *obj) { std::cout << (*obj)->toString() << std::endl; } -void print_myobject_1(const MyObject *obj) { std::cout << obj->toString() << std::endl; } -void print_myobject_2(ref obj) { std::cout << obj->toString() << std::endl; } -void print_myobject_3(const ref &obj) { std::cout << obj->toString() << std::endl; } -void print_myobject_4(const ref *obj) { std::cout << (*obj)->toString() << std::endl; } +void print_myobject1_1(const MyObject1 *obj) { std::cout << obj->toString() << std::endl; } +void print_myobject1_2(ref obj) { std::cout << obj->toString() << std::endl; } +void print_myobject1_3(const ref &obj) { std::cout << obj->toString() << std::endl; } +void print_myobject1_4(const ref *obj) { std::cout << (*obj)->toString() << std::endl; } void print_myobject2_1(const MyObject2 *obj) { std::cout << obj->toString() << std::endl; } void print_myobject2_2(std::shared_ptr obj) { std::cout << obj->toString() << std::endl; } void print_myobject2_3(const std::shared_ptr &obj) { std::cout << obj->toString() << std::endl; } void print_myobject2_4(const std::shared_ptr *obj) { std::cout << (*obj)->toString() << std::endl; } +void print_myobject3_1(const MyObject3 *obj) { std::cout << obj->toString() << std::endl; } +void print_myobject3_2(std::shared_ptr obj) { std::cout << obj->toString() << std::endl; } +void print_myobject3_3(const std::shared_ptr &obj) { std::cout << obj->toString() << std::endl; } +void print_myobject3_4(const std::shared_ptr *obj) { std::cout << (*obj)->toString() << std::endl; } + void init_ex8(py::module &m) { py::class_> obj(m, "Object"); obj.def("getRefCount", &Object::getRefCount); - py::class_>(m, "MyObject", obj) + py::class_>(m, "MyObject1", obj) .def(py::init()); m.def("make_object_1", &make_object_1); m.def("make_object_2", &make_object_2); - m.def("make_myobject_4", &make_myobject_4); - m.def("make_myobject_5", &make_myobject_5); + m.def("make_myobject1_1", &make_myobject1_1); + m.def("make_myobject1_2", &make_myobject1_2); m.def("print_object_1", &print_object_1); m.def("print_object_2", &print_object_2); m.def("print_object_3", &print_object_3); m.def("print_object_4", &print_object_4); - m.def("print_myobject_1", &print_myobject_1); - m.def("print_myobject_2", &print_myobject_2); - m.def("print_myobject_3", &print_myobject_3); - m.def("print_myobject_4", &print_myobject_4); + m.def("print_myobject1_1", &print_myobject1_1); + m.def("print_myobject1_2", &print_myobject1_2); + m.def("print_myobject1_3", &print_myobject1_3); + m.def("print_myobject1_4", &print_myobject1_4); py::class_>(m, "MyObject2") .def(py::init()); @@ -105,5 +134,14 @@ void init_ex8(py::module &m) { m.def("print_myobject2_3", &print_myobject2_3); m.def("print_myobject2_4", &print_myobject2_4); - py::implicitly_convertible(); + py::class_>(m, "MyObject3") + .def(py::init()); + m.def("make_myobject3_1", &make_myobject3_1); + m.def("make_myobject3_2", &make_myobject3_2); + m.def("print_myobject3_1", &print_myobject3_1); + m.def("print_myobject3_2", &print_myobject3_2); + m.def("print_myobject3_3", &print_myobject3_3); + m.def("print_myobject3_4", &print_myobject3_4); + + py::implicitly_convertible(); } diff --git a/example/example8.py b/example/example8.py index 339e008ec..b12c5b999 100755 --- a/example/example8.py +++ b/example/example8.py @@ -3,49 +3,68 @@ from __future__ import print_function import sys sys.path.append('.') -from example import MyObject +from example import MyObject1 +from example import MyObject2 +from example import MyObject3 + from example import make_object_1 from example import make_object_2 -from example import make_myobject_4 -from example import make_myobject_5 +from example import make_myobject1_1 +from example import make_myobject1_2 from example import make_myobject2_1 from example import make_myobject2_2 +from example import make_myobject3_1 +from example import make_myobject3_2 + from example import print_object_1 from example import print_object_2 from example import print_object_3 from example import print_object_4 -from example import print_myobject_1 -from example import print_myobject_2 -from example import print_myobject_3 -from example import print_myobject_4 + +from example import print_myobject1_1 +from example import print_myobject1_2 +from example import print_myobject1_3 +from example import print_myobject1_4 + from example import print_myobject2_1 from example import print_myobject2_2 from example import print_myobject2_3 from example import print_myobject2_4 -for o in [make_object_1(), make_object_2(), MyObject(3)]: +from example import print_myobject3_1 +from example import print_myobject3_2 +from example import print_myobject3_3 +from example import print_myobject3_4 + +for o in [make_object_1(), make_object_2(), MyObject1(3)]: print("Reference count = %i" % o.getRefCount()) print_object_1(o) print_object_2(o) print_object_3(o) print_object_4(o) -for o in [make_myobject_4(), make_myobject_5(), MyObject(6), 7]: +for o in [make_myobject1_1(), make_myobject1_2(), MyObject1(6), 7]: print(o) if not isinstance(o, int): print_object_1(o) print_object_2(o) print_object_3(o) print_object_4(o) - print_myobject_1(o) - print_myobject_2(o) - print_myobject_3(o) - print_myobject_4(o) + print_myobject1_1(o) + print_myobject1_2(o) + print_myobject1_3(o) + print_myobject1_4(o) - -for o in [make_myobject2_1(), make_myobject2_2()]: +for o in [MyObject2(8), make_myobject2_1(), make_myobject2_2()]: print(o) print_myobject2_1(o) print_myobject2_2(o) print_myobject2_3(o) print_myobject2_4(o) + +for o in [MyObject3(9), make_myobject3_1(), make_myobject3_2()]: + print(o) + print_myobject3_1(o) + print_myobject3_2(o) + print_myobject3_3(o) + print_myobject3_4(o) diff --git a/example/example8.ref b/example/example8.ref index 26f42e3f1..641abd0c4 100644 --- a/example/example8.ref +++ b/example/example8.ref @@ -1,275 +1,243 @@ -MyObject[1] constructor -Initialized ref from pointer 0x7ffdb2c16a50 -MyObject[2] constructor -Initialized ref from pointer 0x7ffdb2c167a0 -Initialized ref from pointer 0x7ffdb2c167a0 -Destructing ref 0x7ffdb2c167a0 -MyObject[3] constructor -Initialized ref from pointer 0x7ffdb2c17420 +MyObject1[1] constructor +Initialized ref from pointer 0x1347ba0 +MyObject1[2] constructor +Initialized ref from pointer 0x12b9270 +Initialized ref from ref 0x12b9270 +Destructing ref 0x12b9270 +MyObject1[3] constructor +Initialized ref from pointer 0x12a2a90 +MyObject1[1] +Created empty ref +Assigning ref 0x1347ba0 +Initialized ref from ref 0x1347ba0 +MyObject1[1] +Destructing ref 0x1347ba0 +Destructing ref 0x1347ba0 +Created empty ref +Assigning ref 0x1347ba0 +MyObject1[1] +Destructing ref 0x1347ba0 +Created empty ref +Assigning ref 0x1347ba0 +MyObject1[1] +Destructing ref 0x1347ba0 +MyObject1[2] +Created empty ref +Assigning ref 0x12b9270 +Initialized ref from ref 0x12b9270 +MyObject1[2] +Destructing ref 0x12b9270 +Destructing ref 0x12b9270 +Created empty ref +Assigning ref 0x12b9270 +MyObject1[2] +Destructing ref 0x12b9270 +Created empty ref +Assigning ref 0x12b9270 +MyObject1[2] +Destructing ref 0x12b9270 +MyObject1[3] +Created empty ref +Assigning ref 0x12a2a90 +Initialized ref from ref 0x12a2a90 +MyObject1[3] +Destructing ref 0x12a2a90 +Destructing ref 0x12a2a90 +Created empty ref +Assigning ref 0x12a2a90 +MyObject1[3] +Destructing ref 0x12a2a90 +Created empty ref +Assigning ref 0x12a2a90 +MyObject1[3] +Destructing ref 0x12a2a90 +Destructing ref 0x12b9270 +MyObject1[2] destructor +Destructing ref 0x1347ba0 +MyObject1[1] destructor +MyObject1[4] constructor +Initialized ref from pointer 0x1347ba0 +MyObject1[5] constructor +Initialized ref from pointer 0x1299190 +Initialized ref from ref 0x1299190 +Destructing ref 0x1299190 +MyObject1[6] constructor +Initialized ref from pointer 0x133e2f0 +Destructing ref 0x12a2a90 +MyObject1[3] destructor +MyObject1[4] +Created empty ref +Assigning ref 0x1347ba0 +Initialized ref from ref 0x1347ba0 +MyObject1[4] +Destructing ref 0x1347ba0 +Destructing ref 0x1347ba0 +Created empty ref +Assigning ref 0x1347ba0 +MyObject1[4] +Destructing ref 0x1347ba0 +Created empty ref +Assigning ref 0x1347ba0 +MyObject1[4] +Destructing ref 0x1347ba0 +MyObject1[4] +Created empty ref +Assigning ref 0x1347ba0 +Initialized ref from ref 0x1347ba0 +MyObject1[4] +Destructing ref 0x1347ba0 +Destructing ref 0x1347ba0 +Created empty ref +Assigning ref 0x1347ba0 +MyObject1[4] +Destructing ref 0x1347ba0 +Created empty ref +Assigning ref 0x1347ba0 +MyObject1[4] +Destructing ref 0x1347ba0 +MyObject1[5] +Created empty ref +Assigning ref 0x1299190 +Initialized ref from ref 0x1299190 +MyObject1[5] +Destructing ref 0x1299190 +Destructing ref 0x1299190 +Created empty ref +Assigning ref 0x1299190 +MyObject1[5] +Destructing ref 0x1299190 +Created empty ref +Assigning ref 0x1299190 +MyObject1[5] +Destructing ref 0x1299190 +MyObject1[5] +Created empty ref +Assigning ref 0x1299190 +Initialized ref from ref 0x1299190 +MyObject1[5] +Destructing ref 0x1299190 +Destructing ref 0x1299190 +Created empty ref +Assigning ref 0x1299190 +MyObject1[5] +Destructing ref 0x1299190 +Created empty ref +Assigning ref 0x1299190 +MyObject1[5] +Destructing ref 0x1299190 +MyObject1[6] +Created empty ref +Assigning ref 0x133e2f0 +Initialized ref from ref 0x133e2f0 +MyObject1[6] +Destructing ref 0x133e2f0 +Destructing ref 0x133e2f0 +Created empty ref +Assigning ref 0x133e2f0 +MyObject1[6] +Destructing ref 0x133e2f0 +Created empty ref +Assigning ref 0x133e2f0 +MyObject1[6] +Destructing ref 0x133e2f0 +MyObject1[6] +Created empty ref +Assigning ref 0x133e2f0 +Initialized ref from ref 0x133e2f0 +MyObject1[6] +Destructing ref 0x133e2f0 +Destructing ref 0x133e2f0 +Created empty ref +Assigning ref 0x133e2f0 +MyObject1[6] +Destructing ref 0x133e2f0 +Created empty ref +Assigning ref 0x133e2f0 +MyObject1[6] +Destructing ref 0x133e2f0 +MyObject1[7] constructor +Initialized ref from pointer 0x133f3a0 +MyObject1[7] +Destructing ref 0x133f3a0 +MyObject1[7] destructor +Created empty ref +MyObject1[7] constructor +Initialized ref from pointer 0x12a2a90 +Assigning ref 0x12a2a90 +Initialized ref from ref 0x12a2a90 +MyObject1[7] +Destructing ref 0x12a2a90 +Destructing ref 0x12a2a90 +Destructing ref 0x12a2a90 +MyObject1[7] destructor +Created empty ref +MyObject1[7] constructor +Initialized ref from pointer 0x133f3a0 +Assigning ref 0x133f3a0 +MyObject1[7] +Destructing ref 0x133f3a0 +Destructing ref 0x133f3a0 +MyObject1[7] destructor +Created empty ref +MyObject1[7] constructor +Initialized ref from pointer 0x12a2a90 +Assigning ref 0x12a2a90 +MyObject1[7] +Destructing ref 0x12a2a90 +Destructing ref 0x12a2a90 +MyObject1[7] destructor +Destructing ref 0x133e2f0 +MyObject1[6] destructor +Destructing ref 0x1299190 +MyObject1[5] destructor +Destructing ref 0x1347ba0 +MyObject1[4] destructor +MyObject2[8] constructor +MyObject2[6] constructor +MyObject2[7] constructor +MyObject2[8] +MyObject2[8] +MyObject2[8] +MyObject2[8] +MyObject2[6] +MyObject2[6] +MyObject2[6] +MyObject2[6] +MyObject2[7] +MyObject2[7] +MyObject2[7] +MyObject2[7] +MyObject2[6] destructor +MyObject2[8] destructor +MyObject3[9] constructor +MyObject3[8] constructor +MyObject3[9] constructor +MyObject2[7] destructor +MyObject3[9] +MyObject3[9] +MyObject3[9] +MyObject3[9] +MyObject3[8] +MyObject3[8] +MyObject3[8] +MyObject3[8] +MyObject3[9] +MyObject3[9] +MyObject3[9] +MyObject3[9] +MyObject3[8] destructor +MyObject3[9] destructor Reference count = 1 -MyObject[1] -Created empty ref -Initialized ref from pointer 0x7ffdb2c16a50 -Move-assigning ref 0x7ffdb2c16a50 -Destructing ref 0x0 -Initialized ref from ref 0x7ffdb2c16a50 -MyObject[1] -Destructing ref 0x7ffdb2c16a50 -Destructing ref 0x7ffdb2c16a50 -Created empty ref -Initialized ref from pointer 0x7ffdb2c16a50 -Move-assigning ref 0x7ffdb2c16a50 -Destructing ref 0x0 -MyObject[1] -Destructing ref 0x7ffdb2c16a50 -Created empty ref -Initialized ref from pointer 0x7ffdb2c16a50 -Move-assigning ref 0x7ffdb2c16a50 -Destructing ref 0x0 -MyObject[1] -Destructing ref 0x7ffdb2c16a50 Reference count = 1 -MyObject[2] -Created empty ref -Initialized ref from pointer 0x7ffdb2c167a0 -Move-assigning ref 0x7ffdb2c167a0 -Destructing ref 0x0 -Initialized ref from ref 0x7ffdb2c167a0 -MyObject[2] -Destructing ref 0x7ffdb2c167a0 -Destructing ref 0x7ffdb2c167a0 -Created empty ref -Initialized ref from pointer 0x7ffdb2c167a0 -Move-assigning ref 0x7ffdb2c167a0 -Destructing ref 0x0 -MyObject[2] -Destructing ref 0x7ffdb2c167a0 -Created empty ref -Initialized ref from pointer 0x7ffdb2c167a0 -Move-assigning ref 0x7ffdb2c167a0 -Destructing ref 0x0 -MyObject[2] -Destructing ref 0x7ffdb2c167a0 Reference count = 1 -MyObject[3] -Created empty ref -Initialized ref from pointer 0x7ffdb2c17420 -Move-assigning ref 0x7ffdb2c17420 -Destructing ref 0x0 -Initialized ref from ref 0x7ffdb2c17420 -MyObject[3] -Destructing ref 0x7ffdb2c17420 -Destructing ref 0x7ffdb2c17420 -Created empty ref -Initialized ref from pointer 0x7ffdb2c17420 -Move-assigning ref 0x7ffdb2c17420 -Destructing ref 0x0 -MyObject[3] -Destructing ref 0x7ffdb2c17420 -Created empty ref -Initialized ref from pointer 0x7ffdb2c17420 -Move-assigning ref 0x7ffdb2c17420 -Destructing ref 0x0 -MyObject[3] -Destructing ref 0x7ffdb2c17420 -Destructing ref 0x7ffdb2c167a0 -MyObject[2] destructor -Destructing ref 0x7ffdb2c16a50 -MyObject[1] destructor -MyObject[4] constructor -Initialized ref from pointer 0x7ffdb2c16a50 -MyObject[5] constructor -Initialized ref from pointer 0x7ffdb2c167a0 -Initialized ref from pointer 0x7ffdb2c167a0 -Destructing ref 0x7ffdb2c167a0 -MyObject[6] constructor -Initialized ref from pointer 0x7ffdb2c161d0 -Destructing ref 0x7ffdb2c17420 -MyObject[3] destructor - -MyObject[4] -Created empty ref -Initialized ref from pointer 0x7ffdb2c16a50 -Move-assigning ref 0x7ffdb2c16a50 -Destructing ref 0x0 -Initialized ref from ref 0x7ffdb2c16a50 -MyObject[4] -Destructing ref 0x7ffdb2c16a50 -Destructing ref 0x7ffdb2c16a50 -Created empty ref -Initialized ref from pointer 0x7ffdb2c16a50 -Move-assigning ref 0x7ffdb2c16a50 -Destructing ref 0x0 -MyObject[4] -Destructing ref 0x7ffdb2c16a50 -Created empty ref -Initialized ref from pointer 0x7ffdb2c16a50 -Move-assigning ref 0x7ffdb2c16a50 -Destructing ref 0x0 -MyObject[4] -Destructing ref 0x7ffdb2c16a50 -MyObject[4] -Created empty ref -Initialized ref from pointer 0x7ffdb2c16a50 -Move-assigning ref 0x7ffdb2c16a50 -Destructing ref 0x0 -Initialized ref from ref 0x7ffdb2c16a50 -MyObject[4] -Destructing ref 0x7ffdb2c16a50 -Destructing ref 0x7ffdb2c16a50 -Created empty ref -Initialized ref from pointer 0x7ffdb2c16a50 -Move-assigning ref 0x7ffdb2c16a50 -Destructing ref 0x0 -MyObject[4] -Destructing ref 0x7ffdb2c16a50 -Created empty ref -Initialized ref from pointer 0x7ffdb2c16a50 -Move-assigning ref 0x7ffdb2c16a50 -Destructing ref 0x0 -MyObject[4] -Destructing ref 0x7ffdb2c16a50 - -MyObject[5] -Created empty ref -Initialized ref from pointer 0x7ffdb2c167a0 -Move-assigning ref 0x7ffdb2c167a0 -Destructing ref 0x0 -Initialized ref from ref 0x7ffdb2c167a0 -MyObject[5] -Destructing ref 0x7ffdb2c167a0 -Destructing ref 0x7ffdb2c167a0 -Created empty ref -Initialized ref from pointer 0x7ffdb2c167a0 -Move-assigning ref 0x7ffdb2c167a0 -Destructing ref 0x0 -MyObject[5] -Destructing ref 0x7ffdb2c167a0 -Created empty ref -Initialized ref from pointer 0x7ffdb2c167a0 -Move-assigning ref 0x7ffdb2c167a0 -Destructing ref 0x0 -MyObject[5] -Destructing ref 0x7ffdb2c167a0 -MyObject[5] -Created empty ref -Initialized ref from pointer 0x7ffdb2c167a0 -Move-assigning ref 0x7ffdb2c167a0 -Destructing ref 0x0 -Initialized ref from ref 0x7ffdb2c167a0 -MyObject[5] -Destructing ref 0x7ffdb2c167a0 -Destructing ref 0x7ffdb2c167a0 -Created empty ref -Initialized ref from pointer 0x7ffdb2c167a0 -Move-assigning ref 0x7ffdb2c167a0 -Destructing ref 0x0 -MyObject[5] -Destructing ref 0x7ffdb2c167a0 -Created empty ref -Initialized ref from pointer 0x7ffdb2c167a0 -Move-assigning ref 0x7ffdb2c167a0 -Destructing ref 0x0 -MyObject[5] -Destructing ref 0x7ffdb2c167a0 - -MyObject[6] -Created empty ref -Initialized ref from pointer 0x7ffdb2c161d0 -Move-assigning ref 0x7ffdb2c161d0 -Destructing ref 0x0 -Initialized ref from ref 0x7ffdb2c161d0 -MyObject[6] -Destructing ref 0x7ffdb2c161d0 -Destructing ref 0x7ffdb2c161d0 -Created empty ref -Initialized ref from pointer 0x7ffdb2c161d0 -Move-assigning ref 0x7ffdb2c161d0 -Destructing ref 0x0 -MyObject[6] -Destructing ref 0x7ffdb2c161d0 -Created empty ref -Initialized ref from pointer 0x7ffdb2c161d0 -Move-assigning ref 0x7ffdb2c161d0 -Destructing ref 0x0 -MyObject[6] -Destructing ref 0x7ffdb2c161d0 -MyObject[6] -Created empty ref -Initialized ref from pointer 0x7ffdb2c161d0 -Move-assigning ref 0x7ffdb2c161d0 -Destructing ref 0x0 -Initialized ref from ref 0x7ffdb2c161d0 -MyObject[6] -Destructing ref 0x7ffdb2c161d0 -Destructing ref 0x7ffdb2c161d0 -Created empty ref -Initialized ref from pointer 0x7ffdb2c161d0 -Move-assigning ref 0x7ffdb2c161d0 -Destructing ref 0x0 -MyObject[6] -Destructing ref 0x7ffdb2c161d0 -Created empty ref -Initialized ref from pointer 0x7ffdb2c161d0 -Move-assigning ref 0x7ffdb2c161d0 -Destructing ref 0x0 -MyObject[6] -Destructing ref 0x7ffdb2c161d0 + + + 7 -MyObject[7] constructor -Initialized ref from pointer 0x7ffdb2c17420 -MyObject[7] -Destructing ref 0x7ffdb2c17420 -MyObject[7] destructor -Created empty ref -MyObject[7] constructor -Initialized ref from pointer 0x7ffdb2c17420 -Initialized ref from pointer 0x7ffdb2c17420 -Move-assigning ref 0x7ffdb2c17420 -Destructing ref 0x0 -Initialized ref from ref 0x7ffdb2c17420 -MyObject[7] -Destructing ref 0x7ffdb2c17420 -Destructing ref 0x7ffdb2c17420 -Destructing ref 0x7ffdb2c17420 -MyObject[7] destructor -Created empty ref -MyObject[7] constructor -Initialized ref from pointer 0x7ffdb2c17420 -Initialized ref from pointer 0x7ffdb2c17420 -Move-assigning ref 0x7ffdb2c17420 -Destructing ref 0x0 -MyObject[7] -Destructing ref 0x7ffdb2c17420 -Destructing ref 0x7ffdb2c17420 -MyObject[7] destructor -Created empty ref -MyObject[7] constructor -Initialized ref from pointer 0x7ffdb2c17420 -Initialized ref from pointer 0x7ffdb2c17420 -Move-assigning ref 0x7ffdb2c17420 -Destructing ref 0x0 -MyObject[7] -Destructing ref 0x7ffdb2c17420 -Destructing ref 0x7ffdb2c17420 -MyObject[7] destructor -Destructing ref 0x7ffdb2c161d0 -MyObject[6] destructor -Destructing ref 0x7ffdb2c167a0 -MyObject[5] destructor -Destructing ref 0x7ffdb2c16a50 -MyObject[4] destructor -MyObject2[1] constructor -MyObject2[2] constructor - -MyObject2[1] -MyObject2[1] -MyObject2[1] -MyObject2[1] - -MyObject2[2] -MyObject2[2] -MyObject2[2] -MyObject2[2] -MyObject2[1] destructor -MyObject2[2] destructor + + + + + + +MyObject3[9] destructor diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index 29f0a87cb..13f42d155 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -19,9 +19,9 @@ NAMESPACE_BEGIN(pybind11) NAMESPACE_BEGIN(detail) -class type_caster_custom { +class type_caster_generic { public: - PYBIND11_NOINLINE type_caster_custom(const std::type_info *type_info) { + PYBIND11_NOINLINE type_caster_generic(const std::type_info *type_info) { auto & registered_types = get_internals().registered_types; auto it = registered_types.find(type_info); if (it != registered_types.end()) { @@ -57,7 +57,9 @@ public: } PYBIND11_NOINLINE static PyObject *cast(const void *_src, return_value_policy policy, PyObject *parent, - const std::type_info *type_info, void *(*copy_constructor)(const void *)) { + const std::type_info *type_info, + void *(*copy_constructor)(const void *), + const void *existing_holder = nullptr) { void *src = const_cast(_src); if (src == nullptr) { Py_INCREF(Py_None); @@ -100,7 +102,7 @@ public: Py_XINCREF(parent); } PyObject *inst_pyobj = (PyObject *) inst; - reg_type.init_holder(inst_pyobj); + reg_type.init_holder(inst_pyobj, existing_holder); if (!dont_cache) internals.registered_instances[inst->value] = inst_pyobj; return inst_pyobj; @@ -113,20 +115,20 @@ protected: }; /// Generic type caster for objects stored on the heap -template class type_caster : public type_caster_custom { +template class type_caster : public type_caster_generic { public: static PYBIND11_DESCR name() { return type_descr(_()); } - type_caster() : type_caster_custom(&typeid(type)) { } + type_caster() : type_caster_generic(&typeid(type)) { } static PyObject *cast(const type &src, return_value_policy policy, PyObject *parent) { if (policy == return_value_policy::automatic) policy = return_value_policy::copy; - return type_caster_custom::cast(&src, policy, parent, &typeid(type), ©_constructor); + return type_caster_generic::cast(&src, policy, parent, &typeid(type), ©_constructor); } static PyObject *cast(const type *src, return_value_policy policy, PyObject *parent) { - return type_caster_custom::cast(src, policy, parent, &typeid(type), ©_constructor); + return type_caster_generic::cast(src, policy, parent, &typeid(type), ©_constructor); } operator type*() { return (type *) value; } @@ -134,7 +136,7 @@ public: protected: template ::value, int>::type = 0> static void *copy_constructor(const void *arg) { - return new type(*((const type *)arg)); + return new type(*((const type *) arg)); } template ::value, int>::type = 0> static void *copy_constructor(const void *) { return nullptr; } @@ -433,24 +435,29 @@ protected: /// Type caster for holder types like std::shared_ptr, etc. template class type_caster_holder : public type_caster { public: - typedef type_caster parent; + using type_caster::cast; + using type_caster::typeinfo; + using type_caster::value; + using type_caster::temp; + using type_caster::copy_constructor; - template , T>::value, int>::type = 0> bool load(PyObject *src, bool convert) { - if (!parent::load(src, convert)) + if (src == nullptr || typeinfo == nullptr) return false; - holder = holder_type(((type *) parent::value)->shared_from_this()); - return true; - } - - template , T>::value, int>::type = 0> - bool load(PyObject *src, bool convert) { - if (!parent::load(src, convert)) - return false; - holder = holder_type((type *) parent::value); - return true; + if (PyType_IsSubtype(Py_TYPE(src), typeinfo->type)) { + auto inst = (instance *) src; + value = inst->value; + holder = inst->holder; + return true; + } + if (convert) { + for (auto &converter : typeinfo->implicit_conversions) { + temp = object(converter(src, typeinfo->type), false); + if (load(temp.ptr(), false)) + return true; + } + } + return false; } explicit operator type*() { return this->value; } @@ -458,9 +465,9 @@ public: explicit operator holder_type&() { return holder; } explicit operator holder_type*() { return &holder; } - using type_caster::cast; static PyObject *cast(const holder_type &src, return_value_policy policy, PyObject *parent) { - return type_caster::cast(src.get(), policy, parent); + return type_caster_generic::cast( + src.get(), policy, parent, &typeid(type), ©_constructor, &src); } protected: diff --git a/include/pybind11/common.h b/include/pybind11/common.h index a2a44989e..742e24a0a 100644 --- a/include/pybind11/common.h +++ b/include/pybind11/common.h @@ -177,7 +177,7 @@ template > struct in struct type_info { PyTypeObject *type; size_t type_size; - void (*init_holder)(PyObject *); + void (*init_holder)(PyObject *, const void *); std::vector implicit_conversions; buffer_info *(*get_buffer)(PyObject *, void *) = nullptr; void *get_buffer_data = nullptr; diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index e8b385cb0..9de62e466 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -122,32 +122,32 @@ private: template using arg_value_caster = detail::type_caster>; - template static void process_extras(const std::tuple &args, function_entry *entry) { - process_extras(args, entry, typename detail::make_index_sequence::type()); + template static void process_static(const std::tuple &args, function_entry *entry) { + process_static(args, entry, typename detail::make_index_sequence::type()); } - template static void process_extras(const std::tuple &args, + template static void process_static(const std::tuple &args, function_entry *entry, detail::index_sequence) { - int unused[] = { 0, (process_extra(std::get(args), entry), 0)... }; + int unused[] = { 0, (process_static(std::get(args), entry), 0)... }; (void) unused; } template - static void process_extra(const keep_alive &, function_entry *) { } - static void process_extra(const char *doc, function_entry *entry) { entry->doc = (char *) doc; } - static void process_extra(const pybind11::doc &d, function_entry *entry) { entry->doc = (char *) d.value; } - static void process_extra(const pybind11::name &n, function_entry *entry) { entry->name = (char *) n.value; } - static void process_extra(const pybind11::return_value_policy p, function_entry *entry) { entry->policy = p; } - static void process_extra(const pybind11::sibling s, function_entry *entry) { entry->sibling = s.value; } - static void process_extra(const pybind11::is_method &m, function_entry *entry) { entry->class_ = m.class_; } - static void process_extra(const pybind11::arg &a, function_entry *entry) { + static void process_static(const keep_alive &, function_entry *) { } + static void process_static(const char *doc, function_entry *entry) { entry->doc = (char *) doc; } + static void process_static(const pybind11::doc &d, function_entry *entry) { entry->doc = (char *) d.value; } + static void process_static(const pybind11::name &n, function_entry *entry) { entry->name = (char *) n.value; } + static void process_static(const pybind11::return_value_policy p, function_entry *entry) { entry->policy = p; } + static void process_static(const pybind11::sibling s, function_entry *entry) { entry->sibling = s.value; } + static void process_static(const pybind11::is_method &m, function_entry *entry) { entry->class_ = m.class_; } + static void process_static(const pybind11::arg &a, function_entry *entry) { if (entry->class_ && entry->args.empty()) entry->args.emplace_back("self", nullptr, nullptr); entry->args.emplace_back(a.name, nullptr, nullptr); } template - static void process_extra(const pybind11::arg_t &a, function_entry *entry) { + static void process_static(const pybind11::arg_t &a, function_entry *entry) { if (entry->class_ && entry->args.empty()) entry->args.emplace_back("self", nullptr, nullptr); @@ -184,7 +184,7 @@ public: return result; }; - process_extras(std::make_tuple(std::forward(extra)...), m_entry); + process_static(std::make_tuple(std::forward(extra)...), m_entry); PYBIND11_DESCR signature = cast_in::name() + detail::_(" -> ") + cast_out::name(); initialize(signature.text(), signature.types(), sizeof...(Args)); } @@ -243,7 +243,7 @@ private: return result; }; - process_extras(std::make_tuple(std::forward(extra)...), m_entry); + process_static(std::make_tuple(std::forward(extra)...), m_entry); PYBIND11_DESCR signature = cast_in::name() + detail::_(" -> ") + cast_out::name(); initialize(signature.text(), signature.types(), sizeof...(Args)); } @@ -332,7 +332,7 @@ private: const detail::type_info *type_info = capsule(PyObject_GetAttrString((PyObject *) Py_TYPE(inst), const_cast("__pybind11__")), false); - type_info->init_holder(inst); + type_info->init_holder(inst, nullptr); } return result; } @@ -396,7 +396,7 @@ private: } } else if (c == '%') { const std::type_info *t = types[type_index++]; - if (!t) + if (!t) throw std::runtime_error("Internal error while parsing type signature (1)"); auto it = registered_types.find(t); if (it != registered_types.end()) { @@ -558,7 +558,7 @@ public: custom_type(object &scope, const char *name_, const std::type_info *tinfo, size_t type_size, size_t instance_size, - void (*init_holder)(PyObject *), const destructor &dealloc, + void (*init_holder)(PyObject *, const void *), const destructor &dealloc, PyObject *parent, const char *doc) { PyHeapTypeObject *type = (PyHeapTypeObject*) PyType_Type.tp_alloc(&PyType_Type, 0); #if PY_MAJOR_VERSION >= 3 @@ -884,24 +884,37 @@ public: return *this; } private: - template , T>::value, int>::type = 0> - static void init_holder(PyObject *inst_) { - instance_type *inst = (instance_type *) inst_; - new (&inst->holder) holder_type(inst->value); - inst->constructed = true; - } - - template , T>::value, int>::type = 0> - static void init_holder(PyObject *inst_) { - instance_type *inst = (instance_type *) inst_; + /// Initialize holder object, variant 1: object derives from enable_shared_from_this + template + static void init_holder_helper(instance_type *inst, const holder_type * /* unused */, const std::enable_shared_from_this * /* dummy */) { try { - new (&inst->holder) holder_type( - inst->value->shared_from_this()); + new (&inst->holder) holder_type(inst->value->shared_from_this()); } catch (const std::bad_weak_ptr &) { new (&inst->holder) holder_type(inst->value); } + } + + /// Initialize holder object, variant 2: try to construct from existing holder object, if possible + template ::value, int>::type = 0> + static void init_holder_helper(instance_type *inst, const holder_type *holder_ptr, const void * /* dummy */) { + if (holder_ptr) + new (&inst->holder) holder_type(*holder_ptr); + else + new (&inst->holder) holder_type(inst->value); + } + + /// Initialize holder object, variant 3: holder is not copy constructible (e.g. unique_ptr), always initialize from raw pointer + template ::value, int>::type = 0> + static void init_holder_helper(instance_type *inst, const holder_type * /* unused */, const void * /* dummy */) { + new (&inst->holder) holder_type(inst->value); + } + + /// Initialize holder object of an instance, possibly given a pointer to an existing holder + static void init_holder(PyObject *inst_, const void *holder_ptr) { + auto inst = (instance_type *) inst_; + init_holder_helper(inst, (const holder_type *) holder_ptr, inst->value); inst->constructed = true; } @@ -964,21 +977,21 @@ template struct init { PYBIND11_NOINLINE inline void keep_alive_impl(int Nurse, int Patient, PyObject *arg, PyObject *ret) { /* Clever approach based on weak references taken from Boost.Python */ - PyObject *nurse = Nurse > 0 ? PyTuple_GetItem(arg, Nurse - 1) : ret; - PyObject *patient = Patient > 0 ? PyTuple_GetItem(arg, Patient - 1) : ret; + handle nurse (Nurse > 0 ? PyTuple_GetItem(arg, Nurse - 1) : ret); + handle patient(Patient > 0 ? PyTuple_GetItem(arg, Patient - 1) : ret); - if (nurse == nullptr || patient == nullptr) - throw std::runtime_error("Could not activate keep_alive"); + if (!nurse || !patient) + throw std::runtime_error("Could not activate keep_alive!"); cpp_function disable_lifesupport( - [patient](handle weakref) { Py_DECREF(patient); weakref.dec_ref(); } - ); + [patient](handle weakref) { patient.dec_ref(); weakref.dec_ref(); }); - PyObject *weakref = PyWeakref_NewRef(nurse, disable_lifesupport.ptr()); - if (weakref == nullptr) + weakref wr(nurse, disable_lifesupport); + if (!wr) throw std::runtime_error("Could not allocate weak reference!"); - Py_INCREF(patient); /* reference patient and leak the weak reference */ + patient.inc_ref(); /* reference patient and leak the weak reference */ + (void) wr.release(); } template struct process_dynamic> : public process_dynamic { diff --git a/include/pybind11/pytypes.h b/include/pybind11/pytypes.h index 03ebc3229..377b5164d 100644 --- a/include/pybind11/pytypes.h +++ b/include/pybind11/pytypes.h @@ -334,6 +334,12 @@ public: operator double() const { return (double) PyFloat_AsDouble(m_ptr); } }; +class weakref : public object { +public: + PYBIND11_OBJECT_DEFAULT(weakref, object, PyWeakref_Check) + weakref(handle obj, handle callback = handle()) : object(PyWeakref_NewRef(obj.ptr(), callback.ptr()), false) { } +}; + class slice : public object { public: PYBIND11_OBJECT_DEFAULT(slice, object, PySlice_Check)