Monthly archive for August, 2008

Metody wirtualne i override

2008-08-29 15:43

W wielu językach obiektowych występuje słowo kluczowe override, którego dodawanie jest obowiązkowe podczas nadpisywania metod wirtualnych w klasach pochodnych (w przeciwnym wypadku generowanie jest ostrzeżenie). Jak można się spodziewać, C++ do takich języków nie należy :) A szkoda, bo wymóg stosowania override zapobiega pomyłkom przy nadpisywaniu (np. literówkom w nazwach metod), które można wykryć dopiero w trakcie działania programu, gdy ze zdziwieniem odkrywamy, że zamiast nowej wersji metody wywoływana jest stara.

Można częściowo temu zaradzić, ale sposób jest brzydki. Polega on na stworzeniu makra zawierającego całą definicję klasy (albo przynajmniej deklaracje jej metod wirtualnych):

  1. #define DECLARE_FOO(__suffix__) \
  2.     public: \
  3.         virtual void AbstractDoSomething() __suffix__; \
  4.         virtual void DoSomethingElse();

Następnie używamy go zarówno w definicji klasy bazowej, jak i pochodnych:

  1. class Foo
  2. {
  3.     DECLARE_FOO(=0)
  4. };
  5.  
  6. class Bar : public Foo
  7. {
  8.     DECLARE_FOO( )
  9. };

Dodatkowy parametr pozwala odróżnić obie sytuacje, co jest koniecznie w przypadku metod czysto wirtualnych (które nie posiadają implementacji).

Sztuczka jest być może pomysłowa, ale jednocześnie dość przygnębiająca. Dlaczego bowiem tak prosta sprawa jak zapobieganie pomyłkom przy nadpisywaniu metod wirtualnych musi być w C++ realizowana za pomocą brzydkich makr?…

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

Pospolite ruszenie

2008-08-27 8:20

Okazjonalnie na Warsztacie zdarza się, że ktoś zaproponuje zebranie całego community i stworzenie czegoś wspólnymi siłami. To ‘coś’ oznacza oczywiście jakąś grę. Ponieważ zwykle robią to nowi członkowie społeczności, niemal jedynym skutkiem podobnej propozycji jest ożywiona dyskusja na temat tego, dlaczego to się na pewno nie uda ;] Zresztą, taka opinia jak dotąd okazywała się jak najbardziej słuszna :D

Tym razem jednak – choć zaczęło się podobno – w sprawę niespodziewanie zaangażowały się osoby cieszące się dużym autorytetem na Warsztacie. Do pracy nad projektem zgłosiło się też całkiem sporo osób, co aczkolwiek nie musi automatycznie wróżyć sukcesu, ale świadczy przynajmniej o dużym zapale początkowym (oby nie okazał się słomiany ;]). Wreszcie, nie było zbyt dużo zwyczajowego gadania i narzekania, bo prawie od razu rzecz przeszła do czynów – między innymi do utworzenia środowiska do pracy grupowej w serwisie Assembla.

A zatem – kto wie? Możliwe, że coś z tego wyjdzie… Ale przynajmniej na razie postaram się jeszcze zbytnio nie zapeszać :)

Tags:
Author: Xion, posted under Games, Programming » 5 comments

Triki z PowerShellem #10 – XML

2008-08-25 0:12

Obecnie magiczne trzy literki XML pojawiają się przy okazji niemal każdego aspektu programowania i informatyki w ogóle. Do przetwarzania danych zapisanych w tej postaci istnieje niezmierzona wręcz ilość różnych narzędzi. Nie jest więc niespodzianką, że nasz ulubiony PowerShell w tej dziedzinie również daleko nie odstaje :)
Jako parsera używa on oczywiście narzędzi zawartych w .NET, a dokładniej w przestrzeni nazw System.Xml. Są one jednak o wiele wygodniejsze w stosowaniu w skryptach PSh niż w zwykłych aplikacjach .NET. Spójrzmy chociażby na poniższy przykład:

  1. # rss.ps1
  2. # Czytnik kanałów RSS
  3. param ([string]$url = $(throw "No RSS specified"))
  4.  
  5. # Ściągnij zawartość kanału RSS jako XML
  6. $webClient = New-Object Net.WebClient
  7. $xml = [xml]$webClient.DownloadString($url)
  8.  
  9. # Wyświetl pozycje z wszystkich kanałów
  10. foreach ($chan in $xml.rss.channel)
  11. {
  12.     foreach ($item in $chan.item)
  13.     {
  14.         "[" + $item.pubDate + "] " + $item.title | Out-Host
  15.     }
  16. }

To nic innego, jak najprostszy czytnik kanałów RSS. Pobiera on i wyświetla nagłówki wraz z datą ich publikacji z podanego URL-a, co wygląda choćby tak:

