C++ und die praktische Arbeit mit GNU C 2.6.3: Neue Dimensionen

Softwareservice Seidel liefert demnächst die neue GNU C 2.6.3-[1]Version für ATARI-Computer aus. Um die Installation zu vereinfachen, wurde eine CD als Medium gewählt, da diese es erlaubt, alles Benötigte auf die CD zu packen, ohne daß man sich Gedanken über die daraus resultierende Diskettenanzahl machen muß. Außerdem wird alles ungepackt und bereits installiert ausgeliefert, was die Benutzung erheblich vereinfacht. Zusätzlich gibt es für alle, die noch kein CD-ROM-Laufwerk besitzen, eine Diskettenversion.

Pure C, Lattice C, Sozobon C W und GNU C 2.x.x sind die vier auf dem ATARI relevanten C-Compiler, aus denen sich - im wesentlichen - die ATARI-C-Welt zusammensetzt. An Universitäten ist GNU C der C-Compiler schlechthin, weil er im Quelltext vorliegt und auf alle Rechner portiert werden kann. Man kann ihn benutzen, ohne teure Lizenzen an ein Software-Haus entrichten zu müssen, und man zahlt auch keine teuren Updates. Die Quelltexte besorgt man sich via FTP-Server und kann nun GNU C 2.6.3 selbst compilieren, wobei auf dem ATARI noch spezielle Patches existieren, die auf die ursprünglichen Sourcen, die auf allen Computern gleich sind, angewendet wurden. Jedoch kann man nun GNU C immer nur mit sich selbst compilieren. Konkret bedeutet dies, daß man, um GNU C 2.6.3 selbst zu compilieren, ein GNU C 2.5.8 oder GNU C 2.6.1 benötigt.

Der „usr\local“-Ordner mit den GNU-C-2.6.3-Binär-Files

Die Einarbeitung in C++ ist zeitintensiv, da man sich erst vom „prozeduralen Denkansatz“ lösen muß. Man muß sich anfangs erst daran gewöhnen, in Objekten und Klassen zu denken; aber das Umdenken macht sich rasch bezahlt, da C++ auf allen Plattformen zu Hause und Standard ist. Manch einer mag jetzt einwenden, dies sei zwar alles richtig, aber ziemlich allgemein. C++ ist nicht nur ein „besseres C“, sondern unterscheidet sich in vielen Dingen vom konventionellen C. In C++ gibt es eine Menge Besonderheiten, z.B.: Klassen, abgeleitete Klassen, überladene Operatoren, Templates, Komponenten, Ausnahmebehandlung & Streams und viele weitere Features. Auch wenn es ein ziemliches Unterfangen ist, C++-Konzepte kurz zu umreißen, möchte ich es dennoch, ansatzweise, versuchen, um „^‘-Programmierern eine grobe Vorstellung davon zu geben. Zumindest „Klassen“ & „abgeleitete Klassen“ lassen sich gut erklären, ohne allzu sehr ausholen zu müssen.

Was sind Klassen?

Klassen sind ein C++-Konzept mit der Intention, dem Programmierer ein Werkzeug zum Erzeugen neuer Typen zur Verfügung zu stellen, welches sich so einfach handhaben läßt wie ein bereits eingebauter Typ. Im Idealfall sollte sich eine Klasse, also ein benutzerdefinierter Typ, nicht in der Art der Verwendung, sondern lediglich in der Art und Weise der Erzeugung unterscheiden.

Ein Beispiel für die Implementation einer Klasse: „dass date":

class date {
	int month, day, year;
public:
	void set(int, int, int); 
	void get(int*, int*, int*); 
	void next(); 
	void print();
};

„public" ist ein C++-Schlüsselwort, d.h., es ist C++-spezifisch. Das „public"-Label dividiert den Klassenrumpf in zwei Gebiete. Im ersten Gebiet befinden sich die privaten („private") Teile, die nur(!) innerhalb von Elementfunktionen verwendet werden dürfen. Und dann gibt es noch einen zweiten Teil, der öffentlich ist, aiso nicht „private". Dieser zweite Teil bildet die Schnittstelle zwischen den Objekten einer Klasse und dem Anwenderprogramm. In konkreten und einfachen Worten gesagt, ist ein „struct" also nichts anderes als eine Klasse („dass"), deren Elemente standardmäßig öffentlich sind. Die Elemente einer Klasse können also ganz normal, wie bisher auch, definiert werden. Beispiel:

