Monthly archive for July, 2008

Asercje, wyjątki i inne błędy

2008-07-16 17:09

W każdym programie większym niż Hello World istnieje możliwość wystąpienia błędów w czasie działania. Dotyczy to zwłaszcza takich, które nie są zależne od programisty piszącego kod aplikacji, lecz na przykład od danych zewnętrznych pochodzących od użytkownika czy z plików.
W zależności od typu i stopnia dolegliwości błędy mogą być obsługiwane na różne sposoby. Dość często nie jest wcale łatwo zdecydować się na któryś z nich. Dlatego należy znać typowe metody sygnalizowania błędów i przynajmniej ogólne zasady opisujące sytuacje, w których każda z tych metod jest najwłaściwsza.

Sam przez bardzo długi czas miałem na przykład pewne wątpliwości co do przydatności asercji jako mechanizmu powiadamiania o błędach. W szczególności, nie potrafiłem odróżnić sytuacji, w których właściwsze jest korzystanie właśnie z asercji niż chociażby z wyjątków. Ostatnio aczkolwiek całkiem przypadkowo nadrobiłem te niechlubne zaległości :)
Okazuje się bowiem, że różnica między asercją a wyjątkiem jest znaczna. Asercje służą do sprawdzania pewnych warunków, które uznajemy za obiektywnie prawdziwe. Są to po prostu założenia, które muszą być spełnione w każdym okolicznościach, gdyż warunkują poprawność kodu. Pisanie asercji jest więc częściowo sprawdzaniem samego siebie: dzięki nim łatwiej wykryjemy błędy programistyczne we wczesnej fazie powstawania kodu (czyniąc naturalnie optymistyczne założenie, że same asercje są w porządku :]).

Wniosek z tego taki, że w dobrze działającej aplikacji asercje zawsze powinny być spełnione i nigdy nie przerywać działania programu. W przeciwieństwie do nich wyjątki (exceptions) mogą pojawiać się w zupełnie poprawnym kodzie, bo dotyczą rzeczy, na które program nie ma wpływu. Jednocześnie jednak musi on przewidywać ich ewentualne wystąpienie i posiadać odpowiedni kod ich obsługi. W przeciwnym razie konsekwencje bywają nieprzyjemne.
Dlatego wyjątki (i podobne do nich mechanizmy, jak np. symbianowe leaves) służą do powiadamiania o sytuacjach mających potencjalnie poważne konsekwencje. Dla pozostałych należy stosować mniej radykalne metody informowania o błędach. Najpopularniejszym jest oczywiście wartość zwracana przez funkcję, w której wystąpił błąd (ewentualnie w połączeniu z czymś takim jak errno lub GetLastError w Windows API). Kluczową cechą tego sposobu powiadamiania jest to, że należy specjalnie zadbać o sprawdzenie, czy błąd wystąpił; inaczej zostanie on zignorowany.

Tak w skrócie przedstawia się sprawa od strony teoretycznej. W praktyce odróżnienie sytuacji podpadającej pod asercję od tej, gdzie uprawniony jest wyjątek – a już zwłaszcza wyjątku od zwykłego “return -1;” – bywa trudne, a często subiektywne. Zwykle jednak daje się stosować przynajmniej jedną zasadę: przynajmniej w fazie testów lepiej jest przesadzać i reagować nazbyt histerycznie niż przeoczyć lub zignorować jakiś ważny szczegół.

Tags: , ,
Author: Xion, posted under Programming » 1 comment

using nieco inaczej

2008-07-14 18:42

W C++ istnieje słowo kluczowe using, z którym zetknął się chyba każdy. Zwykle dotyczy to nieśmiertelnej linijki:

  1. using namespace std;

Dlatego też słowo to kojarzy się przede wszystkim z przestrzeniami nazw (namespaces), a najczęściej tylko i wyłącznie z nimi.

Jednak using ma też swoje zastosowanie – i to zapewne znacznie ciekawsze – przy definiowaniu klas. Formalnie rzecz ujmując, słówko to pozwala wprowadzić składową pochodzącą z klasy bazowej (metodę lub pole) do zasięgu klasy pochodnej. Niezbyt to ekscytujące na pierwszy rzut oka, ale faktem jest, że dzięki skorzystaniu z using możemy dokonać przynajmniej jednej koniecznej czasami operacji. Możliwa jest mianowicie zmiana kontroli dostępu do danego składnika klasy, czyli określenie, czy ma on być prywatny czy może publiczny.
Wyobraźmy sobie na przykład, że w klasie bazowej mamy jakieś funkcje niepubliczne, które chcemy udostępnić na zewnątrz w klasie pochodnej:

  1. class Base { protected: void Fun(); };
  2.  
  3. class Foo : public Base
  4. {
  5.     public:
  6.         using Base::Fun; // teraz metoda Fun() jest publiczna
  7. };

