Objekt C (7)

Mit Veröffentlichung dieses Artikels liegen auf der CPR-Homepage (http://our-world.compuserve.com/home-pages/CPR_ObjectC) neue Da teien zum Download bereit:

  1. Object C 2.7 für ATARI TOS
  2. Sozobon C mit erweitertem Linker
  3. Sozobon-kompatible Header-Dateien zur GEM-Programmierung
  4. GCC 1.5.4 für ATARI TOS

Volker Seebode, der letzte Administrator der erweiterten Version von Sozo-bon C, hat die Quelldateien des Compilers der Firma CPR zur weiteren Verbesserung überlassen. Das wurde auch nötig, denn mit über 1 MB an Objektcode und 80 Quelldateien der Object C GUI Bibliothek war der Linker von Sozobon C auch überfordert. Der erweiterte Linker kann nun ebenfalls von der Homepage geladen werden. Die GEM-Headerdateien sollen aus einer älteren GCC Distribution stammen und gut mit Sozobon C arbeiten.

Unabhängig hiervon versucht Christian Felsch, Autor der MagiC-Anpassung von GCC 2.7.2.3, eine Kompatibilität von GCC zu MagiCMac zu erreichen. Falls es ihm gelingt, wird Object C kurzfristig auf diesen Release von GCC angepaßt. In jedem Fall wird man versuchen, das GCC mit Object C auf TOS2WIN oder anderen Emulatoren abzugleichen, womit dann neben Sozobon C und dem alten GCC 1.5.4 auch ein aktuellerer GCC Compiler zur Auswahl steht.

Adreßverwaltung im Eigenbau

Im letzten Artikel hatten wir begonnen, eine StdIO-basierte Adreßverwaltung zu implementieren. Damit soll allen Lesern die Möglichkeit geboten werden, die Anwendung des erlernten Object C Syntax zu ver-H tiefen.

Wir hatten zunächst in Datei "adr.i“ eine Reihe von Klassen beschrieben, die wir hier noch um weitere Attribute erweitern. Den Namen des Attributes 'Laddresses' haben wir in ’persons' geändert, denn unser Programm soll schließlich Personen verwalten, die Adressen haben und nicht umgekehrt. Der neue Konstruktor für Globals befindet sich bei den anderen Konstruktoren (Person, Phone, etc.).

Aus der Funktion "main()" wurde nach den nötigen Object C Initialisierungen die Haupteingabeschleife "$$Main-Loop(o_glo);" aufgerufen. Ferner haben wir eine gets()-ähnliche Methode $$lnput(o_glo, str);". In der Datei "adr_file.m" haben wir eine Reihe Methoden zum Dateizugriff bereitgestellt (Open, Close, Read, Write, Delete), die allesamt auf Objekten der Klasse File arbeiten. Wir haben dazu die Basisfunktionen der Object C Laufzeitbibliothek verwendet und müssen uns daher keine Sorgen um Portabilität machen.

Es hätte natürlich schöner ausgesehen, wenn eine Person eine Adresse erben würde ($dat Person : Address) und jede Adresse wiederum einen Telefonanschluß ($dat Address : Phone). Weil jedoch jede Person mehrere Wohnungen besitzen und auch mehr als einen Telefonanschluß pro Wohnung haben kann, wurden Listen verwendet. Object C entfernt freigegebene Objekte dann für uns automatisch aus allen Listen (Listing 1).

Implementieren wir also die fehlenden Do-Methoden der MainLoop. Es empfiehlt sich, mit dem DoNew (legt einen neuen Adreßeintrag an) zu beginnen, weil wir dann gleich sehen, welche Daten wir zu verwalten haben. Zuvor schreiben wir noch eine angemessene Eingabe-Methode "Enter()": (Listing 2)

Die Methode "Enter" soll nun lediglich ausgeben, welche Art von Daten einzugeben sind und dem Anwender mit-teilen, daß ein Abbruch durch Eingabe von ’a’ möglich ist. In diesem Fall wird op_glo->abort auf TR gesetzt, andernfalls wird ’inputstr' geliefert.

Eingabe von Adressen

Wie bereits erwähnt, kann jede Person eine oder mehrere (oder auch gar keine) Adresse haben und jede Adresse wiederum beliebig viele Telefonanschlüsse. Wir erzeugen also im DoNew der Klasse Global zunächst ein Objekt der Klasse Person und erlauben dann dem Anwender, den Vornamen und Nachnamen der betroffenen Person einzugeben. Falls der Anwender seine Eingabe nicht durch ’a’ abgebrochen hat, wird das Objekt ’o_person’ an die Liste der Personen angefügt. Dann hat der Anwender die Möglichkeit, eine Adresse anzugeben, für die dann beliebig viele Telefonanschlüsse (genauer Email-Adresse, Telefonnummer, Faxnummer und Handy-nummer) angegeben werden können. Anschließend können weitere Adressen angegeben werden. (Listing 3)

In der Implementierung von DoNew zeigt sich übrigens ein weiterer starker Vorteil von Object C gegenüber C++ und Java. Methoden gleichen Namens haben nämlich oft inhaltlich starke Ähnlichkeiten, wie die sogenannten "klassenspezifischen Blöcke“ von Person. Address und Phone zeigen. Wir können so unseren Quellcode sehr gut auf Fehler und auch auf inhaltliche Unstimmigkeiten überprüfen. Ferner kann die Notwendigkeit neuer Methoden zur besseren Modularisierung schneller erkannt werden. Hätten wir in C++ programmiert, dann würden sich diese gemeinsamen Codeblöcke wahrscheinlich nicht einmal in den gleichen Dateien befinden, weil in C++ nach Klassen und nicht nach Funktionalität getrennt wird.

Entfernung von Adressen

Die Entfernung von Personen und den personenbezogenen Daten aus unserer Adreßvewaltung läßt sich wesentlich einfacher realisieren. Hierbei wird die aktuell selektierte Person 'op_glo->ind_selected' der Personenmenge ’op_glo->selected' oder auch alle selektierten Personen einfach freigegeben. Die Schwierigkeit liegt hier vor allem in der Benutzerführung der Selektion. Wir verlagern dieses Problem deshalb auf die Do-Query-Methode. Das bedeutet, der Anwender soll zuerst die zu löschenden Einträge durch DoQuery spezifizieren und dann DoDel aufrufen. (Listirig 4)

Konstruktoren & Destruktoren

Wir benötigen natürlich auch die Konstruktoren der Klassen Person, Address und Phone. Üblicherweise würden die wichtigsten Attribute dem Konstruktor als Parameter übergeben werden, wir müßten dann aber lokale Eingabebuffer für alle Attribute in unserer DoNew-Metho-de reservieren und hätten trotzdem noch unsere Sorgen mit den in DoNew vorgenommenen Append von Objekten in die Listen von Person und Address. Werden in den Attributen einer Klasse nur Nutzwerte wie zum Beispiel Namen oder Zahlen gespeichert, dann können wie im Beispiel von ’Phone’ der Destruktor und der Cloning-Block leer bleiben. Jedes Cloning funktioniert dann ganz problemlos.

Werden hingegen Listen in Attributen gespeichert, dann besteht immer die Frage, ob im Destruktor nur die Liste oder auch die Objekte der Liste freizugeben sind. Der Programmierer muß also entscheiden, welches Objekt letztendlich für die zentrale Speicherung und Zerstörung bestimmter Objekte zuständig sein soll. Das klingt kompliziert, ist aber in den meisten Fällen recht einfach. In unserem Beispiel sollen Adressen zu einer Person gehören und beim Entfernen dieser Person auch aus unserem Computer verschwinden und mit diesen Adressen auch alle zugehörigen Telefonnummern. Wir müssen also zunächst alle Objekte durch "$freeobjs(list);" und dann auch die List selbst durch "$freelist(list);" zerstören. Im Gegensatz hierzu soll unsere Liste ’selected' (Klasse Globals) nur auf eine Menge von Personen verweisen und diese nicht in Besitz nehmen und sogar löschen. Wir rufen deshalb auch kein "$freeobjs(..selected);n im Destruktor der Klasse Globals auf.

Für alle nicht-trivialen Clo-ning-Blöcke (trivial ist beispielsweise das Cloning von 'Phone') sollte erst einmal ein "$serr("not impl.");" eingesetzt werden und erst bei tatsächlichem Bedarf über die Implementierung des Clonings nachgedacht werden; ungetestete Cloning-Blöcke "herumliegen" zu lassen, ist nämlich nicht ratsam, denn oft ändern sich die Anforderungen an verschiedene Attribute im Laufe der Programmentwicklung.

Object C - Listings Heft 3/98


Listing 1:
----------

Klassenbeschreibungen "ADR.I"

/* Datei: adr.i */ 
#include 

$classes
Globals, File, Person, Address, Phone; 
$

$dat Globals
List persons;
List selected;
long ind_selected;
Obj o_file; /* actual opened file */ 
Bool abort; /* for Do-Methods */
$

$dat File 
char name[32];
Hdl hdl;
$

$dat Person 
List addresses; 
char first_name[ 32 ]; 
char lastname[32]; 
char birthday[32];
$

$dat Address 
List phones; 
char country[32]; 
char city[32]; 
char Street[32]; 
char postcode[16];
$

$dat Phone 
char email[80]; 
char fax[20]; 
char voice[20]; 
char handy[20];
$

extern Obj o_glo;
extern $ptype(Globals) op_glo;

#include 

Listing 2:
----------

$meth Enter (Obj obj, char* valuename, char* inputstr)
$support(Globals);
$call(obj);
$class Globals
printf("Enter '%s' or <a> to abort valuename);
$$Input(obj, inputstr); 
if (*inputstr == ' a’)
	..abort = TR;
$

Listing 3:
----------

$meth DoNew (Obj obj)
$support(Globals, Person, Address, Phone); 
$call (obj);

$class Globals 
char inp[80];
Obj o_person;

..abort FA;

o_person - $new(Person);

$$DoNew(o_person); 
if (..abort)
{
	$free (o_person);
}
else
{
	$app(op_glo->persons, o_person);
	..o_selected = o_person;



printf("Do you want to create an Address field (y/n) ?");

$ $Input(o_glo, inp); while (*inp *= ' y')

{

Obj o_address = $new(Address);

$$DoNew(o_address)? if (..abort)

{

$free(o_address);

..abort = FA; break;

}
else
{

$ptr (Person, o_person, op_j>erson) ; $app(op_person->addresses, o_address);

printf("Do you want to create a Phone field (y/n) ?"); 
$$Input(o_glo, inp);

while (*inp = ' y')
{
	Obj o_phone = $new(Phone);

$$DoNew(o_phone) ; 
if (..abort)
{
	$free(o_phone);
	..abort = FA; break?
}
else
{
	$ptr(Address, o_address, opaddress); $app(op_address->phones, ophone)?

}

printf("Do you want to create another Phone field (y/n)?");

$$Input(o_glo, inp);


printf("Do you want to create another Address field (y/n) ?");

$$Input(o_glo, inp);

I

)

$class Person

$$Enter(o_glo, "First Name", ..first_name); 
if (!op_glo->abort)
{
	$$Enter(o_glo, "Last Name", ..last_name); 
	if (!op_glo->abort)
	{
		$$Enter(o_glo, "Birthday", ..birthday);
	}

$class Address

$$Enter(ojglo, "Country", ..country); 
if (!op_glo->abort)
{

$$Enter(oglo, "City", ,.city); 
if (!op_glo->abort)
{

$$Enter(o^glo, "Street", ..Street); if (!op_glo->abort)

(

$$Enter(o_glo, "Postcode", ..postcode);

)

I

$class Phone

$$Enter(o_glo, "Fax”, ..fax); if (!op__glo->abort)

(

$$Enter(o_glo, "Voice", ..voice); if (!op_glo->abort)

{

$$Enter(o_glo, "Handy”, ..handy); if (!op_glo~>abort) i

$$Enter(o_glo, "Email", ..email);

)

]

)

$

Listing 4:
----------

$meth DoDel (Obj obj)

$support(Globals);

$call(obj);

$class Globals 
Long elems;

$elems(..selected, fielems); 
if (elems)
{

char inp[80];

if (..ind_selected >»= OL)
{

printf("Delete [c]urrent item, delete [all] items or [a]bort ?");

$$Input(o_glo, inp);

}
else
{

printf("Delete [all] items or [a]bort ?");

$ $Input(o_glo, inp); if (EQU(inp, "c"))

CPY(inp, ’a');

if (EQU(inp, "c"))

{

Obj o_person;

$elem (.. selected, .. ind_selected, &o_j>erson) ;

$free(o_person);

—elems?

if (.. ind_selected >=* elems)

.ind_selected;

[

if (EQU(inp, "all"))

{

$freeobj s(..selected);

..ind_selected = -1L;

!

)

else

printf("There are no selected items.\n");

$

Listing 5:
----------
$new Person ()

CLR(..first_name); CLR(..last^name); CLR(..birthday); 
$newlist(&..addresses);
$clone
$serr("not impl.");
$free
$freeobjs(..addresses); $freelist(..addresses);
$

$new Address ()

CLR(..country); CLR(..city); CLR(..Street); CLR(..postcode); 
$newlist(&..phones)?
$clone
$serr("not impl.");
$free
$freeobjs(..phones); $freelist(..phones);
$

$new Phone ()
CLR(email); CLR(fax); CLR(voice); CLR(handy); 
$clone 
$free 
$

$new Globals () 
o_glo = obj; 
op_glo * objp;
..o_file = $new(File);
$newlist(&..persons);
..ind_selected = -1L;
$newlist(&..selected);
$clone
$serr("not supported!");
$free
$free(..o_file);
$freelist(..selected);
$freeobjs(..persons)? $freelist(..persons);
$

Andreas Guthke
Aus: ST-Computer 03 / 1998, Seite 40

Links

Copyright-Bestimmungen: siehe Über diese Seite