Posts from 2010

so’o lamji je jai cinri be sera’a la lojban.

2010-05-23 19:07

…czyli kilka kolejnych ciekawych rzeczy o lojbanie.

Trzecią notkę o lojbanie zacznę od przypomnienia części tego, o czym wspominałem w pierwszym wpisie. Mam tu na myśli postać “zdań” w lojbanie (bridi), składających się z predykatu (selbri) i określonej liczby argumentów (sumti). Przykładem może być:

mi sampla lo ceirdero

co bardzo dobrze pokazuje, że w lojbanie można mówić o wielu ciekawych rzeczach, jako że znaczenie tego bridi to… ‘Ja programuję shader‘ :) Użyty tu predykat sampla definiuje relację między programistą a pisanym przez niego programem/kodem i umożliwia również podanie trzeciego argumentu (docelowego przeznaczenia tego programu), który tutaj nie został wypełniony. Dwa pierwsze – mi i lo ceirdero – zostały jednak podane, wskakując, odpowiednio, na pierwsze i drugie miejsce predykatu sampla, zgodnie ze zdefiniowanym dla niego porządkiem argumentów, których nie da się dowolnie przestawiać bez zmiany znaczenia. Widać więc, że selbri w lojbanie przypominają funkcje z języków programowania ze wszystkimi parametrami zadeklarowanymi jako opcjonalne.


le tcati kabri
(Źródło obrazka)

Ta analogia jest jednak tylko trochę adekwatna, bo bridi może mieć znacznie bardziej skomplikowaną strukturę niż najbardziej nawet zagnieżdżone wywołanie funkcji. I tak pierwszą ciekawostką jest to, że argumenty (czyli sumti) możemy przestawiać niemal wedle uznania, zachowując to samo znaczenie wypowiedzi. Mogę więc powiedzieć:

lo ceirdero cu se sampla mi

uzyskując coś w rodzaju strony biernej – ‘Shader jest pisany przeze mnie’. Niech ów shader ma w założeniu wykonywać najbardziej typową dla shaderów czynność – czyli wyświetlać imbryczek :) Wiedząc o tym, mogę wypełnić pominięte wcześniej trzecie miejsce sampla:

mi sampla lo ceirdero lonu jarco tu’a le tcati kabri

Mając trzy argumenty mogę z kolei poprzestawiać je w nieco bardziej zakręcony sposób – na przykład tak:

fi lonu jarco tu’a le tcati kabri cu sampla fa mi fe lo ceirdero

Fizycznie żaden argument nie jest tu na swoim miejscu, ale znaczenie pozostaje to samo. Taki zabieg, jak widać, wymagał dodania znaczników fa, fe oraz fi. Przypomina to trochę tzw. argumenty słownikowe w niektórych wysokopoziomowych językach programowania, takich jak Python.

Od znaczników FA niedaleko już z kolei do tzw. etykiet sumti (sumti tcita, zwanych też “tagami modalnymi” – modal tags). Jest to mechanizm umożliwiający dodanie do bridi argumentów, które nie są przewidziane w “deklaracji” selbri. Przykładowo, w naszym predykacie sampla wypełnione są już wszystkie miejsca, ale to nie znaczy wcale, że nie możemy do niego nic dodać. Skoro mówimy o kodowaniu, to naturalne jest chociażby określenie języka, w którym piszemy. Na szczęście odpowiednie miejsce możemy sobie dodać właśnie przy pomocy pewnego sumti tcita – mianowicie bau:

mi sampla lo ceirdero bau la’oi .HLSL.
Piszę shader w języku HLSL.

Możliwych etykiet jest oczywiście mnóstwo. Część z nich wykazuje podobieństwo do przyimków, inne przypominają frazy określające położenie w czasie lub przestrzeni (‘zanim’, ‘po tym jak’, ‘po prawej’, ‘na górze’, itp.), a jeszcze inne opisują bardziej złożone relacje (‘z powodu’, ‘w celu’, ‘z punktu widzenia’, ‘używając’, …), które w zwykłych językach rzadko da się wyrazić jednym słowem.

Ogólny wniosek z tego jest taki, że chociaż bridi w lojbanie i funkcje w językach programowania są ideowo do siebie zbliżone, to te pierwsze wykazują znacznie większą elastyczność. Nie powinno być to zresztą specjalnie zaskakujące – w końcu mówimy o języku naturalnym dla ludzi (głównie), a nie o kodzie dla kompilatorów :)

