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:

def event(self, event):
    if event.type() == QEvent.Type.MousePress:
        self.do_things(event.position())
...
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 polymorphic-id-expression attribute must be specified to be used as a check replacing dynamic_cast<>.

In addition, a 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 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 polymorphic-id-expression and 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 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.

Type discovery attributes reference#

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

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:

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

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#

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.