GNU-Pascal in Beispielen: Units

Aus Wikibooks
Wechseln zu: Navigation, Suche

zurück zu GNU-Pascal in Beispielen

Units[Bearbeiten]

Units dienen dazu, Quelltexte zu modularisieren und wiederverwertbare Konstanten, Typen und Routinen zu gruppieren. Sie ersparen es, in alten Programmen nach bestimmten Routinen zu suchen oder Ideen doppelt zu entwickeln. Solche Units, auch Module [1] genannt, werden mit der uses-Anweisung eingebunden. Werden mehrere Units eingebunden, so werden die einzelnen Module durch Kommas getrennt angegeben.

Der Aufruf des Compilers zum Erzeugen des Programms lautet:

gpc --automake dateiname.pas -o programmname

Die Option --automake sorgt dafür, dass alle benötigten Module eingebunden werden.


Die Standardunit GPC[Bearbeiten]

Es existieren eine Reihe von Standardunits, die mit dem GNU-Pascal Compiler mitgeliefert werden. Eine von ihnen ist die Unit GPC, die im Folgenden in Auszügen präsentiert wird. GPC enthält viele nützlicher Routinen, darunter auch solche zum Thema " Datum und Zeit":

Programm: Zeit[Bearbeiten]

 program Zeit;
 
 uses GPC;
 
 type
   TTagesString = String (10);
 
 var
   Tag, Monat, Jahr: Integer;
   Time: TimeStamp;
   NullString: array [Boolean] of String (1) = ('', '0');
   KeinString: array [Boolean] of String (4) = ('kein', 'ein');
 
 function TagName (t, m, j: Integer): TTagesString;
 var
   TagNummer: Integer;
   TagNamen: array [0..6] of String (10) = (
     'Sonntag',    'Montag',  'Dienstag', 'Mittwoch',
     'Donnerstag', 'Freitag', 'Samstag');
 begin
   TagNummer := GetDayOfWeek (t, m, j);
   TagName   := TagNamen[TagNummer]
 end;
 
 begin
   GetTimeStamp (Time);
   with Time do
     begin
       Tag   := Day;
       Monat := Month; 
       Jahr  := Year
     end;
   WriteLn (TagName (Tag, Monat, Jahr),' ',
     NullString[Tag < 10], Tag, '.',
     NullString[Monat < 10], Monat, '.', Jahr);
   WriteLn ('Dieses Jahr ist ', KeinString[IsLeapYear (Jahr)],
     ' Schaltjahr.');
   WriteLn ('Es ist der ', GetDayOfYear (Tag, Monat, Jahr),
     '. Tag des Jahres.')
 end.

Erklärung[Bearbeiten]

Dieses Programm gibt den Tagnamen gefolgt vom Datum aus. Dazu kommen Informationen darüber, ob das gegenwärtige Jahr ein Schaltjahr ist und welcher Tag des Jahres gerade ist. Die drei Funktionen GetDayOfWeek, IsLeapYear und GetDayOfYear sind Bestandteil der Unit GPC.

Die Funktion TagName liefert uns den Namen des Tages. Da die Funktion GetDayOfWeek " Sonntag" als nullten Tag ansieht, wurde das Array TagNamen so gestaltet, wie es ist. GetTimeStamp liefert uns Informationen über das aktuelle Datum im Record Time zurück. Drei der Felder sind Day, Month und Year. NullString dient dazu, bei Werten kleiner als 10 eine führende Null auszugeben. IsLeapYear ist True, wenn das angegebene Jahr ein Schaltjahr ist. KeinString[True] hat den Wert " ein". GetDayOfYear gibt die aktuelle Tagesnummer des Jahres zurück.

Eine weitere Gruppe von Funktionen innerhalb der GPC-Unit beschäftigt sich mit dem Thema Kommandozeilenoptionen. Kommandozeilenoptionen sind Parameter, die einem Programms auf der Kommandozeile mitgegeben werden können. Die Option -o des GNU-Pascal Compilers ist ein solcher Parameter.

