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=ARG
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
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" );
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);
* 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 PolicyDeklarujemy 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).
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
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.
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.5.5