Do programowania używamy głównie pełnowymiarowych IDE. W repertuarze narzędzi koderskich dobrze jest jednak mieć także edytor plików tekstowych bez całej tej projektowo-kompilacyjnej otoczki. Przydaje się on w wielu różnych sytuacjach, począwszy chociażby od typowego przypadku, gdy chcemy podejrzeć zawartość jakiegoś pliku z kodem bez odpalania całego “ciężkiego” środowiska. Poza tym wcale nie tak rzadko zdarza się, że musimy skrobnąć coś w języku, którego niekoniecznie używamy intensywnie na co dzień (jak choćby którymś z języków skryptowych).
Minimum, jakie taki programistyczny edytor powinien spełniać, to oczywiście podświetlanie składni. Wypadałoby też, by zawierał on większość funkcjonalności, jaką w zakresie manipulowania kodem oferuje IDE, np. wyszukiwanie w oparciu o wyrażenia regularne czy wyświetlanie numerowania linii i białych znaków. Dobrze by też było, gdyby umożliwiał o edycję kilku plików naraz. Prawdopodobnie najważniejsze jest jednak to, aby takie edytor był lekki: uruchamiał się jak najszybciej i tak też działał, nie zajmując przy tym wielu zasobów systemowych.
Pewnie każdy ma swój ulubiony program tego rodzaju, ale mimo to nie omieszkam polecić aplikacji, którą sam używam w charakterze “mniejszego edytora”: Programmer’s Notepad. Umie ona wszystko to, o czym wspomniałem wyżej, i jeszcze wiele więcej. Wśród poważniejszych braków, jakie mogę jej zarzucić, przypominam sobie jedynie brak wbudowanych narzędzi do konwersji między różnymi 8-bitowymi standardami kodowania znaków (z przeróżnymi UTF-ami itp. nie ma bowiem problemu). Ponieważ jednak możliwe jest dodawanie też własnych narzędzi uruchamianych z wiersza poleceń, w razie potrzeby można sobie odpowiednią funkcjonalność dodać samodzielnie :)
W C++ dynamiczne tablice z więcej niż jednym wymiarem tworzy się zwykle w sposób dość złożony. Najpierw bowiem – w przypadku dwóch wymiarów – tworzymy tablicę wskaźników na jej wiersze, a następnie same wiersze:
Skądinąd wiadomo bowiem, że zapis tab[i]
jest tylko cukierkiem składniowym zastępującym *(tab + i)
. Dlatego też w wyniku pierwszej dereferencji (indeksowania) musimy otrzymać wskaźnik (na wiersz tablicy), aby ponowną operację przeprowadzić drugi raz i dostać się do pojedynczego elementu.
Kiedy zaś tablica 2D jest umieszczona w jednym kawałku pamięci, wtedy element tab[i][j]
powinien zostać obliczony inaczej – np. jako *(tab + j * width + i)
, jeśli elementy są układane w pamięci wierszami. Kompilator musiałby więc skądś wiedzieć, jaka jest szerokość (width
) tablicy, a ponadto nie rozpatrywać każdej pary nawiasów kwadratowych osobno, lecz traktować je jako łączną operację indeksowania. Zwłaszcza pierwszy wymóg nie wydaje się rozsądny.
Warto jednak – jeśli zależy nam efektywności – używać drugiego sposobu przechowywania tablic dwuwymiarowych:
Dostęp do elementów jest wtedy szybszy, bo oszczędzamy sobie jednej dereferencji wskaźnika (która może być kosztowna, jeśli tablica jest porozrzucana po pamięci). Szczegóły indeksowania można zaś opakować w zgrabną klasę z przeciążonymi operatorami.
Albo po prostu użyć gotowego rozwiązania, np. klasy multi_array
z Boosta :)
Informatyka zna świetne rozwiązania wielu złożonych problemów, takich jak sortowanie czy wyszukiwanie najkrótszej drogi. Użycie tych powszechnie znanych algorytmów – nawet tych najbardziej skomplikowanych – jest zwykle całkiem proste. Albo bowiem dysponujemy już gotową implementacją, albo bez większych problemów możemy takową samodzielnie popełnić – z ewentualnymi usprawnieniami własnymi.
Często jednak zdarza się, że trzeba wymyślić coś znacznie mniejszego, rozwiązującego mniej modelowy, ale za to bardziej praktyczny problem. Niepotrzebne jest wtedy arcydzieło algorytmiki, lecz coś, co po prostu będzie dobrze działać. Osobiście uważam, że opracowywanie właśnie takich małych rozwiązań jest jedną z najciekawszych rzeczy w programowaniu w ogóle.
Przykłady? Jest ich tak dużo i są tak odmienne od siebie, że chyba niemożliwe jest podanie choćby kilku odpowiednio reprezentatywnych. Może to być krótki kod parsujący jakiś prosty tekst i wyciągający z niego pewnie informacje. Może to być metoda na przeskalowanie obrazka z zachowaniem jego aspektu (ilorazu szerokości do wysokości). Może to być również kod pozycjonujący jakąś kontrolkę wewnątrz okna o zmiennym rozmiarze. Może też chodzić o wyznaczenie rezultatu pojedynczego ataku w turowej grze strategicznej czy RPG. A nawet o, jak to ktoś ładnie nazwał, “silnik do pauzy” w owej grze ;] I tak dalej…
Niby nic skomplikowanego, czasami wręcz oczywistego – a jednak przecież trzeba to w miarę potrzeb wymyślać i zakodowywać. Bo gotowych rozwiązań problemów tak specyficznych po prostu nie ma. A bez takich małych algorytmów nie działałby żaden program – także ten, który musi używać również i tych “dużych” rozwiązań.
Może więc właśnie tutaj tkwi istota programowania?… Kto wie :)
Dzisiaj chciałem polecić pewną książkę, która jest prawdopodobnie jedyną pozycją, opisującą w tak dokładny sposób pewne bardzo obszerne, a ważne i zwykle nie do końca znane zagadnienie. Chodzi o szablony języka C++ i książkę pt. C++. Szablony. Vademecum profesjonalisty (autorzy: D. Vandevoorde, N.M. Josuttis), która o nich właśnie traktuje.
O szablonach co nieco wie prawie każdy programista zajmujący się C++, ale często wiedza ta jest raczej powierzchowna – choć niekiedy zupełnie wystarczająca. Okazuje się jednak, że ten element języka może być wykorzystany na wiele bardziej interesujących sposobów niż tylko do tworzenia klas pojemnikowych podobnych do tych z STL. We wspomnianej książce znajdziemy kilka przykładów takiego zaawansowanego operowania szablonami, łącznie z metaprogramowaniem.
Naturalnie jest tam także dokładny opis wszystkich możliwości języka C++ związanych z szablonami, obejmujący także te mniej znane ich elementy. W skrócie można powiedzieć, że znajdziemy tak niemal wszystko, co każdy programista C++ chciałby wiedzieć o szablonach, lecz boi się zapytać ;)
Taka specjalistyczna wiedza (dotykająca często szczegółów tego, w jaki sposób sam kompilator traktuje kod z klauzulami template<>
) nie jest oczywiście niezbędnie potrzebna. Tym bardziej, że większość z opisanych “sztuczek” (jak np. rozpoznawanie cech typów) została już zaimplementowana w ogólnodostępnych bibliotekach (głównie częściach Boosta). Mimo to chyba warto mieć książkę, która cały bardzo ciekawy (przynajmniej wg mnie ;P) temat szablonów opisuje bardzo dogłębnie.
I znów całkiem długie wakacje się skończyły, więc chcąc nie chcąc trzeba wrócić z powrotem ‘do szkoły’ ;] Ponieważ jednak w tym przypadku mówimy tu o studiach, i to już na czwartym roku, to tragizm tego wydarzenia nie jest znowu aż tak wielki ;) W tym semestrze główną atrakcją jest oczywiście praca inżynierska, której to pisanie ostatecznie mnie nie ominie.
Jednym słowem, oto znów zaczyna się kolejny studencki rok :)
W bardzo dawnych czasach systemy operacyjne słabo radziły sobie z wielozadaniowością (niektóre zgoła wcale). Ostatnio jednak zmieniło się całkowicie; teraz niemal cała funkcjonalność systemu w rodzaju Windows czy Linux opiera się na działających w tle procesach (zwanych usługami lub demonami).
O ile w tych przypadkach ma to najczęściej sens, o tyle ciężko jest mi zrozumieć, dlaczego coraz więcej zupełnie zwyczajnych aplikacji “upiera się”, aby także działać w sposób ciągły. Owszem, w niektórych przypadkach jest to jak najbardziej uzasadnione – weźmy chociażby internetowe komunikatory. Wydaje mi się jednak, że widoczny tu trend idzie zdecydowanie zbyt daleko.
Mam mianowicie wrażenie, że obecność ikonki prawie każdej aplikacji w systemowym zasobniku (tray) stała się niemal obowiązkowa. Nie ma przy tym znaczenia, czy dany program (albo raczej jego główna część) jest uruchomiony czy nie. Dobrym pretekstem do takiej obecności bywa nawet czynność tak absurdalna, jak pilnowanie skojarzeń określonych typów plików z macierzystą aplikacją (domyślnie robi tak np. odtwarzacz Winamp. Najważniejsze jest bowiem to, aby po jedno- lub dwukrotnym kliknięciu w taką ikonkę uruchomił się właściwy program. Zupełnie jakby posiadanie skrótów na Pulpicie czy w menu Start nie było całkowicie wystarczające.
Co więcej, raz wystartowana aplikacja zwykle też nie daje się tak łatwo zamknąć. Bardzo popularna jest, przykładowo, zmiana zachowania przycisku Zamknij (lewy górny róg okna), który wcale aplikacji nie zamyka, a jedynie minimalizuje ją do ikonki we wspomnianym już zasobniku. Dopiero jawne wybranie opcji z menu pozwala faktycznie program zakończyć; w innym przypadku jest to po prostu zamknięcie udawane.
Jakie są tego skutki? Ano takie, że nawet czterordzeniowy procesor z gigabajtami pamięci operacyjnej może mieć ciężkie chwile w obsłudze kilkudziesięciu procesów i kilkuset wątków naraz, z których większość stanowią “niedomknięte” aplikacje. Prawdziwe kłopoty zaczynają się zaś wtedy, gdy chcemy uruchomić naprawdę wymagający program, jak choćby pełnoekranową grę.
A wszystko to dlatego, że aplikacje chcą być mądrzejsze od swoich użytkowników…
Jedną ze szczególnych cech jest C++ jest obecność różnych “nadmiarowych” właściwości, które pojawiły się w języku właściwie przypadkiem. Bardziej znanym przykładem jest chociażby metaprogramowanie za pomocą szablonów, czego pierwszym przykładem jest słynny kod generujący liczby pierwsze na wyjściu kompilatora. Z początku było to nic innego, jak niezamierzony skutek uboczny procesu rozwijania szablonów, acz teraz zostało to oczywiście świadomie rozwinięte.
Podobnie jest na przykład z preprocesorem, jako że C i C++ są jednymi z niewielu języków, które taki mechanizm posiadają. Na początku służył głównie do deklarowania “stałych”, potem także makr naśladujących funkcje… A przecież można go też użyć do niemal całkowitej zmiany składni języka – tak, by zaczął on udawać inny. Przykładowo, za pomocą kilku poniższych #define
‘ów:
#include
#include
#define IF if(
#define THEN )
#define BEGIN {
#define END }
#define WRITELN std::cout<<
#define READLN(x) std::getline(std::cin,x)[/cpp]
możemy sprawić, że w C++ będzie pisało się prawie jak w Pascalu:
[cpp]WRITELN (“Podaj hasło:”);
READLN (pass);
IF pass == “strasznietajnehaslo” THEN
BEGIN
WRITELN (“OK”);
END;[/cpp]
Nie wszystkie niuanse składni innego języka da się oczywiście zasymulować przy pomocy makr, ale w praktyce udaje się bardzo wiele. Mimo to polecam jednak programowanie w C++ przy pomocy C++, a nie Pascala czy Basica ;)