Programm: Kommandozeile[Bearbeiten]

 program Kommandozeile;
 
 uses GPC;
 
 var
   Optionen: array [1..3] of OptionType = (
     ('date',      NoArgument,        nil, 'd'),
     ('leapyear',  RequiredArgument,  nil, 'l'),
     ('time',      OptionalArgument,  nil, 't'));
   Index: Integer;
   Ch: Char;
 
 procedure DatumAusgeben;
 var
   Time: TimeStamp;
 begin
   GetTimeStamp (Time);
   with Time do
     WriteLn (Day, '.', Month, '.', Year)
 end;
 
 procedure SchaltjahrAusgeben (Jahr: Integer);
 begin
   if IsLeapYear (Jahr) then
     WriteLn (Jahr, ' ist ein Schaltjahr.')
   else
     WriteLn (Jahr, ' ist kein Schaltjahr.')
 end;
 
 procedure ZeitAusgeben (Arg: TString);
 var
   Argument: TString = Arg;
   Time: TimeStamp;
 begin
   GetTimeStamp (Time);
   if Argument <> '' then
     begin
       LoCaseString (Argument);
       if Argument <> 'jetzt' then
         WriteLn ('Option für ''--time'': ''jetzt''!')
       else
         with Time do
           WriteLn (Hour, ':', Minute, ':', Second)
     end
   else
     with Time do
       WriteLn (Hour, ':', Minute, ':', Second)
 end;
 
 procedure ParseArgumente (Opt: Char; Arg: TString);
 var
   Zahl, Fehler: Integer;
 begin
   case Opt of
     'd':  DatumAusgeben;
     'l':  begin
             Val (Arg, Zahl, Fehler);
             if Fehler = 0 then
               SchaltjahrAusgeben (Zahl)
             else
               WriteLn ('Fehlerhafte Option von --leapyear: ', Arg)
           end;
     't':  ZeitAusgeben (Arg)
   end
 end;
 
 begin
   repeat
     Ch := GetOptLong ('+-', Optionen, Index, False);
     case Ch of 
       EndOfOptions: { Ende }
       otherwise     ParseArgumente (Ch, OptionArgument)
     end;
   until Ch = EndOfOptions
 end.

Erklärung[Bearbeiten]

Mit Optionen definieren wir ein Array auf OptionType-Argumente, welches die Optionen date, leapyear und time mit den dazugehörigen kurzen Schreibweisen d, l und t auflistet. Es wird beim Aufruf des Programms möglich sein, den Shellbefehl

kommandozeile --date

oder

kommandozeile -l 2002

auszuführen. Der Option date darf kein Argument mitgegeben werden, leapyear benötigt das Jahr als Argument und time darf ein optionales Argument "jetzt" haben. Die Prozeduren DatumAusgeben, SchaltjahrAusgeben und ZeitAusgeben tun genau das, was ihr Name vermuten lässt.

Im Hauptprogramm sorgt die Funktion GetOptLong dafür, dass die dem Programm übergebenen Optionen entgegen genommen werden. Gibt diese Funktion den Wert der vordefinierten Konstanten EndOfOptions zurück, so wird die dazugehörige repeat...until-Schleife beendet. Der erste Parameter bedeutet, dass keine Kommandozeilenoptionen gelesen werden, die nicht im Array Optionen enthalten sind. Das Abschließende - innerhalb des Parameters sorgt dafür, dass nur die kurzen Optionen übergeben werden, auch wenn eine lange Option auf der Kommandozeile angegeben wurde. Der Parameter False verhindert, dass nur lange Optionen berücksichtigt werden. Dieses Vorgehen ist insgesamt sehr praktisch, da wir auf diese Weise kurze und lange Optionen nicht getrennt auswerten müssen.

ParseArgumente übernimmt die ausgewählte kurze Option und den Wert einer globalen Variablen OptionArgument, welche je nach Option ein zusätzliches Argument der Option beinhaltet. Im Fall von -l zum Beispiel das Jahr. Der Prozedurkörper dieser Routine hat nur noch die Aufgabe, die entsprechenden Aufrufe der einzelnen Programmteile vorzubereiten.

Die Standardunit CRT[Bearbeiten]

