Mit CPR Object C für ST steht endlich eine C-basierte OOP-Sprache zur portablen Entwicklung zwischen den Plattformen ATARI ST, AMIGA, MS-DOS (32bit), OS/2 Warp, Windows 3.X/95/NT, MacOS (68K & PPC), LINUX, SUNOS und Solaris (SPARC) zur Verfügung.
Object C bietet eine Reihe von Vorteilen gegenüber Java C, Objective C sowie C++ und ist für die Systeme ATARI und AMIGA im aktuellen Release kostenlos auf http://ourworld.compuserve.com/Homepages/CPR_ObjectC verfügbar!
In der Ausgabe 4/97 der ST-Computer & ATARI-Inside wurde die ATARI-Version von Object C im Artikel "Object C - Die Zukunft für ATARI?" kurz vorgestellt. Während sich einige Spezialisten der ATARI-Szene um die Portierung der GUI-Bibliothek "OCC" (Object C Classes) kümmern, soll der Grundlagenkurs den Sprachsyntax und den Umgang mit der Sprache vermitteln. Nach dem Grundlagenkurs sollten Sie in der Lage sein, portable C-kompatible Programme für etwa 11 Plattformen mit Object C zu entwickeln. Anschließend an den Grundlagenkurs wird die dann portierte GUI-Bibliothek OCC vorgestellt. Im Laufe des OCC-Kurses werden Sie lernen, portable GUI-Programme für Windows 3.X/95/ NT, MacOS, OS/2 und ATARI-TOS zu erstellen. Unter günstigen Umständen wird bis dahin auch eine Portierung der GUI-Bibliothek für Motif und AmigaOS vorliegen.
Folgende Eigenschaften von Object C sollten auch Skeptiker beruhigen:
Die Liste ließe sich noch um ein Vielfaches erweitern. Die genannten Punkte sollten aber Ansporn genug für jeden Programmierer sein, sich Object C aus dem WWW oder der CompuServe Sektion CPROBJC bzw. GERSOFT zu laden. Zwar wird derzeit an einer Anpassung des GCC 2.7/8.X gearbeitet, die einzige erfolgreich getestete Version des GCC ist aber bislang GCC 1.5.4. Neuere Versionen benötigen ein Mint und sind deshalb u.a. für Magic-Mac weniger gut geeignet. Aufgrund der hohen RAM-Anforderung des GCC Compilers sollten Sie möglichst über 4 MB RAM verfügen. Die Installationsbeschreibung ist in der README-Datei enthalten.
Nun ist es in der Tat etwas schwierig, an eine derart alte Version von GCC zu kommen.
Unter "ftp://ftp.fh-mannheim.de/pub/ATARI-st/languages/gnu_c" befindet sich zwar eine 1.4er Version, die 1.5.4er Version scheint sich aber außerhalb des CompuServe Forums "ATARI ST Prod." sehr rar zu machen. Nun ist die CompuServe-Grundgebühr sehr gering, und jeder PC-Besitzer sollte sich neben dem optisch anspruchsvolleren AOL auch einen CS-Zugang anschaffen, zumal man diesen im Gegensatz zu AOL auch kommerziell nutzen kann (beispielsweise die 2 MB der WWW-Homepage). Wenn die völlig überarbeitete Version 3.x von CompuServe auf Ihrem Rechner nicht stabil läuft oder keine Verbindung aufbauen kann, dann beschaffen Sie sich die Netscape-Edition der 2.x Version des CIM (CompuServe Information Manager).
Wenn Sie das alles nicht möchten, dann können Sie GCC 1.5.4 auch auf DD-Disketten bei CPR zur "Bearbeitungsgebühr" (GCC ist ja kostenlos) von 40,- DM inkl. MwSt. und Versandkosten bestellen. Der Object C Compiler wird Ihnen dann gleich kostenlos beigelegt.
Bild 1 zeigt die Ausgaben des Object C Compilers nach dem Start von "OBJC.TTP" ohne Angabe zusätzlicher Parameter. Der Object C Compiler ist dann im Interaktionsmodus und gibt bei Angabe von "help" die Syntaxbeschreibung einer Object C Kommandozeile aus. Eine Kommandozeile ist im wesentlichen eine Aufzählung von bis zu 250 ".i" und ".m" Dateien, die zu einem Objekt C Modul übersetzt werden sollen. Weil 250 Dateinamen so manchen Editor überfordern, kann das Zeilenende durch "" auf die nächste Zeile ausgeweitet werden. Es ist also durchaus möglich, große Programme über nur eine einzige Kommandozeile zu übersetzen.
Das folgende Beispiel erzeugt ein Modul "Appl" mit Deklarationsdatei "d_appl.h" aus den Dateien "appl.i" und "appl1-3.m" Die Datei "appl.i" soll die Klassen und Attribute beschreiben. Die ".m"-Dateien sind zur Implementierung der Methoden und Funktionen vorgesehen.
objc -dd_appl.h appl.i appl1.m appl2.m appl3.m
Mehrere Kommandozeilen lassen sich zu Kommandodateien zusammenfassen. Kommandodateien sollten unter dem Namen "cmdfile.X" gespeichert werden, wobei "X" eine Zahl zwischen 1 und 999 sein kann. Eine Kommandodatei zur Bildung eines Object C Programmes mit mehreren Object C Modulen könnte etwa so aussehen: (siehe Listing 1)
Der Object C Compiler übersetzt dabei Dateien nur, falls es nötig ist. Wir müssen also keine komplizierten Makefiles schreiben. Allerdings ist es für den Compiler wichtig zu wissen, welche Module von anderen Modulen abhängig sind. Die Kommandozeile zur Übersetzung des 10 Modules verwendet dazu "-ld_str.h", um die Abhängigkeit vom Str-Modul anzuzeigen. Das Appl-Modul ist wiederum von beiden Modulen abhängig, was durch "-ld_str.h -ld_io.h" ausgedrückt wird. Im Gegensatz zum letzten Beispiel wird bei Verwendung mehrerer Module die Angabe einer konkreten Modulnummer je Modul durch -mMOD nötig (sonst wird der voreingestellte Wert 0 verwendet).
Im Interaktionsmode ist lediglich die Eingabe "1" + Return zur Übersetzung nötig. Durch Eingabe von "!1" + Return wird ein "clean" ausgeführt, das heißt, alle "*.h"-Dateien und ".c"-Dateien neu erzeugt. Weil unmittelbar nach der Aktualisierung der ".c" und ".h"-Dateien der GNU GCC alle erneuerten ".c" Dateien übersetzen muß, bietet es sich jedoch an, ein Shell-Skript zu schreiben, das zuerst Object C mit "cmdfile.1" durch "objc -ecmdfile.1" startet und danach das übliche Make für C-Dateien aufruft. Üblicherweise verwendet man ein Master-Cmdfile, das dann eine Reihe von Modul-Cmdfiles aufruft. Allerdings akzeptiert der Object C Compiler nur lokale Dateipfade.
Also z.B. "../cmdfiles/cmdfile.10" oder "../../sources/str/strl.m".
Das hat aber auch den Vorteil, den gesamten Entwicklungsordner bequem verschieben zu können - notfalls sogar auf eine andere Systemplattform.
Wie beschrieben, kann jedes Object C Modul aus bis zu 250 Dateien bestehen, von denen wiederum jede mehrere Methoden implementieren kann. Insgesamt sind bis zu 254 Module möglich. Also etwa maximal 60000 bis 200000 universelle Methoden bei beliebiger Erbtiefe auf max. 15-100 Millionen Objekten gespeichert in fragmentationsfreiem Speicher mit halbautomatischer Garbage Collection und dynamischer Mehrfachvererbung arbeitend. Um also überhaupt an die Grenzen von Object C zu kommen, würden sie im Minimalfall 15Mio*32 Bytes = 480 MB RAM benötigen.
Ähnlich natürlicher Sprache bestimmen Syntax und Semantik jeder Programmiersprache die Art und Weise, wie Programme im Gedankengang des Entwicklers entstehen und verstanden werden. Wenn Ihre C++ Projekte gescheitert sind, dann waren also nicht unbedingt Sie, sondern in erster Linie das konfuse Sprachdesign von C++ hierfür verantwortlich. Immerhin kann das Bewußtsein nur etwa 7 "Bilder" gleichzeitig verwalten. Es ist deshalb für eine Programmiersprache besonders wichtig, diesen "Speicher" effizient zu belegen. Weil diese "Bilder" schnell verblassen, ist eine hohe "Übertragungsrate" vom Quellcode zum Gehirn notwendig. Sprachsymbole sollten also möglichst kurz sein und beim Programmierer eine möglichst eindeutige Assoziation hervorrufen.
Aus vielen solcher Sprachkriterien ist eine Mischung aus Syntax und Semantik hervorgegangen, die sich durch folgende Eigenschaften auszeichnet:
Eine einfache Methode könnte in Object C etwa wie diese aussehen:
(siehe Listing 2)
Die ersten beiden Zeilen definieren eine Methode Namens "MyDraw" für ein Zielobjekt der Klasse "Text". "$call(obj);" ruft dann den klassenspezifischen Code auf. Innerhalb des klassenspezifischen Codes wird im Beispiel unter anderem eine Methode "MyGetHwnd" für das Text-Objekt aufgerufen. Ein Methodenaufruf erfolgt also durch "$$Methname(args);" während "$meth Methname (arglist)" eine Methodendefinition beginnt. Der Attributzugriff erfolgt explizit durch ..Attrname oder durch objp->Attrname und kann somit im Gegensatz zu C++ nicht mit Parametern, Konstanten, Makros oder Variablen kollidieren.
Um die Punkte 6-9 verstehen zu können, muß man zunächst einmal gesehen haben, wie eine größere Methode in Object C eigentlich aussieht:
(siehe Listing 3)
Wie wir sehen, unterstützt unsere Methode nun mehrere Klassen und reicht noch automatisch ein weiteres methodenlokales Argument ’hwnd’ an die einzelnen klassenspezifischen Blöcke weiter. Im Gegensatz zum ersten Beispiel ist der Quellcode der Text-Klasse aber nun kürzer. Sie sind soeben Zeuge von "generischem Code" geworden. Generischer Quellcode gilt nämlich für alle unterstützten Klassen einer Methode und steht zwischen dem "$support(...);" und dem ersten "$class Classname" einer Methode. C++ verfügt über diese Eigenschaft nicht und behilft sich statt dessen mit den sehr speicheraufwendigen Templates.
Wenn Sie Entwicklungsleiter einer Softwarefirma sind, dann sind Ihnen sicher Vorgehensmodelle der Softwareentwicklung bekannt. Das bekannteste ist das "Wasserfallmodell". Nach diesem Modell wird zuerst analysiert, dann folgt das Design und am Ende die Implementierung. Das ganze erstreckt sich genau genommen über etwa 9 Phasen. Im Wasserfallmodell muß jede Stufe schön hinter der anderen ausgeführt werden, weshalb das Modell in der Praxis viel gelobt aber selten verwendet wird. Zu oft ändern sich Programmanforderungen oder entstehen erst beim Programmieren die Ideen für neue Programmöglichkeiten. Meist wird deswegen eine Mischform gewählt, die mehr mit dem "Evolutionären Modell" als mit dem Wasserfallmodell zu tun hat. Aufgrund des Methodendesigns ist Object C aber viel besser dazu geeignet, mit den geradezu "chaotischen" Programmiereingriffen, denen ein Programm in seinem Lebenszyklus ausgesetzt ist, umzugehen. Einige wichtige Gründe hierfür sind "konstante Parameterlisten", "generischer Code" und "räumliche Nähe klassenspezifischer Codes".
Aber beginnen wir ein Programm besser erst einmal bei den Objekten. Zunächst müssen wir nämlich in einer unserer ".in-Datei des Modules die Klassen und Attribute definieren:
Unser Modul "IO" exportiert nur eine einzige Klasse "File". Wie in "cmdfile.1" angegeben (siehe oben), benötigt das Modul "10" das Modul "String" (Compileroption -ld_str.h) und lädt folgerichtig auch die Haupt-Includedatei des String-Moduls. Damit jedes Modul getrennt testbar ist, sollte die ".i"-Datei jedes Modules zu Beginn auch "obj.h" laden. Am Ende wird noch die Deklarationsdatei geladen, und fortan genügt es, jeder n.m"-Datei am Anfang die Haupt-Includedatei des eigenen Moduls (in diesem Fall "io.h") zu laden. Ängstliche Gemüter werden befürchten, den Syntax falsch anzuwenden und sogleich nach einer kompletten Backus-Naur Form für Object C rufen. Die Angst ist aber unbegründet, denn der Object C Compiler kümmert sich nur um das Innere seiner eigenen Sprach-Strukturen und reicht den Rest einfach weiter an die C-Datei. Zwischen dem $classes-Block und dem $dat-Block könnte also irgendein C-Code ihrer Wahl stehen. In allen Syntaxfragen hilft ansonsten die Referenzkarte (QUICKREF) weiter. Die komplette Sprachbeschreibung befindet sich im Referenzhandbuch (Book3).
Anders als Java erlaubt Ihnen Object C auch Präprozessoranweisungen. #ifdef, #include, #define usw. funktionieren also nach wie vor (im folgenden C-Übersetzungslauf), werden aber von Object C nicht weiter beachtet. Wir können also leider keine unterschiedlichen Definitionen von Klassen oder Methoden durch #ifdefs umgeben. Der Object C Compiler würde dann zur Auffassung kommen, die Klasse bzw. Methode sei bereits definiert worden und mit entsprechender Fehlerausgabe abbrechen. Wir können aber außerhalb einer Klassendefinition unterschiedliche Typen für Attribute definieren.
A) Das geht (leider) nicht:
#ifdef SYSTEM_WIN
$dat File : String;
int fhdl;
$
#endif
#ifdef SYSTEM_MAC
$dat File : String;
int RefNum;
short dhdl;
$
#endif
B) Das funktioniert:
typedef struct
{
#ifdef SYSTEM_WIN
int fhdl;
#endif
#ifdef SYSTEM_MAC
int RefNum;
short dhdl;
#endif } IO_hdl;
$dat File : String;
TO_hdl hdl;
$
Im nächsten Teil des Kurses werden wir ein komplettes Object C Programm schreiben und etwas über Konstruktoren, Attribute und Vererbung lernen.
Listings zum Object-C Grundlagenartikel
Listing 1:
# "cmdfile.1" # Translate String Module
objc -m1 -dd_str.h str.i str/str1.m str/str2.m str/str3.m str/str4.m
#
# Translate IO Module
objc -m2 -ld_str.h -dd_io.h io.i io/io_read.m \
io/io write.m io/io_open.m io/io misc.m
#
# Translate Appl Module
objc -m3 -ld_str.h -ldio.h -dd_appl.h appl.i \
appl/appl1.m appl/appl2.m appl/appl3.m
Listing 2:
$meth MyDraw (Obj obj, int x, int y)
$support(Text);
$call(obj);
$class Text
HWND hwnd; $$MyGetHwnd(obj, &hwnd);
WinBeginDrawing(hwnd);
WinMoveTo(obj, x, y);
WinDrawString(hwnd, ..str);
WinEndDrawing(hwnd);
$
Listing 3:
$meth MyDraw (Obj obj, int x, int y)
$par(HWND hwnd);
$support(Text, Picture, TitledPicture);
$$MyGetHwnd(obj, &hwnd);
WinBeginDrawing(hwnd);
$$MyMoveTo(obj, x, y);
$call(obj);
WinEndDrawing(hwnd);
$class Text
WinDrawString(hwnd, ..str);
$class Picture
WinDrawPicture(hwnd, ..picthdl);
$class TitledPicture
WinDrawString(hwnd, ..text);
$$MyMoveTo(obj, x, y + 30);
WinDrawPicture(hwnd, ..picthdl);
$
Listing 4:
/*
"io.i” The main include file of the IO module
*/
#include "obj.h" /* load macros required for each Object C program */
#include "str.h" /* load classes and Methods from our examples String module */
$classes
File
$
$dat File : String; /* inherits a String for file-path from String Module */
int fhdl; FILE *fp;
char access[5]; /* "rw" */
$
#include "d_io.h" /* load all method prototypes */