URL dieses Artikels:

zu Ausgabe: 3.2007
Flexible Software in C++ gestalten
Wie plattformunabhängige Klassenbibliotheken den Entwickleralltag vereinfachen
Dr. Christian Dietrich
Will man effiziente Software für mehrere Zielsysteme mit plattformunabhängigem Quellcode bereitstellen, ist C++ meist eine gute Wahl, weil C++ die Kombination aus effizienter maschinennaher Programmierung und moderner objektorientierter Programmierung erlaubt. Allerdings ergibt sich auch ein Mehraufwand. Denn zum einen ist der Quellcode für die verschiedenen Architekturen zu übersetzen und zum anderen sind die Programme auf den einzelnen Zielsystemen zu testen. Der Aufwand lohnt sich dennoch, denn die Bedeutung plattformunabhängiger Software nimmt stetig zu, sorgt für höhere Anwenderakzeptanz und macht den Entwickler insgesamt flexibler. Mit der Klassenbibliothek Qt und den zugehörigen Werkzeugen kann man die Aufgabe zudem einfacher gestalten als vielfach angenommen.

Nach wie vor fällt mit gutem Grund bei der Entwicklung umfangreicher, effizienter und betriebssystemunabhängiger Anwendungen die Wahl häufig auf C++. Betriebssystem-, compiler- und prozessorspezifische Unterschiede müssen in diesem Szenario keine Kopfschmerzen bereiten, denn man kann sie oft plattformabhängig implementieren. Ist dies nicht möglich, lässt sich der Code dem Compiler durch bedingte Übersetzung mithilfe des Präprozessors "unterschlagen". Der Quellcode teilt sich somit in einen großen Anteil portablen Quellcode und einen kleinen Anteil plattformspezifischen Code auf. Listing 1 zeigt anhand der Beispiel-Funktion mysleep deutlich, wie man plattformabhängigen Code am Compiler vorbeischleust.

Listing 1
#idef WIN32
#include<windows.h>
#else
#include<unistd.h>
#endif

void mysleep(long ms){
#idef WIN32
Sleep(ms) ;
#else
sleep(ms * 1000)
#endif
}

Prominente Beispiele hierfür sind der Internetbrowser Firefox, das E-Mail-Programm Thunderbird oder das Grafikprogramm GIMP. Beachtet man einige Rahmenbedingungen, so lassen sich weite Teile des Quellcodes portabel implementieren, da die International Organisation for Standardisation (ISO) C++ normiert hat. Bei Anwendungen, die keine Treiber und keinen hardwarenahen Code enthalten, kann der plattformspezifische Code auch oft entfallen. Eine immense Arbeitserleichterung kann man durch die Standard Template Library (STL) erzielen. Denn sie bietet effiziente Container-Klassen in Form von Vektoren, Listen, Maps oder Stacks. Darüber hinaus enthält sie Algorithmen à la Binärsuche, Sortieren sowie Merging. Und schließlich ermöglicht sie dem Entwickler, auf Iteratoren und Funktionsobjekte zurückzugreifen.

Plattformunabhängige Bibliothek hilft in vielen Situationen
Weil die Darstellung grafischer Steuerelemente aufgrund der fehlenden Funktion im C++-Sprachumfang problematisch ist, kommen vielfach systemabhängige Bibliotheken wie die Microsoft Foundation Classes (MFC) unter Windows oder OSF/Motif unter Unix zum Einsatz. Schließlich basiert die grafische Ausgabe der Betriebssysteme – insbesondere Unix/Linux, Windows und Mac – auf unterschiedlichen Prinzipien und ist eng an das Betriebssystem gekoppelt. Dies führt jedoch zu dem entscheidenden Nachteil, dass die Plattformunabhängigkeit des Quellcodes verloren geht, weil die Sprach-Syntax zum Ansprechen der grafischen Fensterobjekte abhängig von der verwendeten Bibliothek ist. Eine Bibliothek mit plattformunabhängiger Schnittstelle zur Darstellung der grafischen Steuerelemente kann diese Hürde jedoch aus dem Weg räumen. Qt ist hier eine mögliche Option! Denn bei dem Qt-Toolkit handelt es sich um ein umfangreiches Framework zur Entwicklung portabler Software basierend auf C++. Allerdings ist Qt ebenfalls an systemabhängige Bibliotheken gebunden. Diese basieren jedoch größtenteils auf plattformunabhängigem Quellcode mit dem entscheidenden Vorteil, dass die Bibliothek dem Anwendungscode eine betriebssystemunabhängige Schnittstelle zur Verfügung stellt. Damit bleibt die Syntax des Quellcodes unverändert, was den Anteil des portablen Quellcodes stark erhöht. Die folgenden Qt-Bibliotheken stehen zur Verfügung:
  • Qt/X11 für Linux/Unix (unterstützt viele Derivate)
  • Qt/Embedded (Qtopia) für embedded Linux
  • Qt/Windows für Microsoft Windows
  • Qt/Mac für Apple Macintosh