Uwaga ogólna: ze względu na oczywisty brak miejsca nie mogę wyjaśniać każdej konstrukcji gramatycznej użytej w przykładach. Jeśli chcemy koniecznie poznać znaczenie i funkcje jakiegoś nieznanego słówka, najlepiej wpisać je w słowniku takim jak vlasisku. W tej notce takimi nowymi i pewnie nieznanymi słowami są np. cu, nu, tu’a i la’oi.

Tags: ,
Author: Xion, posted under Culture » Comments Off on so’o lamji je jai cinri be sera’a la lojban.

if (false) { }

2010-05-22 16:49

Jeśli widzieliśmy już w swoim wystarczająco dużą ilość kodu w C lub C++, to istnieje pewna szansa, że natrafiliśmy na konstrukcję znaną jaką do-while(false). Występuje ona w dwóch wersjach. W pierwszej jest to właściwie goto w przebraniu – dzięki otoczeniu jakiegoś fragmentu kodu taką “pętlą” sprawiamy, że instrukcja break będzie powodowała skok tuż za nią. To pozwala na “wyjście” z danego fragmentu kodu np. wtedy, gdy wystąpi w nim jakiś błąd – oczywiście przy założeniu, że w środku nie ma kolejnej pętli lub switcha ;)
O drugiej wersji pisałem kilkanaście miesięcy temu. Jej zastosowaniem są makra preprocesora zawierająca kilka instrukcji; zamknięcie ich w do-while(false) zamiast w zwykły blok kodu ({ }) gwarantuje nam brak błędów składniowych niezależnie od miejsca użycia takiego makra.

Widać więc, że pętla z zawsze fałszywym warunkiem ma – mimo wszystko – jakieś zastosowanie… A co z analogicznym ifem? Czy if(false) ma jakieś sensowne zastosowanie poza ukazywaniem, że osoba używająca takiej konstrukcji do wyłączania kodu z kompilacji zapomniała o istnieniu dyrektywy #if, że o komentarzach nie wspomnę?…
Okazuje się, że jednak ma – przynajmniej mi dało się takowe zastosowanie znaleźć. Chcąc je zaprezentować, zacznę od takiego oto ciągu ifów:

  1. if (s == "foo") DoSth1(z);
  2. else if (s == "bar") DoSth2(x, y);
  3. else if (s == "baz") DoSth3();

który w założeniu może być oczywiście dowolnie długi i niekoniecznie musi też udawać switcha dla stringów. Rzecz w tym, aby każdy if miał podobną formę – na tyle podobną, że aż prosi się o jej zmakrowanie:

  1. #define C(val,code) else if (s == #val) { code; }

To by wyszło gdyby nie feler w postaci pierwszego ifa, który bynajmniej nie zaczyna się wcale od else‘a. Można jednak sprawdzić, żeby tak było. Jak?… Ano właśnie dzięki zawsze fałszywemu if(false):

  1. if (false) { }
  2. else if (s == "foo") DoSomething1(z);
  3. // ...

Robi on dokładnie nic, ale jednocześnie sprawia, że następujące dalej, prawdziwe ify mają dokładnie taką samą strukturę. Teraz już możemy je skrócić:

  1. #define C(val,code) else if (s==#val) {code;}
  2. if (false) { } C(foo,DoSth1(z)) C(bar,DoSth2(x,y)) C(baz,DoSth3())
  3. #undef C

Naturalnie dla trzech wariantów taka zabawa nie ma specjalnego sensu, ale dla dłuższej listy – czemu nie? Sam użyłem tej sztuczki parę razy, m.in. w parserze wyrażeń matematycznych, gdzie ciąg ifów wybierał wywołanie jednej z ponad 20 wbudowanych w niego funkcji. Nie jest to ładne – trzeba to przyznać – ale cóż, takie jest życie: czasami rozwiązania eleganckiego po prostu nie ma…

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

XML w C++ – proste rozwiązanie

2010-05-19 16:13

XML kojarzy się raczej z technologiami webowymi i językami, które się z nimi wiążą – jak C#, Java, Ruby, itp. Nie zapominajmy jednak, że jest to także pewien format zapisu informacji. Ma on swoje wady (duży rozmiar), ale również zalety – jak np. możliwość edycji dowolnym edytorem tekstu – które mogą być pożyteczne w wielu zastosowaniach. Także wtedy, gdy piszemy w C++.
Oczywiście stworzenie własnego parsera XML nie jest bardzo trudne – mogę to powiedzieć z czystym sumieniem, gdyż samemu zdarzyło mi się takie dzieła (o różnym stopniu skomplikowania i funkcjonalności) popełnić w ilości co najmniej dwóch :) Nie jest to jednak zajęcia specjalnie kreatywne. Są też dla niego alternatywy i właśnie o jednej z nich – bibliotece dla C++ przeznaczonej do obsługi XML-a – chciałbym dzisiaj napisać.