Sytuacja nie jest wcale taka hipotetyczna. Sam miałem ostatnio taki przypadek, gdy najwygodniej było po prostu wbudować w klasę bazową pewną ukrytą funkcjonalność, która była używana przez wiele z jej klas pochodnych do ich wewnętrznych celów. Jednak niektóre z tych klas musiały tę “bazową” funkcjonalność udostępnić na zewnątrz jako publiczną. I tu przydało się użycie using w sposób zaprezentowany wyżej. (Dla zainteresowanych wspomnę, że opisywana sytuacja praktycznie wystąpiła u mnie w kodzie systemu GUI :]).

Tags: ,
Author: Xion, posted under Programming » 9 comments

Triki z PowerShellem #7 – Zliczanie wierszy

2008-07-12 10:10

Gdy zawzięcie i wytrwale tworzymy jakiś koderski projekt, chciałoby się od czasu do czasu zmierzyć, ile to już pracy zdołaliśmy w nim wykonać. Pewnym sposobem na to jest policzenie ilości wszystkich linii kodu, które udało nam się już napisać.
Istnieją do tego nawet oddzielne aplikacje, niekiedy z rozbudowanym interfejsem graficznym. Jednak takie proste zadanie można by przecież zrealizować przy pomocy równie prostego narzędzia. Jakiego? Oczywiście – niewielkiego skryptu PowerShella :) Chociażby takiego jak ten:

  1. # lines.ps1
  2. # Skrypt liczący wiersze kodu w plikach podanego katalogu
  3.  
  4. # Parametr: ścieżka do katalogu z plikami
  5. param ([string]$path = ".")
  6.  
  7. # Stałe
  8. $EXTENSIONS = @("cpp", "h", "hpp")  # Rozszerzenia interesujących plików
  9.  
  10. # Zmienne
  11. $filesCount = 0
  12. $totalLinesCount = 0
  13. $emptyLinesCount = 0
  14.  
  15. # Przeszukujemy podany katalog rekurencyjnie (wszerz)
  16. $dirQueue = New-Object Collections.Queue
  17. $dirQueue.Enqueue($path)
  18. while ($dirQueue.Count -gt 0)
  19. {
  20.     # Dodajemy podkatalogi do przeszukania
  21.     $dir = [string]$dirQueue.Dequeue()
  22.     foreach ($subDir in [IO.Directory]::GetDirectories($dir))
  23.         { $dirQueue.Enqueue($subDir) }
  24.        
  25.     # Pobieramy wszystkie pliki w aktualnym katalogu pasujące do filtra
  26.     $files = New-Object Collections.ArrayList
  27.     foreach ($ext in $EXTENSIONS)
  28.         { $files.AddRange([IO.Directory]::GetFiles($dir, "*." + $ext)) }
  29.    
  30.     # Liczymy w nich wiersze
  31.     foreach ($file in $files)
  32.     {
  33.         ++$filesCount
  34.         foreach ($line in [IO.File]::ReadAllLines($file))
  35.         {
  36.             ++$totalLinesCount
  37.             if ($line.Trim().Length -eq 0) { ++$emptyLinesCount }
  38.         }
  39.     }
  40. }
  41.  
  42. # Wyświetlamy statystyki
  43. "Files: " + $filesCount | Out-Host
  44. "Total lines: " + $totalLinesCount | Out-Host
  45. "Non-empty lines: " + ($totalLinesCount - $emptyLinesCount) | Out-Host

Podajemy mu tylko ścieżkę do katalogu z naszym kodem, a on przeglądnie wszystkie pliki z ustalonymi rozszerzeniami i wyświetli statystykę z liczbą linii: wszystkich oraz niepustych. Cała ta czynność ta nie zajmuje w kodzie specjalnie dużo miejsca, gdyż najważniejsze jej elementy: pobranie podkatalogów, plików w katalogu i wierszy tekstu w pliku dają się zrobić pojedynczymi wywołaniami funkcji (odpowiednio: GetDirectories i GetFiles z System.IO.Directory oraz System.IO.File.ReadAllLines). To aczkolwiek zasługa nie samego PowerShella, tylko niezawodnej platformy .NET ;)

