aboutsummaryrefslogtreecommitdiffstats
path: root/sources/shiboken6/doc/typediscovery.rst
blob: 76d3adf7b343bc3a4523d1edc55cd9bc2432a6f4 (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
.. _typediscovery:

**************
Type Discovery
**************

When converting objects which are part of a class hierarchy from a pointer to a
base class, it is expected to get the Python type of the actual, most derived
type, as opposed to C++ which requires a cast for this:

.. code-block:: python

    def event(self, event):
        if event.type() == QEvent.Type.MousePress:
            self.do_things(event.position())
    ...


.. code-block:: c++

    bool event(QEvent *event) override
    {
        if (event->type() == QEvent::MousePress) {
            auto *mouseEvent = static_cast<QMouseEvent *>(event);
            doThings(mouseEvent->position());
        ...
    }

The process of determining the type of the event is called `type discovery`.

Shiboken generates code to automatically detect the type. First, it tries to
find a converter for the name obtained by ``typeid(*pointer).name()``. This
should normally work as this name is registered by the binding. If that fails,
it starts walking a type inheritance graph built up in libshiboken to find the
most derived class by using a cast function (``dynamic_cast<>`` by default) to
check.

For normal class hierarchies with virtual destructors, no special handling
is required since ``typeid()`` usually detects the proper class name.

Multiple inheritance
====================

In case of multiple inheritance in C++, the conversion to the derived class is
not done in case it is not a single-line direct inheritance. For example, in
Qt, the class ``QWidget`` inherits both ``QObject`` (base of the ``QObject``
hierarchy) and ``QPaintDevice``.

When calling a function returning a ``QPaintDevice *``, for example
``QPainter.device()``, a Python type representing ``QPaintDevice`` is returned
instead of the underlying widget type. This restriction exists because the
underlying pointer in C++ is a pointer to a ``QPaintDevice *`` and differs from
the pointer to the ``QWidget``.

Hierarchies of classes with non-virtual destructors
===================================================

There are some hierarchies of value-ish C++ classes that do not have virtual
destructors. This makes type discovery based on ``typeid()`` and
``dynamic_cast<>`` impossible.

Examples in Qt are the ``QStyleOption``-derived or the ``QGradient``
-derived classes.

For such classes, some attributes need to be specified on the type entries:

Primarily, a :ref:`polymorphic-id-expression` attribute
must be specified to be used as a check replacing ``dynamic_cast<>``.

In addition, a :ref:`polymorphic-name-function` attribute can be specified.
This replaces the type name guess obtained by ``typeid()`` and is mainly a hint
to speed things up by skipping the checks for each type in the inheritance
graph.

A :ref:`polymorphic-base` attribute identifies the base class of a hierarchy.
It should be given in case the base class inherits from another class to
prevent the logic from going below the base class.

Using type discovery attributes for class hierarchies with virtual destructors
==============================================================================

It is possible to use :ref:`polymorphic-id-expression` and
:ref:`polymorphic-name-function` for normal class hierarchies with virtual
destructors as well since they basically replace ``typeid()`` and
``dynamic_cast<>``. This makes sense if expressions can be specified that are
faster than the checks on virtual tables.

Specifying :ref:`polymorphic-base` can also make sense for generating special
cast functions in case of multiple inheritance. For example, in Qt,
``QWindow``, ``QLayout``, ``QWidget`` are base classes of hierarchies. Since
they all inherit from ``QObject``, indicating the base classes prevents
the logic from using ``QObject`` as a base class.

.. _typediscovery-attributes:

Type discovery attributes reference
===================================

The following attributes related to type discovery may be be specified on the
:ref:`object-type` or :ref:`value-type` elements:

.. _polymorphic-id-expression:

polymorphic-id-expression
+++++++++++++++++++++++++

The **polymorphic-id-expression** attribute specifies an expression checking
whether a base class pointer is of the matching type. For example, in a
``virtual eventHandler(BaseEvent *e)`` function, this is used to construct a
Python wrapper matching the derived class (for example, a ``MouseEvent`` or
similar). The attribute value may contain placeholders:

%1
    Fully qualified class name

%B
    Fully qualified name of the base class (found by base class
    search or as indicated by **polymorphic-base**).

To check for a class inheriting ``BaseEvent``, specify:

.. code-block:: xml

        <object-type name="MouseEvent"
                     polymorphic-id-expression="%B-&gt;type() == BaseEvent::MouseEvent"/>

.. _polymorphic-name-function:

polymorphic-name-function
+++++++++++++++++++++++++

The **polymorphic-name-function** attribute specifies the name of a function
returning the type name of a derived class on the base class type entry.
Normally, ``typeid(ptr).name()`` is used for this.

The function is expected to return ``const char *``.

.. _polymorphic-base:

polymorphic-base
++++++++++++++++

The boolean **polymorphic-base** attribute indicates whether the class is the
base class of a class hierarchy. It is used for the *%B* placeholder in
**polymorphic-id-expression** and for cast operations in multiple inheritance.