Auf der Basis portablen Quellcodes lassen sich Binärdateien für viele unterschiedliche Zielsysteme generieren, die sich in Prozessor und Betriebssystem unterscheiden können. Dazu ist allerdings ein Compiler für das entsprechende Zielsystem erforderlich. Zwei Szenarien sind hier denkbar:
  1. Das Übersetzen der Quellen auf dem Zielsystem
  2. Das Übersetzen der Quellen durch einen Cross-Compiler für das Zielsystem
Beim ersten Ansatz wird ein Compiler verwendet, der nativen Code erzeugt (Übersetzungssystem = Zielsystem). Der Hersteller Trolltech empfiehlt auf seiner Website die folgenden Compiler:
  • GCC für fast alle Plattformen
  • HP aC++ für HP-UX
  • Intel C++ für Linux, Windows, FreeBSD
  • MIPSpro für IRIX
  • Forte Developer / Sun Studio für Solaris
  • Visual C++ / Visual Studio für Windows 98/Me/NT/2000/XP/Server 2003/Vista
  • IBM xlC für AIX
Bei Embedded-Systemen (siehe zweiter Ansatz) wird oftmals ein Cross-Compiler bevorzugt, weil das Zielsystem nicht über genügend Ressourcen für das Entwicklungssystem verfügt. Die erste Wahl ist hier meist die GNU Compiler Collection (GCC), weil der GNU-Compiler für viele Übersetzungs- und Zielsysteme zur Verfügung steht.

Qt im Kurzüberblick

Qt steht gegenwärtig in der Version 4.2 und für eine Vielzahl von Plattformen in einem dualen Lizenzsystem zur Verfügung. Der Hersteller Trolltech bietet auf seiner Website eine kostenfreie Open-Source-Edition mit einer GNU General Public License (GPL) zum Herunterladen an. Daneben kann man auch eine kostenpflichtige, kommerzielle Lizenz erwerben, für die Trolltech im Gegensatz zur freien Version Produktsupport anbietet. Letztere ist allerdings nur dann notwendig, falls mit Qt Produkte entwickelt werden, die nicht unter einer freien Lizenz stehen. Beim Funktionsumfang der Open-Source-Edition gegenüber der kommerziellen Version ergeben sich nur geringfügige Einschränkungen.
Maßgebliche Stadien und Entwicklungen
Maßgeblich für Version 4.0 waren folgende Technologien:
  • Tulip: Eine Ansammlung von Container-Klassen, die eine Alternative zur STL darstellen.
  • Interview: Eine Modell-Ansicht-Architektur für elementbasierte Ansichten.
  • Arthur: Ein neues 2D-Zeichenframework, basierend auf der abstrakten Klasse QPaintEngine.
  • Scribe: Unicode-Textausgabe mit öffentlicher API zur Unterstützung von einfachen Textlayouts.
  • MainWindow: Eine überarbeitete Hauptfenster-Architektur, die Symbolleisten, Menüs und andockbare Fenster ermöglicht.
Im Dezember 2005 hat Trolltech die Qt-Klassenbibliothek in der Version 4.1 freigegeben. Überarbeitet wurden insbesondere wenige grundlegende Sprachelemente und die Framework-Architektur. Schon die Vorgängerversionen unterstützten zahlreiche Grafikformate für Pixel-Grafiken. Dazu gehören unter anderem GIF, BMP, JPG oder PNG. Mit der Version 4.1 erweiterte Trolltech die Unterstützung um das SVG Grafik-Format, welches zweidimensionale Vektorgrafiken in der XML-Syntax beschreibt. Außerdem bietet Qt ab der Version 4.1 ein Backend, das es Anwendern erlaubt, aus der Applikation heraus PDF-Dateien zu erstellen. In der aktuellen Version 4.2, die Ende 2006 erschienen ist, kam ein Framework für eine automatische Textvervollständigung innerhalb von Dialogen und Dialogelementen (beides wird in der Qt-Welt als Widget bezeichnet), hinzu. Mithilfe der Klasse QCompleter lassen sich somit Eingabefelder erzeugen, welche die Texteingabe automatisch vervollständigen, ähnlich wie bei der URL-Eingabe bei einem Internetbrowser. Ferner kann man Widget-Texte und das Aussehen der Widgets über die Widget Style Sheets anpassen. Letztere ähneln den CSS (Cascading Style Sheets). Die Klasse, welche die Funktion setStyleSheet aufruft, definiert den Gültigkeitsbereich des Styles. So lässt sich der Style für ein Widget als Style aller Widgets innerhalb der Applikation leicht modifizieren. Darüber hinaus enthält die Version 4.2 auch neue Widgets, mit denen man unter anderem Kalender darstellen kann. Die Klassen, die für das Hauptfenster zuständig sind, wurden im Bezug auf Benutzer-Ansprechbarkeit verbessert. Ein weiteres wichtiges Feature ist die Unterstützung des serviceorientierten D-Bus-Protokolls, welches immer mehr an Bedeutung gewinnt. Qt stellt dieses in einem neuen Modul namens QtDBus zur Verfügung. D-Bus ist ein IPC-System, mit dem sich verschiedene Programme via Interprozesskommunikation miteinander austauschen können. Die Version 1.0 ist erst am 9. November 2006 erschienen. Für die Version 4.3 der Qt-Klassenbibliothek hat Trolltech unter anderem eine neue Skriptsprache mit dem Namen Qt Scripting Language angekündigt. Die Qt Scripting Language soll wie auch JavaScript ECMA-262 kompatibel sein und lässt sich in C++-Anwendungen einbetten, um dynamisch mit Qt-Objekten zu interagieren. Dadurch werden Qt-Anwendungen steuerbar, ohne dass die Applikation selbst neu zu übersetzen ist.