Skrypt ten można rzecz jasna znacznie usprawnić, pozwalając chociażby na definiowanie filtra nazw plików z kodem w jego wywołaniu czy też umożliwiając wykluczanie niektórych katalogów (np. bin, Debug, Release) w celu przyspieszenia zliczania. Jeśli ktoś miałby na to ochotę, może się dodawaniem takich feature‘ów pobawić :)

Tags:
Author: Xion, posted under Applications, Programming » Comments Off on Triki z PowerShellem #7 – Zliczanie wierszy

Podwójna okazja

2008-07-10 23:35

Dawno, dawno temu… A właściwie to prawie dokładnie rok temu z bliżej niewyjaśnionych do dzisiaj przyczyn naszła mnie ochota, aby reaktywować swoją stronę domową w postaci tego oto (dev)bloga. Od tego czasu zdążyło zmienić się sporo, ale wbrew przeciwnościom losu (uosabianym głównie przez lenistwo, które to, jak wiadomo, ma wśród wszystkich zadań zawsze najwyższy priorytet :]) blog ów nadal istnieje i nie wydaje się, żeby przynajmniej w najbliższej przyszłości miało się coś w tej kwestii zmienić. Bowiem podobno rok właśnie to taka magiczna granica, której przekroczenie udaje się tylko w mniej więcej połowie przypadków. Oczywiście to tylko statystyka (a według niej ja i mój pies mamy po trzy nogi), ale daje ona chociaż powody do umiarkowanego optymizmu – a ich nigdy dosyć.

Ten rok był też czasem “ucierania się” stylu oraz tematyki tego, co w regularnych odstępach czasu staram się tutaj publikować. Po drodze życie zweryfikowało rzecz jasna zbyt ambitne założenia i z devloga zrobiło się… no właśnie, powiedzmy, że ‘blog IT’ – żeby nie powiedzieć “nie-wiadomo-co” :) W zasadzie nie było to zresztą trudne do przewidzenia: aby systematycznie zdawać relacje z koderskiej pracy w sposób chociaż śladowo interesujący, trzeba by chyba dysponować dobą o długości co najmniej 50 godzin. Nie da się też ukryć, że formuła devloga jest też dość sztywna i zapewne dla niewielu czytelników byłaby ona na swój sposób ciekawa.
Z tych (i pewnie nie tylko z tych) powodów założona forma ewoluowała w kierunku czegoś nieco odmiennego, albo raczej trochę rozszerzonego względem pierwotnych planów. Zamiast opisywać tylko to, co ostatnio udało mi się zakodować, zacząłem też włączać różnego rodzaju programistyczne ciekawostki, dziwnostki, sugestie, porównania, krótkie omówienia, wskazówki, przemyślenia i inne tego typu “kawałki informacji”, opatrzone zwykle moją subiektywną opinią lub komentarzem. Trudno powiedzieć, co tak naprawdę je łączy – naturalnie poza technikaliami w rodzaju często przewijających się języków lub platform programistycznych. Publikując je spodziewam się jednak, że ktoś może na nie przypadkiem lub celowo natrafić i stwierdzić: “O, to całkiem interesujące…” albo “Hej, tego wcześniej nie wiedziałem!”. Być może w ten sposób zainteresuje się też jakimś tematem i zapragnie rozszerzyć swoją wiedzę w danym kierunku, co rzecz jasna przyniesie pożytek nie tylko jemu (lub jej).

Wychodzi więc na to, że mamy do czynienia z wysoce subiektywnym, nieuporządkowanym, a także stronniczym, wybiórczym i w gruncie rzeczy nie do końca godnym zaufania źródłem informacji na tematy okołokoderskie i okołoinformatyczne. Cóż to więc za dziwny twór?… Zapewne z braku lepszych określeń pozostaje posługiwanie się bardzo ogólnym słowem-wytrychem: blog.
Muszę tutaj zaznaczyć, że przyznanie się do prowadzenia czegoś, co tak właśnie można nazwać, to dla mnie coś wciąż jeszcze lekko kłopotliwego. Dlaczego? Ano dlatego, że termin ‘blog’ nadal kojarzy mi się trochę ze złotą erą różowiasto-pokemoniastych wynurzeń znudzonych dwunastolatek :) Wiem, wiem – teraz to już przeszłość lub margines, a blogowanie stało się sprawą poważną, powszechną i różnorodną. Przepastna blogosfera (paskudny neologizm, swoją drogą) powinna zatem bez problemu pomieścić także i to, czym w regularnych odstępach czasu raczę wszystkich, którzy pojawiają się na tej stronie.

