marți, 10 ianuarie 2012

Fata nevazuta a template-urilor. Capitolul 3 - Generare de cod.

   Generarea de cod la compilare este mai degraba un subiect cunoscut celor care scriu librarii. Chiar si asa merita dezbatuta problema  generarii din zbor a unor structuri.
    Inainte de toate o structura presupune o succesiune de tipuri de date, cu alte cuvinte avem nevoie de descriptorul structurii dupa care se va produce generarea. In economia template-urilor asta presupune stocarea tipurilor. Ne dorim in cele ce urmeaza sa stocam o serie de campuri, tipuri de forma urmatoare: TYPELIST_X(t1,t2....tx), unde t1,t2...tx sunt tipurile enuntate. Intrucat numarul tipurilor necesare rezolvarii problemei curente nu este cunoscut, singura modalitate si cea mai facila este stocarea tipurilor intr-o list, una cate una. Lucrurile ar arata ceva de genul: TYPELIST_X(t1,t2....tx) = TYPELIST(t1, TYPELIST_X(t1, TYPELIST_X-1(t2,t3....tx)). Nodul unei astfel de liste ar arata astfel:

template<typename H, typename T>
struct Typelist
{
      typedef H Head;
      typedef T Tail;
};

Va fi desigur nevoie si de un terminator de lista. Sa-i spunem struct NullType{}
Iata si cum vom genera o astfel de lista:

#define TYPELIST_1(T1)Typelist<T1,NullType>
#define TYPELIST_2(T1,T2)Typelist<T1,TYPELIST_1(T2) >
#define TYPELIST_3(T1,T2,T3)Typelist<T1,TYPELIST_2(T2,T3) >
#define TYPELIST_4(T1,T2,T3,T4)Typelist<T1,TYPELIST_3(T2,T3,T4) >

De dragul exemplului ne vom opri la o lista cu patru tipuri. Pentru o functionare acoperitoare, definitiile trebuie extinse la X tipuri dorite.
Acum descriptorul poate fi definit. Sa zicem ca vrem o structura care tine coordonatele unui punct (x,y,z).
typedef TYPELIST_4(float,float,float,NullType)  FieldTraits;

Inainte de a trece mai departe avem nevoie de cateva functii ajutatore pentru manevrarea unei astfel de liste.
Determinarea dimensiunii listei
Recusivitatea va crea structurile conform figurii de mai jos. Capul listei va contine constanta cu lungimea listei.

template<typename TList>
struct Length
{
    enum{result = 1 + Length<TList::Tail>::result};
}; 
O specializare care sa opreasca recursivitatea. 
template<>
struct Length<NullType>
{
   enum{result = 0};
};


Numarul structurilor ce se genereaza intern, nu afecteaza consumul de memorie ci doar timpul necesar compilarii si dimensiunea codului. Dat fiind faptul ca lucrurile se petrec la compilare sa zicem ca ne permitem si liste mult mai mari.

Aflarea tipului de la un anumit index

template<typename TList, int i>
struct TypeAt
{
    typedef typename TypeAt<typename TList::Tail, i-1>::result result;
};
Specializarea necesara opririi recursivitatii:

template<typename TList>
struct TypeAt<TList,0>
{
    typedef typename TList::Head result;
};

Aflarea indexului unui anumit tip

Definitia generala:
template<typename TList, typename T> struct IndexOf;

Specializare pentru situatia in care tipul cautat coincide cu capul de lista, adica situatia in care am ajuns pe nodul cautat.
template<typename T, typename TAIL>
struct IndexOf<Typelist<T,TAIL>,T>
{
    enum {result = 0};
};
Specializare pentru situatia in care tipul nu exista in lista(se ajunge pe sfarsitul listei).
template<typename T>
struct IndexOf<NullType,T>
{
    enum {result = -1};//not found
};
Cautarea generala:
template<typename HEAD, typename TAIL, typename T>
struct IndexOf<Typelist<HEAD,TAIL>, T>
{
    enum {temp = IndexOf<TAIL,T>::result } ;
    enum {result = temp==-1? -1 : 1 + temp};
};

Acum ca avem functiile necesare operarii descriptorului unei structuri putem insfarsit sa definim structura. O prima metoda ar fi sa avem o structura ce mosteneste pentru fiecare camp o structura ce detine campul de tipul respectiv, o structura generica de tipul:
template<typename T>
struct Holder{ T value_;}
Daca am avea un descriptor: Typelist(int,float,double,NullType) atunci structura finala ar mosteni clasele: Holder<int>,Holder<float>, Holder<double>, Holder<NullType>. In cazul cu cele trei coordonate, structura va mosteni 3 clase identice de tipul float.
Iata si modul in care se genereaza structura:
template<typename TList, template<class> class Unit>
struct GenStructByInheritance<Typelist<T1,T2>,Unit>
{
       typedef GenStructByInheritance<T1,Unit> left;
       typedef GenStructByInheritance<T2,Unit> right;
 };
template<typename AtomicType, template<class> class Unit>
struct  GenStructByInheritance:public Unit<AtomicType>
{
        typedef Unit<AtomicType> left;
 };

typedef GenStructByInheritance<FieldTraits,Holder> Point3f; 
Inca o functie ajutatoare care de data aceasta ne va ajuta sa accesam campurile structurii : 
template<int N>
typename TypeAt<FieldTraits, N>::result& Field(Point3f& obj)
{
    typedef TypeAt<FieldTraits, N>::result type;
    return ((Holder<type>&) obj).value_;
};
And voila:
Point3f mypoint;
float& x = Field<0>(mypoint);
float& y = Field<1>(mypoint);
float& z = Field<2>(mypoint);

Niciun comentariu:

Trimiteți un comentariu