void date::print()
// Ausgabe das Datums
{
	cout = day «c '.' « month « '.' «. year;
}

Nicht-Element-Funktionen sind allerdings vom Zugriff auf private Elemente ausgeschlossen:

void backdate() {
	today.day-; // Fehler
}

Der Vorteil dieses Konzeptes liegt darin, daß der Anwender eines solchen Typs nur die Definition der Element-Funktion betrachten muß, um den Gebrauch der Klasse zu erlernen. Ein weiterer Vorteil ist, daß Fehler, die dazu führen, daß z.B.: ein „date"-Objekt einen unsinnigen Wert bekommt (wie z.B.: 99.12.87), irgendwo im Code der Element-Funktionen begründet sein muß. Der Fehler ist also lokalisierbar, bevor das Programm überhaupt gelaufen ist.

Vererbung und abgeleitete Klassen

Eine abgeleitete Klasse stellt dem C++-Programmierer einen einfachen, flexiblen und effizienten Mechanismus zur Verfügung, der es erlaubt, die Fähigkeiten einer bereits existierenden Klasse zu erweitern, ohne daß die Klasse neu programmiert oder compiliert werden muß. Mit abgeleiteten Klassen kann man verschiedene Klassen mit einem gemeinsamen Interface ausrüsten, so daß Objekte dieser Klassen in anderen Programmteilen einheitlich behandelt werden können. Mittels des Konzeptes der „virtuellen Funktion“ (virtual function) wird die adäquate Verarbeitung von Objekten auch in Kontexten, in denen der Typ des Objektes zur Übersetzungszeit unbekannt(!) ist, möglich. Sinn und Zweck einer abgeleiteten Klasse ist, dem Programmierer zu ermöglichen, Gemeinsamkeiten zwischen Klassen einfacher formulieren zu können.

Hier nun ein Beispiel zu abgeleiteten Klassen:

struct mitarbeiter { 
	char*	name;
	short	alter;
	short	abteilung;
	Int	gehalt;
	mitarbeiter* next;
	//...
};

Hierbei dient das „next“-Feld als Link in einer Liste von Beschäftigten. Man könnte nun versuchen, einen Abteilungsleiter zu definieren:

struct abteilungsleiter ( 
	mitarbeiter mit;
	// Mitarbeiterdaten d. Abteilungsleiters 
	mitarbeiter* gruppe;
	// die Untergebenen
	short	gehaltsstufe;
	// ...
};

Da ein Abteilungsleiter auch nur ein Mitarbeiter ist, werden seine „mitarbeiter"-Daten im „mit“-Element des „abteilungsleiter“-Objekts abgelegt. Für einen Menschen eine logische Sache, für den Compiler aber nicht, da er nichts über mögliche Sinnzusammenhänge wissen kann. Es sei denn, wir drücken die Tatsache, daß ein Abteilungsleiter auch nur ein Mitarbeiter ist, explizit aus. Ein Abteilungsleiter ist also ein Mitarbeiter, der lediglich um ein paar zusätzliche Eigenschaften ergänzt wurde. Jetzt ein konkretes Beispiel:

struct abtellungslelter : mitarbeiter { 
	mitarbeiter* gruppe;
	short	gehaltsstufe;
	// ...
};

Man sagt auch, daß die „abgeleitete Klasse“ von der Basisklasse erbt, so daß diese Verwandtschaft auch als „Vererbung (inheritance)" bezeichnet wird. Mit der neuen Definition von „mitarbeiter" und „abteilungsleiter" kann man nun eine „mitarbeiter“-Liste zusammenstellen, die unter anderem auch „abteilungsleiter"-Objekte enthält:

void f()
{
	abteilungsleiter abt1, abt2; 
	mitarbeiter	mit1, mit2;
	mitarbeiter*	mlist;
	mlist	= &abt1;
	// abt1 in mlist eintragen 
	abt1.next = &mit1;
	// mit1 in mlist eintragen 
	mit1.next = &abt2;
	// abt2 in mlist eintragen 
	abt2.next = &mlt2;
	// mit2 in mllst eintragen 
	mit2.next =0;
	// Listenende
}