Diese Unit enthält eine Sammlung von Routinen, die das Schreiben von Textmodus-Anwendungen erleichtern. So enthält sie Prozeduren, um farbige Fenster und Töne zu erzeugen. Manche der Routinen, so auch solche zum Erzeugen von Tönen, sind nicht auf jeder Plattform vorhanden, insbesondere nicht unter X11. Sie sollten solche Programme wirklich nur im Textmodus benutzen.

Programm: CrtTest[Bearbeiten]

 program CrtTest;
 
 uses CRT;
 
 var
   Ende: Boolean = False;
   
 procedure Ton;
 begin
   { Nicht unter X11! }
   Sound (440);
   Delay (100);
   NoSound
 end;
 
 procedure Hilfe;
 var
   HilfeSichtbar: Boolean = False; attribute (static)
 begin
   if HilfeSichtbar then
     begin
       TextBackground (Black);
       TextColor (Black);
       ClrScr
     end
   else
     begin
       TextBackground (Blue);
       TextColor (White);
       Window (10, 7, 70, 13);
       ClrScr;
       GotoXY (1, 1);
       Write ('Anleitung');
       GotoXY (5, 3);
       Write ('Druecken Sie ''t'', um einen Ton zu erzeugen.');
       GotoXY (5, 4);
       Write ('Druecken Sie ''h'', um diese Hilfe zu benden.');
       GotoXY (5, 5);
       Write ('Druecken Sie ''e'', um das Programm zu verlassen.')
     end;
   HilfeSichtbar := Not HilfeSichtbar
 end;
 
 begin
   CRTInit;
   TextColor (LightGray);
   TextBackground (Blue);
   Window (1, 1, 80, 1);
   ClrScr;
   Write ('(t) Ton    (e) Ende    (h) Hilfe');
   repeat
     case ReadKey of
       'e', 'E':  Ende := True;
       't', 'T':  Ton;
       'h', 'H':  Hilfe
     end
   until Ende
 end.

Erklärung[Bearbeiten]

Ein typisches CRT-Programm

Die erste Prozedur, CRTInit, initialisiert die Unit. TextColor legt die Vordergrundfarbe, TextBackground die Hintergrundfarbe fest. Window legt ein farbiges Fenster mit den angegebenen X-, Y-Koordinaten fest, welches durch die Prozedur ClrScr in Blau gezeichnet wird. Der erste Window-Aufruf sorgt für ein Menü in der obersten Zeile. Die folgende Write-Anweisung schreibt in das zuletzt definierte Fenster hinein. ReadKey wartet auf eine Tastendruck, der entsprechend ausgewertet wird.

Innerhalb der Prozedur Ton schaltet Sound den Ton ein. Das Argument der Prozedur ist die Frequenz des Tones. Der Ton bleibt so lange hörbar, bis ein Aufruf von NoSound ihn abschaltet. Delay sorgt für eine kleine Pause von , in der der Ton hörbar ist.

Hilfe schreibt eine Anleitung auf den Bildschirm. Bei jedem zweiten Aufruf der Hilfe wird das erzeugte blaufarbene Fenster schwarz überschrieben. Die Prozedur GoToXY setzt den Textcursor an die ausgewählte Stelle innerhalb des neu erzeugten Fensters. Die linke obere Ecke eines Fensters ist die Koordinate .

Schreiben eigener Units[Bearbeiten]

Eigene Units bestehen zumeist aus Sammlungen von Prozeduren, Funktionen und Typen die im Laufe einer Programmiertätigkeit anfallen. Sie sollten der Übersichtlichkeit halber thematisch geordnet sein. Die zugehörigen Dateinamen müssen kleingeschrieben werden. Eine Unit enthält mindestens zwei Bereiche, genannt interface- und implementation-Teil. Jeder dieser Bereiche darf seine eigenen Units einbinden, eigene Typen definieren und selbstverständlich auch Konstanten und Variablen deklarieren, wobei dem implementation-Bereich alle Informationen des interface-Teils bekannt sind, umgekehrt jedoch nicht.