Nazywa się ona TinyXML i jest to nazwa nadzwyczaj trafna. Jest to bowiem mały (ok. kilka tysięcy linii) i zgrabny kawałek kodu, który pozwala operować na dokumentach XML zarówno sensie odczytu, jak i zapisu. Rozprowadzany jest on na licencji ZLib, pozwalającej na używanie w produktach komercyjnych i niekomercyjnych. A ponieważ składa się na niego tylko kilka plików .cpp, łatwo korzystać z niego w projektach albo nawet dołączyć do własnego frameworka czy engine‘u.
A korzystać z TinyXML warto. Wśród istotnych cech tej biblioteki można wymienić to, że:

  • parsuje ogromną większość dokumentów XML; aktualnie kłopoty ma jedynie z tymi, w których specyfikacje DTD są osadzone bezpośrednio w sekcji <!DOCTYPE> (czyli z czymś, czego nikt normalny już nie używa ;-))
  • opiera się na modelu DOM (Document Object Model), czyli udostępnia zawartość plików XML w postaci drzewka elementów
  • obsługuje kodowanie UTF-8
  • używa STL i operatorów strumieniowych – ale tylko wtedy, jeśli o to poprosimy, definiując odpowiednie makro w czasie kompilacji

Nie mniej ważne jest to, że biblioteka TinyXML jest prosta w obsłudze. Jeśli mieliśmy coś wspólnego z przetwarzaniem XML w innych językach programowania, nie powinna nam ona sprawić żadnego kłopotu. Nie ma w niej naturalnie żadnych zaawansowanych feature‘ów w rodzaju zapytań XPath, transformacji XSL czy walidacji w oparciu o XML Schema, bo nie do takich zastosowań została ona stworzona. Jeżeli jednak nie potrzebujemy takiej zaawansowanej funkcjonalności, a chcemy jedynie np. odczytywać i zapisywać XML-owe pliki konfiguracyjne, stworzyć klienta RSS czy wykonywać tysiące innych pożytecznych, a nieskomplikowanych operacji na znacznikowym formacie danych, to TinyXML jest całkiem dobrym wyborem.
No, chyba że bardzo lubimy samodzielne pisanie parserów ;)

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

Co się rzuca, czyli natywne klasy wyjątków

2010-05-16 13:46

W wielu językach wyjątki to niemal obowiązkowa metoda zgłaszania i obsługi błędów. W innych (tj. w C++) jest ona w dużym stopniu opcjonalna. W obu przypadkach istnieją zwykle dostępne out-of-the-box klasy wyjątków, których obiekty można wyrzucać, bez tworzenia swoich własnych. Ich używanie jest zdecydowanie wskazanie. Warto więc przyjrzeć się, jakie predefiniowane obiekty wyjątków przewidują standardowej biblioteki różnych języków.

I tak w C++ mamy bazową klasę exception, która zawiera w sumie niewiele więcej ponad komunikat o błędzie. Zresztą dotyczy to tak naprawdę wszystkich wbudowanych w C++ klas wyjątków; tym, co je rozróżnia, jest po prosty typ. Mamy tu prostą hierarchię dziedziczenia, wyróżniającą klasy bazowe: runtime_error dla niskopoziomowych błędów czasu wykonania (jak na przykład overflow_error) oraz logic_error dla błędów logicznych aplikacji. Z tej drugiej, bardziej dla nas interesującej, wyprowadzone są też wyjątki nieco bardziej szczegółowe, jak np.: invalid_argument, range_error, domain_error czy lenght_error. Wszystkie cztery są zresztą podobne do siebie pojęciowo i wydają się służyć głównie do sygnalizowania nieprawidłowych wartości argumentów funkcji. Do innego rodzaju błędów sensowniej jest używać klas bazowych lub niestety napisać własne (najlepiej dziedziczące z nich).

Na platformie .NET i w Javie jest z tym dużo lepiej – tam typów wyjątków jest niemal za dużo. Nie ma tu miejsca na omawianie ich wszystkich i nie jest to zresztą konieczne, gdyż nazwy klas są zwykle wystarczające do zorientowania się, z jakim rodzajem błędu mamy tu do czynienia. Według mnie najczęstszymi sytuacjami, w których można wyrzucić obiekt którejś z natywnych klas, są:

  • ogólnie pojęte niepoprawne wartości argumentów funkcji – System.ArgumentException / java.lang.IllegalArgumentException
  • argument będący null-em w sytuacji, gdy wymagany jest obiekt – System.ArgumentNullException
  • wykroczenie poza zakres argumentu działającego jak indeks – System.IndexOutOfRangeException / java.lang.IndexOutOfBoundsException
  • próba wykonania operacji na obiekcie, którego stan jest niepoprawny – System.InvalidOperationException / java.lang.IllegalStateException
  • wywołanie niezaimplementowanej metody wirtualnej – System.NotImplementedException
  • próba wykonania operacji, której obiekt nie wspiera – System.NotSupportedException / java.lang.UnsupportedOperationException
  • błąd podczas operacji wejścia/wyjścia – System.IO.IOException / java.io.IOException
  • przekroczenie czasu operacji (timeout) – System.TimeoutException / java.util.concurrent.TimeoutException