[Fri, 22 Aug 2008 11:21:52 +0000] Używanie nagłówków Windows
[Tue, 19 Aug 2008 10:53:24 +0000] To, czego oczekuje kompilator
[Sat, 16 Aug 2008 19:28:58 +0000] Statystyczny warsztatowicz
[Thu, 14 Aug 2008 06:24:54 +0000] Makra z wielokropkiem
[Mon, 11 Aug 2008 20:12:17 +0000] Długie ciągi odwołań
[Fri, 08 Aug 2008 20:00:57 +0000] Obiektowy szowinizm
[Tue, 05 Aug 2008 15:21:31 +0000] Czy void jest typem
[Mon, 04 Aug 2008 15:12:30 +0000] Prawie jak mapa
[Sun, 03 Aug 2008 15:33:28 +0000] Triki z PowerShellem #9 – Potokowanie
[Thu, 31 Jul 2008 14:34:14 +0000] Przeciążanie przecinka

Taka jest operacja jest całkiem łatwa do przeprowadzenia. Sparsowanie łańcucha znaków do postaci XML-owego drzewka DOM, reprezentującego jego zawartość polega na przykład na “rzutowaniu” tekstu na typ xml (będący aliasem na System.Xml.XmlDocument) – i już. Następnie możemy dostać się do poszczególnych tagów używając po prostu kropki. Zatem np. $xml.rss pozwala odwołać się do głównego elementu (<rss>) w dokumencie RSS. Jeśli zaś pod daną nazwą kryje się więcej niż jeden element, możemy je iterować tak samo, jak każdą inną kolekcję (a więc np. przez foreach).

Do bardziej zaawansowanych operacji należy zapewne wykorzystać właściwości i metody klasy XmlDocument. Ale do prostych zastosowań związanych z XML-em, jakie mniej lub bardziej systematycznie przytrafiają się każdemu, powyższy sposób postępowania może być całkowicie wystarczający.

Tags: ,
Author: Xion, posted under Applications, Internet » 3 comments

Używanie nagłówków Windows

2008-08-22 13:21

Chociaż Windows API ciągnie za sobą skutki kilkunastu lat wstecznej kompatybilności, nie znaczy to, że nie pojawiają się tam nowe możliwości. Ponieważ jednak wszystkie funkcje są importowane z DLL-i w sposób statyczny, nie jest możliwe, by każda nowsza była dostępna automatycznie. W przeciwnym wypadku program, który faktycznie działałby bez problemu np. na Windows 98 wymagałby w praktyce najnowszej wersji systemu – mimo tego, że nie korzysta z żadnej nowej funkcjonalności.
Niekiedy jednak nie chcemy ograniczać się do “wspólnego mianownika” wszystkich aktualnie obsługiwanych wersji Windows i chcemy użyć czegoś, co rzeczywiście jest dostępne począwszy od jakiejś nowszej edycji systemu. Co wtedy zrobić?

W dokumentacji (czyli MSDN) jest na szczęście dokładnie zaznaczone to, w której wersji Windows wprowadzoną daną funkcję czy strukturę. Ale żeby odpowiedni symbol został włączony do kompilacji i linkowania, należy jeszcze przed dołączeniem windowsowego nagłówka (zazwyczaj windows.h) zdefiniować odpowiednie makro.
A właściwie to nawet więcej niż jedno, jako że MSDN wspomina o czterech. Są to:

  • WINVER i _WIN32_WINNT, wskazujące na liczbową wersję systemu. Wartość 0x0502 oznacza na przykład wersję 5.1, czyli Windows XP z SP2.
  • _WIN32_IE, mające zawierać docelową wymaganą wersję Internet Explorera
  • NTDDI_VERSION, ustawialne na którąś z predefiniowanych stałych przypisanych każdej większej edycji Windows (np. NTDDI_WINXPSP1 lub NTDDI_WIN2K)

Pierwsze trzy są już uważane za przestarzałe i aktualnie używać należy NTDDI_VERSION, które jest zresztą znacznie wygodniejsze. W praktyce dobrze jest jednak ustawiać wszystkie makra, zwłaszcza jeśli nasz kod może być potencjalnie kompilowany na wielu różnych wersjach Platform SDK. I tak na przykład jeśli chcemy wykorzystać pule wątków (thread pools) dostępne począwszy od Windows Vista, powinniśmy dodać taki oto kod:

  1. #define NTDDI_VERSION NTDDI_VISTA
  2. #define WINVER 0x0600
  3. #define _WIN32_WINNT 0x0600
  4. #include <windows.h>

Po wartości makr odpowiadające wszystkim edycjom Windows należy udać do MSDN, do sekcji:

Win32 and COM Development > Development Guides > Windows API > Using Windows Headers

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

To, czego oczekuje kompilator

2008-08-19 12:53

Język C++ jest tworem skomplikowanym i jego złożoność daje się we znaki nie tylko programistom. Dość często mają z nią problemy także kompilatory. Obecnie zdecydowana większość narzędzi tego typu spełnia prawie całkowicie wymagania aktualnego standardu. Istnieje jednak sporo miejsca na usprawnienia, chociażby w bodaj najbardziej niedopracowanej dziedzinie: jakości komunikatów o błędach generowanych przez kompilatory.
Jeśli na przykład trafi nam się taka deklaracja zmiennej:

  1. SomeType variable;