Der Funktionsumfang der Qt-Klassenbibliothek
Selbst in der Open-Source-Version bietet Qt umfangreiche Funktionen in über 400 Klassen. Wesentliche Komponenten sind:
  • Der Makefile-Generator qmake: Dieser erzeugt aus einer plattformunabhängigen Projektdatei durch einen simplen Aufruf von qmake auf der Kommandozeile die plattformspezifischen Makefiles.
  • Vektor- und Pixelgrafiken: Gängige Grafikformate lassen sich mit Qt lesen, darstellen, manipulieren und schreiben. Des Weiteren unterstützt Qt die Open Graphics Library (OpenGL) und seit neuestem das XML-basierte Vektor-Grafikformat SVG (Scalable Vector Graphics). Dieses vereinfacht die dynamische Erzeugung von Vektor-Grafiken innerhalb eines Programms.
  • Internationalisierung: Der Qt-Linguist (siehe Abb. 1) unterstützt das Übersetzen von Texten in unterschiedliche Sprachen auf der Grundlage von i18n. Des Weiteren trennt er den Aufgabenbereich des Programmierers und des Übersetzers eindeutig, indem er den Übersetzer vom Quellcode fern hält.
  • GUI-Design: Qt bietet einen GUI-Designer, mit dem sich das GUI-Layout per Drag-and-Drop erstellen lässt. Für die dynamische Positionierung der grafischen Steuerelemente stehen mehrere Layout Manager zur Verfügung. Das Design der Steuerelemente kann man automatisch an das Design der gängigen Betriebssysteme anpassen. Selbst eigene GUI-Themes lassen sich implementieren.
  • Datenbankanbindung: Ein eigenes Modul unterstützt die nahtlose Anbindung an verschiedene Datenbanken. Dies hat den Vorteil, dass man den Quellcode unabhängig von der Datenbank implementieren und die Datenbank zu einem späteren Zeitpunkt leicht ersetzen kann.
  • XML-Kompatibilität: Qt bietet Funktionen zum Lesen, Manipulieren und Schreiben von XML-Inhalten. Das Framework unterstützt außerdem sowohl den Pseudo-Standard SAX2 (Simple API for XML) als auch das Document Object Model (DOM) Level 2.
Neben der Standard-C++-Implementierung gibt es auch verschiedene Open-Source-Varianten der Qt-Bibliothek für weitere Sprachen. Hierzu zählen Python, Ruby, C, C#, Java und Perl. Eine Java-Implementierung der Qt-Bibliothek mit dem Namen Jambi, die Trolltech bereits als Technology-Preview offeriert, ist kurz vor der Fertigstellung.


Abb. 1: Der Qt-Linguist bietet eine Oberfläche, um Programme in verschiedene Sprachen zu übersetzen. Dies vereinfacht die Arbeit, denn der Übersetzer muss sich nicht in den Quellcode einarbeiten und kann die Texte zu einem späteren Zeitpunkt dynamisch anpassen oder in weiteren Sprachen zur Verfügung stellen.

