• Kurs języka E - część 7

20.02.2005 13:43, autor artykułu: Wojciech Zatoski (Amiga Computer Studio)
odsłon: 2093, powiększ obrazki, wersja do wydruku,

W dzisiejszym 1235 odcinku naszego ulubionego serialu omówimy zasady postepowania w sytuacjach ekstremalnych. Pisząc programy zapewne nieraz wam się zdarzy, że program u was działa, a u kolegi już nie. Albo, że program raz działa, a raz nie. W takich przypadkach niezbędne stają się programy pokroju MuForce, PatchWork. Oprócz tego w dzisiejszym odcinku postaram się opisać jak zabezpieczyć programy przed błędami.

Do czego służy MuForce?

Jego zadaniem jest sprawdzanie czy jakiś program nielegalnie "wykorzystuje" pamięć, czyli jeśli twój program zamiast zapisać pod adres $20000, zapisze pod adres $0000 to program natychmiast wywali błąd. MuForce wymaga procesora z MMU. Więcej na temat tego narządzia możecie również przeczytać w osobnym artykule na PPA.

Jak uruchomić?

Po uruchomieniu systemu (setpatch musi być uruchomiony!) z okienka cli wpisujemy:

MuForce stdio

co spowoduje, że wszystkie błędy będzie wyświetlał w okienku cli.


Do czego służy PatchWork?

Działa na podobnej zasadzie jak MuForce ale jego zadanie jest nieco inne, sprawdza poprawność odwołań do funkcji systemowych. Czyli jeśli użyjesz w swoim programie funkcji OldOpenLibrary() to PatchWork wypisze błąd, a dlaczego? Ponieważ ta funkcja nie powinna być już używana (zobacz autodocs!).

Jak uruchamiać?

Ma te same wymogi co MuForce, oprócz tych wymagań nie za bardzo lubi się z MagicMenu, po uruchomieniu PatchWorka z uruchomionym MagicMenu następuje od razu zwis systemu. Dlatego jak autor pisze w dokumentacji należy na czas testowania wyłączyć programy powodujące zwis (no coż nie wszyscy stosują się do zasady os-friendly). Dokładny opis programu znajdziecie w dołączonej do niego dokumentacji.

Tak więc jeśli, któryś z tych programów zwróci błąd podczas działania waszego programu to znaczy, że coś jest źle w programie i należy to natychmiast poprawić!

A gdy błąd się ściele gęsto...

Pamiętaj, że wiekszość funkcji zwraca również kod błędu (przeważnie jeśli <=0 to błąd), dokładny opis jakie wartości dana komenda zwraca znajdziecie w autodocs'ach, np.

IF mem:=New(100)=NIL THEN error()

czyli jeśli polecenie New() - rezerwuj pamięć - zwróci zero to znaczy, że pamięć nie została zarezerwowana (bo np. nie ma już wolnej pamięci) i program skoczy do procedury error().

Oprócz tego E dysponuje całym "systemem obsługi błędów", tzn. zamiast pisać:

PROC main()
DEF mem

mem:=New(100)

IF mem

-> dalsza część kodu

ELSE
WriteF('Błąd!n')
ENDIF

możemy napisać:
ENUM NOMEM=1

PROC main() HANDLE
DEF mem

IF mem:=New(100)=NIL THEN Raise(NOMEM)

-> dalsza część kodu

EXCEPT
IF exception=NOMEM THEN WriteF('Błąd!n')
ENDPROC


Czym się to różni? Na początku deklarujemy stałą NOMEM o wartości 1. Czym się różni ENUM od CONST? Najłatwiej będzie to przedstawić na przykładzie:

CONST a=1,b=2,c=3,d=4,e=55,f=56
ENUM a=1,b,c,d,e=55,f


Czyli przy wykorzystaniu CONST musimy podawać wartość każdego elementu, natomiast przy użyciu ENUM jeśli podamy przy pierwszym elemencie 1 to wiadomo, że następny będzie wynosił 2, kolejny 3, itd. (czyli będzie się zwiększał zawsze o jeden).

Kolejnym nieznanym dla nas poleceniem jest HANDLE, podanie tego parametru przy PROC mówi kompilatorowi, że procedura ma własną obsługę błędów (ta obsługa znajduje się za poleceniem EXCEPT).

Polcenie EXCEPT razem ze słowem DO mówi kompilatorowi, że jeśli nawet nie było błędu to program ma się wykonywać za poleceniem EXCEPT, tzn. jeśli program przejdzie całą procedurę i napotka EXCEPT to dalej się wykonuje, natomiast jeśli nie dodamy do EXCEPT słowa DO to program napotykając EXCEPT skoczy do ENDPROC.

