Design solutions used in Qt Jambi


Design solutions used in Qt Jambi

The purpose of this document is to point out the differences and explain how to deal with them.

Table of contents

Value types vs Object types

Qt has the concept of value types. These are types which one does not have to allocate using the “new” operator and which are passed to functions seemingly “by value” or by “const &”. Since value types are put on the stack they will be automatically deleted when the function goes out of scope. Examples of value types are: QRect, QStyleOptions and QPixmap. A complete list of the value types in Qt Jambi is given below. Value types are automatically deleted by the Java garbage collector.

Object types are classes which needs to be instantiated using the “new” operator and are passed to functions as pointers to the objects. Widgets, for instance, fall into this category. These types need to be explicity deleted in C++, either by calling the “delete” operator directly or by assigning the object a parent that will be delete by the object when the parent is deleted.

These objects do not rely on garbage collection in Java. Consider a QWidget with a QPushButton inside it. After setting up the widget, the QPushButton goes out of scope in Java, but it is still referenced by the parent on the C++ side. In this case QPushButton will be deleted when the parent is deleted, but for the QWidget one must explictly call the function dispose() which will delete the widget on the C++ side.

Some object types are still garbage collected because this fits nicely with the way they are usually used.

Objects, Pointers and references

Java has a simpler type system compared to that of C++, with only primitives and objects. Primitives are passed by value and objects are passed by reference. Qt Jambi maps the signatures in Qt/C++ to those in Jambi according to the following:

  • Object Types passed by pointer
  • Value Types passed by value or const &
  • Primitives

If a type does not fit into this category, such as the signature

QApplication::QApplication(const char **, int &)

The arguments are converted into a QNativePointer in Java which is a C pointer structure that one can use to create and manipulate C pointers from Java. The QNativePointer based API can be difficult and dangerous to use, so Qt Jambi tries to minimize its presence in the public API, but adding convenience functions such as:

QApplication(String args[])

To help the Java programmer.

Implicit sharing and detaching

In short, value types will behave slightly different when passed to a Java function as opposed to a Qt/C++ function, via the native interface.

While in Java one might expect:

QPen pen = new QPen(QColor.black, 10);
painter.setPen(pen);
pen.setWidth(1);

to change the pen width from 10 to 1 for the painter that has a reference to the pen, this is indeed not the case as the pen is passed to the setPen function by value on the C++ side, causing a separate copy to be made of it, thus calling pen.setWidth(1) will only change the pen width of the local pen in Java.

If the pen been passed to another Java function the behaviour would have been different.

class Shape {
    QPen pen;
    void setPen(QPen pen) { this.pen = pen; }
}

…

pen = new QPen(QColor.black, 10);
shape.setPen(pen);
pen.setWidth(1);

In this case, we are passing the value type as a Java reference so both the shape and the local pen are the same object.

Ownership and Garbage collection

Java has built in garbage collection for all objects. As a result of this any object this is no longer referenced by the virtual machine will be deleted. When an object is passed to a Qt/C++ function that takes ownership of the object, such as QEvent being passed to QApplication::postEvent(), the event object might no longer be reachable from the Virtual Machine and can be scheduled for deletion by the Garbage Collector.

In these cases it is required to call the function QtObject.disableGarbageCollection() which will make sure that the virtual machine never deletes the object. It is important that this function is only used for functions that take ownership, otherwise the application will leak memory.

The Qt Jambi team has tried to identify the locations where this situation arises and added patched the Qt Jambi library so it is not required to call the disableGarbageCollection function, so this is not required for postEvent() above, but not all places are covered.

Enums and QFlags

Enums in Java and enums in C++ aim to provide the same functionality, namely typesafe constant values, but are implemented using fairly different techniques. Enums in C++ are integers. Enums in Java are objects and enums with defaultvalues are implemented as a special enum subclass with limited use, for instance comparison between types is not possible (read: its all a nasty hack).

Since enums in Qt are used as both enums and bitmasks the Java enum class construction is too limited to be used. Enums and QFlags in Qt/C++ are mapped to integers in Qt/Java.

Signals and Slots

Signals and slots are used for communication between components. The signals and slots mechanism is a central feature of Qt and probably the part that differs most from the features provided by other frameworks.

