Posts from 2009

volatile?…

2009-02-23 13:18

Modyfikator volatile nie należy do często używanych elementów języka C(++). Jeśli więc gdzieś się go napotka, ma się sporą szansę nie odgadnąć od razu, dlaczego został użyty. Można też samemu przeoczyć sytuacje, gdy należałoby z niego korzystać.
Teoretycznie to słowo kluczowe nie powinno w ogóle istnieć; jest ono “techniczną” częścią języka, związaną ze sposobem jego kompilacji – trochę tak, jak klauzula checked z C# jest ściśle związana z metodami wykonywania obliczeń przez (ko)procesor. Wiemy jednak doskonale, że C++ wysokim poziomem abstrakcji nie grzeszy (już o C nie wspominając), więc obecność takich elementów nie powinna specjalnie dziwić.

volatile jako modyfikator zastosowany do (typu) zmiennej sprawia, że będzie ona traktowana tak, jakby jej zawartość mogła się w każdej chwili zmienić. Inaczej mówiąc, kompilator nie powinien nigdy czynić jakichkolwiek założeń co do wartości takiej zmiennej, bo jej wartość może być w dowolnym momencie przez coś zmodyfikowana.
Tak to wygląda od strony specyfikacji. W praktyce volatile stosuje się głównie w dwóch przypadkach:

  • Po pierwsze, używamy tego słowa jeśli rzeczywiście wartość naszej zmiennej może być zmodyfikowana przez jakiś ‘czynnik zewnętrzny’. Na niektórych maszynach tak na przykład działają urządzenia wejściowe: akcja w rodzaju wciśnięcia klawisza przez użytkownika powoduje zmianę zawartości jakiejś komórki pamięci. Chcąc na nią zareagować, należy więc cały czas monitorować to miejsce, bo może ono zmienić swoją zawartość zupełnie niespodziewanie. Oczywiście teraz takich rozwiązań się już nie stosuje, bo urządzenia wejściowe generują po prostu przerwania (interrupts), które są potem przerabiane przez system operacyjny na odpowiednie zdarzenia.
    Z globalnymi zmiennymi volatile można jednak często się spotkać w programach uniksowych, które funkcjonują w oparciu o sygnały. Typowym przykładem są serwery sieciowe, działające w pętli aż do momentu, gdy im się przerwie:

    1. while (!interrupted) do_work();

    Zmienna z warunku jest wtedy deklarowana jako:

    1. volatile sig_atomic_t interrupted = 0;

    zaś w procedurze obsługi sygnału – która może być wywołana w dowolnym momencie w odpowiedzi na przyjście żądanego sygnału (zwykle SIGINT) – jest ona ustawiana na 1. Gdyby nie była volatile, kompilator uznałby, że zawsze ma ona wartość zerową i “zoptymalizował” powyższą pętlę, zamieniając ją na nieskończoną.

  • Po drugie, z volatile korzysta się wtedy, chcemy w sposób zamierzony zapobiec pewnym optymalizacjom kodu. Zwykle chodzi tutaj o jakieś testowanie szybkości pewnych operacji, np. arytmetycznych. Powinno się wtedy wykonywać je na zmiennych opatrzonych modyfikatorem volatile, żeby mieć pewność, że wszystkie działania się wykonają i żadne nie zostanie wyeliminowane w procesie kompilacji. Można oczywiście wątpić w zdolności optymalizacyjne kompilatora, ale jest całkiem możliwe, że – zgodnie ze sprawdzonym po wielokroć prawem Murphy’ego – włączą się one właśnie tam, gdzie akurat byśmy ich sobie nie życzyli :)

Jeśli więc któryś z tych dwóch powyższych przypadków pasuje do naszej sytuacji, zapewne powinniśmy posłużyć się słowem kluczowym volatile.

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

Find & Replace i wyrażenia regularne

2009-02-15 11:48

Przy wyszukiwaniu czegoś w kodzie czasami przydają się wyrażenia regularne. Pozwalają one na znajdowanie nie tyle dokładnie określonych ciągów znaków, ale ciągów pasujących do określonego wzorca. Przy odrobinie wprawy można na przykład wyszukać wszystkie deklaracje zmiennych danego typu, wszystkie wywołania funkcji przeciążonej wyłącznie w wersji z trzema parametrami, i tak dalej.

Zwykle takich rzeczy szukamy jednak nie dla samego znajdowania, lecz po to, aby je potem zmodyfikować. Czasem można to zrobić ręcznie, ale jeśli oznacza to konieczność wykonania tych samych poprawek kilkanaście czy kilkadziesiąt razy, to szybko może nam to się znudzić. Pamiętajmy zresztą, że każdy programista dysponuje tylko skończonym czasem przeznaczonym na kodowanie :)
Wtedy może przydać się zastosowanie wyrażeń regularnych nie tylko do wyszukiwania (find), ale też do zastępowania (replace). Każde porządne IDE powinno bowiem oferować możliwość dopasowywania podwyrażeń (subexpressions lub tagged expressions) w znajdowanym ciągu. Nie inaczej jest w Visual Studio.