a typ SomeType z jakiegoś powodu (niedołączony nagłówek, kwestia zasięgu, itd.) będzie w jej miejscu nieznany, to treść wyprodukowanego przy okazji komunikatu o błędzie może nas trochę zaskoczyć. Zarówno CL (kompilator Microsoftu używany w Visual C++), jak i GCC wyplują bowiem coś w stylu:

error: expected ‘;’ before ‘variable’

To znaczy ni mniej, ni więcej, tylko to, że według tych kompilatorów właściwa jest “deklaracja”:

  1. SomeType;

Dość zaskakujące, nieprawdaż?

Takie zachowanie można częściowo wyjaśnić tym, że kompilator “nie patrzy” na kod tak, jak programista. My widzimy tutaj deklarację zmiennej, natomiast dla kompilatora jest to tylko sekwencja: ‘nieznany identyfikator’, po którym następuje kolejny ‘nieznany identyfikator’. Ponieważ mówi się, że C++ jest językiem kontekstowym (co swoją drogą nie jest do końca ścisłe), taki fragment może sugerować wiele różnych rodzajów pomyłek. To, co sugeruje kompilator, wbrew pozorom też pewnie ma sens, chociaż dość specyficzny. Postawienie średnika w “spodziewanym” miejscu sprawi bowiem, że zamiast dwóch nieznanych nazw w jednej instrukcji będziemy mieli tylko jedną. Cóż za postęp! :)
Na tym prostym przykładzie widać, że interpretacja wyników kompilacji zakończonej niepowodzeniem wymaga niestety pewnego doświadczenia. Dopiero po jakimś czasie nauczymy się trafnie(j) zgadywać, co tak naprawdę oznacza ten czy inny błąd. Najwyraźniej taki już urok C++…

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

Statystyczny warsztatowicz

2008-08-16 21:28

…nie jest żadną konkretną osobą. Jednak mimo tego, że nie istnieje, dzięki przeprowadzonej niedawno Wielkiej Warsztatowej Ankiecie możemy powiedzieć całkiem sporo o jego cechach szczególnych. Taka już uroda statystyki, średnich i median: niby nikt dokładnie nie przystaje do przeciętnych wyników, ale pomimo to mówią one całkiem sporo o badanej grupie.
Ankieta badająca przeróżne cechy członków społeczności Warsztatu jest przeprowadzana od czterech lat. Za każdym razem wypełnia ją coraz większa liczba osób, które poczuwają się do jakiejś przynależności do tego community. Jednak w tej edycji przyrost ten jest wręcz niepokojąco duży – aż trzykrotny (z ok. 200 do ponad 600), co według mnie stawia pod dużym znakiem zapytania sensowność uzyskanych wyników.

Bo i skąd się wzięli ci wszyscy ludzie? Czy nie było przypadkiem tak, że każdy kto ledwie parę razy odwiedził stronę gamedev.pl, uznał się za adresata ankiety? Innej możliwości nie widzę, zwłaszcza biorąc pod uwagę przeciętną ilość osób przebywających na kanale #warsztat (mniej niż 50) czy uczestniczących w dyskusjach na forum (z grubsza około setki).
Dwa czy nawet cztery lata temu te liczby były może o jakieś 20% mniejsze. Nawet jeśli mylę się w swoich zgrubnych szacunkach, nie ma żadnego uzasadnienia dla aż tak dużej liczby rzekomych warsztatowiczów, którzy “ujawnili się” w ankiecie.

No, może poza bardzo, bardzo luźną definicją warsztatowicza. Ale czy odwiedzanie jakieś strony internetowej jest wystarczającym kryterium bycia członkiem community? Osobiście “trochę” w to wątpię.

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

Makra z wielokropkiem

2008-08-14 8:24

Chociaż nie jest zalecane, w C++ (podobnie jak w jego poprzedniku, C) możliwe jest wciąż tworzenie funkcji z nieokreśloną liczbą argumentów. Najbardziej znanym przykładem jest oczywiście printf:

  1. void printf(const char*, ...);

Nie wszyscy wiedzą jednak, że zmienną liczbą parametrów mogą mieć też makra (co nazywa się po angielsku variadic macros). Deklaruje się je wtedy w bardzo podobny sposób jak funkcje, też używając wielokropka:

  1. #define LOG(fmt, ...) fprintf(Logger::GetFile(), fmt, __VA_ARGS__);

Żeby jednak dostać się do ‘listy argumentów’ makra, używamy specjalnego identyfikatora __VA_ARGS__. W jego miejsce zostaną podstawione wszystkie podane parametry (z wyjątkiem pierwszego), oddzielone przecinkami tak, jak w oryginalnym “wywołaniu” makra.

Do czego może się to przydać? Jak widać powyżej, za pomocą takich makr możemy na przykład opakowywać wywołania funkcji ze zmienną liczbą argumentów – jak w powyższym przykładzie z logowaniem. Prawdopodobnie mogą być one przydatne także w implementacji jakiejś formy delegatów w C++, zwłaszcza jeśli chodzi o sposób ich wywoływania. Istnieje też ciekawa technika, pozwalająca na tworzenie (w C99 i późniejszych wersjach C/C++) funkcji z nieokreśloną liczbą argumentów, ale z zachowaną kontrolą ich typów.

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


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