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

miercuri, 4 ianuarie 2012

Fata nevazuta a template-urilor. Capitolul2 - Detectia pointerilor.

 
   Am spus in tutorialul anterior ca avand doua calificari ale unei structuri template, compilatorul va selecta pe cea mai apropiata dintre ele. Iata un exemplu simplu prin care se va face detectia unui tip pointer.

template<typename T>
struct PointerType
{
    enum{ispointer = false};
};
template<typename T>struct PointerType<T*>
{
    enum{ispointer = true};
};
    Aveti in fata o prima calificare generica ce primeste orice tip inclusiv pointeri si decide in modul default ca tipul nu este pointer si o supracalificare ce va fi utilizata de compilator in cazul in care tipul coincide unui pointer. Se  suprascrie intr-un fel regula anterioara. Iata si testul final:
 bool res = PointerType<int>::ispointer; //FALSE
res = PointerType<int*>::ispointer; //TRUE

marți, 3 ianuarie 2012

Fata nevazuta a template-urilor. Partea 1 - Detectia mostenirilor

    Majoritatea celor ce ajung sa foloseasca template-uri o fac de cele mai multe ori datorita versatilitatii sectiunii de cod/clasa pe care au elaborato. Putini stiu insa fata nevazuta a template-urilor si posibilitatile lor. In acest scurt tutorial o sa va arat cum se face detectia la compilare a relatiilor de legatura/mostenire intre doua clase A si B.
         Se stie ca functia sizeof reuseste sa returneze dimensiunea oricat de complicata ar arata expresia ce ii este trimisa ca argument. Defapt nici nu evalueaza expresia, sa zicem ca pur si simplu foloseste  tipul atasat expresiei.  De aici putem conchide ca si compilatorul stie cate ceva si despre relatiile de mostenire dintre obiecte.
          Vom scrie o clasa  template, ce primeste cele doua tipuri de date si  decide daca exista o relatie de legatura intre cele doua. Mai exact returneaza existenta relatiei U->T.

template<class T, class U>
struct Conversion
{
    typedef char size1;
    typedef struct {
        char dummy[2];
    }size2;
    static T TType;
    static size1 Test(U);
    static size2 Test(...);
public:
    enum{exists = sizeof(Test(TType)) == sizeof(size1), sametype = false};
};
          Clasa declara doua tipuri care sa fie in mod evident diferite ca dimensiune: size1 si size2. Pe langa aceastea, clasa mai vine si cu doua functii Test: una generica ce foloseste puncte de suspensie si returneaza dimensiunea size2 si una explicita ce returneaza dimensiunea size1. Definitiile lor nu sunt necesare fiindca am zis ca functia sizeof " arunca" expresia si returneaza doar tipul. Acuma, daca tipul T este cu adevarat derivat din tipul U, compilatorul  va folosi cea mai calificata functie Test, adica functia Test(U) intrucat compilatorul "stie" ca se poate face downcast de la tipul T la tipul U.  Variabila exists se va dezvolta in sizeof(Test(U)) == sizeof(size1) care este un adevar evident.
         Pe langa asta va trebui sa ne asiguram si ca cele doua tipuri nu sunt identice pentru a putea vorbi de mostenire. Vom face o calificare partiala a clasei pentru situatia asta.

template<class T>
struct Conversion<T,T>
{
    enum{exists = 1, sametype = 1};
};

Iata si testul final:


struct A
{
    int dummy1;
};
struct B:public A
{
    int dummy2[3];
};

bool inheritance = Conversion<B,A>::exists && !Conversion<B,A>::sametype; // TRUE
inheritance = Conversion<B,size_t>::exists && !Conversion<B,A>::sametype; // FALSE

luni, 2 ianuarie 2012

Aplicarea filtrului de culoare degrade

Filtru de culoare sau procesare computerizata? In cele ce urmeaza va voi arata in cativa pasi simpli cum puteti substitui un filtru de culoare degrade folosind ca software de editare Photoshop. Ca imagine de test va propun urmatoarea:


1. Selectati din panoul din dreapta Adjustments->PhotoFilter





2. Selectati din filtrele disponibile Cool Filter(80) si modificati densitatea pana la o valoare multumitoare.

3. Cu layer-ul masca selectat alegeti un gradient liniar  din bara de unelte(unelta Gradient) si trasati o linie verticala de sus in jos astfel incat numai cerul sa fie colorat. Veti obtine astfel o imagine cu un aspect mai rece.


 Daca doriti o colorare uniforma atunci e necesar un filtrul de culoare simplu. Puteti foarte bine realiza acest lucru selectand din meniu Image->Adjustments->PhotoFilter si calibrand in mod analog densitatea.