Załóżmy przykładowo, że piszemy w C++ i mamy klasę działającą jak abstrakcyjny interfejs (wszystkie metody czysto wirtualne) i chcemy go zaimplementować. Kopiujemy więc całą definicję klasy i chcielibyśmy teraz zmienić deklaracje jej metod: ze wszystkich usunąć słówko ‘virtual‘ i frazę ‘= 0‘.
Jak to zrobić? Dość łatwo skonstruować wyrażenie regularne, które wyszuka nam wszystkie deklaracje:

  1. virtual .* = 0;

Co jednak z polem Replace with? Tam chcielibyśmy wstawić wszystkie te znaki, które dopasują się do wyrażenia .*. W tym celu powinniśmy wpierw zmienić tę frazę na tagged expression, otaczając ją nawiasami klamrowymi:

  1. virtual {.*} = 0;

Teraz stało się ono podwyrażeniem i możemy odwołać się do dopasowanych do niego znaków poprzez sekwencję \1. Zatem jako wyrażenie docelowe wstawimy po prostu:

  1. \1;

Jak nietrudno się domyślić, podwyrażeń może być więcej; możemy wtedy odwoływać się do nich za pomocą kolejnych numerków: \2, \3, itd. (są one numerowane oczywiście od lewej). Dodatkowo symbol \0 odpowiada za cały znaleziony ciąg.
Widać więc, że przy użyciu tego mechanizmu można automatycznie dokonywać zmian, które bez niego zajęłyby nam dużo czasu i były dość żmudne.

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

Przemożna chęć regulacji

2009-02-01 17:53

Przedwczoraj na forum Warsztatu rozpoczęto dyskusję na temat gier zrobionych w tzw. klikach, które podobno coraz częściej pojawiają się wśród projektów zamieszczanych w serwisie gamedev.pl. Wątek zdążył szybko urosnąć w kilkadziesiąt postów, w których jedni postulują zwalczanie tego zjawiska, a drudzy (nieśmiało) proponują włączenie klikowców do warsztatowego mainstreamu.

Niejako automatycznie powstał więc dylemat: “Co z tym zrobić?”, na rozwiązanie którego daje się te właśnie dwa wyjścia. Którekolwiek z nich się wybierze, najważniejsze jest to, że coś ma być zrobione. Koniecznie przecież trzeba coś z tym zrobić, jakoś to ogarnąć, opanować, zorganizować, uregulować…
A ja zadaję wtedy proste pytanie: po co? Dlaczego niby należy każde zjawisko od razu łapać w regulamin, FAQ czy inny rodzaj mniej lub bardziej formalnych zasad? Od kiedy to w naszym narodzie – mającym bogatą, ponaddwustuletnią tradycję omijania wszelkich nakazów i zakazów – nagle zrodziła się taka wiara w nieuchronną konieczność odgórnej regulacji wszystkiego?…

Wydaje mi się to cokolwiek dziwaczne.

Tags:
Author: Xion, posted under Thoughts » 11 comments

Magister papierkologii stosowanej

2009-01-29 13:56

Studentem jest się w okresach półrocznych. Jak bowiem powszechnie wiadomo, co pół roku należy zrobić coś, aby owym studentem nadal pozostać. Zazwyczaj jest to znany i powszechnie lubiany obowiązek przebrnięcia przez tzw. sesję. Bywa jednak, że jest on zastępowany przez coś zupełnie innego.

Właśnie coś takiego zdarzyło mi się w tym semestrze. Ominęła mnie tym razem przyjemność (;D) zdawania kilku egzaminów, gdyż została ona zamieniona na misję tworzenia, kolekcjonowania i składania różnych – nazwijmy to – dokumentów. I wcale nie było to takie proste, bo mówimy tutaj o kilkudziesięciostronnicowej pracy w pięciu egzemplarzach, podobnej ilości płyt DVD, paru podaniach, zestawieniach ocen, że o pracowicie wypełnianym indeksie nie wspomnę.

Krótko mówiąc, nie tak łatwo jest zostać inżynierem – trzeba wpierw wykazać się w paru dziedzinach, z papierkologią na czele :)


Author: Xion, posted under Studies » 6 comments

Nawet SQL ma pętle

2009-01-12 12:35

Stwierdzenie ‘programować w HTML’ jest rzecz jasna nadużyciem, ale istnieją przecież inne języki, dla których określenie ‘programistyczne’ (lub jego brak) nie jest wcale takie oczywiste. Weźmy choćby SQL, teoretycznie pretendujący do miana języków deklaratywnych. Charakteryzują się one tym, że pisząc w nich określamy tylko to, co ma zostać zrobione – nie zaś jak. Na pierwszy rzut oka ma to sens: w końcu pisząc proste lub nawet całkiem skomplikowane zapytanie SELECT w ogóle nas nie interesuje to, przy pomocy jakich struktur danych zostanie ono wykonane i jaka pętla będzie się za nim kryć.
Bo przecież SQL nie ma pętli, prawda? :)

