@@ -3,66 +3,52 @@ Smart pointers & ``py::class_``
3
3
4
4
The binding generator for classes, ``py::class_ ``, can be passed a template
5
5
type that denotes a special *holder * type that is used to manage references to
6
- the object. If no such holder type template argument is given, the default for
6
+ the object. If no such holder type template argument is given, the default for
7
7
a type ``T `` is ``std::unique_ptr<T> ``.
8
8
9
+ .. note ::
10
+
11
+ A ``py::class_ `` for a given C++ type ``T `` — and all its derived types —
12
+ can only use a single holder type.
13
+
14
+
15
+ .. _smart_holder :
16
+
9
17
``py::smart_holder ``
10
18
====================
11
19
12
20
Starting with pybind11v3, ``py::smart_holder `` is built into pybind11. It is
13
- the recommended ``py::class_ `` holder for all situations, but it is **not **
14
- the default holder, and there is no intent to make it the default holder in
15
- the future, based on the assumption that this would cause more disruption
16
- than it is worth.
17
-
18
- It is extremely easy to change existing pybind11 client code to use the safer
19
- and more versatile ``py::smart_holder ``. For a given C++ type ``T ``, simply
20
- change
21
+ the recommended ``py::class_ `` holder for most situations, but it is **not **
22
+ the default holder, and there are no plans to make it the default holder in
23
+ the future. This is based on the assumption that such a change would cause
24
+ more disruption than it is worth, especially because it is extremely easy
25
+ to use the safer and more versatile ``py::smart_holder ``. For a given C++
26
+ type ``T ``, simply change
21
27
22
28
* ``py::class_<T> `` to
23
- * ``py::classh<T> ``
29
+
30
+ * ``py::classh<T> ``.
24
31
25
32
.. note ::
26
33
27
- ``py::classh<T> `` is simply a shortcut for ``py::class_<T, py::smart_holder> ``.
34
+ ``py::classh<T> `` is a shortcut for ``py::class_<T, py::smart_holder> ``.
28
35
29
- The ``py::classh<T> `` functionality includes
36
+ The ``py::classh<T> `` functionality includes the following:
30
37
31
- * support for **two-way ** Python/C++ conversions for both
38
+ * Support for **two-way ** Python/C++ conversions for both
32
39
``std::unique_ptr<T> `` and ``std::shared_ptr<T> `` **simultaneously **.
33
- — In contrast, ``py::class_<T> `` only supports one-way C++-to-Python
34
- conversions for ``std::unique_ptr<T> ``, or alternatively two-way
35
- Python/C++ conversions for ``std::shared_ptr<T> ``, which then excludes
36
- the one-way C++-to-Python ``std::unique_ptr<T> `` conversions (this manifests
37
- itself through undefined runtime behavior, often a segmentation fault
38
- or double free).
39
-
40
- * passing a Python object back to C++ via ``std::unique_ptr<T> ``, safely
40
+
41
+ * Passing a Python object back to C++ via ``std::unique_ptr<T> ``, safely
41
42
**disowning ** the Python object.
42
43
43
- * safely passing `"trampoline"
44
- <https://pybind11.readthedocs.io/en/stable/advanced/classes.html#overriding-virtual-functions-in-python> `_
45
- objects (objects with C++ virtual function overrides implemented in
46
- Python) via ``std::unique_ptr<T> `` or ``std::shared_ptr<T> `` back to C++:
44
+ * Safely passing "trampoline" objects (objects with C++ virtual function
45
+ overrides implemented in Python, see :ref: `overriding_virtuals `) via
46
+ ``std::unique_ptr<T> `` or ``std::shared_ptr<T> `` back to C++:
47
47
associated Python objects are automatically kept alive for the lifetime
48
48
of the smart-pointer.
49
49
50
- TODO(rwgk): Move to classes.rst
51
-
52
- A pybind11 `"trampoline"
53
- <https://pybind11.readthedocs.io/en/stable/advanced/classes.html#overriding-virtual-functions-in-python> `_
54
- is a C++ helper class with virtual function overrides that transparently
55
- call back from C++ into Python. To enable safely passing a ``std::unique_ptr ``
56
- to a trampoline object between Python and C++, the trampoline class must
57
- inherit from ``py::trampoline_self_life_support ``, for example:
58
-
59
- .. code-block :: cpp
60
-
61
- class PyAnimal : public Animal, public py::trampoline_self_life_support {
62
- ...
63
- };
64
-
65
- A fairly minimal but complete example is :file: `tests/test_class_sh_trampoline_unique_ptr.cpp `.
50
+ * Full support for ``std::enable_shared_from_this `` (`cppreference
51
+ <http://en.cppreference.com/w/cpp/memory/enable_shared_from_this> `_).
66
52
67
53
68
54
``std::unique_ptr ``
@@ -71,7 +57,7 @@ A fairly minimal but complete example is :file:`tests/test_class_sh_trampoline_u
71
57
This is the default ``py::class_ `` holder and works as expected in most
72
58
situations. However, note that the handling of base-and-derived classes
73
59
involves a ``reinterpret_cast `` that has strictly speaking undefined
74
- behavior. Also note that the ``std::unique_ptr `` holder only support passing
60
+ behavior. Also note that the ``std::unique_ptr `` holder only supports passing
75
61
a ``std::unique_ptr `` from C++ to Python, but not the other way around. For
76
62
example, this code will work as expected when using ``py::class_<Example> ``:
77
63
@@ -83,7 +69,7 @@ example, this code will work as expected when using ``py::class_<Example>``:
83
69
84
70
m.def("create_example", &create_example);
85
71
86
- However, this will fail with ``py::class_<Example> `` (but work with
72
+ However, this will fail with ``py::class_<Example> `` (but works with
87
73
``py::classh<Example> ``):
88
74
89
75
.. code-block :: cpp
@@ -102,11 +88,10 @@ It is possible to use ``std::shared_ptr`` as the holder, for example:
102
88
103
89
Compared to using ``py::classh ``, there are two noteworthy disadvantages:
104
90
105
- * A ``py::class_ `` for any particular C++ type ``T `` (and all its derived types)
106
- can only use a single holder type. Therefore, ``std::unique_ptr<T> ``
107
- cannot even be passed from C++ to Python if the ``std::shared_ptr<T> `` holder
108
- is used. This will become apparent only at runtime, often through a
109
- segmentation fault or double free.
91
+ * Because a ``py::class_ `` for a given C++ type ``T `` can only use a
92
+ single holder type, ``std::unique_ptr<T> `` cannot even be passed from C++
93
+ to Python. This will become apparent only at runtime, often through a
94
+ segmentation fault.
110
95
111
96
* Similar to the ``std::unique_ptr `` holder, the handling of base-and-derived
112
97
classes involves a ``reinterpret_cast `` that has strictly speaking undefined
@@ -118,9 +103,9 @@ Compared to using ``py::classh``, there are two noteworthy disadvantages:
118
103
Custom smart pointers
119
104
=====================
120
105
121
- For custom smart pointer, transparent conversions can be enabled
122
- using a macro invocation similar to the following. It must be declared at the
123
- top namespace level before any binding code:
106
+ For custom smart pointers (e.g. `` c10::intrusive_ptr `` in pytorch), transparent
107
+ conversions can be enabled using a macro invocation similar to the following.
108
+ It must be declared at the top namespace level before any binding code:
124
109
125
110
.. code-block :: cpp
126
111
@@ -167,19 +152,28 @@ specialized:
167
152
The above specialization informs pybind11 that the custom ``SmartPtr `` class
168
153
provides ``.get() `` functionality via ``.getPointer() ``.
169
154
155
+ .. note ::
156
+
157
+ The two noteworthy disadvantages mentioned under the ``std::shared_ptr ``
158
+ section apply similarly to custom smart pointer holders, but there is no
159
+ established safe alternative in this case.
160
+
170
161
.. seealso ::
171
162
172
163
The file :file: `tests/test_smart_ptr.cpp ` contains a complete example
173
164
that demonstrates how to work with custom reference-counting holder types
174
165
in more detail.
175
166
176
167
177
- Be careful to not undermine automatic lifetime management
178
- =========================================================
168
+ Be careful not to accidentally undermine automatic lifetime management
169
+ ======================================================================
179
170
180
- One potential stumbling block when using holder types is that they need to be
181
- applied consistently. Can you guess what's broken about the following binding
182
- code?
171
+ ``py::class_ ``-wrapped objects automatically manage the lifetime of the
172
+ wrapped C++ object, in collaboration with the chosen holder type.
173
+ When wrapping C++ functions involving raw pointers, care needs to be taken
174
+ to not inadvertently transfer ownership, resulting in multiple Python
175
+ objects acting as owners, causing heap-use-after-free or double-free errors.
176
+ For example:
183
177
184
178
.. code-block :: cpp
185
179
@@ -188,7 +182,7 @@ code?
188
182
class Parent {
189
183
public:
190
184
Parent() : child(std::make_shared<Child>()) { }
191
- Child *get_child() { return child.get(); } /* Hint: ** DON'T DO THIS ** */
185
+ Child *get_child() { return child.get(); } /* DANGER */
192
186
private:
193
187
std::shared_ptr<Child> child;
194
188
};
@@ -198,7 +192,7 @@ code?
198
192
199
193
py::class_<Parent, std::shared_ptr<Parent>>(m, "Parent")
200
194
.def(py::init<>())
201
- .def("get_child", &Parent::get_child);
195
+ .def("get_child", &Parent::get_child); /* PROBLEM */
202
196
}
203
197
204
198
The following Python code will cause undefined behavior (and likely a
@@ -210,34 +204,18 @@ segmentation fault).
210
204
211
205
print (Parent().get_child())
212
206
213
- The problem is that ``Parent::get_child() `` returns a pointer to an instance of
214
- ``Child ``, but the fact that this instance is already managed by
215
- ``std::shared_ptr<...> `` is lost when passing raw pointers. In this case,
216
- pybind11 will create a second independent ``std::shared_ptr<...> `` that also
217
- claims ownership of the pointer. In the end, the object will be freed **twice **
218
- since these shared pointers have no way of knowing about each other.
219
-
220
- There are two ways to resolve this issue:
221
-
222
- 1. For types that are managed by a smart pointer class, never use raw pointers
223
- in function arguments or return values. In other words: always consistently
224
- wrap pointers into their designated holder types (such as
225
- ``std::shared_ptr<...> ``). In this case, the signature of ``get_child() ``
226
- should be modified as follows:
227
-
228
- .. code-block :: cpp
229
-
230
- std::shared_ptr<Child> get_child() { return child; }
231
-
232
- 2. Adjust the definition of ``Child `` by specifying
233
- ``std::enable_shared_from_this<T> `` (see cppreference _ for details) as a
234
- base class. This adds a small bit of information to ``Child `` that allows
235
- pybind11 to realize that there is already an existing
236
- ``std::shared_ptr<...> `` and communicate with it. In this case, the
237
- declaration of ``Child `` should look as follows:
238
-
239
- .. _cppreference : http://en.cppreference.com/w/cpp/memory/enable_shared_from_this
240
-
241
- .. code-block :: cpp
242
-
243
- class Child : public std::enable_shared_from_this<Child> { };
207
+ Part of the ``/* PROBLEM */ `` here is that pybind11 falls back to using
208
+ ``return_value_policy::take_ownership `` as the default (see
209
+ :ref: `return_value_policies `). The fact that the ``Child `` instance is
210
+ already managed by ``std::shared_ptr<Child> `` is lost. Therefore pybind11
211
+ will create a second independent ``std::shared_ptr<Child> `` that also
212
+ claims ownership of the pointer, eventually leading to heap-use-after-free
213
+ or double-free errors.
214
+
215
+ There are various ways to resolve this issue, either by changing
216
+ the ``Child `` or ``Parent `` C++ implementations (e.g. using
217
+ ``std::enable_shared_from_this<Child> `` as a base class for
218
+ ``Child ``, or adding a member function to ``Parent `` that returns
219
+ ``std::shared_ptr<Child> ``), or if that is not feasible, by using
220
+ ``return_value_policy::reference_internal ``. What is the best approach
221
+ depends on the exact situation.
0 commit comments