A zatem minął pierwszy rok istnienia tego bloga, zaś jednocześnie liczba zamieszczonych na nim notek wraz z niniejszą osiągnęła dokładnie 256 – co jest przecież porządną i bardzo okrągłą liczbą :) Zbieżności obu tych zdarzeń komentować raczej nie będę (;P), a zamiast tego pozwolę sobie wyrazić nieśmiałe życzenie – od siebie dla siebie i wszystkich czytających – abyśmy doczekali się kolejnych rocznic i jeszcze co najmniej kilku podobnych okazji. Ze swej strony mogę zapewnić, że dołożę wszelkich możliwych starań, aby tak właśnie się stało.

Tags:
Author: Xion, posted under Thoughts, Website » 15 comments

Samowskaźnik i usuwanie siebie

2008-07-10 17:35

Bez używania mechanizmu odśmiecania istnieje zawsze ryzyko, że skończymy z odwołaniem do obiektu, który został już zniszczony. (Przy używaniu GC też istnieje taka możliwość, ale musielibyśmy niejako sami się postarać, aby wystąpiła). Dlatego zaleca się na przykład, by każdemu wywołaniu delete towarzyszyło zerowanie wskaźnika:

  1. delete p; p = NULL;

dzięki czemu można ochronić się przez problemem wiszących wskaźników (dangling pointers).

Można? No cóż, nie do końca. Istnieje jeszcze możliwość, że obiekt zniszczy się sam, używając po prostu instrukcji delete this (jest to jak najbardziej legalne). A wtedy wszelkie odnoszące się do niego odwołania będą już nieważne.
Chyba że… obiekt sam je wyzeruje. Istnieje mianowicie śmieszny trik, polegający na przekazaniu do niego w konstruktorze wskaźnika na siebie (self-pointer):

  1. Foo* pFoo = new Foo(/* parametry konstruktora */, &pFoo);

A mówiąc dokładniej: wskaźnika na ów wskaźnik (zaczyna się robić ciekawie, prawda? ;]). Musi być on oczywiście zapamiętany, a cała sztuczka polega na tym, że w destruktorze obiektu następuje jego zerowanie:

  1. class Foo
  2. {
  3.     public:
  4.         Foo(/* ... */, Foo** ppSelf = NULL) : m_ppSelf(ppSelf) { /* ... */ }
  5.         ~Foo() { if (ppSelf) *ppSelf = NULL; }
  6.     private:
  7.         Foo* m_ppSelf;
  8. };

I teraz obiekt może radośnie się usuwać kiedy tylko zechce. A tak swoją drogą, jeśli rzeczywiście może on zniknąć w każdej chwili, to może wypadałoby też, aby sprawdzał, czy przypadkiem… już nie istnieje:

  1. void Foo::SomeMethod(/* ... */)
  2. {
  3.     if (!this) return;
  4.     // ...
  5. }

Pokręcone i przekombinowane? Powiedziałbym wręcz, że szalone :) Ale do takich sztuczek trzeba się uciekać, jeśli nie korzystamy z mechanizmów odśmiecania pamięci i nie potrafimy jednoznacznie określić czasu życia obiektów i ich wzajemnej przynależności.
Albo po prostu: gdy nie mamy lepszych pomysłów. Bo przecież w ostateczności lepiej chyba sprawdzi się zwyczajna flaga logiczna z metodą typu IsAlive, jeśli obiekt rzeczywiście może popełnić nagłe samobójstwo. Najlepiej jednak, żeby takich emo-obiektów było jak najmniej ;)

Tags:
Author: Xion, posted under Programming » 2 comments

Wskaźniki i referencje jako parametry

2008-07-09 19:07

Kiedy w C++ chcemy przekazać do funkcji odwołanie do obiektu (zezwalające na jego modyfikację wewnątrz funkcji), mamy do wyboru dwie metody. Ta alternatywa to posłużenie się wskaźnikiem albo referencją:

  1. void Function(Foo* pFoo);
  2. void Function(Foo& foo)