Można zauważyć, że dla większości pozycji odpowiadające klasy wyjątków istnieją na obu platformach i nawet nazywają się podobnie. Zapewne to kolejny znak wzajemnej inspiracji ich twórców ;)

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

Po co jest FORCE_DWORD

2010-05-12 15:28

Przeglądając dokumentację do DirectX (a przynajmniej do części graficznej) można natknąć się na wiele typów wyliczeniowych. Większość z nich (a może wszystkie?) na końcu swojej definicji ma stałą o nazwie kończącej się na _FORCE_DWORD. Przykładem jest znany, lubiany i przez wszystkich używany D3DRENDERSTATETYPE:
typedef enum D3DRENDERSTATETYPE
{
// (co najmniej 1<<8 różnych stałych) D3DRS_FORCE_WORD = 0x7fffffff; } D3DRENDERSTATETYPE, *LPD3DRENDERSTATETYPE;[/cpp] Zjechanie na sam dół pomocy pouczy nas, że stała ta zasadniczo... nie jest używana. Jednocześnie jednak ma ona wymuszać kompilację typu wyliczeniowego jako 32-bitowego. O co tutaj właściwie chodzi? Kompilatory C++ mogą mianowicie wybierać dla typów wyliczeniowych właściwie dowolne typy liczbowe - byle tylko wszystkie wartości stałych się zmieściły. To sprawia, że wielkość enuma może się różnić nie tylko między kompilatorami, ale i między różnymi ustawieniami kompilacji. Nietrudno na przykład wyobrazić sobie, że przy optymalizacji szybkości ów enum będzie miał rozmiar równy słowu maszynowemu, zaś przy optymalizacji zajętości pamięci będzie to rozmiar najmniejszy możliwy.
No i tu zaczynają się schody tudzież pagórki. Zmienność (a raczej niezdefiniowanie) wielkości typu wyliczeniowego jest może kłopotliwa w pewnych sytuacjach. Trudno byłoby na przykład przewidzieć to, w jaki sposób należy odczytać wartość zwróconą przez metodę GetRenderState urządzenia, która jest zapisana w 32-bitowym DWORD-zie, jeśli nie zajmowałaby ona w nim wszystkich 4 bajtów. Podejrzewam też, że na którymś etapie renderowania we wnętrzu DirectX określony rozmiar pewnych flag (np. typów prymitywów) jest po prostu wymuszany przez sterownik karty graficznej. Całkiem rozsądne jest więc zapewnienie go od samego początku – czyli już w kodzie pisanym przez programistę-użytkownika DirectX.

Czemu jednak potrzebny jest takich hack? Ano tutaj znowu wychodzi niedookreślenie pewnych rzeczy w standardzie C++, zapewne z powodu źle pojętej przenośności. Częściowo zostanie to naprawione w przyszłej wersji standardu, gdzie – podobnie jak np. w C# – możliwe będzie określenie typów liczbowych używanych wewnętrznie przez enumy.

Tags: , , , ,
Author: Xion, posted under Programming » Comments Off on Po co jest FORCE_DWORD

Zamiast szumu wiatraczka

2010-05-07 15:53

Dzisiaj chcę napisać o ważnej kwestii okołokoderskiej, przed którą chyba każdy prędzej czy później stanął. Pisząc kod, nie zawsze potrzebujemy wielkiego skupienia i absolutnej ciszy; przeciwnie, wiele osób lubi programować przy dźwiękach muzyki. Zazwyczaj jest to jakaś własna kolekcja utworów, zorganizowana w playlisty.
Ma to swoje wady. Jedną z nich jest to, że wymaga sporo manualnej obsługi; jeśli jej zabraknie, to zawartość list(y) może nam się znudzić nawet w przypadku losowej kolejności odtwarzania (no, chyba że nasza kolekcja jest wystarczająco gigantyczna ;P). Poza tym w ten sposób nie usłyszymy nigdy niczego nowego, a być może wartego usłyszenia.
Z drugiej strony włączenie radia zwykle nie jest sensowną opcją – nawet jeśli chodzi o radia internetowe. Nie da się bowiem znaleźć takiego, które idealnie w wpasuje się w nasz muzyczny gust. A gdyby tak można było stworzyć własne?… Eh, pomarzyć dobra rzecz :)