Da ein „abteilungsleiter" ein „mitarbeiter" ist, kann ein „abteilungsleiter*“ als „mitarbeiter*“ genutzt werden. Aber ein „mitarbeiter“ muß nicht unbedingt ein „abteilungsleiter" sein, so daß ein „mitarbeiter*" nicht als „abteilungsleiter*“ genutzt werden darf.

Allgemein: Hat eine Klasse „derived“ die öffentliche Basisklasse „base", kann ein Zeiger auf „derived“ einer Variablen vom Typ „base*" ohne explizite Typumwandlung zugewiesen werden. Die umgekehrte Umwandlung eines Zeigers vom Typ „base* “ in einen Zeiger vom Typ „derived*" muß hingegen explizit sein.

Weniger abstrakt könnte man es auch so formulieren: Objekte einer abgeleiteten Klasse können als Objekt der Basisklasse behandelt werden, soweit der Zugriff darauf über Zeiger geschieht. Beachte: Andersherum gilt es aber nicht! Nach diesem kurzen Einblick ins C++ nun wieder zu unserem GNU-C-2.6.3-Paket.

GNU-C-2.6.3-Voraussetzungen

Für den korrekten Betrieb des GNU-C-Systems sind ca. 3 bis 3,5 MB freies RAM nötig, MiNT 1.12 H2 (und/oder MultiTOS), weiterhin ist eine Festplatte unbedingt erforderlich, da ca. 13 bis 14 HD- bzw. 26 bis 28 DD-Disketten geliefert werden, deren Inhalt komprimiert ist. Die CD-Version wird voraussichtlich 30,- DM kosten. Die Diskettenversion wird bei ca. 50,- bis 60,- DM liegen. Exakte Preise und Modalitäten standen bei Redaktionsschluß aber noch nicht definitiv fest.

Alles Benötigte, also inkl. MiNT 1.12 H2, Minix XFS 0.60 PL11 (siehe [4]; mit der Lizenz des Autors Stephen Henson!) wird mitgeliefert. Die Mupfel l.a (auch mit der Erlaubnis des Autors Stefan Eissing), ist im GNU-C-2.6.3-Paket enthalten. Ausnahme: MultiTOS ist nicht unbedingt nötig, wäre aber durchaus sinnvoll, da es ideal mit MiNT zusammenarbeitet. Es wird von COMPO vertrieben und kann auf Wunsch zusätzlich erworben werden.

Außerdem paßt Gemini 1.a (siehe [5]) ideal zur Mupfel, darf aber aus lizenzrechtlichen Gründen nicht mitgeliefert werden. Die Mupfel, die unbedingt nötig ist, liegt aber bei. Besten Dank an die Autoren!

Weiterhin wird ein ATARI-Rechnera b 68000er und ein TOS ab Version 1.04 (oder höher) benötigt. TOS 1.0 & TOS 1.02 ist ungeeignet, da die Arbeit auf der Festplatte mit altem TOS keinen Sinn macht.

Zur Installation von GNU C 2.6.3 ist MiNT 1.12 MH2 (von Michael Hohmuth) zu empfehlen. Da lange Namen in den Sourcen Vorkommen und GNU C 2.6.3 speziell im Hinblick auf MiNT angepaßt wurde, ist es erforderlich, MiNT bzw. MultiTOS & MinixXFS 0.60 PL11 zu installieren. Man kann natürlich auch MultiTOS & Minix XFS verwenden. Dabei ist zu beachten, daß man erst MultiTOS auf der Platte installieren sollte und dann einfach das von MultiTOS mitgelieferte MiNT, falls es älter als MiNT 1.12 H2 ist, einfach ersetzt.

MiNT befindet sich im Autoordner und heißt MINTNP.PRG oder MINT.PRG. MultiTOS ist eine kommerzielle Software und ist z.B.: über COMPO & Overscan, ACCs (ATARI Competence Center) &ASF (ATARI-System-Fachhänd!er) lieferbar. Um Mißverständnisse zu vermeiden, möchte ich hier die Unterschiede zwischen MiNT und MultiTOS kurz klären. MiNT 1.12 H2 ist frei verfügbar, und MultiTOS ist eine lizensierte Software von ATARI & Compo. Cie Treiberfunktionalität ist in MiNT enthalten (also XFS &XDD Schnittstel-e, u:\pipe, u:\dev, u:\proc, u:\shm 5tc.). MultiTOS hat zusätzlich ein Mul-:iAES 4.x, welches es ermöglicht, GEM-Programme parallel laufen zu lassen! Dies kann mit MiNT alleine nicht erzielt werden. Unter MiNT kann man lediglich TOS-Programme parallel lauen lassen.

