aboutsummaryrefslogtreecommitdiffstats
path: root/sources/shiboken6/doc/typesystem_ownership.rst
blob: f9e705b4f9f5f2b5bd63dc0085d1c5798af8cbc5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
.. _objectownership:

****************
Object ownership
****************

One of the main things a binding developer should have in mind is
how the C++ instances lives will cope with Python's reference count.
The last thing you want is to crash a program due to a segfault
when your C++ instance was deleted and the
wrapper object tries to access the invalid memory there.

In this section we'll show how |project| deals with object ownership
and parentship, taking advantage of the information provided by the
APIExtractor.

Ownership basics
================

As any python binding, |project|-based bindings uses reference counting
to handle the life of the wrapper object (the Python object that contains the
C++ object, do not confuse with the *wrapped* C++ object).
When a reference count reaches zero, the wrapper is deleted by Python garbage
collector and tries to delete the wrapped instance, but sometimes the wrapped
C++ object is already deleted, or maybe the C++ object should not be freed after
the Python wrapper go out of scope and die, because C++ is already taking care of
the wrapped instance.

This is not a concern for value types specified by :ref:`value-type`, which can
be freely created, copied and destroyed, however object types specified by
:ref:`object-type` pointing to C++ instances with life cycle constraints
may require attention.

In order to handle this, you should tell the
generator whether the instance's ownership belongs to the binding or
to the C++ Library. When belonging to the binding, we are sure that the C++ object
won't be deleted by C++ code and we can call the C++ destructor when the refcount
reaches 0. Otherwise, instances owned by C++ code can be destroyed arbitrarily,
without notifying the Python wrapper of its destruction.

By default, objects created in Python have ownership. A relevant case are
return values of virtual factory methods reimplemented in Python
(C++ Wrapper Code) which pass the bindings code. Objects obtained from C++
(for example, ``QGuiApplication::clipoard()``) do not have ownership.

The :ref:`shiboken-module` module provides the ``dump()`` utility function,
which prints the relevant information for an object.

Invalidating objects
====================

To prevent segfaults and double frees, the wrapper objects are invalidated.
An invalidated can't be passed as argument or have an attribute or method accessed.
Trying to do this will raise RuntimeError.

The following situations can invalidate an object:

C++ taking ownership
--------------------

    When an object is passed to a function or method that takes ownership of it, the wrapper
    is invalidated as we can't be sure of when the object is destroyed, unless it has a
    :ref:`virtual destructor <ownership-virt-method>` or the transfer is due to the special case
    of :ref:`parent ownership <ownership-parent>`.

    Besides being passed as argument, the called object can have its ownership changed, like
    the `setParent` method in Qt's `QObject`.

Invalidate after use
--------------------

    Objects marked with *invalidate-after-use* in the type system description always are
    virtual method arguments provided by a C++ originated call. They should be
    invalidated right after the Python function returns (see :ref:`invalidationafteruse`).

.. _ownership-virt-method:

Objects with virtual methods
----------------------------

    A little bit of implementation details (see also :ref:`codegenerationterminology`):
    virtual methods are supported by creating a C++ class, the **shell**, that inherits
    from the class with virtual methods, the native one, and override those methods to check if
    any derived class in Python also override it.

    If the class has a virtual destructor (and C++ classes with virtual methods should have), this
    C++ instance invalidates the wrapper only when the overridden destructor is called.

    An instance of the **shell** is created when created in Python. However,
    when  the object is created in C++, like in a factory method or a parameter
    to a virtual function like ``QObject::event(QEvent *)``, the wrapped object
    is a C++ instance of the native class, not the **shell** one, and we cannot
    know when it is destroyed.

.. _ownership-parent:

Parent-child relationship
=========================

One special type of ownership is the parent-child relationship.
Being a child of an object means that when the object's parent dies,
the C++ instance also dies, so the Python references will be invalidated.
Qt's QObject system, for example, implements this behavior, but this is valid
for any C++ library with similar behavior.

.. _ownership-parent-heuristics:

Parentship heuristics
---------------------

    As the parent-child relationship is very common, |project| tries to automatically
    infer what methods falls into the parent-child scheme, adding the extra
    directives related to ownership.

    This heuristic will be triggered when generating code for a method and:

    * The function is a constructor.
    * The argument name is `parent`.
    * The argument type is a pointer to an object.

    When triggered, the heuristic will set the argument named "parent"
    as the parent of the object being created by the constructor.

    The main focus of this process was to remove a lot of hand written code from
    type system when binding Qt libraries. For Qt, this heuristic works in all cases,
    but be aware that it might not when binding your own libraries.

    To activate this heuristic, use the :ref:`--enable-parent-ctor-heuristic <parent-heuristic>`
    command line switch.

