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.
{
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
Prześlij komentarz