Minix XFS

Minix XFS hat zwar eine Menge Dokumentation, diese ist aber erstens in englischer Sprache abgefaßt, und zweitens ist man meist erst hinterher klug. Im Moment der Installation kann man noch gar nicht wirklich wissen, welche Informationen relevant sein könnten und welche weniger bedeutend sind. Außerdem ist soviel Dokumentation dabei, daß es für den Einstieg zuviel sein könnte. Da sich aber nicht alles nachträglich ändern läßt, möchte ich nun kurz die Installation von Minix XFS 0.60 PL11 so erklären, daß man damit möglichst problemlos zurechtkommt. Man nimmt dazu die Binärdistribution von Minix XFS 0.60 PL11 (MFS6011B.Z00) und entpackt das Archiv mit einem Zoo-Packer. Also z. B.:

zoo.ttp x d:\Minix\mfs6011b.zoo

Analog dazu kann man auch die anderen Archive entpacken. Auf der CD ist bereits alles entpackt, lediglich auf der Diskettendistribution ist das Entpacken unvermeidlich. Zuvor sollte man erstmal die Platte so installieren, daß sie unter GEMDOS fehlerfrei läuft, wobei man eine gute Harddisksoftware ersetzen sollte, um ideale Ergebnisse zu erzielen. Angetreten waren im ST-Computer-Harddisk-Schwerpunkt (siehe [3]) die folgenden Programme:

AHDI 6.06/HDX5.04, HDDriver2.80, HD-Plus 6.0 und Hushi 5.04 / SCSI Tool 4.26. AHDI markierte das Minimum an Funktionen, war also quasi die untere Meßlatte (nur 3 von 5 Mäusen), HDDriver & HD Plus hatten eine etwa gleiche Bewertung (jeweils 3,5 von 5 Mäusen), und der eindeutige Sieger mit 4,5 von 5 Mäusen war das Treiberpaket von Hard & Soft, welches „eindeutig die S-Klasse unter den Festplattentreibern“ (Zitat aus dem Testbericht) darstellt.

Also Formatieren, Partitionieren und Installation eines Festplattentreibers. Außerdem läßt man kaputte Sektoren, falls vorhanden, von der Harddisksoftware markieren. Wichtig ist es, sinnvolle Partitionsgrößen zu wählen! GNU C 2.6.3 benötigt alleine ca. 27 MB an Sourcen und ca. 10 MB an Binär-Files. Nicht jedes TOS unterstützt jede Partitionsgröße. Minix XFS 0.60 PL11 weigert sich, zumindest auf meinem Rechner, Partitionen ab einer gewissen Größe zu initialisieren. Dies ist zwar schade, aber nicht weiter tragisch. Denn es liegt an der Funktion zur automatischen Erkennung, die scheint wohl noch nicht implementiert zu sein - doch manuell geht es immer. Als Abhilfe bietet es sich an, notfalls selbst die richtigen Daten einzugeben, und zwar folgendermaßen:

minit.ttp -t d:

testet Laufwerk d: und gibt die Anzahl der blocks & Inodes an. Jetzt gibt man statt:

minit.ttp -P -V -n 4 d:
minit.ttp -b Blöcke -i inodes -P -V -n 4 d:

an. Wobei „Blöcke“ eine Zahl ist, die zuvor mit „minit.ttp -t d:" ermittelt wurde. Gleiches gilt auch für „Inodes". Minix XFS 0.60 PL11 hat seit Patchle-vel 11 keine(!) Beschränkung der Festplattengröße mehr, und auch der „triple indirection bug“ ist inzwischen behoben.