Ano właśnie nieprawda. Ponadto czasami są one jedynym wyjściem, jeśli mamy do czynienia z nieco bardziej skomplikowanymi danymi – jak choćby z jakąś hierarchią drzewiastą. Zwykle zapisuje się ją w relacyjnej bazie danych tak, że każdy element zawiera informacje o identyfikatorze elementu do niego nadrzędnego. Jest to całkowicie wystarczającą informacją do odtworzenia ich hierarchii.
Do takiego drzewka łatwo dodawać nowego elementy, ale ich usuwanie może być już problemem. Wyrzucenie jednej pozycji powinno bowiem oznaczać odcięcie całego poddrzewa – zwłaszcza że na pozostawienie osieroconych potomków często nie pozwoli sam silnik bazy danych, jeśli sprawdza poprawność relacji. Musimy zatem usuwać od dołu, a następnie przesuwać się w górę… Tylko jak niby zapisać to w postaci zapytania SQL?
Okazuje się, że nic prostszego. No, a przynajmniej okazuje się, że da się to zrobić:

  1. CREATE PROCEDURE DeleteItem
  2.     @ID int
  3. AS
  4. BEGIN
  5.     DECLARE @cur CURSOR
  6.     DECLARE @ChildID int
  7.    
  8.     SET @cur = CURSOR FOR (SELECT ID FROM Items WHERE ParentID = @ID)
  9.     OPEN @cur
  10.     FETCH NEXT FROM @cur INTO @ChildID
  11.     WHILE @@FETCH_STATUS = 0
  12.     BEGIN
  13.         EXECUTE DeleteItem @ChildID
  14.         FETCH NEXT FROM @cur INTO @ChildID
  15.     END
  16.    
  17.     DELETE FROM Items WHERE ID = @ID
  18. END

Nawet bez specjalnej znajomości składni można się domyślić, co tutaj jest wykonywane. Oto używamy kursora (coś w stylu iteratora), żeby najpierw usunąć elementy podrzędne do tego, który zamierzamy wykasować. W tym celu dla każdego z nich wywołujemy po prostu tę samą procedurę. Na koniec dokonujemy usunięcia pierwotnego elementu, który teraz na pewno jest już liściem (nie ma żadnych potomków), więc może być wyrzucony bez przeszkód.

Całkiem proste, czyż nie? Można powiedzieć, że w każdym języku programowania algorytm ten wyglądałby podobnie… Sęk w tym, że tu właśnie tkwi problem. Bo skoro w rzekomo deklaratywnym języku SQL można (a w tej sytuacji nawet trzeba) używać takich narzędzi jak pętle czy rekurencja, to przecież nie różni się on wtedy niczym od “normalnych” języków programowania. Jeśli całą operację trzeba zakodować krok po kroku, to nie mamy już żadnej korzyści z filozofii polegającej na określaniu ‘co’, a nie ‘jak’.
Może więc znaczy to, że inaczej programować po prostu się nie da? :)

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

Rok++ i co z tego wynika

2009-01-03 23:39

Jak trudno było nie zauważyć, przedwczoraj rozpoczął się kolejny rok kalendarzowy. Jeśli patrzeć na sam jego numer, to nie zapowiada się on nadzwyczajnie. 2009 nie jest bowiem żadną okrągła liczbą – i to nie tylko w naszym powszechnie używanym systemie dziesiętnym, ale też i w innych: szesnastkowym (7D9), czy ósemkowym (3731). Nawet wersja binarna (11111011001) nie prezentuje żadnego szczególnego ułożenia cyfr. A dodatkowo 2009 nie jest “nawet” liczbą pierwszą (rozkłada się na 7 * 7 * 41).
Jednym słowem, jest to liczba zupełnie nieciekawa, więc opatrzony nią rok też pewnie ma duże szanse być absolutnie przeciętny…

Jednak jest coś, co może to zmienić. Coś co musi wydarzyć się w tym właśnie roku. Dokładnie tak, musi – gdyż w przeciwnym wypadku nie wydarzy się wcale. Jest to przy tym coś wyjątkowo pożądanego i bardzo długo oczekiwanego…
Cóż to takiego? Ano oczywiście opublikowanie standardu C++0x :) To przecież ostatnia chwila, aby mógł on wystąpić pod tą nazwą. Zważywszy, że kiedyś oczekiwano, iż ‘x’ zostanie zastąpione raczej przez ‘2’ lub ‘3’, można powiedzieć z całą mocą: najwyższy czas :D


Author: Xion, posted under Life, Programming » 8 comments
 


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