Przy wprowadzaniu operatorów przypisania typu +=
czy *=
w większości kursów C++ stwierdza się, że ich używanie jest głównie skrótem w stosunku do korzystania ze zwykłego przypisania (=
) i odpowiednich operatorów binarnych (jak +
czy *
). Takie wyjaśnienie jest na początek zupełnie wystarczające, a przy tym łatwe do zrozumienia i co ważniejsze, wydaje się poprawne. I tak faktycznie jest – ono tylko “wydaje się” :)
Później dowiadujemy się bowiem, że sprawa jest nieco bardziej skomplikowana. Zapis typu a += b
nie musi zawsze nawet w przybliżeniu odpowiadać “wersji długiej” w postaci a = a + b
. Trzeba tu wziąć pod uwagę kilka rzeczy – głównie to, co kryje się pod nazwami a
i b
:
=
i np. +
, to nie oznacza to, że automatycznie dostajemy też przeciążony operator +=
(i vice versa). Po “skróceniu” dana instrukcja może się więc w ogóle nie skompilować. Dotyczy to oczywiście sytuacji, w których mamy do czynienia z własnymi typami danych.x = x - y + 2;
to nie jest to samo co x -= y + 2;
.Pamiętajmy więc, że operatory typu +=
są właśnie operatorami, całkowicie odrębnymi od wszystkich innych, nie zaś żadnymi skrótami, jak to się często “dla uproszczenia” mówi.
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):
Następnie używamy go zarówno w definicji klasy bazowej, jak i pochodnych:
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?…
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ć :)
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:
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.
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 ExploreraNTDDI_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:
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
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:
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”:
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++…
…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ę.