aboutsummaryrefslogtreecommitdiffstats
path: root/sources/shiboken6/doc/typesystem_ownership.rst
diff options
context:
space:
mode:
Diffstat (limited to 'sources/shiboken6/doc/typesystem_ownership.rst')
-rw-r--r--sources/shiboken6/doc/typesystem_ownership.rst291
1 files changed, 291 insertions, 0 deletions
diff --git a/sources/shiboken6/doc/typesystem_ownership.rst b/sources/shiboken6/doc/typesystem_ownership.rst
new file mode 100644
index 000000000..a5440e49e
--- /dev/null
+++ b/sources/shiboken6/doc/typesystem_ownership.rst
@@ -0,0 +1,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`: https://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.