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.
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.
Po uruchomieniu systemu (setpatch musi być uruchomiony!) z okienka cli wpisujemy:
MuForce stdio
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!).
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ć!
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
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.
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 |