2009 11 Klasy cech w programowaniu generycznym [Programowanie C C ]


Programowanie C++
Klasy cech
w programowaniu
generycznym
W języku C++ do tworzenia generycznych algorytmów lub struktur
danych używamy szablonów. Artykuł zawiera techniki odpowiadające
instrukcji warunkowej, która będzie wykonywana w czasie kompilacji.
C++200x będzie zawierał kolejnych kilka-
Dowiesz się: Powinieneś wiedzieć: dziesiąt, obecnie udostępnianych przez bi-
" Jak wybierać algorytm lub wartość w czasie " Jak pisać proste programy w C++; blioteki boost (type_traits, call_traits,
kompilacji; " Co to są szablony (templates). function_types). Biblioteki boost są zna-
" Co to są klasy cech (trejty). nym zbiorem bibliotek eksperymental-
nych C++, z których wiele będzie umiesz-
czonych w nowej wersji standardu.
Trejty albo klasy cech są to typy, któ- Przykładem wykorzystania trejtu has_
rych głównym zadaniem jest przechowy- trivial_assign, dostępnego w omawia-
Poziom wanie informacji o innych typach. Mecha- nym zbiorze, jest funkcja fastCopy, po-
trudności nizm ten pozwala uporządkować dostęp kazana na Listingu 2, która kopiuje tabli-
do stałych, które mają podobne znacze- ce, wykorzystując std::memcpy (kopiowa-
nie. Biblioteka standardowa dostarcza trej- nie bajtów), jeżeli elementy tablicy są ty-
tów std::numeric_limits, które definiu- pów, dla których kopiowanie takie jest po-
rogramowanie generyczne w C++(patrz ją wartości graniczne dla wbudowanych ty- prawne, albo algorytm std::copy, jeżeli na-
ramka) wykorzystuje podzbiór kon- pów liczbowych. Aby pobrać wartość takiej leży wołać operator przypisania dla każde-
Pstrukcji języka, ponieważ byty, który- stałej, piszemy numeric_limits: go obiektu. Trejt has_trivial_assign ba-
mi operujemy, muszą mieć ustaloną wartość, :min() zamiast __DBL_MIN__, numeric_ da, czy typ ma trywialny operator przypi-
znaną podczas kompilacji. Nie można uży- limits::min() zamiast INT_MIN, sania, to znaczy, jeżeli przypisanie dla ty-
wać zmiennych, nie można wykonywać itera- numeric_limits::max() zamiast pu T jest równoznaczne z kopiowaniem
cji (tworzyć pętli) ani używać instrukcji warun- INT_MAX, itd. Taki zapis zwalnia programi- pamięci zajmowanej przez obiekt, to has_
kowych. Zamiast tego stosujemy techniki, które stę z obowiązku wyszukiwania nazwy sta- trivial_assign jest typu true_type,
dają równoważne efekty. W dalszej części tekstu łej dla danego typu oraz nagłówka, który ją has_trivial_assign::value ma war-
będzie przedstawione rozwiązanie, pozwalające deklaruje. tość true, w przeciwnym wypadku trejt
na wybór algorytmu, typu lub pewnej stałej, w dziedziczy po false_type, zaś składowa
zależności od parametrów szablonu, co w pro- Wybór algorytmu value ma wartość false.
gramowaniu generycznym odpowiada instruk- w czasie kompilacji Funkcja fastCopy wykorzystuje dodatko-
cji warunkowej. Biblioteka standardowa udostępnia kilka wy, czwarty argument, który jest tworzony
Przedstawiona technika do wyboru od- innych trejtów, natomiast nowy standard w czasie kompilacji na podstawie informa-
powiedniego algorytmu lub odpowiedniej
wartości wykorzystuje dodatkowe klasy na-
zywane trejtami lub klasami cech. Na Li- Szybki start
Aby uruchomić przedstawione rozwiązania, należy mieć dostęp do dowolnego kompi-
stingu 1 trejtami są klasy numeric_traits,
latora C++ oraz edytora tekstu. Niektóre przykłady zakładają dostęp do bibliotek bo-
dostarczają one stałej min_value, o wartości
ost. Warunkiem ich uruchomienia jest instalacja tych bibliotek (w wersji 1.36 lub now-
zależnej od parametru szablonu, wykorzy-
szej) oraz wykorzystywać kompilator oficjalnie przez nie wspierany, to znaczy msvc
stując specjalizację. Funkcja find_max znaj-
7.1 lub nowszy, gcc g++ 3.4 lub nowszy, Intell C++ 8.1 lub nowszy, Sun Studio 12 lub
duje maksymalną wartość w tablicy. Inicjuje
Darvin/GNU C++ 4.x. Na wydrukach pominięto dołączanie odpowiednich nagłówków
ona zmienną current_max za pomocą trej- oraz udostępnianie przestrzeni nazw, pełne zródła dołączono jako materiały pomoc-
nicze.
tów, a więc różnymi wartościami dla róż-
nych typów.
44 11/2009
Klasy cech w programowaniu generycznym
cji o typie. Jego wartość nie jest istotna, na- Klasa cech boost::call_traits, dostar- dzie Foo (przykładowy typ użytownika), to
tomiast typ pozwala wybrać odpowiednią czana przez biblioteki boost, definiuje mię- param_type dostarczy typu const Foo&.
funkcję kopiującą. Dodatkowy argument, dzy innymi optymalny sposób przekazywa- Możemy zdefinować nagłówek naszej
którego typ jest jedyną istotną informacją nia argumentów dla obiektów danego ty- funkcji tak jak poniżej,
jest często stosowaną techniką w programo- pu. Trejt ten definiuje, oprócz stałych, pew-
waniu generycznym.Kompilator wykorzy- ne pomocnicze typy, co pokazano na Listin- template
stuje typ argumentu do wyboru odpowied- gu 4, pozwalając optymalnie przekazywać void f(typename call_traits::param_type
niej wersji funkcji lub metody, nie zwięk- parametry. Jeżeli parametrem tego szablonu value) {}
szając wielkości kodu wynikowego (opty- będzie int ,składowa param_type będzie de-
maliztor będący częścią kompilatora usuwa finiowała typ int (typy wbudowane przeka- wtedy argument będzie przekazywany
kod związany z argumentami, które nie są zujemy przez wartość), jeżeli parametrem bę- przez wartość dla typów wbudowanych
wykorzystywane).
Innym przykładem wykorzystania klas
Listing 1. Inicjowanie zmiennej za pomocą trejtów
cech jest szablon getId dostarczający identy-
fikatora obiektu. Funkcja ta zwraca identyfi- template struct number_traits {
kator przechowywany w obiekcie, dla obiek- static const int min_value = 0; //dla dowolnego typu stała ma wartość zero
tów typu pochodnego po HasId, albo adres };
dla pozostałych obiektów. Aby wybrać odpo- template<> struct number_traits { //specjalizacja dla typu int
wiedni sposób ,stosujemy trejt is_base_of, static const int min_value = INT_MIN; //definiuje odpowiednią stałą
patrz Listing 3. };
Rozwiązanie wykorzystuje trejt is_base_ template<> struct number_traits { //specjalizacja dla typu long
of, zależny od dwu parametrów, dostarcza- static const long min_value = LONG_MIN;
jący informacji o tym, czy pierwszy typ };
jest klasą bazową dla drugiego. Gdy Base //znajduje maksymalną wartość w tablicy
jest klasą bazową Derived ,to is_base_ template find_max(const T* first, const T* last) {
of jest typu true_type, w T current_max = number_traits::min_value; //wykorzystuje trejty
przeciwnym wypadku is_base_ofDerived> jest typu false_type. Trejt ten if( current_max < *first )
wykorzystujemy do utworzenia pomoc- current_max = *first;
niczego obiektu, a następnie przekazuje- return current_max;
my go jako dodatkowy parametr, który po- }
zwala wybrać jedną z kilku przeciążonych
funkcji w czasie kompilacji. Funkcji getId
możemy używać dla dowolnych obiektów,
Szablony przypomnienie
uzyskując albo adres, albo wynik wołania
Szablony (templates) dostępne w języku C++ umożliwiają implementację generycznych,
metody getId.
to znaczy niezależnych od typów, algorytmów oraz struktur danych. Przykładowy szablon
swap, pokazany poniżej, zamienia zawartość dwu obiektów, możemy go wołać dla dowol-
nych obiektów tego samego typu, jeżeli dostarczają one konstruktora kopiującego i opera-
//typ z własnym identyfikatorem
tora przypisania.
struct ClassWithId : public HasId {
ClassWithId(long id) : HasId(id) { }
template void swap(T& a, T& b) {
};
T tmp = a;
//typ bez identyfikatora
a = b;
struct ClassWithoutId { };
b = tmp;
}
ClassWithId c1(1);
//obiekt z identyfikatorem równym 1
Podczas kompilacji następuje konkretyzacja szablonu, co oznacza generowanie kodu dla wła-
ClassWithoutId c2;
ściwych typów. Kod generowany na podstawie szablonów nie różni się od kodu tworzone-
//obiekt bez identyfikatora
go ręcznie, nie ma żadnych narzutów pamięciowych i czasowych, jedyną niedogodnością jest
getId(c1); //zwraca wartość 1
dłuższy czas kompilacji, ale to zazwyczaj nie jest problemem.
getId(c2); //zwraca adres obiektu c2 Specjalizacja to wersja szablonu, która będzie użyta do generacji kodu zamiast wersji
ogólnej, gdy parametrami będą odpowiednie typy. Przykładem specjalizacji jest szablon
swap pokazany poniżej. Ponieważ typ Foo zawiera jedynie wskaznik na obiekt zawie-
0ptymalizacja
rający składowe (klasa Foo ukrywa implementację), wystarczy zamienić te wskazniki, jeże-
przy pomocy klas cech
li chcemy zamienić zawartość obiektów. Jest to bardziej wydajne niż zamiana przy pomocy
Klasy cech możemy wykorzystywać do
obiektu tymczasowego.
optymalizacji przekazywania argumentów.
struct Foo { //przykładowa klasa, która ukrywa implementację
Dla typów użytkownika argumenty powin-
struct Impl; //klasa wewnętrzna, przechowuje składowe
ny być przekazywane przez stałą referen-
Impl* pImpl_; //wskaznik jest składową publiczną, aby uprościć szablon
cję, ponieważ unika się tworzenia kopii,
};
natomiast dla typów wbudowanych oraz
template<> void swap(Foo& a, Foo& b) { //specjalizacja szablonu swap
dla wskazników argumenty przekazujemy
Foo::Impl* tmp = a.pImpl_; //zamienia wskazniki, a nie całe obiekty
przez wartość, ponieważ tworzenie kopii
a.pImpl_ = b.pImpl_;
jest mało kosztowne, natomiast referencja b.pImpl_ = tmp;
}
wprowadza narzut przy odwoływaniu się
do obiektu.
11/2009 www.sdjournal.org 45
Programowanie C++
(oraz wskazników i referencji) lub przez sta- wa referencji do typu T, który jest parame- call_traits zapewni, że jeżeli parametrem
łą referencję dla typów użytkownika. trem, to gdy przekażemy typ referencyjny ja- będzie typ referencyjny, to referencją będzie
Przy pomocy tego samego trejtu rozwią- ko parametr następuje błąd kompilacji.Roz- ten sam typ.
zuje się problem podwójnej referencji, któ- wiązanie to wykorzystywanie w szablonach Trejty możemy stosować, aby zmniejszyć
ry wynika z tego, że nie można tworzyć re- typu call_traits::reference zamiast wielkość kodu wynikowego oraz aby opty-
ferencji do referencji. Jeżeli szablon uży- T&. Odpowiednia specjalizacja klasy cech malizować jego czas wykonania. Dla każ-
dego typu, dla którego szablon został uży-
ty, jest generowany kod, który jest kompi-
Listing 2. Wykorzystanie klas cech do wyboru algorytmu kopiowania
lowany i dołączany do wersji binarnej two-
template //kopiowanie za pomocą memcpy rzonej aplikacji czy biblioteki. Aby zmniej-
void doFastCopy(const T* first, const T* last, T* result, true_type) { szyć wielkość kodu wynikowego, stosuje się
memcpy(result, first, (last - first)*sizeof(T) ); te same rozwinięcia szablonów dla różnych
} typów, jeżeli są dozwolone konwersje po-
template //kopiowanie za pomocą std::copy między tymi typami. Dodatkową zaletą te-
void doFastCopy(const T* first, const T* last, T* result, false_type) { go rozwiązania jest możliwość wyboru ty-
std::copy( first, last, result ); pu, dla którego operacje na danej platfor-
} mie wykonywane są najszybciej. Przykład
template //algorytm wykorzystuje trejty pokazany na Listingu 5 wykorzystuje trej-
void fastCopy(const T* first, const T* last, T* result) { ty do promocji dla liczb rzeczywistych udo-
doFastCopy(first, last, result, has_trivial_assign() ); //tworzy dodatkowy stępniane przez biblioteki boost.
argument Dla typów reprezentujących liczby rze-
} czywiste, które można konwertować do
double, będzie użyty ten sam kod funk-
Listing 3. Szablon dostarczający identyfikator dla obiektów klasy
cji complicateCalculationImpl, ponie-
waż typ, który jest parametrem tego szablo-
class HasId { //klasa dostarczająca identyfikator nu, uzyskujemy za pomocą trejtu promote.
public: W przedstawionym rozwiązaniu, jeżeli sza-
HasId(long id) : id_(id) { } blonu używamy dla różnych typów, będzie
virtual ~HasId() { } wykorzystywany ten sam kod binarny, któ-
long getId() const { return id_; } ry będzie używał obiektów typu najlepiej
private: wspieranego przez daną platformę. Podob-
long id_; ną technikę możemy stosować wykorzystu-
}; jąc promocję dla typów całkowitych.
template long doGetId(const T& t, true_type) {
return t.getId(); //zwraca wewnętrzny identyfikator Podsumowanie
} Techniki stosowane w programowaniu ge-
template long doGetId(const T& t, false_type) { nerycznym (inna nazwa to programowanie
return reinterpret_cast(&t); //zwraca adres jako wartość long uogólnione) różnią się od tych stosowanych
} w programowaniu obiektowym i struktural-
template long getId(const T& t) { //wykorzystuje trejty nym, ich znajomość pozwala zmniejszać roz-
return getIdInternal(t, is_base_of() ); miar kodu zródłowego, zwiększając jego czy-
} telność bez wpływu na wydajność. Szablo-
ny dają możliwość tworzenia ogólnych roz-
Listing 4. Fragment trejtów boost::call_traits
wiązań, z tego względu technika ta dominu-
je wśród bibliotek.
template call_traits { //szablon dla typów użytkownika
typedef const T& param_type; //sposób przekazywania parametrów danego typu
};
template call_traits { //specjalizacja dla wskazników
W Sieci
typedef T param_type; //wskazniki lepiej przekazywać przez wartość
};
" http://www.boost.org;
" http://www.open-std.org;
Listing 5. Wykorzystanie trejtów promote udostępnianych przez boost::type_traits
" http://www.ddj.com/cpp/184404270.
template T complicateCalculation( T input ) { //tylko woła inną funkcję
return complicateCalculationImpl(typename promote::type(input) );
} R0BERT N0WAK
template T complicateCalculationImpl( T input ) { Adiunkt w Zakładzie Sztucznej Inteligencji Insty-
//tutaj złożony kod, który oblicza wartość tutu Systemów Elektronicznych Politechniki War-
//dla typów float i double będzie wykorzystywany ten sam kod binarny szawskiej, zainteresowany tworzeniem aplikacji
} dla biologii i medycyny, programuje w C++ od
ponad 10 lat.
Kontakt z autorem:rno@o2.pl
46 11/2009


Wyszukiwarka

Podobne podstrony:
2009 11 Statyczne asercje w C [Programowanie C C ]
2009 11 Sprytne wskaźniki [Programowanie C C ]
2008 11 Radio w GNOME [Programowanie]
2009 11 Informatyka śledcza
2009 11 Connected
Meta najnowsza najlepsza 2009 11 26
Eucharystia Orędzie z dnia 27 12 2009, 11 06 2010
2009 11 the Gatekeeper Network Access Control on Wired Networks with Ieee 802 1X
RMZ o upr rat med zmiana 2009 11 64
2009 11 17 arduino basics

więcej podobnych podstron