Plattformunabhängigkeit durch Qt
Die oben bereits angerissenen Makefiles sind für viele Entwickler im C/C++-Kontext ein eher ungeliebtes Stiefkind, insbesondere dann, wenn die Dateiabhängigkeiten im Makefile angegeben werden müssen. Durch qmake, einen Generator für Makefiles, verlieren die plattformspezifischen Makefiles deutlich an "Schrecken", da diese automatisch durch qmake erzeugt werden. Die gewohnten Einstellungen können in einer plattformunabhängigen Projekt-Datei eingegeben werden, welche lediglich Anweisungen, die man für die Erstellung der Makefiles benötigt, enthält. Mit dem Befehl qmake-project ist es sogar möglich, die Projektdatei völlig automatisch zu erstellen. Empfehlenswert ist dieser Befehl allerdings nur, wenn mit einem neuen Projekt begonnen wird oder ein bestehendes Projekt mit qmake übersetzt werden soll. Im Verlauf des Entwicklungsprozesses wird die Projektdatei typischerweise an die Erfordernisse angepasst. So lassen sich je nach Zielsystem selbst unterschiedliche Sourcecode-Dateien für die Erstellung der Makefiles angeben (Listing 2).

Listing 2

win32 {
SOURCES += driverwin.cpp
}
unix {
SOURCES += driverunix.cpp
}

Ein weiteres gängiges Problem ist die Anpassung der Anwendung an typische Eigenschaften des Betriebssystems. Diese betriebssystemspezifischen Modifikationen kann aber auch Qt übernehmen. Dies erleichtert nicht nur die Arbeit des Entwicklers, sondern erhöht auch die Qualität der Software. Ein konkretes Beispiel ist etwa das Speichern persistenter betriebssystemunabhängiger Settings (Listing 3). Diese werden von der Klasse QSettings implementiert. Die Memberfunktionen writeEntry und readNumEntry speichern oder lesen Integer-Werte. Die Identifikation des Settings erfolgt über einen String, den key. Während der Qt-basierte Quellcode identisch ist, speichert die Qt-Library die Daten betriebssystemtypisch:
  • Linux/Unix: QSettings speichert die Daten in den gewohnten Textdateien.
  • Windows: QSettings speichert die Daten in der Registry.
  • Mac OS X: QSettings verwendet die Carbon-API für Settings, um die Daten zu speichern.
Selbstverständlich unterstützt die Klasse QSettings das Speichern und Lesen unterschiedlicher Datentypen. Neben den üblichen primitiven Datentypen werden wichtige abstrakte Datentypen, beispielsweise QString, QStringList und QVariant, unterstützt.

Listing 3

bool writeEntry (const QString& key, int value)

const int readNumEntry (const QString& key, int def = 0, bool* ok = 0) const

Die Betriebssystemabhängigkeit der Verzeichnisstruktur und der Pfadangaben hat sich bisweilen als nervenaufreibendes Problem erwiesen, weil sowohl die Begrenzer als auch die Verzeichnisstruktur abhängig von dem verwendeten Betriebssystem sind. Eine große Hilfe zur Vermeidung solcher Probleme ist die Klasse QDir, denn sie kann einige betriebssystemspezifische Unterschiede ausgleichen, indem man den Verzeichnisnamen mit ihr manipuliert. Den Begrenzer (unter Windows "\" und unter Linux/Unix "/") stellt Qt dabei durch die statische Memberfunktion QDir::separator() zur Verfügung. Listing 4 zeigt die Manipulation des Verzeichnisnamens mithilfe der Klasse QDir. Dabei erzeugt man einen String, der auf das Verzeichnis mit dem Namen Test im Home-Verzeichnis des aktuellen Benutzers durch die Verkettung dreier Strings (alle vom Typ QString) verweist.

Listing 4

QDir::homeDirPath() + QDir::seperator() + "Test"

Sind all diese "kleinen Problemchen" ausgemerzt, steht der Entwicklung von portabler Software im Grunde nichts mehr im Wege.

Fazit: Vorteile überwiegen deutlich
Der Mehraufwand für die Entwicklung plattformunabhängiger Software in C++ ist vergleichsweise gering, erfordert jedoch entsprechendes Know-how. Systemabhängige Bibliotheken, deren Quellcode nicht zur Verfügung steht, sollte man vermeiden, weil diese das Produkt einengen. So lässt sich ein Programm beispielsweise nicht auf ein weiteres Zielsystem portieren, wenn nur eine der verwendeten Closed-Source-Bibliotheken nicht für dieses System zur Verfügung steht. Anders verhält es sich bei offenen Bibliotheken, denn diese kann man im Notfall anpassen und für ein weiteres Zielsystem übersetzen. Abschließend kann man sagen, dass die Relevanz der Plattformunabhängigkeit kontinuierlich wächst. Im Bereich der Standardsoftware erhöht sie die Akzeptanz der Software, weil der Anwender ein vertrautes Programm auf verschiedenen Systemen verwenden kann. Im Bereich der Individualsoftware kann man durch geringen Aufwand besser auf Kundenwünsche reagieren, was den Entwickler insgesamt flexibler macht. Auf den Punkt gebracht: Die Vorteile dieses Ansatzes überwiegen seine Nachteile deutlich.

© 2004 Software & Support Verlag GmbH. Vervielfältigung nur mit Genehmigung des Verlags. Fragen?