The traditional approach for inter-component communcation in Java is using the listener pattern. This has the disadvantage that when components are written they needs to know about all the interfaces they intend to support. Consider a image viewer and a slider for zooming. If the two components are written independently one would need a separate class connecting them.

A signal is emitted when a particular event occurs. Qt’s widgets have many predefined signals, but we can always subclass widgets to add our own signals to them. A slot is a method that is called in reponse to a particular signal. In Jambi, all methods are treated as slots. If we then revisit the slider and image viewer example it is possible to connect the slider’s “value” signal to the image viewers setZoom method.

Signals in Java are declared as this:

public class Slider {
    protected Signal1 value = new Signal1();
    ...
};

Connecting a signal to a method is done like this:

connect(slider.value, imageViewer, "setZoom(int)");

As one can see from the example, Jambi makes use of Java Generics to make the inter-component communication type safe. The number “1″ specifies that its a signal that emits one argument.

Qt Jambi also supports the convenient connectSlotsByName() mechanism which makes it possible to automatically connect to a childs signal by following a naming conventions. For instance to react to the signal “clicked” in a childwidget called “okButton”, the following syntax can be used:

public void on_okButton_clicked() { ... }

Threading

Both Qt/C++ and Java provides its own threading API, where the Java approach is integrated into the programming language, with features such as synchronization using the synchronized keyword and execution encapsulation using the java.lang.Runnable interface. With these possibilities built into the language, we decided to take advantage of those and layer threading Jambi on top of the existing Java threading API, so that QObject::thread() in Java returns a java.lang.Thread.

Existing threading concepts from Qt/C++ are present in Qt Jambi, such as posting events to objects in another thread and queued signal/slot connections across different threads.

Application Resource Management

Qt has a system for embedding resources, binary and textual, into an application and for accessing these at runtime, making it possible to install a single binary with out external dependencies on icons, translation files, etc.

In Java it is common to bundle all these resources into a .jar file and ship the application as a .jar file instead. Accessing arbitrary resources in such a jar file can be a tedious task, so Jambi provides a file engine for traversing the classpath. This makes it easy to embed and access resourses in .jar. The following example will load the pixmap file_new.png from the filesystem or from a .jar file in the classpath in the subdirectory images.

QPixmap pixmap = new QPixmap(":cp:images/file_new.png");

Objects ownership and garbage collection

It is a well known fact that much of the work being invested into traditional C++ development is related to memory management. Java provides garbage collection. When these two languages are merged, through the Java Native Interface, it is common that native resources need to be released manually from Java. The primary pattern for this is to implement a dispose() method that releases handles the cleanup. By reimplementing the finalize() method, which is called when the object is garbage collected, it is sometimes possible to automate cleanup of native resources.

Qt/C++ provides several mechanisms to simplify memory management. These techniques include smart pointers, value types and object trees, where the latter two are the most relevant for Jambi. Value types are “simple” types, like QRect, QPoint which are passed around between different objects. In Jambi these objects are garbage collected as any normal Java objects. Subclasses of QObject will normally follow a different set of rules since they have a lifetime beyond what is visible to the Java garbage collector, such as toplevel widgets. The rules that apply to widgets are that top level widgets need to be explicitly disposed to be cleaned up and child widgets are disposed automatically by their parent, so by diposing the toplevel window one will cover all cases.

Event system

Qt Jambi implements its own event system based on Qt/C++. This event loop is separate from the one in other toolkits such as AWT/Swing or in SWT. As a concequence, it is not possible to have Jambi widgets and widgets from other toolkits in the same part of the user interface. Having a toplevel window running Jambi and another toplevel window running AWT/Swing does not run into this limitation.

Although it is possible to merge event loops for different toolkits, so that a QPushButton can inhabit a JSplitPane, it often not desired as this means that different parts of the same user inteface may have slightly different look&feel. Different repaint behaviour, fonts etc.

Separate render pipeline

Qt Jambi implements its own render pipeline based on the Qt Painting Subsystem and requires that graphics related tasks, such as painting, text handling, printing and imaging goes through the Jambi classes.

Some of the advantages of this is that graphics processing is done primarly in C++, making it faster than normal graphics in Java.

