next up previous
Nächste Seite: Charakterisierung von Ausnahmen Aufwärts: Ausnahmenbehandlungen Vorherige Seite: Traditionelle Techniken

Mechanismen für Ausnahmenbehandlungen

Allen Mechanismen für Ausnahmenbehandlungen ist gemein, daß eine Prozedur auf verschiedenen Wegen beendet werden kann. Um die Diskussion zu vereinfachen, kann man jede Anweisung als einen Prozeduraufruf betrachten, selbst wenn der Übersetzer dafür keine Prozeduraufrufe generiert. Bei einigen Programmiersprachen ist es auch in diesem Zusammenhang sinnvoll, lokale Blöcke als lokale Prozeduren zu betrachten (insbesondere C++ und Modula-3).

Entweder wird eine Prozedur normal beendet oder sie initiiert eine Ausnahmenbehandlung aufgrund eines entdeckten Ausfalls. Eine Ausnahmenbehandlung führt dazu, daß die Kontrolle an einen Ausnahmenbearbeiter weitergeleitet wird. Die Prozedur, die die Ausnahmenbehandlung initiiert hat, wird als Signalgeber bezeichnet. Die Prozedur, die den Ausnahmenbearbeiter enthält, der aufgrund der Ausnahmenbehandlung aktiviert wird, heißt Signalempfänger.

Es gibt eine Reihe von Techniken, einen Signalempfänger zu lokalisieren. Denkbare Kandidaten sind aktivierte Prozeduren (also der Klient und der Klient des Klienten usw.), statisch übergeordnete Prozeduren, globale und damit kontextunabhängige Ausnahmenbearbeiter und objektbezogene Bearbeiter, die von einem der Parameter des Signalgebers abhängen. Der Signalgeber selbst wird als Signalempfänger nicht in Betracht gezogen, da er per Definition dazu nicht in der Lage ist.

Wenn der Signalempfänger eine der aktivierten Prozeduren sein soll, so kann dies der direkte Aufrufer sein (Ebenenmodell oder single level mechanism) oder einer der übergeordneten Klienten (distanziertes Ebenenmodell oder multilevel mechanism), d.h. im Beispiel könnte A der Signalempfänger für den Signalgeber C sein, ohne daß B daran beteiligt ist. Da dies zum Bruch von Abstraktionsgrenzen führt, wird dies von kaum einer Sprache unterstützt (PL/1 ist eine Ausnahme). BETA unterstützt das distanzierte Ebenenmodell nur in Bezug auf statisch übergeordnete Klienten und vermeidet damit auf elegante Weise den Bruch von Abstraktionsgrenzen.

Globale Ausnahmenbearbeiter sind einfach zu implementieren und benötigen keine zusätzliche Sprachunterstützung, wenn es Prozedurtypen gibt. Allerdings können sie im Bereich von Bibliotheken oder größeren Software-Systemen nicht die einzige Lösung sein.

Einige objekt-orientierte Sprachen (z.B. Eiffel oder Lore) unterstützen objektbezogene Ausnahmenbearbeiter.

Abbildung 5.3: Optionen eines Ausnahmenbearbeiters
\begin{figure}\epsfig{file=ab-optionen.eps}\end{figure}

Ausnahmenbearbeitern stehen - nach einigen normalen Anweisungen - folgende Optionen offen (siehe Abbildung 5.3.3):

Weiterleitung
In diesem Fall wird eine neue Ausnahmenbehandlung initiiert, d.h. der Signalempfänger wird selbst zum Signalgeber. Bei dem Ebenenmodell handelt es sich dabei um den jeweils nächsten Klienten. Normalerweise ist diese Option die Voreinstellung, wenn kein Ausnahmenbearbeiter für einen Signalempfänger explizit gegeben ist.

Wiederaufnahme
Diese Option erlaubt dem Ausnahmenbearbeiter, die Ausführung am Punkt des Ausfalls fortzusetzen. Dies ist eine denkbare Reaktion, wenn das zugrundeliegende Problem gelöst worden ist. Die Kombination des Ebenenmodells mit einer möglichen Wiederaufnahme ist nicht einfach zu implementieren, da beide Prozedurkontexte analog zu Koroutinen parallel benutzt werden. Berücksichtigt man, daß Ausnahmenbearbeiter andere Prozeduren aufrufen können, die wiederum zu Ausnahmenbehandlungen führen können, dann ist klar, daß man eine Reihe von Kellern mit gemeinsamen Teilen benötigt (sogenannte cactus stacks). Im Falle von globalen oder objektbezogenen Ausnahmenbearbeitern ist Wiederaufnahme kein Problem. Allerdings muß dann der Signalgeber damit rechnen, daß das Auslösen einer Ausnahmenbehandlung nicht notwendigerweise ein Punkt ohne Rückkehr ist.

Neustart
Dies ist eine Technik, die von einigen Mechanismen, die auf dem Ebenenmodell basieren, unterstützt wird. Hierbei wird der Keller bis zu den Daten des Signalempfängers abgebaut und anschließend die Prozedur bzw. der aktuelle Block neu gestartet. Wenn der Ausnahmenbearbeiter die Anzahl der Versuche in einer lokalen Variablen notiert, dann können auf diese Weise auch Alternativen durchprobiert werden.