Unit: Zeit1[Bearbeiten]

 unit Zeit1;
 
 interface
 
 procedure SchaltjahrAusgeben (Jahr: Integer);
 procedure DatumAusgeben;
 
 implementation
 
 uses GPC;
 
 procedure SchaltjahrAusgeben (Jahr: Integer);
 begin
   if IsLeapYear (Jahr) then
     WriteLn (Jahr, ' ist ein Schaltjahr.')
   else
     WriteLn (Jahr, ' ist kein Schaltjahr.')
 end;
 
 procedure DatumAusgeben;
 var
   Time: TimeStamp;
 begin
   GetTimeStamp (Time);
   with Time do
     WriteLn (Day, '.', Month, '.', Year)
 end;
 
 end.

Erklärung[Bearbeiten]

Eine Unit wird deklariert, indem das Schlüsselwort unit an den Anfang der Datei geschrieben wird. Diesem Schlüsselwort folgt der Name der Unit, der mit dem Dateinamen inhaltlich übereinstimmen muss. Die Unit endet mit dem abschließenden end gefolgt von einem Punkt. Im interface-Bereich der Unit werden diejenigen Konstanten, Typen, Variablen und Routinen aufgeführt, welche diese Unit exportiert. Nur die hier aufgeführten Elemente sind einem Programm, welches diese Unit mit uses einbindet, bekannt.

Im implementation-Teil der Unit werden die im interface-Bereich aufgeführten Routinen deklariert. Da wir zur Deklaration der beiden Routinen die Unit GPC einbinden müssen, das Interface jedoch keinen Nutzen von dieser Einbindung hat, wird die entsprechende uses-Anweisung im implementation-Bereich aufgeführt.

Diese Unit alleine ist noch nicht lauffähig. Zu ihr gehört ein Programm, welches die Unit nutzt:

Programm: UnitTest1[Bearbeiten]

 program UnitTest1;
 
 uses Zeit1;
 
 var
   Jahr: Cardinal;
 
 begin
   Write ('Bitte geben Sie eine Jahreszahl ein: ');
   ReadLn (Jahr);
   SchaltjahrAusgeben (Jahr)
 end.

Initialisierung einer Unit[Bearbeiten]

Eine Unit zu initialisieren bedeutet, zum Zeitpunkt des Einbindens eine Reihe von Anweisungen auszuführen. Diese Anweisungen werden noch vor denen des Hauptprogramms ausgeführt. Eine Deinitialisierung führt analog Code aus, nachdem das Programm abgearbeitet wurde. Mit diesen Techniken ist es beispielsweise möglich, einen Stapel zu initialisieren und zum Ende des Programms wieder freizugeben. Unser Beispiel zeigt die grundsätzliche Vorgehensweise:

Unit: Zeit2[Bearbeiten]

 unit Zeit2;
 
 interface
 
 procedure DatumAusgeben;
 
 implementation
 
 uses GPC;
 
 procedure DatumAusgeben;
 var
   Time: TimeStamp;
 begin
   GetTimeStamp (Time);
   with Time do
     WriteLn (Day, '.', Month, '.', Year)
 end;
 
 to begin do
   begin
     WriteLn ('Dies ist die Initialisierung');
     DatumAusgeben
   end;
 
 to end do
   begin
     WriteLn ('Dies ist die Deinitialisierung');
     DatumAusgeben
   end;
 
 end.

Erklärung[Bearbeiten]

Die Initialisierung erfolgt im to begin do-Block. Hier werden alle Anweisungen aufgeführt, die vor dem eigentlichen Programmlauf ausgeführt werden sollen. Die Deinitialisierung erfolgt im to end do-Block. Das folgende Programm nutzt diese Unit:

Programm: UnitTest2[Bearbeiten]

 program UnitTest2;
 
 uses Zeit2;
 
 begin
   WriteLn ('Dies ist das Hauptprogramm')
 end.

Erklärung[Bearbeiten]

Die Ausgabe des Programms ist:

Dies ist die Initialisierung
1.12.2002
Dies ist das Hauptprogramm
Dies ist die Deinitialisierung
1.12.2002


Anmerkungen[Bearbeiten]

  1. Streng genommen ist ein Modul eine weitere Möglichkeit zur Modularisierung, die in dieser Einführung nicht behandelt wird.


Wikibooks buchseite.svg Zurück zu Routinen | One wikibook.svg Hoch zu Inhaltsverzeichnis | Wikibooks buchseite.svg Vor zu Dateien