CommonLib 8.0
Biblioteka CommonLib
Wersja: 8.0
Data: 29 marca 2008
CommonLib to uniwersalna biblioteka dla C++ zawierająca różnorodne elementy. Powstała przy okazji pisania silnika graficznego i różnych innych moich projektów. Obecna wersja jest pisana od wakacji 2006.
Właściwie od dziecka, odkąd tylko zacząłem programować (wtedy to był jeszcze Turbo Pascal pod DOS) zawsze rozwijałem jakąś swoją bibliotekę modułów bazowych, z którymi potem nigdy się nie rozstawałem pisząc swoje projekty. Teraz uważam, że nadal jest to potrzebne - szczególnie w C++, którego biblioteka standardowa - w przeciwieństwie do nowoczesnych języków i środowisk programowania takich jak Delphi, C# czy Java - jest bardzo uboga, a to co zawiera pozostawia wiele do życzenia.
- Jest to uniwersalna biblioteka do C++, chociaż pisana głównie z myślą o programowaniu gier.
- Jest przenośna na Windows i Linux.
- Używa tylko biblioteki standardowej C, C++ oraz API systemowego. Opcjonalnie współpracuje także z DirectX (D3DX), zlib i innymi bibliotekami.
- Do przechowywania wszelkich łańcuchów stosuje typ string/wstring z biblioteki C++.
- W systemie Windows może zostać przełączona w tryb pracy w Unicode.
- Do obsługi błędów stosowane są wyjątki własnych klas z modułu Error. Jedynie funkcje wykonujące proste obliczenia lub konwersje nigdy nie rzucają wyjątków.
- Nastawiona jest na maksymalną wydajność, ale nie kosztem kontroli błędów. Bardzo ważna jest też wygoda.
- Wymaga dobrego kompilatora C++, ponieważ używa zaawansowanych cech języka takich jak częściowa specjalizacja szablonów. Kompilacja była testowana na Visual C++ 2005 oraz GCC 4.
- Wszystkie elementy zgromadzone są w przestrzeni nazw common.
- Komentarze w kodzie oraz dokumentacja są po polsku. Identyfikatory w kodzie są po angielsku. Komunikaty błędów i inne łańcuchy są po angielsku.
- Nagłówki są udokumentowane za pomocą Doxygen.
- Pliki z kodem mają polskie znaki w kodowaniu Windows-1250 z końcami wiersza w formacie Windows
"\r\n". Biblioteka posiada funkcje do konwersji polskich znaków i końców wiersza. - Polega na rozmiarze, a nawet budowie bitowej typów atomowych takich jak znaki, liczby całkowite i zmiennoprzecinkowe. Przez to nie nadaje się do kompilacji na platformach z inną endianowością czy 64-bitowych.
Biblioteka ma postać plików z kodem C++ - CPP i HPP. Możesz z tym kodem zrobić co chcesz. Najprościej jest po prostu skopiować te pliki i dodać do swojego projektu.
Poniżej opisana jest jedna z możliwości - przygotowanie kodu jako biblioteki LIB i jej użycie w projekcie w Visual C++ 2005. Niestety nie jest to tak banalne jak rozpakować i od razu zacząć używać. To nie moja wina - takie są uroki języka C++...
Aby przygotować bibliotekę LIB:
- Utwórz projekt Visual C++ lub otwórz istniejący, który znajdziesz w pliku Common.sln.
- Wejdź do opcji projektu i zmień:
- Configuration Properties > General > Character Set: zależnie czy chcesz używać Unicode, ustaw na "Not Set" lub "Use Unicode Character Set" (w obydwu konfiguracjach - Debug i Release).
- Configuration Properties > C/C++ > Code Generation > Runtime Library: zależnie jak będziesz kompilował kod swoich programów korzystających z CommonLib: jeśli chcesz aby nie musiały używać dodatkowych plików DLL z biblioteką standardową C (których część użytkowników nie ma), przestaw na "Multithreaded Debug" (konfiguracja Debug) i "Multithreaded" (konfiguracja Release). Jeśli wolisz otrzymywać mniejsze pliki EXE, wybierze "Multithreaded Debug DLL" (konfiguracja Debug) i "Multithreaded DLL" (konfiguracja Release).
- Configuration Properties > C/C++ > Preprocessor > Preprocessor Definitions: W obydwu konfiguracjach (w każdej osobno) dopisz wybrane makra odpowiednio do bibliotek, których będziesz używał w swoim kodzie: USE_SDL, USE_OPENGL, USE_FMOD, USE_WINSOCK, USE_DEVIL, USE_AVI_FILE
- Jeśli nie masz zainstalowanej biblioteki zlib, nie chcesz jej używać albo w ogóle nie wiesz co to jest, usuń z projektu pliki ZlibUtils.cpp i ZlibUtils.hpp.
- Skompiluj bibliotekę w obydwu konfiguracjach - Debug i Release.
- Załóz na swoim dysku jakiś katalog, gdzie umieścisz skompilowaną bibliotekę jako gotową do używania w projektach. Utwórz w nim podkatalog include i skopiuj do niego pliki HPP. Utwórz katalog lib i skopiuj do niego pliki Common.lib (który powstał w podkatalogu Release) i Common_d.lib (który powstał w katalogu Debug).
- Skonfiguruj ścieżki do katalogów w Visual C++. W tym celu wybierze polecenie z menu Tools > Options. Po lewej zaznacz Projects and Solutions > VC++ Directories. Do "Include files" dopisz ścieżkę do podkatalogu includes. Do "Library files" dopisz ścieżkę do podkatalogu lib.
Aby skorzystać z tak skompilowanej biblioteki w swoim projekcie:
- Utwórz lub otwórz istniejący projekt programu w Visual C++.
- Dołącz wybane nagłówki biblioeki w postaci takiej jak np.
include <Common/Base.hpp>. Jeśli używasz jakichkolwiek nagłówków CommonLib, musisz najpierw włączyć nagłówek Base.hpp. - Wejdź do opcji projektu i zmień:
- Configuration Properties > General > Character Set: Tak jak wyżej.
- Configuration Properties > C/C++ > Code Generation > Runtime Library: Tak jak wyżej.
- Configuration Properties > C/C++ > Preprocessor > Preprocessor Definitions: Tak jak wyżej.
- Opcjonalnie, jeśli masz procesor dwurdzeniowy: Configuration Properties > C/C++ > Command Line > Additional Options: Wpisz "/MP" (w obydwu konfiguracjach). To przyspieszy kompilację.
- Opcjonalnie, skonfiguruj Precompiled Headers. Dopisz też do nich włączenie nagłówków CommonLib. Jeśli jeszcze nie wiesz, co to jest Precompiled Headers, dowiedz się czym prędzej - ten prosty mechanizm Visual C++ wielokrotnie przyspiesza kompilację!
Moduł podstawowy
Dokumentacja: Module_Base
Elementy modułu: Moduł Base
Nagłówek: Base.hpp
- Podstawy:
- Włącza m.in.: <cassert>, <string>, <vector>
- Typy podstawowe, m.in.: uint1, uint2, uint4, uint8 int1, int2, int4, int8
- Różne drobne, użyteczne makra i funkcje
- Operator dosłownej reinterpretacji bitowej common::absolute_cast
- Inteligentne wskaźnki i uchwyty
- Parametryzowane polityką zwalniania
- Z wyłączną własnością lub ze zliczaniem referencji
- Funkcje do operacji na znakach i łańcuchach, w tym oprócz prostych także:
- Konwersja między stronami kodowymi: Windows-1250, ISO-8859-2, IBM CP852, UTF-8
- Konwersja między łańcuchami ANSI i Unicode (tylko Windows)
- Dopasowanie do maski ze znakami wieloznacznymi
'*' i '?' - Odległość edycyjna łańcuchów - Levenshtein Distance
- Porównywanie łańcuchów w tzw. porządku naturalnym
- Funkcje do operacji na ścieżkach do plików
- Działające zarówno dla ścieżek Windows jak i Linux
- Konwersje różnych typów do i z łańcucha znaków, w tym:
- Dokładny pomiar czasu i czekanie określony czas
- Podstawowe typy, stałe, makra i funkcje matematyczne, w tym oprócz prostych także:
- Funkcja wygładzająca metodą Critically Damped Smoothing
- Funkcje okresowe - sinus, trójkąta, prostokątna, piłokształtna, PWM
- Generator liczb pseudolosowych
- Bardzo szybki, deterministyczny
- Losowanie liczb różnych typów
- Generowanie losowych danych binarnych
- Losowanie liczb o rozkładzie normalnym (Gaussa)
- Generator unikatowych identyfikatorów
- Parser parametrów przekazanych z wiersza poleceń
- Szybki i prosty w użyciu
- Wsparcie zarówno dla main (
int argc, char **argv), jak i WinMain (char *CmdLine) - Obsługuje opcje krótkie (jednoznakowe) i długie (wieloznakowe), w formatach:
- Linuksa:
-a -b -c -abc --długa -lARG -l ARG -l=ARG - Windowsa:
/a /b /c /długa /lARG /l ARG /l=ARG
- Klasa bazowa dla wzorca projektowego Singleton
Dokumentacja:
Module_Config
Elementy modułu:
Moduł Config
Nagłówek:
Config.hpp
Obsługa plików konfiguracyjnych
Obsługuje hierarchiczne pliki konfiguracyjne o składni wzorowanej na C/C++.
Data i czas
Dokumentacja: Module_DateTime
Elementy modułu: Moduł DateTime
Nagłówek: DateTime.hpp
- Dostępne typy:
- Dokładne pobieranie bieżącego czasu
- Konwersja do i z łańcucha, dowolne formatowanie
- Konwersje między typami daty i czasu - z tej biblioteki oraz z biblioteki standardowej C
- Przeładowane operatory
Otoczka na pojedynczą wartość dowolnego typu
Dokumentacja: Module_Dator
Elementy modułu: Moduł Dator
Nagłówek: Dator.hpp
Klasy wyjątków do obsługi błędów
Dokumentacja: Module_Error
Elementy modułu: Moduł Error
Nagłówek: Error.hpp
- Ujednolicony mechanizm obsługi błędów w programie z użyciem wyjątków C++
- Wyjątek przechowuje stos komunikatów
- Makra ułatwiające obsługę błędów
- Wsparcie dla błędów zgłaszanych przez: errno, Win32API, SDL, OpenGL, DirectX, FMOD, WinSock, DevIL, AVIFile
Obsługa plików i systemu plików
Dokumentacja: Module_Files
Elementy modułu: Moduł Files
Nagłówek: Files.hpp
- common::FileStream - klasa strumienia do zapisywania i odczytywania treści pliku
- common::DirLister - klasa do listowania zawartości katalogu
- Funkcje do operacji na systemie plików, w tym:
- Zapisywanie i odczytywanie całych plików
- Sprawdzanie, czy plik albo katalog istnieje
- Odczytywanie i zapisywanie daty modyfikacji, utworzenia i ostatniego dostępu
- Tworzenie, usuwanie, zmiana nazwy i przenoszenie plików i katalogów
- Zapisywanie i odczytywanie plików i strumieni tekstowych w różnym kodowaniu, w tym Unicode (tylko Windows).
Bardzo szybki alokator pamięci
Dokumentacja: Module_FreeList
Elementy modułu: Moduł FreeList
Nagłówek: FreeList.hpp
Szablony klas common::FreeList oraz common::DynamicFreeList. Obiekty tych klas to napisane we własnym zakresie alokatory przeznaczone do alokowania dużych ilości zmiennych jednego wybranego typu, które działają znacząco szybciej niż standardowe operatory new i delete.
Mechanizm logowania komunikatów
Dokumentacja: Module_Logger
Elementy modułu: Moduł Logger
Nagłówek: Logger.hpp
- Bezpieczny wątkowo.
- Opcjonalnie logowaine odbywa się w osobnym wątku.
- Uogólnienie logów: Log to abstrakcyjny obiekt, którym może być plik, konsola i dowolne inne miejsce. Można pisać własne logi.
- Rozdzielenie komunikatów od logów: Zapisywane komunikaty trafiają do centralnego loggera, a stamtąd dopiero każdy jest mapowany na zero, jeden lub więcej logów.
- Uogólniony podział komunikatów: Komuniaty są opisywane przez maskę bitową, która może oznaczać kategorię, priorytet lub cokolwiek i którą można wykorzystać do mapowania na logi docelowe, na prefiksy, kolory i dowolne inne rzeczy zależnie od loga.
Moduł matematyczny
Dokumentacja: Module_Math
Elementy modułu: Moduł Math
Nagłówek: Math.hpp
- Wsparcie dla obiektów geometrycznych:
- Punkt 2D, prostokąt 2D, prosta 2D
- Wektor 2D, 3D, 4D
- Macierz 4x4
- Kwaternion
- Płaszczyzna 3D
- Prostopadłościan
- Kolor
- Promień/prosta 3D
- Sfera
- Trójkąt 3D
- Frustum
- Kolizje między różnego rodzaju obiektami geometrycznymi
- Tablice liczb losowych o rozkładzie progresywnego Poisson Disc
Przyrząd do pomiarów czasu i wydajności
Dokumentacja: Module_Profiler
Elementy modułu: Moduł Profiler
Nagłówek: Profiler.hpp
Hierarchia klas strumieni
Dokumentacja: Module_Stream
Elementy modułu: Moduł Stream
Nagłówek: Stream.hpp
Biblioteka do wielowątkowości i synchronizacji
Dokumentacja: Module_Threads
Elementy modułu: Moduł Threads
Nagłówek: Threads.hpp
Parser składni języka podobnego do C/C++
Dokumentacja: Module_Tokenizer
Elementy modułu: Moduł Tokenizer
Nagłówek: Tokenizer.hpp
Moduł wspmagający biblioteki zlib
Dokumentacja: Module_ZlibUtils
Elementy modułu: Moduł ZlibUtils
Nagłówek: ZlibUtils.hpp
Od wersji 8.0 biblioteka posiada w systemie Windows możliwość pracy ze znakami i łańcuchami Unicode. Aby przełączyć CommonLib w tryb pracy z Unicode, należy podjąć te same kroki, co podczas przełączania do tego trybu WinAPI i DirectX. Trzeba w tym celu zdefiniować dla całego projektu makra preprocesora UNICODE i _UNICODE. W przypadki Visual C++ można przestawić w opcjach projektu, na zakładce Configuration Properties > General, opcję Character Set na Use Unicode Character Set - wówczas to makro zostanie dodane automatycznie.
Przy starannym stosowaniu się do poniższych zaleceń można pisać kod programu korzystający z WinAPI, DirectX i CommonLib tak, aby kompilował się i działał zarówno ze znakami Unicode, jak i zwykłymi ANSI, bez jakichkolwiek zmian w kodzie (wystarczy zmiana opisanego wyżej ustawienia).
Moduł definiuje typ znakowy common::tchar jako alias do char lub wchar_t oraz typ łańcuchowy common::tstring jako alias do std::string lub std::wstring, zależnie od ustawienia. Prawie wszystkie klasy i funkcje CommonLib korzystają z tych typów, tak że automatycznie przełączają się na używanie znaków i łańcuchów Unicode zależnie od tego ustawienia.
Łańcuch znaków ANSI zapisuje się w kodzie C++, jak wiadomo, w taki sposób: "ABC". Łańcuch znaków Unicode zapisuje się tak: L"ABC". Moduł Base definiuje makro, którym należy obejmować wszystkie łańcuchy w kodzie, jeśli chcemy, aby zostały zamienione na odpowiednią postać (ANSI lub Unicode) zależnie od ustawienia. Przykład: _T("ABC").
Jeśli piszemy kod, który ma działać zarówno ze znakami ANSI jak i Unicode, jako makro oznaczające nazwę bieżącego pliku źródłowego (podawane do konstruktora klas wyjątków modułu Error) używać należy zdefiniowanego w module Base makra __TFILE__. Podobnie, makro przechowujące sygnatuję bieżącej funkcji to __TFUNCSIG__.
Do konwersji między standardami kodowania znaków użyj funkcji common::ConvertUnicodeToChars i common::ConvertCharsToUnicode. Działają one tylko w Windows. Są niezależne od ustawienia _UNICODE. Pozwalają zamieniać łańćuchy między kodowaniem UTF-16 (Unicode), UTF-8 i ANSI (np. Windows-1250 w przypadku języka polskiego).
Jeśli piszesz kod, który ma się kompilować bez zmian zarówno w ANSI jak i Unicode, przydatne będą funkcje konwertujące łańćuchy między kodowaniem ASNI (typ string) lub Unicode (typ wstring), a tym używanym zależnie od ustawienia (typ common::tstring). Te funkcje to: common::TstringToString, common::StringToTstring, common::TstringToWstring, common::WstringToTstring. Jak można się domyślić, połowa z nich (która, to zależy od ustawienia) nic nie robi, tylko zwraca podany łańcuch.
Do zapisywania i odczytywania plików tekstowych w różnym kodowaniu służą funkcje modułu Files: common::SaveUnicodeToFile, common::SaveUnicodeToStream, common::LoadUnicodeFromFile, common::LoadUnicodeFromStream. W razie potrzeby automatycznie konwertują kodowanie znaków. Obsługują kodowanie ANSI, UTF-16 LE i UTF-8. Funkcje zapisujące mogą dołączać nagłówek BOM. Funkcje odczytujące, na podstawie nagłówka i analizy treści, mogą automatycznie rozpoznać kodowanie.
Skąd taka nazwa?
W moim głównym kodzie, z którego biblioteka się wywodzi, mam w Visual C++ w Solution szereg projektów. Jednym z nich jest silnik graficzny, innym pomocnicze narzędzie konsolowe czy program testujący. Kod wspólny dla wszystkich projektów zgromadziłem w katalogu, który nazwałem Common i tak już zostało.
Skąd taka wersja?
Faktycznie nie było CommonLib 4 czy 5, a wersja 6.1 była tak różna od 6.0, że powinna się nazywać 7.0. Jednak chciałem zrównać numerację biblioteki z numeracją mojego głównego projektu - silnika graficznego, na potrzeby którego napisałem tą bibliotekę.
Nowe wydanie - CommonLib 8.0 - nie oznacza że zacząłem pisać od nowa swój silnik graficzny. Oznacza raczej, że biblioteka przeszła wielkie zmiany po tym, jak silnik w wersji 7 został uznany za skończony.
Biblioteka przeszła wielkie zmiany. Logicznie większość kodu jest nadal kompatybilna wstecz, ale tekstowo zmiany są tak duże, że praktycznie nie ma sensu robić Diff. Ogólne, duże nowości:
- W systemie Windows biblioteka może być teraz przełączona w tryb wsparcia dla Unicode (patrz Obsługa Unicode).
- Komentarze w nagłówkach zostały zamienione na format Doxygen, co umożliwiło automatyczne wygenerowanie niniejszej dokumentacji.
- Moduł Math: Kiedy zdefiniowane jest makro USE_DIRECTX, typy matematyczne są aliasami do typów z D3DX, więc nie jest juź potrzebna żadna konwersja. (VEC2 to D3DXVECTOR2, VEC3 to D3DXVECTOR3, VEC4 to D3DXVECTOR4, MATRIX to D3DXMATRIX, QUATERNION to D3DXQUATERNION, PLANE to D3DXPLANE.) W przeciwnym wypadku moduł definiuje własne struktury o takiej samej budowie bitowej i zbliżonej funkcjonalności.
- Wszystkie (prawie) łańcuchy w kodzie, w tym głównie komunikaty błędów, są teraz w języku angielskim.
Inne zmiany:
- Moduł Base
- Moduł Math
- Moduł Stream
- Moduł ZlibUtils
- Poprawiony błąd - strumienie dekompresji niepotrzebne rzucały asercją mimo że wszystko było OK.
- Inne drobne poprawki...
Biblioteka była przez ostatnie miesiące uzupełniana i poprawiana na bieżąco wraz z rozwojem silnika graficznego 3D, na potrzeby którego powstała.
- Moduł Base
- Moduł Error
- Dodane makra dla wygody: ERR_GUARD_BOOL, ERR_GUARD_DIRECTX, ERR_GUARD_BOOL_D, ERR_GUARD_DIRECTX_D
- Poprawiony błąd w funkcji common::MustStrToSth.
- Moduł Math
- Dodana funkcja common::PerpedicularVectors - oblicza dwa dowolne wektory prostopadłe do danego.
- Dodana funkcja common::ColorDistance - zwraca "odległość" dwóch kolorów.
- Dodana funkcja common::CapsuleVolume licząca objętość kapsuły.
- Dodane funkcje common::SphereInBox i common::BoxInSphere.
- Dodane funkcje do kolizji trójkąta z prostpadłościanem: common::TriangleInBox, common::TriangleToBox.
- Dodane funkcje do kolizji z płaskim wielokątem w 3D: common::PointInPolygon, common::RayToPolygon.
- Dodane funkcje do kolizji poruszających się obiektów: common::SweptSphereToSphere, common::SweptBoxToBox.
- Dodane funkcje do losowania punktów z wnętrza brył:common:: RandomPointInUnitSphere, common::RandomPointInUnitCircle, common::RandomPointInCapsule.
- Ulepszona funkcja common::RayToTriangle - teraz można wybrać czy ma być Backface Culling.
- Poprawione błędy w funkcjach: common::LinesIntersection, common::RotationYawPitchRoll, common::RotationYawPitchRollInv, common::Normalize (do kwaterninów), common::Log (do kwaternionów), common::ClosestPointOnLineSegment, common::RayToSphere.
- Moduł Stream
- Moduł Tokenizer
- Dodane metody do klasy common::Tokenizer czyniące parsowanie jeszcze wygodniejszym: common::Tokenizer::QueryToken, common::Tokenizer::QueryEOF, common::Tokenizer::QueryEOL, common::Tokenizer::QuerySymbol, common::Tokenizer::QueryIdentifier, common::Tokenizer::QueryKeyword, common::Tokenizer::AssertToken, common::Tokenizer::AssertIdentifier.
- Poprawiony błąd tokenizacji liczb.
- Poprawione metody GetUint*, GetInt*, MustGetUint*, MustGetInt*, GetFloat, GetDouble - w przypadku błędu zamiast wykonywać assert rzucają wyjątek błędu tokenizacji.
Od wersji 6.0 biblioteka zmieniła się właściwie nie do poznania. Dlatego nie ma sensu wypisywać zmian. Szczególnie, że pewnie nikt z niej wcześniej nie korzystał :P
Biblioteka dostępna jest na licencji GNU LGPL, co znaczy m.in., że masz dostęp do jej kodu źródłowego oraz że możesz używać jej za darmo w dowolnych swoich programach, także zamkniętych.
Adam Sawicki
E-mail:
sawickiap@poczta.onet.pl
WWW:
http://regedit.gamedev.pl
IRC: warsztat @ PolNet
Gadu-Gadu: 2272474