Binding technology

Binding technology

The Qt/Java bindings layer is a static layer of code which transmits Java calls made to the Qt/Java API into the natively compiled, C++ based Qt libraries. The bindings layer is implemented part in Java and part in C++, and most of the code is automatically generated by the Qt/Java Generator. Handwritten code in the bindings layer include the foundation of the Qt/Java library, an implementation of the signals and slots mechanism (written in Java), and certain convenience functions that are intended to make the Java interface more accessible to its users.

For convenience, this document will refer to the Qt/Java Generator simply as "the generator", and the C++ based Qt library as "the original library".

The Qt/Java Generator

The generator is a Qt-based, native application which will read class definitions written in C++ and generate code that maps the API of the original library into an equivalent API in Java. It will also generate C++ code for each of the functions it maps. This code will handle conversion back and forth between Java types and Qt/C++ types, and it will redirect calls to the appropriate functions in the original library.

The generator supports a carefully selected subset of C++. This subset covers, but is not limited to, the class definitions of Qt/Core and Qt/Gui in Qt 4.1.1. There are some exceptions to this rule, which will be addressed later in the document. The documentation for the generator contains more specific information about which C++ constructs are supported.

The C++ part of the bindings layer uses the Java Native Interface (JNI) for communication between Java and the natively compiled code. JNI is part of the Java Software Developer's Kit (SDK) and provides, among other things, the possibility for calls from Java to natively compiled code, as well as accesses from the natively compiled code to members of Java classes.

The following example illustrates the mapping of a C++ class "Foo" which contains a single function "bar". The function expects one parameter of type "QRect".

class Foo { void bar(const QRect &) { /* ... */ } }

The generator will create a Java class definition for the class Foo, which will contain the declaration of a function bar, which takes a "QRect" as its argument, returns void and is indicated to be implemented in native code. The generator will also create portable C++ code which maps between the Java version of "bar" and the version implemented in the original library.

When executed, the generated C++ code will first convert the Java "this" reference to a pointer which points to the location of the native "Foo" object. Further, it will convert Java's "QRect" object to C++, and finally it will call the original implementation of "bar" on the native "Foo" object, passing the converted "QRect" object as an argument.

Type conversion

Type conversion in the bindings is based on an XML mark-up which assigns certain attributes to each of the mapped classes. The XML document is handwritten, and mainly provides information on the usage pattern for each class. Classes that are not declared in the XML document will not be handled by the Qt/Java Generator, and neither will functions that happen to facilitate such a class.

The supported usage types for classes are:

  • Object types: Objects of such a class are always passed as pointers or references in function calls.
  • Value types: Objects of such a class are either made constant or copied onto the stack when they are passed in function calls.
  • Interface types: A special class type which will be described under the heading 'Multiple inheritance' later in this document.
  • Primitive types: Objects of a primitive type can be copied by assignment, and should map to a primitive type in Java that has an equivalent resolution.
  • Enum types: Types that are declared as enums in C++.

The generator will assume that objects of object type classes are never passed by copy in any of the original native code. Examples of such classes in Qt are: any QObject subclass, any QEvent subclass, QPainter, and others. Java objects of these types will at all times maintain a connection to equivalent native objects, and when converted from Java to C++, the bindings layer will look up the connection, fetch a pointer to the native object, and pass this pointer to the mapped function in the original library.

When objects of object type classes are converted from C++ to Java, they will be handled differently based on whether they are QObject subclasses or not, and also based on whether they were originally constructed in Java or inside the original library. QObjects created in Java always contain a pointer link to its Java object, and will be converted by a simple lookup for this link. Any object which is not of a QObject subclass, or any object allocated within the original library will be converted to Java by the construction of a Java object (of the appropriate Java class) which contains a link to the original C++ object. The link is referred to as the object's "native id" in the code.

Value type classes are objects whose memory is copied when they are passed to or returned from functions in the original library. They are also often passed as constant references. The important assumption is that a function which operates on an object of a value type class never alters the original object. Likewise, a value type object returned from a function in the original library can be altered, but it will never affect objects within the original library.

In the Java part of the bindings layer, the value type classes are identical to any other class, and their objects are passed by reference like any other Java object. In the C++ implementation, however, such objects will be copied using Qt's QMetaType system before they are passed to functions in the original library, or (in the case of return values) before they are passed back into Java.

Interface type classes handled equivalently to object type classes, and primitive types are always passed by copy. Enum types are handled like primitive integers.

Multiple inheritance

As Java does not support the concept of proper multiple inheritance, the generator will generate replacement code which implements an interface pattern, whenever multiple inheritance is present in the original C++ code. Interfaces are used as substitution for the concept of multiple super classes in Java. The generator's implementation of the pattern requires that any class that inherits multiple classes, have all but one superclass declared as interfaces in the XML type declaration.

Interface type classes will be generated as Java interfaces with "Interface" appended to their original name. Such an interface is referred to as the "designated interface" of its interface type class, while the class itself is referred to as "the originating class" of its designated interface.

The appropriate method declarations will be inserted into the definitions of all designated interfaces, and the interfaces will substitute the originating class in all function signatures that facilitate it. In addition, a Java class definition will be generated for the originating class. This Java class will implement each designated interface, and provide a way to instantiate objects of the originating class, as well as inherit its structure and functionality.

If the originating class contains function definitions (which is not legal in Java) these will be implemented for each of its implementing classes in the bindings.

Handling template classes

As template parameterization in C++ is a static mechanism, template classes cannot be mapped automatically to Java. The generator still handles several of the most common template classes in Qt's Tulip API. These are special cases in the generator's type system, and will map to Java's own generic container classes in the Java API.

The shell classes

In order to support, among other things, functions that are declared as virtual in the original library's API, the generator will create thin shell classes that wrap around the classes in the original library. These inherit from the original class, and whenever an instance of a class is constructed in Java, a corresponding native object of the shell class will be constructed by the generated native code. If a class has been extended by a user's custom Java class, and one or more of the virtual functions in the class have been overridden, then the generated shell class will see to it that the overriding Java implementations are called instead of the implementations in the original library.

As Qt/Java's signals and slots mechanism is implemented independently of the mechanism in the native Qt library, the generator must also provide a way of binding the two together, so that the emission of a signal inside the original library causes the corresponding Java signal to be emitted as well. This is done by defining a QObject subclass that serves as a link between each signal in the original library and their twin in the generated Java code. The first time Java code creates a connection, an initialization function will be called on the object which owns the signal. This function will prepare the object so that each of the signals in its class are connected to corresponding slots in the generated link class, which will in turn emit the appropriate Java signal when executed.

Operator overloading

Many of the Qt/C++ classes make use of operator overloading, a language feature that is not available in Java. In this case a Java function is created instead, named operator_ followed by the C++ name for the operator, so all operators are mapped, but they are no longer ideally named according to their purpose.

This page was updated at 17.05.2016