Czy istnieje uniwersalna odpowiedź na to, którą wybrać? Chyba nie. Jeśli chodzi o wskaźnik, to za jego użyciem może przemawiać:

  • Możliwość przekazania odwołania pustego, jeśli parametr nie jest obowiązkowy. Obejmuje to oczywiście zdefiniowanie NULL jako domyślnej wartości dla tego parametru w deklaracji funkcji. Nie jest to możliwe dla referencji (w C++).
  • Fakt, że w wywołaniu funkcji bardziej widoczne jest to, iż przekazany do niej za pośrednictwem wskaźnika obiekt może się zmienić. Jeśli na przykład obiekt ten jest zmienną lokalną, to konieczne jest posłużenie się operatorem &, który daje o tym jakąś widoczną wskazówkę (nie tak jasną jak ref/out w C#, ale zawsze). Nie jestem też wielkim fanem notacji węgierskiej, lecz w przypadku wskaźników stosowanie przedrostka p wydaje mi się akurat wskazane i w tym kontekście też zwiększa czytelność wywołania funkcji, wskazując, że przekazywany obiekt (alokowany na stercie) też może się zmienić.
    1. Foo foo; Foo* pFoo = new Foo();
    2. Function (&Foo); Function (pFoo); // funkcja może zmienić obiekt

Z kolei referencje mogą się popisać innymi zaletami:

  • Nie można do nich przekazać odwołania pustego. To może być zaletą, jeśli taka sytuacja jest niepożądana. Ponadto brak konieczności sprawdzania tego, czy referencja jest “pusta”, może lekko poprawić wydajność kodu generowanego przez kompilator.
  • Składnia użycia obiektu przekazywanego przez referencję zwykle bywa bardziej przejrzysta. Jest tak zwłaszcza wtedy, gdy używamy względem niego operatorów. Na przykład kolekcja dostępna przez wskaźnik musiałaby być indeksowana przez (*pArray)[i], zaś przez referencję po prostu jako array[i].

Widać więc, że jeśli kwestia odwołania pustego nie jest dla nas istotna, to decyzja może być trudna. Ale naturalnie jest tak tylko wtedy, gdy zechcemy się nad takimi sprawami zastanawiać ;]

Tags: ,
Author: Xion, posted under Programming » 6 comments

Kolorowanie semantyczne

2008-07-07 19:04

W dzisiejszych czasach niemal każdy edytor plików tekstowych nieco bardziej zaawansowany od windowsowego Notatnika oferuje funkcję kolorowania składni (syntax highlighting) przynajmniej kilku najpopularniejszych języków programowania. W przypadku całych IDE jest to oczywiście funkcjonalność absolutnie oczywista i niezbędna, jednak coraz więcej środowisk oferuje też coś więcej. Coś, co dla odróżnienia nazywam kolorowaniem semantycznym; nie jestem bowiem pewien, czy ten mechanizm ma jakąś ogólnie przyjętą nazwę.


Kodowanie semantyczne w Visual Studio z pluginem AssistX

O co w nim chodzi? Standardowe kolorowanie potrafi między innymi odróżnić słowa kluczowe od identyfikatorów, wyróżniając zwykle te pierwsze innym stylem czcionki, zaś te drugie pozostawiając bez zmian. Wszystkie identyfikatory: nazwy zmiennych, funkcji, typów, stałych, szablonów, itp., są więc formatowane tak samo i wyglądają identycznie.
Trik kolorowania semantycznego polega właśnie na rozróżnieniu tych wszystkich kategorii identyfikatorów. Oczywiście jest to możliwe tylko wtedy, gdy IDE dysponuje bazą danych o całej strukturze projektu. Ale taka baza jest przecież coraz częściej tworzona, tyle że w innym celu: zapewnienia automatycznego uzupełniania poprzez mechanizmy w rodzaju IntelliSense w Visual Studio. Na szczęście ktoś kreatywny wpadł na pomysł, że można ją wykorzystać także w inny sposób – i chwała mu za to :)

Kolorowanie semantyczne ma przynajmniej dwie zalety. Ułatwia ono zorientowanie się w przeglądanym kodzie, zwłaszcza takim który widzimy po raz pierwszy. Ponadto zaś pozwala ono na wczesne wykrycie prostych acz uciążliwych błędów w rodzaju literówek. Sprawiają one bowiem, że dany identyfikator – jako nieznany – jest kolorowany inaczej, co pozwala na łatwiejsze dostrzeżenie pomyłki.
A wady? Jak każdy mechanizm działający w tle, kolorowanie semantyczne zjada trochę zasobów systemowych – m.in. dla niego opłaca się mieć w procesorze więcej niż jeden rdzeń. A poza tym można zwyczajnie nie lubić pstrokatego kodu i preferować bardziej jednolitą kolorystykę… O gustach się przecież nie dyskutuje, a teoretycznie kodować można i w Notatniku :)

Tags:
Author: Xion, posted under Applications, Programming » 8 comments
 


© 2017 Karol Kuczmarski "Xion". Layout by Urszulka. Powered by WordPress with QuickLaTeX.com.