====== Biblioteka Boost Format ====== --- //[[tpiechot@stud.elka.pw.edu.pl|Tomasz Piechota G1ISI]] 2008/04/16 23:44// Biblioteka Boost Format dostarcza klasę do formatowania argumentów zgodnie z dostarczonym łańcuchem formatującym. Podstawowe różnice w stosunku do funkcji printf, to m.in.: * deterministyczne zachowanie, nawet w przypadku podania błędnych argumentów (generuje wyjątek lub ignoruje błąd, w zależności od ustawień). * naturalne wsparcie typów zdefiniowanych przez użytkownika. * podawanie argumentów poprzez kolejne wywołania operatora %. ===== Podstawowe przykłady ===== Aby skorzystać z biblioteki Boost Format należy przede wszystkim dołączyć nagłówek boost/format.hpp. Nagłówek ten dostarcza nam do użycia klasę boost::format posiadającą cztery konstruktory: format(const charT* str); format(const charT* str, const std::locale & loc); format(const string_t& s); format(const string_t& s, const std::locale & loc); Myślę, że komentarz tutaj jest zbędny. Jako pierwszy argument przekazujemy oczywiście łańcuch formatujący. Oto kilka najprostszych przykładów użycia: #include #include // (...) boost::format frm("Abc %1% %2% %3% %4% cbA"); // Tworzymy nowy obiekt frm % "A"; // Przypisujemy pierwszy argument frm % "B" % "C" % "D"; // Kolejne argumenty przypisujemy za jednym zamachem std::cout << frm << std::endl; // --> "Abc A B C D cbA" Wszystkie operacje możemy oczywiście wykonać w jednej linii. Liczba argumentów przekazywanych do obiektu jest dowolna, a każdy argument może wystąpić w przekazywanym łańcuchu dowolną ilość razy. std::cout << boost::format("%1% %2% %1%") % "A" % "B" << std::endl; // --> "A B A" Sformatowany string możemy także pobrać do zmiennej: std::string s = frm.str(); // Odpowiednia liczba argumentów musi być uprzednio przekazana do obiektu std::string s2 = boost::str(frm); Tego samego obiektu format możemy użyć ile razy chcemy, dostarczając nowego zestawu argumentów. boost::format frm("%% _%1%_ _%2%_ _%3%_ %%"); // Podwójne użycie znaku specjalnego % spowoduje jego wypisanie na ekran frm % "A" % 2.32 % 10; std::cout << frm << std::endl; // --> "% _A_ _2.32_ _10_ %" frm % "Foo" % 2 % "Bar"; std::cout << frm << std::endl; // --> "% _Foo_ _2_ _Bar_ %" ===== Formatowanie argumentów ===== Na wstępie należałoby zaznaczyć, że argumenty w łańcuchu formatującym mogą być definiowane na trzy różne sposoby: - Odziedziczony po funkcji printf format: ''%spec'' - ''%|spec|'' z nawiasami użytymi w celu dopuszczenia parametry ''type-char'' jako opcjonalny - ''%N%'' - prosty, przejrzysty sposób bez żadnego formatowania. gdzie spec ma następującą składnie: ''[ N$ ] [ flags ] [ width ] [ .precision ] type-char'' Argumenty ujęte w nawiasy kwadratowe są opcjonalne, a dla typu drugiego (%|spec|) również ''type-char''. ==== Znaczenie poszczególnych parametrów ==== * N - numer argumentu * flags - jedna lub więcej spośród flag: * '-' - wyrównuje wynikowy łańcuch do lewej strony (ma sens przy jednoczesnym użyciu parametru ''width'' * '=' - centruje wynikowy łańcuch (sens w przypadku jak wyżej) * '_' - wyrównuje wynikowy łańcuch do prawej strony * '+' - włącza wyświetlanie znaku także liczb dodatnich * '#' - włącza wyświetlanie bazy liczby (np. ''0x'' dla heksadecymalnych) oraz przecinek oddzielający część dziesiętną * '0' - wypełnia puste miejsca zerami * ' ' - poprzedza wynikowy łańcuch dodatkową spacją, jeśli nie jest wyświetlany minus ani plus. * width - wymuszenie minimalnej szerokości * precision - ilość cyfr po przecinku dla np trybu naukowego (type-char = e) lub ilość cyfr w ogóle dla np trybu general (type-char = g) * type-char - typ formatowania (jest to tylko sugestia typu, jeśli formatowanie nie powiedzie się, zostanie użyty inny domyślny typ): * p lub x - heksadecymalny * o - ósemkowy * e - naukowy * f - zmiennoprzecinkowy * g - ogólny (domyślny) * X, E or G - tak samo jak w przypadku x, e i g, ale wyświetla duże litery * d, i or u - dziesiętny * s or S - łańcuch znaków * c or C - jeden znak ==== Przykłady formatowania przy użyciu łańcucha znaków ==== Przykłady użycia poszczególnych formatowań pokazane są w poniższym kodzie: // Sposób pierwszy według formatu %spec std::cout << boost::format("_%1$d_ _%1$o_ _%1$#o_") % 10 << std::endl; // --> "_10_ _12_ _012_" std::cout << boost::format("_%1$d_ _%1$x_ _%1$#X_") % 47 << std::endl; // --> "_47_ _2f_ _0X2F_" std::cout << boost::format("_%1$5.2f_ _%1$=5.0f_ _%1$05.0f_") % 48.237 << std::endl; // --> "_48.24_ _ 48 _ _00048_" // Sposób drugi według formatu %|spec| std::cout << boost::format("_%|1$-6|_ _%|2$+-7.2f|_") % "foo" % 23.199 << std::endl; // --> "_foo _ _+23.20 _" std::cout << boost::format("_%|6.2e|_ _%|11.3e|_") % 30.2312 % 312.3 << std::endl; // --> "_3.02e+001_ _ 3.123e+002_" // Trzeci sposób według formatu %N% std::cout << boost::format("%1% %2% %1% %2%") % "o_O" % "O_o" << std::endl; // --> "o_O O_o o_O O_o" W jednym łańcuchu możemy korzystać z różnych sposóbów, natomiast błędem jest użycie zarówno argumentów numerowanych, jak i nienumerowanych. std::cout << boost::format("%d %|s|") % 10 % "foo" << std::endl; // OK std::cout << boost::format("%1$s %2%") % "foo" % "bar" << std::endl; // OK std::cout << boost::format("%1$d %s") % 10 % "bar" << std::endl; // BŁĄD! Ciekawym przykładem może być również technika wymuszająca wcięcie (z wypełnieniem lub bez) niezależnie od długości poprzednich argumentów: // w obu przypadkach liczba 199 zaczyna się po 15 znakach std::cout << boost::format("%s %|15t|%d%") % "Abcdefgh" % 199 << std::endl; // --> "Abcdefgh 199" // z wypełnieniem (duże T + wypełniacz) std::cout << boost::format("%s %|15T-|%d%") % "Abcde" % 199 << std::endl; // --> "Abcde ---------199" ==== Formatowanie poprzez manipulatory - metoda group() ==== Na wygląd formatowania poszczególnych parametrów możemy wpływać także mając już zdefiniowany łańcuch formatujący (utworzony obiekt klasy boost::format). Tak zdefiniowane formatowanie ma wyższy priorytet od tego wpisanego w konstruktorze. Manipulatory dostępne są po dołączeniu biblioteki ''iomanip''. #include // (...) // Pierwsze wystapienie argumentu pierwszego również ma szerokość 5 (priorytet manipulatora większy) std::cout << boost::format("_%1$3d_ _%1%_") % boost::io::group(showpos, std::setw(5), 101); // --> "_ +101_ _ +101_" // Manipulatory nie resetują wszystkich, tylko nadpisują istniejące flagi std::cout << boost::format("_%1$-3d_ _%1%_") % boost::io::group(showpos, std::setw(5), 101); // --> "_+101 _ _ +101_" , setfill('x') // Inny przykład std::cout << boost::format("_%1%_") % boost::io::group(hex, std::setw(5), 99); // --> "_ 63_" std::cout << boost::format("_%|=10|_") % boost::io::group(setfill('-'), 101); // --> "_----101---_" Funkcja ''boost::io::group'' może przyjąc wiele manipulatorów, jednak ostatnim argumentem musi być wartość przekazywanego parametru. ===== Wyjątki i ich konfiguracja ===== Boost Format wprowadza pewną liczbę zasad korzystania z objektów ''boost::format'': * Łańcuch znaków musi przestrzegać składni opisanej powyżej * Użytkownik musi dostarczyć dokładnie taką liczbę argumentów, jakie zadeklarował w łańcuchu, przed pobraniem sformatowanego wyjścia * Używając metod ''modify_item'' oraz ''bind_arg'', indeksy elementów i argumentów muszą być właściwe Jeśli klasa format wykryje, że jedna z powyższych zasad nie jest spełniona, odpowiedni wyjątek zostanie rzucony. Użytkownik jednak ma możliwość wpłynięcia na zachowanie obiektu, deklarując które błędy mają skutkować wyjątkiem, a które mają zostać przemilczane (wyjście zostanie wygenerowane z pustą wartością danego argumentu). W celu ustawienia ''bitów błędów'' wykorzystuje się metody: unsigned char exceptions(unsigned char newexcept); // ustaw i zwróć unsigned char exceptions() const; // tylko zwróć Dostępne są następujące bity błędów: * boost::io::bad_format_string_bit - reakcja na błędny format łańcucha formatującego * boost::io::too_few_args_bit - za mało argumentów * boost::io::too_many_args_bit - za dużo argumentów * boost::io::out_of_range_bit - nieprawidłowy index dostarczony podczas wywołania np metody ''modify_item'' * boost::io::all_error_bits - reakcja na wszystkie błędy * boost::io::no_error_bits - brak reakcji na jakikolwiek błąd Przykładowy kod prezentujący działane metod exceptions i bitów błędów: boost::format f("_%1%_ _%2%_"); f % 10; try { std::cout << f << std::endl; } catch (boost::io::too_few_args &e) { std::cout << "Za malo argumentow" << std::endl; } f.exceptions(~boost::io::too_few_args_bit); try { std::cout << f << std::endl; } catch (boost::io::too_few_args &e) { std::cout << e.what() << std::endl; } // --> "Za malo argumentow" // --> "_10_ __" ===== Przykład użycia ===== Poniższy program wyświetla listę produktów w postaci sformatowanej tabeli #include #include #include #include using namespace std; using namespace boost; class Produkt { public: Produkt(string n, int i, float c, float z) : nazwa(n), ilosc(i), cena(c), zmiana(z) { } string nazwa; int ilosc; float cena; float zmiana; }; int main(int argc, char *argv[]) { // Lista obiektów list p; p.push_back(Produkt("Widelec", 4, 4.99f, 1.021f)); p.push_back(Produkt("Łopatka", 32, 2.99f, -10.2292f)); p.push_back(Produkt("Packa", 19, 19.39f, 19.1133f)); // pozioma linia format line("%|55T-|"); // nazwa: %|1$-.20s| - wyrownanie do lewej, maksimum 20 znakow // ilość: %2% - bez dodatkowego formatowania // cena: %|3$.2f| - liczba zmiennoprzecinkowa, do dwóch miejsc po przecinku // zmiana: %|4$+10.4f| - wymuszenie wyświetlania znaku +, szerokosc 10, do 4 miejsc po przecinku, wyrównanie do prawej format row("|%|1$-20.20s|%|21t||%2%%|32t||%|3$.2f|%|43t||%|4$+10.4f|%|54t||"); cout << line << endl; cout << row % "Nazwa produktu" % "Ilosc" % "Cena" % "Zmiana (%)" << endl; cout << line << endl; for (list::const_iterator it = p.begin(); it != p.end(); it++) cout << row % it->nazwa % it->ilosc % it->cena % it->zmiana << endl; cout << line << endl; return 0; }