Przejdź do głównej zawartości

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

Dzisiaj omówię ostatnie z podstawowych zastosowań dla metaprogramowania. Nie przeciągając dziś będzie o „pętlach bez pętli”. W wielu przypadkach (a może i nie w aż tak wielu ale w kilku na pewno) mamy do czynienia z pętlą której ilość iteracji jest znana już w momencie kompilacji programu. W takich sytuacjach kompilator potrafi sam powielić kod zachowując odpowiedni indeks iteratora np.:

for(int i=0; i<3; i++)
{
  func(i);
}

func(0);
func(1);
func(2); 


Taki kod zadziała nieco szybciej gdyż nie ma potrzeby inkrementowania zmiennej
i oraz jej alokacji dealokacji. Niestety nie mamy gwarancji że kompilator tak postąpi. Prezentowane zachowanie zależne jest od samego kompilatora jego wersji oraz wykorzystywanych optymalizacji. Aby wymusić wygenerowanie kodu przez kompiilator można skorzystać z template'ów. A oto rozwiązanie problemu

void func(int i){std::cout<< i << std::endl;}
template<int i, typename FuncType>
class MetaInlineLoop
{
 public:
   static void Do(FuncType f)
   {
     MetaInlineLoop<i-1, FuncType>::Do(f);
     f(i);
   }
};

template<typename FuncType>
class MetaInlineLoop<-1, FuncType>
{
 public:
   static void Do(FuncType f)
   {
   }
};

wywołanie:
std::function<void(int i)> f = func;
MetaInlineLoop<10, decltype(f)>::Do(f);


Szablon MetaInlineLoop przyjmuje 2 parametry jednym z nich jest numer iteracji a drugim typ funkcji która ma być wywołana. Ponadto szablon ten definiuje statyczną metodę Do której jedynym zadaniem jest wywołanie funkcji f przekazanej jako parametr, oraz rekurencyjne wywołanie kolejnej instancji szablonu MetaInlineLoop z zdekrementowaną wartością indeksu. Następnie widzimy specjalizację która przedefiniowywuje statyczną metodę Do w sposób kończący rekurencję. Na uwagę zasługuje sposób przekazania funkcji func do wnętrza szablonu. Prezentowana metoda wykorzystuje szablon std::function który wraz z std::bind daje bardzo szerokie możliwości (o std::bind i std::function napiszę innym razem). Jak widać w powyższym przykładzie zastosowań dla metaprogramowania jest całkiem sporo a jak mogę zapewnić jest to dopiero wierzchołek góry lodowej.

Komentarze

  1. Nie trzeba być programistą, żeby stworzyć i prowadzić kilka stron jednocześnie! Tak sie sklada, ze https://web360.opteam.pl daje sporo możliwości i nie wymaga znajomości kodowania.

    OdpowiedzUsuń

Prześlij komentarz

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++ 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 progr...

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();               ...