Przejdź do głównej zawartości

C++ metaprogramowanie z wykorzystaniem szablonów cz. 1


Ten wpis rozpoczyna cykl dotyczący metaprogramowania, ilość oraz częstotliwość publikacji kolejnych części pozostaje nieustalona.

Poniżej zaprezentowane zostanie absolutny elementarz, czyli wyznaczanie dowolnego elementów wyrazu ciągu Fibonacciego w czasie kompilacji.

#include <iostream>

template<unsigned int N>
struct Fib
{
  static const unsigned int el = Fib<N-1>::el + Fib<N-2>::el;
};

template<>
struct Fib<0>
{
  static const unsigned int el = 0;
};

template<>
struct Fib<1>
{
  static const unsigned int el = 1;
};

int main(void)
{
  const unsigned int N = 5;
  std::cout << "fib( " << N << " ) = " << Fib<N>::el << std::endl;
  return 0;
}

Tych parę linijek zmusza do wyliczenia 5 elementu ciągu Fibonacciego kompilator, co oznacza że jedynymi instrukcjami jakie zostaną wykonane przez nasz program po skompilowaniu będą te odpowiedzialne za wyświetlenie stosownej informacji na ekranie. Analizując powyższy kod widzimy template struktury Fib. Struktura ta zawiera jedną statyczną stałą el której wartość definiowana jest w sposób rekurencyjny. Następnie wyspecjalizowano strukturę Fib oraz wartość statycznej zmiennej el dla odpowiednich wartości N (0 oraz 1). Takie podejście spowoduje iż rekurencyjne rozwinięcie będzie miało miejsce podczas procesu kompilacji. Należy zaznaczyć że Fib<1> oraz Fib<0> to tak naprawdę dwa różne typy (podobnie dla wszystkich wartości pośrednich od 0 do N). Możliwości prawidłowego działania mojego kompilatora (gcc version 4.7.2 Ubuntu/Linaro 4.7.2-2ubuntu1) kończą się gdzieś między 1800 a 1900 elementem ciągu. Jak zatem widać podejście takie ma swoje ograniczenia do najważniejszych należy fakt iż w tym przypadku konieczna jest ustalenie który element ciągu chcemy pozyskać już w momencie kompilacji. Prezentowany przykład może nie wydawać się nazbyt praktyczny i w rzeczywistości taki właśnie jest. Jego zadaniem jest zwrócenie jedynie uwagi na pewne szczególne podejście do szblonów oraz metaprogramowania, więcej na ten temat można znaleźć w linkach poniżej oraz w kolejnych wpisach na tym blogu dotyczących metaprogramowania.

Linki:





Komentarze

Popularne posty z tego bloga

Makra i preprocesor

Jako programista klasycznego C przyszło mi wielokrotnie ścierać się z makrami. Makro to zestaw instrukcji umieszczanych w kodzie są jednak interpretowane nie przez kompilator ale przez preprocesor. Preprocesor jest „pomocnikiem” kompilatora, zajmuje się on np. wstawianiem treści plików nagłówkowych do plików z kodem za pomocą instrukcji #include. Preprocesor należy rozumieć jako prymitywny edytor tekstu dokonujący „korekcji” plików z kodem źródłowym przed rozpoczęciem ich przetwarzania przez kompilator. Jakie są zalety wykorzystywania tych rozwiązań w kodzie? Tak naprawdę w języku C w czasach przed wprowadzeniem słowa kluczowego inline umożliwiały wstawianie kodu we wskazane miejsce. Należy bowiem pamiętać że każdorazowe wstawienie makroinstrukcji powoduje ingerencję w kod źródłowy (innymi słowy we wskazanym miejscu zostanie wstawiony stosowny fragment kodu). Łatwo obserwowalnym efektem częstego wykorzystywania makr w plikach z kodem jest rozrost pliku binarnego oraz jego szybsze d

C++11 Iterowanie

Zasadniczo iterowanie po elementach tablicy/vectora/listy itp. nie jest niczym nowym, interesującym ani pasjonującym, ot szara codzienność. Przyjrzyjmy się zatem jak robimy to najczęściej: class Image { public:   Image();   void rotate(float angle);   void display();   void* serialize(); }; class ShowImage { public:   void operator()(Image image)   {     image.display();   } }; //... std::vector<Image> imageCollection(10); for(int i=0; i<imageCollection.size(); ++i) {   imageCollection[i].display(); } for( std::vector<Image>::iterator                          currentImage = imageCollection.begin();                         currentImage < imageCollection.end();                         ++currentImage ) {   currentImage->display(); } std::for_each(imageCollection.begin(), imageCollection.end(),               ShowImage()); Dla naszych potrzeb stworzyliśmy sp

C++11 Variadic templates – szablony ze zmienną liczba parametrów

Witam w nowym roku. Dziś będzie o nowym elemencie szablonów czyli o szablonach ze zmienną liczbą parametrów. Ich implementacja i zachowanie różni się nieco od klasycznych szablonów. Nowa funkcjonalność pozwala na tworzenie bezpiecznych list typów. template<typename ...Ts> void variadic_template(){} Pierwszą nowością jest zastosowanie ...(wielokropka) przy określaniu typów szablonu. W ten sposób sygnalizujemy mnogość typów. Niestety nie mamy możliwości iterowania po kolejnych typach wewnątrz naszego szablonu. Ponadto taka definicja pozwala na stworzenie/wywołanie naszej szablonowej funkcji bez typów. variadic_template<>(); Aby tego uniknąć można uciec się do następującej sztuczki: template<typename T1, typename ...Ts> void variadic_template(T1 arg, Ts... args) W powyższym przykładzie jawnie wymuszamy podanie przynajmniej jednego typ dla naszej funkcji szablonowej. Jak już wspomniałem nie możemy jawnie iterować po typach przekazanych do