.. _return-value-heuristics:

Return value heuristics
-----------------------

    When enabled, object returned as pointer in C++ will become child of the object on which the method
    was called.

    To activate this heuristic, use the command line switch
    :ref:`--enable-return-value-heuristic <return-heuristic>`.

    To disable this heuristic for specific cases, specify ``default`` as
    ownership:

    .. code-block:: xml

        <modify-argument index="0">
            <define-ownership class="target" owner="default" />
        </modify-argument>

Common pitfalls
===============

Not saving unowned objects references
-------------------------------------

    Sometimes when you pass an instance as argument to a method and the receiving
    instance will need that object to live indefinitely, but will not take ownership
    of the argument instance. In this case, you should hold a reference to the argument
    instance.

    For example, let's say that you have a renderer class that will use a source class
    in a setSource method but will not take ownership of it. The following code is wrong,
    because when `render` is called the `Source` object created during the call to `setSource`
    is already destroyed.

    .. code-block:: python

       renderer.setModel(Source())
       renderer.render()

    To solve this, you should hold a reference to the source object, like in

    .. code-block:: python

       source = Source()
       renderer.setSource(source)
       renderer.render()


Ownership Management in  the Typesystem
=======================================

Python Wrapper Code
-------------------

For this code, the ``class`` attribute takes the value ``target``
(see :ref:`codegenerationterminology`).

Ownership transfer from C++ to target
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

    When an object currently owned by C++ has its ownership transferred
    back to the target language, the binding can know for sure when the object will be deleted and
    tie the C++ instance existence to the wrapper, calling the C++ destructor normally when the
    wrapper is deleted.

    .. code-block:: xml

        <modify-argument index="1">
            <define-ownership class="target" owner="target" />
        </modify-argument>

    A typical use case would be returning an object allocated in C++, for
    example from ``clone()`` or other factory methods.

Ownership transfer from target to C++
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

    In the opposite direction, when an object ownership is transferred from the target language
    to C++, the native code takes full control of the object life and you don't
    know when that object will be deleted, rendering the wrapper object invalid,
    unless you're wrapping an object with a virtual destructor,
    so you can override it and be notified of its destruction.

    By default it's safer to just render the wrapper
    object invalid and raise some error if the user tries to access
    one of this objects members or pass it as argument to some function, to avoid unpleasant segfaults.
    Also you should avoid calling the C++ destructor when deleting the wrapper.

    .. code-block:: xml

        <modify-argument index="1">
            <define-ownership class="target" owner="c++" />
        </modify-argument>

    Use cases would be an returning a member object by pointer
    or passing an object by pointer into a function where the class
    takes ownership, for example
    ``QNetworkAccessManager::setCookieJar(QNetworkCookieJar *)``.

Parent-child relationship
^^^^^^^^^^^^^^^^^^^^^^^^^

One special type of relationship is the parent-child. When an object is called
the parent of another object (the child), the former is in charge of deleting its
child when deleted and the target language can trust that the child will be alive
as long as the parent is, unless some other method can take the C++ ownership away from the parent.

One of the main uses of this scheme is Qt's object system, with ownership among QObject-derived
classes, creating "trees" of instances.

    .. code-block:: xml

        <modify-argument index="this">
            <parent index="1" action="add"/>
        </modify-argument>

In this example, the instance with the method that is being invoked (indicated by 'index="this"' on
modify-argument) will be marked as a child
of the first argument using the `parent` tag. To remove ownership, just use "remove" in the action attribute. **Removing
parentship also transfers the ownership back to python.**

See `Object Trees and Object Ownership in Qt`_.

.. _`Object Trees and Object Ownership in Qt`: http://doc.qt.io/qt-6/objecttrees.html

C++ Wrapper Code
----------------

For this code, the ``class`` attribute takes the value ``native``. The
modifications affect code called from within C++, typically when calling
virtual C++ methods reimplemented in Python
(see :ref:`codegenerationterminology`).

Return values of virtual functions
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

The ownership of C++ objects returned by pointer should be set to ``c++`` to
prevent them from being deleted by Python, since objects created
in Python have ownership by default.

Ownership transfers specified for other arguments do not have any effect.

.. _invalidationafteruse:

Invalidation after use
^^^^^^^^^^^^^^^^^^^^^^

Sometimes an object is created in C++ and passed as a virtual method call
argument and destroyed after the call returned
(see :ref:`ownership-virt-method`).
In this case, you should use the ``invalidate-after-use`` attribute in the
:ref:`modify-argument` tag to mark the wrapper as invalid right after the
virtual method returns.

    .. code-block:: xml

        <modify-argument index="2" invalidate-after-use="yes"/>

In this example the second argument will be invalidated after this method call.