Beendigung
Einige Programmiersprachen erlauben dem Ausnahmenbearbeiter, beim Ebenenmodell den Signalempfänger mit RETURN zu beenden, ohne daß die Ausnahmenbehandlung weitergeleitet wird. Auf den ersten Blick verletzt dies das Prinzip, daß Ausfälle nicht unerkannt bleiben sollen: Wenn ein einfaches RETURN ausreicht, um die Nachbedingung zu erfüllen, warum überhaupt etwas anderes unternehmen? Allerdings gibt es Fälle, in denen es sinnvoll ist, mögliche Ausfälle in normale Rückgabewerte zu konvertieren. Als Beispiel kann eine Prozedur genommen werden, die überprüft, ob eine gegebene Datei existiert. Hierbei ist sie möglicherweise auf eine Operation zum Eröffnen angewiesen, die in ihrer Vorbedingung die Existenz der Datei voraussetzt.

Von der Sprache unterstützte Ausnahmenbehandlungen können einige der genannten Probleme der traditionellen Technik lösen. Als ein Beispiel mag in Modula-3 die Prozedur PutChar aus dem Modul Wr dienen:

(* excerpt of the interface of module Wr *)
TYPE
   Code = {Closed, Unseekable};
EXCEPTION
   Failure(REFANY);
   Error(Code);

PROCEDURE PutChar(wr: T; ch: CHAR) RAISES {Failure, Error};

Die RAISES-Deklaration gibt an, daß PutChar entweder erfolgreich beendet wird oder PutChar eine der angegebenen Ausnahmen (Failure oder Error) initiiert. Beide Ausnahmen sind parametrisiert: Failure durch REFANY, einen Typ, der zu allen Zeigertypen kompatibel ist, und Error durch Code. In dem Beispiel initiiert PutChar selbst nur Error, während Failure von den zugrundeliegenden Implementierungen ausgelöst werden kann.

Ein Klient von Wr könnte folgendermaßen aussehen:

PROCEDURE SomeOperation(...) RAISES {Wr.Failure, Wr.Error, ...};
   VAR
      failure: REFANY;
      code: Wr.Code;
BEGIN
   (* ... *)
   TRY
      PutChar(wr, ch);
   EXCEPT
   | Wr.Failure(failure) =>
      TYPECASE failure OF
      | FileStream.Failure => (* ... *)
      (* ... *)
      END;
   | Wr.Error(code) =>
      (* ... *)
   END;
END SomeOperation;

Hierbei wird mit TRY...EXCEPT...END eine Folge von Anweisungen mit einem Ausnahmenbearbeiter verknüpft. Der Ausnahmenbearbeiter hat die Möglichkeit, zwischen den einzelnen Ausnahmen zu unterscheiden und auf die zugehörigen Parameter zuzugreifen. Wenn notwendig, kann der Ausnahmenbearbeiter auch alle weiteren und möglicherweise unbekannten Ausnahmen abfangen.

Ada unterstützt keine Parametrisierungen von Ausnahmen, und Eiffel läßt nur einen STRING-Parameter zu. In Eiffel bezieht sich ein Ausnahmenbearbeiter immer auf eine vollständige Prozedur und nicht auf eine beliebige Anweisungsfolge innerhalb der Prozedur. Darüber hinaus erlaubt Eiffel den Ausnahmenbearbeitern nur Neustarts oder eine Weiterleitung der Ausnahme. Dies soll zwar ein stilles RETURN verhindern, allerdings ist es möglich, dies über einen Neustart zu simulieren.

Während diese eingebauten Mechanismen eine mächtige Kontrollstruktur für Ausnahmenbehandlungen sind, können sie genauso für große Sprünge über Prozedurgrenzen hinweg mißbraucht werden (analog zu longjmp in C). Im Falle von tief verschachtelten Ausfällen ist weder die detaillierte Fehlermeldung der unten liegenden Prozedur noch die Fehlerindikation auf der höheren Abstraktionsebene allein ausreichend, um eine aussagefähige Fehlermeldung zusammenzustellen. Für lesbare Fehlermeldungen wäre eine Beschreibung des Verlaufs der Ausfälle interessant. Im Beispiel: A fiel aus, da B wegen des Ausfalls von C ausfiel.

Modula-3, Eiffel und Exceptional C versuchen, auch asynchrone Ereignisse (wie z.B. SIGINT unter UNIX) als Ausnahmen zu behandeln. Hier erweist sich jedoch ein Modell auf Basis von Ereignissen und Interessenten als günstiger (wie es in der Ulmer Oberon-Bibliothek als Events realisiert ist),5.4da es wichtig ist, daß asynchrone Ereignisse, die nicht aufgrund von Fehlern entstehen, unabhängig vom lokalen Kontext bearbeitet werden. Sonst kann es im Ebenenmodell passieren, daß keine der aktivierten Prozeduren darauf vorbereitet ist, oder daß eine ungestörte Fortsetzung nach der Bearbeitung unmöglich ist.


next up previous
Nächste Seite: Charakterisierung von Ausnahmen Aufwärts: Ausnahmenbehandlungen Vorherige Seite: Traditionelle Techniken
Andreas Borchert 2000-12-18