Nagłówek: Base.hpp
Elementy modułu: Base Module
Ten moduł to moduł bazowy. Plik Base.hpp włączają wszystkie pozostałe pliki biblioteki i użytkownik również jest spodziewany aby to robić przed włączaniem wszelkich innych nagłówków biblioteki, ponieważ korzystają one ze zdefiniowanych w Base.hpp typów i innych elementów, a same go nie włączają.
Nie używa Error ani żadnego innego modułu. To inne jego używają. Żadne z jego funkcji nie zgłaszają błędów przez wyjątki.
'*' i '?'string s = common::Format("x=#, y=#") % x % y;-a -b -c -abc --długa -lARG -l ARG -l=ARG/a /b /c /długa /lARG /l ARG /l=ARGTo brakujący w C++ operator dosłownej reinterpretacji bitowej między wartościami dowolnego typu. Ma sens tylko w przypadku typów o takim samym rozmiarze w sensie liczby zajmowanych bajtów. Jest to sprytnie napisana funkcja szablonona, której można używać niczym wbudowanych operatorów rzutowania C++ takich jak static_cast czy reinterpret_cast. Jej wynalazcą jest Karol Kuczmarski "Xion". Przykład:
float F = 10.5f; uint4 I1 = static_cast<uint4>(F); uint4 I2 = absolute_cast<uint4>(F);
I1 będzie wynosiło 10 - nastąpiła konwersja wartości liczbowej. I2 będzie wynosiło 1093140480 - nastąpiła dosłowna reinterpretacja bitów.
Więcej informacji na temat problemu konwersji między typami w C++ znajdziesz w moim artykule "Rzutowanie typów w C++", Adam Sawicki, Czerwiec 2006, http://regedit.gamedev.pl/produkcje/artykuly/rzutowanie_typow_w_cpp.php5
Moduł Base oferuje konwersje między różnymi typami a łańcuchami w obydwie strony, także z dodatkowymi opcjami. Służą do tego funkcje takie jak np.: common::IntToStr, common::StrToInt, common::FloatToStr, common::StrToFloat, common::BoolToStr, common::StrToBool itd.
Uogólnioną wersją tych konwersji są szablony funkcji SthToStr i StrToSth. Obsługują one następujące typy:
Próba użycia tych funkcji dla nieobsługiwanego typu zakończy się Assertion Failed. Można sprawdzić, czy dany typ jest obsługiwany używając:
bool B1 = StrToSth_obj<SOME_TYPE>::IsSupported(); bool B2 = SthToStr_obj<SOME_TYPE>::IsSupported();
Można pisać konwersje do i z własnych typów. Szablon takiej konwersji jest następujący. Należy zamienić T na konkretny typ i wpisać konwersję w miejscu //. Kod ten należy wpisać poza jakąkolwiek przestrzenią nazw.
template <> struct SthToStr_obj<T> { void operator () (string *Str, const T &Sth) { // } static inline bool IsSupported() { return true; } }; template <> struct StrToSth_obj<T> { bool operator () (T *Sth, const string &Str) { // } static inline bool IsSupported() { return true; } };
Jeśli StrToSth się nie uda, wartość parametru wyjściowego jest niezdefiniowana.
Moduł Error rozszerza ten mechanizm o szablon funkcji MustStrToSth, który w przypadku niepowodzenia rzuca wyjątek.
Klasa common::Format (wraz z przeładowanym dla niej operatorem %) robi za sprintf dla łańcuchów string. Nie jest super szybka, ale jest wygodna w użyciu. Obsługuje wszystkie te typy, co SthToStr. Sposób użycia (jeśli jakiśtam OutputLine oczekuje const string&):
OutputLine( Format("Błąd w pliku # w wierszu #") % "plik.txt" % 123 );
Jeśli potrzeba zapisać do strumienia lub z innych przyczyn operator string() nie działa, używamy metody str:
std::cout << ( Format("Błąd w pliku # w wierszu #") % "plik.txt" % 123 ).str();
Jeśli chcemy niestandardowe formatowanie podczas konwersji z jakiegoś typu na łańcuch, konwertujemy we własnym zakresie:
OutputLine( Format("Błąd w pliku # w wierszu #") % "plik.txt" % IntToStr2R(123, 5) );
Jeśli potrzebujemy znaku '#' i chcemy jako specjalny użyć innego znaku:
OutputLine( Format("Błąd # % w pliku %", '%') % 123 % "plik.txt" );
Moduł zawiera własną, elastyczną i dość potężną, acz prostą implementację inteligentnych wskaźników.
Są cztery rodzaje wskaźników:
Są trzy polityki zwalniania dla wskaźników:
delete x;delete [] x;x->Release();Oraz dwie dla uchwytów [tylko Windows]:
CloseHandle(x);DeleteObject(x);Można też pisać własne polityki zwalniania. Przykłady:
class ReleasePolicy { public: template <typename T> static void Destroy(T *p) { if (p) p->Release(); } };class DeleteObjectPolicy { public: template <typename T> static void Destroy(T p) { if (p != NULL) DeleteObject(p); } };Przykłady klas wskaźników:
scoped_ptr<Klasa> p1;shared_ptr<Klasa> p2;scoped_ptr<Klasa, DeleteArrayPolicy> p3;shared_ptr<IDirect3DTexture9, ReleasePolicy> p4;typedef scoped_handle<HANDLE, CloseHandlePolicy> close_handle; close_handle plik(NULL);Sposób użycia common::scoped_ptr:
- Konstruktor domyślny ustawia wskaźnik pusty (NULL)
scoped_ptr<Klasa> p1;
- Konstruktor inicjalizujący (tylko explicit):
scoped_ptr<Klasa> p1(NULL);
scoped_ptr<Klasa> p2(new Klasa());
- Wyzerowanie wskaźnika (jeśli trzeba, sam zwolni obiekt):
p1.reset();
p1.reset(NULL);
- Ustawienie wskaźnika na nowy obiekt (jeśli trzeba, sam zwolni stary):
p1.reset(new Klasa());
- Odwołanie się pod wskaźnik:
Klasa obj2 = *p1;
p1->MetodaKlasy();
- Otrzymanie prawdziwego wskaźnika:
Klasa *RealPtr = p1.get();
- Porównywanie wskaźników:
if (p1 == NULL) { }
if (RealPtr == p1) { }
- Zamiana:
p1.swap(p2);
swap(p1, p2);
- Odwołanie się do elementu tablicy wskazywanej przez wskaźnik:
scoped_ptr<int, DeleteArrayPolicy> tablica(new int[1024]);
tablica[0] = 1;
Sposób użycia shared_ptr:
- Wspiera to samo co scoped_ptr plus dodatkowo...
- Skopiowanie wskaźnika do drugiego:
Działa także dla klas sparametryzowanych typem klasy nieidentycznym, ale
dającym się skonwertować.
shared_ptr<Klasa> p1;
shared_ptr<Klasa> p2 = p1;
p2 = p1;
p2.reset(p1);
- Porównanie dwóch wskaźników:
if (p1 == p2) { }
Sposób użycia common::scoped_handle i common::shared_handle: Jak odpowiednio common::scoped_ptr lub common::shared_ptr, ale:
* -> [] scoped_handle<HANDLE, CloseHandlePolicy> p1(NULL); p1.reset(NULL);Inteligetne wskaźniki są potrzebne, zwłaszcza do unikania wycieków pamięci kiedy używamy wyjątków. Do napisania własnych zmotywowała mnie chęć uwolnienia się od biblioteki Boost. Pisanie takich wskaźników nie jest trudne, ale wiąże się z podjęciem szeregu trudnych decyzji projektowych. Podjąłem następujące:
* i -> zamiast samej metody get: TAK[] do indeksowania tablic: po namyśle jednak TAK== != : TAK< > <= >= : NIE& : NIE* i -> : TAK, assertT* : TAK, ale jako osobne klasy, bez uogólniania tego z użyciem PolicySzablon klasy common::Singleton to klasa bazowa do implementacji wzorca projektowego singletonu, czyli klasy, której główny (najczęściej jedyny) obiekt jest tworzony automatycznie podczas pierwszego użycia.
Deklarujemy tak:
class MyClass : public common::Singleton<MyClass> { int MyMethod(int x); };
Używamy tak:
int i = MyClass::GetInstance().MyMethod(123);
Obiekt powstanie przy pierwszym użyciu i, jeśli kiedykolwiek powstał, zostanie na końcu programu automatycznie usunięty (wywoła się destruktor).
Klasa common::CmdLineParser oferuje parsowanie parametrów przekazanych do programu z wiersza poleceń. Jest trochę podobna do funkcji getopt z Linuksa.
main(int argc, char **argv), jak i z WinMain(HINSTANCE, HINSTANCE, char *CmdLine, int).-a -b -c -abc --długa -lARG -l ARG -l=ARG /a /b /c /długa /lARG /l ARG /l=ARG Użycie parsera polega na pobieraniu kolejnych informacji. Możliwe wartości:
Na przykład jeśli 'a', 'b' i 'c' to opcje krótkie, z których 'c' oczekuje parametru, dopuszczalne są m.in. takie konstrukcje:
-a -b -c parametr -abc parametr -a -b -c=parametr -ab -c"parametr ze spacją" "-abcparametr ze spacją" /a /b /c parametr /a /b /c="parametr ze spacją" /a /b "/cparametr ze spacją"
Jeśli natomiast "AA" i "BBB" to opcje długie, z czego ta druga oczekuje parametru, dopuszczalne są m.in. takie konstrukcje:
--AA --BB parametr --AA --BB=parametr /AA /BB="parametr ze spacją" --AA /BB "parametr ze spacją"
Kolejność opcji nie jest w żaden sposób kontrolowania, podobnie jak ich obecność czy powtarzanie się wiele razy. To leży już w gestii użytkownika - parser tylko odczytuje i zwraca kolejne napotkane informacje.
Obsługiwana składnia jest skomplikowana i oparta na dogłębnych badaniach zachowania wiersza poleceń Windowsa i Linuksa. Niektóre możliwości przedstawia ten przykład. Jeśli zarejestrowane są opcje:
RegisterOpt(1, 'a', false); RegisterOpt(2, 'b', false); RegisterOpt(3, 'c', true); RegisterOpt(11, "AA", false); RegisterOpt(12, "BBB", true);
Wiersz poleceń wygląda tak:
-a -b -c param -abc="param" "-cparam" /AA --AA "/BBB"=param DUPA --BBB "param"
Wówczas wynikiem kolejnych wywołań ReadNext jest:
Result=RESULT_OPT, Id=1, Param="" Result=RESULT_OPT, Id=2, Param="" Result=RESULT_OPT, Id=3, Param="param" Result=RESULT_OPT, Id=1, Param="" Result=RESULT_OPT, Id=2, Param="" Result=RESULT_OPT, Id=3, Param="param" Result=RESULT_OPT, Id=3, Param="param" Result=RESULT_OPT, Id=11, Param="" Result=RESULT_OPT, Id=11, Param="" Result=RESULT_OPT, Id=12, Param="param" Result=RESULT_PARAMETER, Id=0, Param="DUPA" Result=RESULT_OPT, Id=12, Param="param" Result=RESULT_END, Id=0, Param=""
1.6.1