Qt’s text handling is also based on native font support so it is possible to get access to platform features like ClearType(tm) anti-aliasing on Windows and Mac OS X, and sub-pixel anti-antiasing on X11, features that are currently not supported by the Java 2D graphics subsystem.

Polymorphism

The Qt C++ class hierarchy is object oriented and relies on polymorphism, a concept that needs to be preserved when the Qt classes are mapped in Java. Qt Jambi has built in functionality to forward any virtual function call made in C++ to Java so that when for instance QWidget:mouseMoveEvent() is called, the Java implementation will be called instead. Making these calls into Java for each method call is a vaste of cpu so Jambi optimizes away all redundant calls, so that only methods reimplemented in user classes are called.

Member Variable Access

The Qt/C++ API is primarly method based, but there are some classes that give direct access to the member variables of an object. Since JNI only provides access to native resources through functions, each of these member variables is prepresented as set/get method pair with the following name convention.

LanguageExample
C++
QString text;
Java:
String get_text();
void set_text(String text);
Pointer based API’s

The C++ concept of pointers, references and multiple layers of indirection does not map directly into similar concepts in Java, but Jambi is based on API that relies on these concepts. Jambi tries to match normal types into wrapper classes in Java where possible.

For instance:

C++Java
QWidget * QWidget
QString String

Other concepts, such as:

void process(int *data);

does not map automatically. In the above example, the variable data can have three different purposes. It could be an “in” value, an “out”, value or an array of integers. In cases such as this we have introduced the concept of a native pointer (QNativePointer) which encapsulates the pointer and lets the programmer access it as an in/out value and as an array. The QNativePointer API also provides type checks to verify that one does not incorrectly access a byte as int or char ** as a void *, etc.

In most cases where native pointers are used in Qt, the Jambi layer will encapsulate their use with more convenient and bulletproof API’s.

UIC for Java

Qt Jambi comes with its own generator for UIC. UIC is a tool for generating code from UI forms created with Qt Designer, a powerful user interface builder that is tailored for the Qt API.

UIC for Java generates Jambi code and provides features that makes it easy to integrate into a build environment or for standalone use in a command shell.

QVariant

Qt/C++ does, as mentioned above, have the concept of value types. In addition to the concrete value types, Qt also comes with abstract value type. A value that can represent any value and possible be converted to any other value. In Qt/C++ this concept works very well because of built in features of C++ like, implicit conversions. In Java these conversions must be done explicitly and the code would not look nice. To preserve the convenient syntax of C++, Qt/Java uses the java.lang.Object type as container for variants. For mapping between types we provide a series of static functions com.trolltech.qt.QVariant. This provide the convenient usage pattern from C++ in addition to providing the same set of features.

Operator overloading

Operator overloading is not supported by the Java language so all operators in Qt/C++ are converted to normal functions which are given an “operator_” prefix to help identify them and to avoid name clashes with existing functions of that class. The operators are named according to names used in [The C++ Programming Language, Bjarne Stroustrup] and are as follows:

+ add
- subtract
* multiply
/ divide
% modulo
& and
| or
^ xor
~ negate
<< shift_left
>> shift_right

= assign
+= add_assign
-= subtract_assign
*= multiply_assign
/= divide_assign
%= modulo_assign
&= and_assign
|= or_assign
^= xor_assign
<<= shift_left_assign
>>= shift_right_assign

&& logical_and
|| logical_or
! not

++ increment
– decrement

< less
> greater
<= less_or_equal
>= greater_or_equal
!= not_equal
== equal

[] subscript
-> pointer

cast cast_[ClassName]

Type Mapping

There are some classes in Java that are more suitable for programming in Java than their Qt counter parts. The String class for instance which is integrated into the language and has builtin in support for the + operator, and the Java collection classes are supported in using the foreach keyword. For these cases the Qt Jambi API will implictly convert between the Qt/C++ types to the Java types so that programmers can write code using the familiar and convenient constructs of Java while still accessing the power of Qt.

QString -> java.lang.String
QChar -> char and java.lang.Character
QList -> java.util.List
QVector -> java.util.List
QHash -> java.util.HashMap
QMap -> java.util.Map
This page was updated at 17.05.2016