Tak więc jeśli polecenie New zwróci wartość 0 (NIL) to zostanie wywołany błąd o numerze NOMEM i program skoczy do procedury obsługi błędów (Raise(NOMEM)). W procedurze obsługi błędów możesz się dowiedzieć jaki błąd został wywołany sprawdzając zmienną exception. Ale to znowu też nie jest tak bardzo wygodne, dlatego w E istnieje jeszcze jedna możliwość:

ENUM NOMEM=1

RAISE NOMEM IF New()=NIL

PROC main() HANDLE

mem:=New(100)

EXCEPT DO
  IF exception = NOMEM THEN WriteF('Błąd przy próbie rezerwacji pamięci!n')

ENDPROC


Różnica polega na tym, że teraz wywołanie błędu "deklarujemy" na początku programu (jedyną wadą w tym przypadku to jest to, że nie wiemy, który np. New() zwrócił błąd). W tym wypadku wykorzystujemy polecenie RAISE:

RAISE <nazwa_wyjątku> IF <funkcja>() <warunek> <wartość>

<nazwa_wyjątku> - wyraz napisany wielkimi literami JOLKA, GURU, etc lub wartość liczbowa.
<funkcja> - funkcja, której dotyczy błąd New(), AllocMem(),etc.
<warunek> - "<", ">", "=","<>",etc.
<wartość> - wartość jaką ma spełniać/niespełniać (zależnie od warunku)

RAISE NOMEM IF New()=NIL

czyli:

WYWOŁAJ NOMEM GDY New() zwróci NIL

Oprócz Raise() istnieje podobny do tej funkcji rozkaz Throw(exceptionID,wartosc). Różni się tym, że do procedury obsługi błędów zwraca również wartość kryjącą się pod zmienną exceptioninfo, dzięki temu możemy w bardzo łatwy sposób dodać komentarz do występującego błędu, typu:

ENUM NOMEM=1

PROC main() HANDLE
DEF mem

IF mem:=New(100)=NIL THEN Throw(NOMEM, 'Nie mogę zarezerwować pamięci')

-> dalsza część kodu

EXCEPT
IF exception<>0 THEN WriteF('d:"s"n',exception,exceptioninfo)
ENDPROC


W tym wypadku jeśli New() zwróci NIL to zostanie wywołany błąd NOMEM z wartością "Nie moge..."

Oprócz tego istnieje przydatna instrukcja:

ReThrow()

lub

IF exception THEN Throw(exception, exceptioninfo)

Umieszcza się ją na końcu danej procedury (obsługi błędów), pozwala to na wywołanie tego samego błędu w wyższej procedurze. Jeśli np. masz procedure main(), procedure podmain() i wywołujesz procedure podmain() z main(), a w procedurze podmain() umieścisz na końcu (za EXCEPT) ReThrow() to jeśli wystąpi błąd również procedura main() otrzyma ten sam sygnał o błędzie (jeśli oczywiście main() ma również obsługę błędów.

Podsumowowanie

Tak więc już wiecie jak można w E "kontrolować" błędy, już tylko od Was zależy, który sposób wybierzecie. Każdy sposób jest dobry na swój sposób, jeden jest przydatny przy tym programie, a inny w innym...



Dziś poznaliśmy
ENUM - deklaruj stałe "rosnące"
RAISE - wywołaj błąd jeśli ...
HANDLE - dodaj do funkcji "obsługę błędów"
EXCEPT - wydzielona część procedury odpowiedzialna za obsługę błędów
EXCEPT DO - część procedury odpowiedzialna za obsługę błędów
Raise(wyjatek) - wywołaj błąd o numerze
Throw(wyjatek,info) - Raise() z "komentarzem"
ReThrow() - wywołaj ten sam błąd w wyższej procedurze


 głosów: 1   
dodaj komentarz
Na stronie www.PPA.pl, podobnie jak na wielu innych stronach internetowych, wykorzystywane są tzw. cookies (ciasteczka). Służą ona m.in. do tego, aby zalogować się na swoje konto, czy brać udział w ankietach. Ze względu na nowe regulacje prawne jesteśmy zobowiązani do poinformowania Cię o tym w wyraźniejszy niż dotychczas sposób. Dalsze korzystanie z naszej strony bez zmiany ustawień przeglądarki internetowej będzie oznaczać, że zgadzasz się na ich wykorzystywanie.
OK, rozumiem