Um Minix XFS zu installieren, startet man:

  1. minit.ttp -P -V -n 4 d: (stellt alle Parameter ein ...)
  2. mfsconf.ttp d: (konfiguriert die Partition ...)
  3. fsck.ttp -S d: (prüft die Daten ...)
  4. csize.ttp minix.xfs 128 128 128 (Statt 128 können auch andere Werte verwendet werden.)

Wichtig ist es, nur Laufwerke im Minix-XFS-Format zu initialisieren, die nicht zum Booten benötigt werden, also meist alle Laufwerke außer „c:\“. Man sollte es so einstellen, daß lästige Harddisk-Zugriffe unterbleiben. Ein möglicher Wert wäre „csize.ttp minix.xfs 128 128128". Dies installiert jeweils 128 KB für den System-, User- und Inode-Cache, also insgesamt 384 KB Cache. Das wäre es im Prinzip.

Jetzt noch ein paar Erläuterungen zu den Parametern, um ein besseres Verständnis zu gewährleisten: Der Parameter-P sorgt dafür, daß ein Schutz aktiviert wird für den Fall, daß der Rechner einmal ohne Minix.XFS gestartet wird. Der Parameter -V ist sehr wichtig, denn er sorgt dafür, daß ein V2-File-System installiert wird, welches moderner und besser als ein V1-File-System ist. V2-File-Systeme finden z.B. auch unter ATARI-LinuX-68K-Verwendung. Der Parameter -n ist frei wählbar: 1, 2, 4 oder 8, und legt fest, wie groß Dateinamen und Ordnernamen werden dürfen. Das Ganze nennt sich auch „directoryjncrement“ und berechnet sich gemäß der Formel:

directoryjncrement = (n*16) - 2.

Mit den Parametern n=l, 2, 4 oder 8 ergeben sich die folgenden Werte für die maximale Länge eines Datei- oder Ordnernamens: 14, 30, 62,126. Ideal ist n=4, da somit auch sehr lange Dateinamen (also bis 62 Zeichen) abgedeckt werden. Unter Umständen reicht auch schon n=2, aber dieser Parameter läßt sich nicht nachträglich ändern. Daher ist n=4 am sichersten! Andere Werte außer 1,2,4 und 8 sind unzulässig. Mit n=4 ist man eigentlich immer gut beraten.

Die GNU-C-2.6.3-„Oberfläche“

Wer zuvor GNU C 2.5.8 benutzt hat, wird mit GNU C 2.6.3 auf Anhieb zurechtkommen. Denn die Binaries sind in „h:\usr\local" und die Quelltexte von GNU C 2.6.3 in „i:\gcc263".

Relevant ist erstmal der Pfad „h:\usr\local“. Dort sind alle Binaries, Libraries & Includefiles enthalten.

Also in: „h:\usr\local\bin“, „h:\usr-local\include“, h:\usr\local\lib"... etc. Es ist lediglich nötig, seinen GNU-C-Hauptordner umzustellen oder den Pfad in „profile.mup" abzuändern, z.B. auf „f:\usr\local" oder wo auch immer man Platz haben sollte (sollte aber eine Minix-Partition sein, also „d:" oder höher).

Wer z.B. von Pure C 1.1 kommt, vermißt die GEM-Umgebung. Daher hatte ich die Überlegung, die Benutzung trotzdem so einfach wie möglich zu gestalten. Dafür empfiehlt es sich, eine Shell oder ein Desktop mit Shell zu verwenden. Also z.B. Gemini 1.a! Ich persönlich benutze immer Gemini 1.a, da es mittlerweile sehr ausgereift ist und zuverlässig funktioniert. Außerdem ist die Arbeit unter Gemini sehr komfortabel und teilweise sogar unerläßlich, da man lange Dateinamen nur mit Gemini 1.a (bzw. der Mupfel von Gemini 1.a) korrekt bearbeiten kann. Das normale Desktop verkraftet lediglich „8+3"-Namen und ist daher nicht geeignet.

Es wird nur die Mupfel mitgeliefert, um als Shell für GNU C 2.6.3 zu fungieren. Da die Mupfel lange Namen korrekt auswertet, reicht dies auch völlig aus. Außerdem wird ein gepatchtes Resourcefile für Gemini l.a geliefert, um unter MiNT/MultiTOS korrekt zu laufen.