Okazuje się jednak, że to zupełnie wykonalne. Istnieją serwisy, które właśnie na coś takiego pozwalają. Jednym z najstarszych i najbardziej znanych jest Last.fm, zawierający chyba wszystkie feature‘y, jakich można oczekiwać (łącznie ze stacjonarnym klientem), Niestety, jest on płatny.
Osobiście polecam darmowe Jango. Potrafi on dokładnie tyle, ile potrzeba – tj. tworzy wirtualne stacje radiowe (w dowolnej ilości, o ile mi wiadomo) grające muzykę wybranych wykonawców (z możliwością określenia preferencji do pojedynczych utworów) oraz artystów zbliżonych gatunkowo. To wyszukiwanie podobieństw działa przy tym całkiem dobrze, więc mamy spore szanse na odkrycie ciekawych, nieznanych wcześniej wykonawców. Wady?… Jak to bywa w przypadku takich serwisów, może to być niewystarczająco bogata biblioteka zarówno utworów, jak i artystów. Brakuje też desktopowego klienta, co może niektórym przeszkadzać; można wówczas spróbować zewnętrznego programu Jango Desktop.

Tags: , ,
Author: Xion, posted under Culture, Internet, Programming » 10 comments

Co może udawać obiekt w C++

2010-05-06 14:56

Możliwości przeciążania operatorów w C++ dla własnych typów obiektów sprawiają, że mogą one (tj. te obiekty) zachowywać się w bardzo różny sposób. Mogą na przykład “udawać” pewne wbudowane konstrukcje językowe, nierzadko wykonując ich zadania lepiej i wygodniej. Przykładów na to można podać co najmniej kilka – oto one:

  • Obiekt może zachowywać się jak funkcja, czyli udostępniać możliwość “wywołania” siebie z określonymi parametrami. Takie twory często nazywa się funktorami i bywają używane podczas pracy ze standardową biblioteką STL.
    Działają one przy tym w bardzo prosty sposób, zwyczajnie przeciążając operator nawiasów okrągłych (). Jest on na tyle elastyczny, że może przyjmować dowolne parametry i zwracać dowolne rezultaty, co pozwala nawet na stworzenie więcej niż jednego sposobu “wywoływania” danego obiektu. Jednym z bardziej interesujących zastosowań dla tego operatora jest implementacja w C++ brakującego mechanizmu delegatów, czyli wskaźników na metody obiektów.
  • Na podobnej zasadzie obiekt może udawać tablicę – wie o tym każdy, kto choć raz używał klas STL typu vector. Wymagane jest tu przeciążenie operatora indeksowania []. Daje ono wtedy dostęp do elementów obiektu-pojemnika, który zresztą nie musi być wcale indeksowany liczbami, jak w przypadku tablic wbudowanych (dowodem jest choćby kontener map). Ograniczeniem jest jedynie to, że indeks może być (naraz) tylko jeden, bo chociaż konstrukcja typu:
    1. v[3,4] = 5;

    jest składniowo najzupełniej poprawna, to działa zupełnie inaczej niż można by było się spodziewać :)

  • Obiekty mogą też przypominać wskaźniki, które wtedy określa się mianem ‘sprytnych’ (smart pointers). Dzieje się tak głównie za sprawą przeciążenia operatorów * (w wersji jednoargumentowej) i ->. Normalnie te operatory nie mają zastosowania wobec obiektów, ale można nadać im znaczenie. Wtedy też mamy obiekt, który zachowuje się jak wskaźnik, czego przykładem jest choćby auto_ptr z STL-a czy shared_ptr z Boosta.
  • Wreszcie, obiekt może też działać jako flaga boolowska i być używany jako część warunków ifów i pętli. Sprytne wskaźniki zwykle to robią, a innymi wartymi wspomnienia obiektami, które też takie zachowanie wykazują, są strumienie z biblioteki standardowej. Jest to spowodowane przeciążeniem operatora logicznej negacji ! oraz konwersji na bool lub void*.

Reasumując, w C++ dzięki przeciążaniu operatorów możemy nie tylko implementować takie “oczywiste” typy danych jak struktury matematyczne (wektory, macierze, itp.), ale też tworzyć własne, nowe (i lepsze) wersje konstrukcji obecnych w języku. Szkoda tylko, że często jest to wręcz niezbędne, by w sensowny sposób w nim programować ;)

 


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