Niekiedy przydatna okazuje się możliwość definiowania funkcji widocznych wyłącznie w obrębie innej funkcji – czyli procedur zagnieżdżonych. Używa się tego najczęściej do krótkich, pomocniczych funkcji; oto najprostszy przykład w języku, który to umożliwia (Delphi):
[delphi]// oblicza odległość między dwoma punktami
function Distance(p1, p2 : TPoint) : Double;
function Square(X : Double) : Double;
begin
Result := X * X;
end;
begin
Result := Sqrt(Square(p1.X – p2.X) + Square(p1.Y – p2.Y));
end;[/delphi]
Takim językiem nie jest jednak C++, więc pewnie istnieje ku temu jakiś bardzo ważny powód… Bo czyż nie jest tak z każdą przydatną funkcjonalnością, która została w nim pominięta? ;D
Ale żarty na bok. W C++ można – przy odrobinie pomysłowości – osiągnąć dość podobny efekt:
Nie tworzymy tutaj funkcji, tylko odpowiednio nazwany obiekt anonimowej klasy, któremu przeciążamy operator nawiasów (wywołania funkcji). Efekt na oko jest taki sam: mamy pewną nazwę (Square
), którą możemy użyć jak funkcję, lecz nie jest ona widoczna poza macierzystą procedurą (czyli Distance
). Nasz obiekt deklarujemy też jako statyczny, dzięki koszt jego tworzenia i niszczenia ponosimy tylko raz, a ponadto nie zajmuje on miejsca na stosie (gdyż w innym przypadku z pewnością zająłby je; standard C++ mówi, że nawet obiekty klas bez zadeklarowanych pól nie mają rozmiaru 0).
Ta sztuczka jest jednak daleka od rzeczywistych funkcji zagnieżdżonych. W “prawdziwym” wydaniu mają one bowiem jedną ważną właściwość: posiadają dostęp do zmiennych lokalnych funkcji, w których są zawarte. W naszym przykładzie “funkcja” Square
powinna więc móc odwołać się do (ewentualnych) zmiennych lokalnych z funkcji Distance
. A ponieważ Square
jest tutaj tak naprawdę obiektem jakiejś klasy, która nic nie wie o Distance
, nie jest to rzecz jasna możliwe.
Czy możliwe jest więc, aby pełnowymiarowe funkcje zagnieżdżone pojawiły się np. w przyszłych wersjach języka C++?… Tutaj odpowiedź jest prawie na pewno negatywna. Jest tak dlatego, że obecność takich funkcji ingeruje bardzo głęboko w sposób, w jaki kompilator postępuje ze wszystkimi funkcjami. Pośrednimi konsekwencjami dodania funkcji zagnieżdżonych byłyby: niemożność korzystania ze standardowej konwencji wywoływania cdecl oraz zmiana wewnętrznej struktury wszystkich wskaźników na funkcje (które nie mogłyby być już pojedynczymi adresami).
Dwa komputery i jedno łącze internetowe to pewien kłopot, jeśli chcemy mieć dostęp do sieci na wszystkich maszynach jednocześnie. Istnieje oczywiście możliwość mostkowania (nazywanego w Windows udostępnianiem połączenia), ale wada tego rozwiązania jest znaczna: maszyny stają się od siebie zależne i jedna nie będzie miała dostępu do sieci bez drugiej. Na dłuższą metę jest to raczej niewygodne.
Aż dziwne, że dojście do tego wniosku zajęło mi ładnych parę miesięcy ;) W każdym razie nadarzyła się dobra okazja, by sytuację tę zmienić, więc parę dni temu zaopatrzyłem się w proste urządzenie sieciowe zwane potocznie routerem (chociaż faktycznie chodzi o switch routujący).
Przy tej okazji zauważyłem kilka dość zaskakujących dla mnie faktów. Ogólnie wygląda bowiem na to, że tworzenie domowych sieci stało się już tak bardzo powszechne, że urządzenia takie jak to nabyte przeze mnie są obecnie bardzo tanie. Za nieco ponad sto złotych można już mieć pudełko z powodzeniem radzące sobie ze sterowaniem ruchem w niewielkiej sieci, niekoniecznie przewodowej.
Druga niespodzianka związana była z tym, co było dołączone do urządzenia. Bo co niby może znajdować się na płycie CD, którą to – jak każe instrukcja – należy odczytać jeszcze przed podłączeniem routera? Raczej wątpliwe, by były to sterowniki :) Zagadka wyjaśniła się po zalogowaniu do panelu administracyjnego routera: otóż jest to sprytny programik, który odczytuje ustawienia komputera podłączonego bezpośrednio do Internetu, by potem skopiować je na router. Tym sposobem powinniśmy być w stanie skonfigurować urządzenie nawet wtedy, gdy ‘przeglądarka’ i ‘Internet’ to dla nas jedno i to samo… cóż, przynajmniej w teorii ;)
Oczywiście nie sprawdzałem tego w praktyce, bo zdecydowanie wolę tradycyjną metodę konfiguracji. To znacznie fajniejsze – podobnie zresztą jak dalsze grzebanie w ustawieniach routera i obserwowanie, jak to pakiety zdążają we właściwych sobie kierunkach :D
Poprzednia notka o “rootkitach i innych nieszczęściach” zdołała utrzymać się długo, więc mogła stworzyć wrażenie, że cały czas źle się u mnie dzieje :) Nieprawda to oczywiście, więc wypadałoby w końcu coś na tę dezinformację poradzić.
Ostatnio cierpię na niewielki brak czasu spowodowany koniecznością dokładania nieco poważniejszych wysiłków co do pewnego dzieła, które docelowo ma zapewnić mi otrzymanie tytułu inżyniera informatyki. Z pewnych względów nie zdradzę, czym ono jest, ale pozwolę sobie wspomnieć o pewnych wnioskach, które przy okazji mi się nasunęły.
Parę miesięcy temu wypowiadałem się pochlebnie na temat testów jednostkowych, mając wtedy raczej niezbyt duże doświadczenie w ich stosowaniu. Dzisiaj jest już ono zdecydowanie większe i w związku z tym stwierdzić muszę, że… Nie, nie chcę powiedzieć, że wtedy myliłem się całkowicie ;P Chodzi raczej o równowagę zalet i wad.
To prawda, że unittesty dobrze sprawdzają się jako narzędzie znajdowania błędów i zapobiegania im. Do pewnego stopnia pomagają też w poprawianiu interfejsu tworzonych klas, tak aby ich użycie było wygodniejsze i bardziej intuicyjne. Te korzyści mają jednak swoją cenę, którą jest przede wszystkim czas poświęcony na pisanie owych testów – zwłaszcza w niektórych metodykach wytwarzania oprogramowania, które kładą na nie ogromny nacisk. Całkiem normalna jest bowiem sytuacja, że kod testowy jest dłuższy niż testowany (czasem nawet kilkakrotnie). Dodatkowo nie mamy przecież znikąd żadnej gwarancji, że ów testowy kod sam nie zawiera jakichś błędów… W sumie więc dochodzi nam sporo dodatkowej pracy w zamian za większą niezawodność tworzonego oprogramowania.
Drugi wniosek jest bardziej – że tak powiem – filozoficzny :) Otóż programowanie wedle zasady:
może i jest efektywne, ale na dłuższą metę zaczyna się robić nużące i trochę zniechęcające. Pomyślmy jak łatwo jest pisać kod “rozgrzebany”, którego nie planujemy od razu kompilować i poprawiać tych wszystkich pomyłek, których kompilator nam zasygnalizuje. Porównajmy to z pisaniem kodu, który nie tylko jak najczęściej kompilujemy, ale i regularnie przepuszczamy przez własnoręcznie napisane testy! Toż to straszne, tak samemu kręcić na siebie bat :) Gdzie tu koderska wolność artystyczna? :D
Ale nie ma rady, czasami trzeba zakasać rękawy i testować…
Jedną z rzeczy, którą musiałem wykonać w trakcie (tymczasowej) przesiadki na komputer stacjonarny, było dogłębne sprawdzenie go programem antywirusowym – co “z pewnych względów” nie było czynione przez dobrych kilka miesięcy :) Skan nie wykrył aczkolwiek w zasadzie nic bardziej niebezpiecznego niż typowe ‘śledzące ciasteczka’ (tracking cookie). Dopiero później zaczęło się robić ciekawie.
Zło (i to przez duże Z) zostało bowiem wkrótce wykryte przez skaner działający w tle, który zidentyfikował je heurystycznie jako rootkit i oprócz tej informacji nie powiedział zresztą nic więcej. Przyznam, że było to moje pierwsze zetknięcie z tego typu szkodnikami, więc tym bardziej energicznie zabrałem się do rozwiązywania problemu – zwłaszcza że ze wszystkich typów złośliwego oprogramowania to właśnie rootkity sa zdecydowanie najgroźniejsze.
Na szczęście ten okazał się dość znanym i nawet nieszczególnie niebezpiecznym drobnoustrojem, przenoszącym się – o dziwo – nie przez sieć, lecz przez dyski wymienne. Zapewne dlatego też moje kilkugodzinne starcie z przeciwnikiem zakończyło się (prawdopodobnie) sukcesem, zaś wnioski z tego są następujące:
Krótko mówiąc, wykrywanie i walka z tym rodzajem szkodliwego oprogramowania to coś znacznie poważniejszego niż wciśnięcie przycisku Scan i czekanie na wyniki :] Rezultaty też nigdy nie są pewne.
O reinstalacji systemu na komputerze stacjonarnym trzeba więc będzie pomyśleć jak najszybciej, a od wczoraj stało się to prostsze, gdyż mój laptop wrócił z naprawy. Okazało się, że konieczna była wymiana płyty głównej (!) – oryginalna prawdopodobnie miała fabryczną usterkę… No cóż, dobrą stroną jest to, że za tę skomplikowaną operację nie musiałem zapłacić ani grosza; niech żyją gwarancje :)