GNU C 2.6.3 liegen Anleitungen im TeX-Format bei und außerdem noch diverse Manualseiten, die man unbedingt beachten sollte. Unter anderem in „gcc263“ in den Ordnern:

  1. „gcc263\Changes\ChangeLog*“
  2. „gcc263\Manuals_.l\cccp.l“ „gcc263\Manuals_.l\cpp.l“ „gcc263\Manua!s_.l\gcc.l"
  3. „gcc263\Readme"
  4. „gcc263\gcc_infos\“

GNU C hat von der Version 2.5.8 zur Version 2.6.3 viele Erweiterungen erfahren. Während die Version 2.6.1 nur ein Zwischen-Release war, das schnell wieder überholt war, ist das GNU-C-2.6.3-Re!ease eine besonders gute und stabile Version. Der einzige Schönheitsfehler von GNU C ist die fehlende GEM-Einbindung. Dank der Mupfel 1.a ist das Arbeiten jedoch deutlich komfortabler geworden, ja teilweise richtig elegant. Dank der MiNT-Libraries, z.Zt. PL 45, und der MiNT-Patches von Andreas Schwab ist GNU C 2.6.3 nicht nur vom Sprachumfang her, sondern auch bezüglich der Anpassung an das' Betriebssystem, auf dem neuesten Stand.

Der Programmierer, der erst einmal von C zu C++ umgestiegen ist, wird seine Effektivität deutlich erhöhen, auch wenn die Umstellung von C nach C++ am Anfang nicht leicht fallen wird. GEM++ von Warwick ist bisher die einzige C++-Library für GEM. Daher hat GNU C 2.6.3 alle Features und Qualitäten, um zum C-Entwicklungssystem schlechthin zu werden, denn Rivalen mit einem auch nur annähernd vergleichbaren Sprachumfang gibt es - zumindest in der ATARI-Welt - nicht.

Der „gcc263“-Ordner mit den GNU-C-2.6.3-Quelltexten

Fazit

Der Vorzug von GNU C 2.6.3 ist, daß die ganze C++-Programmiererwelt ihr Know-how und Können in dieses System einfließen läßt, weil es der GNU Public License unterliegt und somit immer frei verfügbar ist.

Die Fairneß gebietet es, daß ich keine Wertung abgebe, da ich Mitautor (siehe [1]) der GNU-C-2.6.3-Distribution für ATARI ST/STE/TT/Falcon/ Medusa/Eagle bin. Außerdem gibt es zwar viele C-Entwicklungssysteme, aber keines für C++. Ich habe alle mir bekannten Nachteile und Vorzüge genannt. Eine Gewichtung dieser Punkte bleibt jedem Leser selbst überlassen.

Bezugsquelle: Softwareservice Seidel Hafenstr. 16 24226 Heikendorf

Literatur & Copyrights:

[1] GNU-C-2.6.3-Distribution von Softwareservice Seidel (Dirk Stadler, Fillpe Martins & Steffen Aumüller)
[2] „GNU GENERAL PUBLIC UCENSE“, Version 2, June 1991, Copyright (C) 1989,1991 Free Software Foundation, Inc.
[3] Festplattentreiber, „What You Drive Is What You Gef“, ST-Computer 11/93, S.32-37
[4] Minix XFS 0.60 PL11, Copyright by S.N. Henson 1991-1395,
[5] Gemini l.a und die Mupfel 1.a sind Shareware der Autoren: Stefan Elsslng, Julian Reschke (Flydials und Gemini-Tools) und Arnd Beissner (VT-52).

GNU C 2.6.3

Positiv:

einziger C++-Compiler für ATARI-Computer
vollständige C++-Implementation
aktuelle MiNT Libraries verfügbar
günstigstes C/C++-System auf nahezu allen Computern erhältlich
fertig installierte CD lauffähig auf 68000 bis 68060
deutsche FAQ

Negativ:

mind. 3 MB Speicherbedarf
keine GEM-Oberfläche (mir CLI-Mode der Mupfel)
Diskettenversion umständlich
viel Festplattenplatz nötig (Source: ca. 26 MB, Progr. ca. 10 MB, plus Swap-Platz auf Platte)
englische Originaldokumentation


Filipe P. Martins
Aus: ST-Computer 02 / 1996, Seite 34

Links

Copyright-Bestimmungen: siehe Über diese Seite