Poss0502 slides

51

Transcript of Poss0502 slides

Page 1: Poss0502 slides

Techniques d'implémentation de la POO en C++ statique

Le paradigme objet et le C++

Page 2: Poss0502 slides

Techniques d'implémentation de la POO en C++ statique

Classes, objets et messages

� dé�nition d'une classe

� messages reconnus :

déclaration des méthodes

� envoi de message :convention d'appel

� réception de message :implémentation des mé-thodes

Page 3: Poss0502 slides

Techniques d'implémentation de la POO en C++ statique

Pour la modélisation suivante :

Cow+eat(Food&)

On écrit traditionnellement :¥

class Cow{public :

// `Cow' objects can receive// ` eat ' messages with an// argument of type ` Food'void eat(Food);

}

Page 4: Poss0502 slides

Techniques d'implémentation de la POO en C++ statique

Envoi d'un message :¥

// send message `eat '// to object ` cow',// with argument `grass 'cow . eat ( grass );

Réception par une méthode :¥

void Cow::eat(Food& f){cout � "Moo, I like that "

� f � endl ;}

Page 5: Poss0502 slides

Techniques d'implémentation de la POO en C++ statique

Héritage et polymorphisme

� dé�nition d'une relation d'hé-ritage

� liaison retardée :méthodes virtuelles

� � voir comme � :références et coercion

� polymorphisme d'héritage :agir sur une référence versune classe de base

Page 6: Poss0502 slides

Techniques d'implémentation de la POO en C++ statique

Pour la modélisation suivante :

Herbivor+eat(Food&)

Cow+eat(Food&)

Sheep+eat(Food&)

On écrit traditionnellement :¥

class Herbivor{public :

virtual void eat(Food&) = 0;};

class Cow : public Herbivor{public :

void eat(Food&);}

class Sheep : public Herbivor{public :

void eat(Food&);}

Page 7: Poss0502 slides

Techniques d'implémentation de la POO en C++ statique

Démonstration de la liaisonretardée : ¥

// ` thingy ' is a reference to// an object of base class// ` Herbivor '

thingy . eat ( grass );

L'appel est résolu par liaisonretardée.

Alorithme polymorphe : ¥void Farmer:: feed(Herbivor& thingy){Grass g;thingy . eat(g);

}// [...]

farmer . feed(cow);farmer . feed(sheep);

// [...]

Page 8: Poss0502 slides

Techniques d'implémentation de la POO en C++ statique

Mais...

� 1 invocation d'une méthodevirtuelle ⇒ 1 indirection pourobtenir son adresse avantl'appel

� le compilateur n'a pas la pos-sibilité d'inliner les méthodes

� les règles de typage im-posent une coercion dangeu-reuse pour des cas de modé-lisation courants

� on voudrait des � types vir-tuels �

Page 9: Poss0502 slides

Techniques d'implémentation de la POO en C++ statique

Objectifs :

� résoudre statiquement la liai-son retardée

� conserver, si possible, le� sucre syntaxique � duC++

� le code doit exprimer la mo-délisation théorique

Moyens mis en ÷uvre :

� la méta-programmation� le polymorphisme paramé-trique

⇒ utiliser les templates duC++ pour faire de la

Méta-POO

Page 10: Poss0502 slides

Techniques d'implémentation de la POO en C++ statique

Première approche et introduction auxtechniques

Page 11: Poss0502 slides

Techniques d'implémentation de la POO en C++ statique

Objectif : � instruire � la classe de base.

Première modélisation :

HerbivorSubClass

Cow

Herbivor<Cow>

Sheep

Herbivor<Sheep>

Implémentation :¥

template<typename Exact>class Herbivor{public :

void eat(Food&);}

¥template<typename Exact>void Herbivor<Exact>::eat(Food& f){// emulate method dispatching// with controlled coercionstatic_cast<Exact∗>(this)−>eat(f);

class Cow : public Herbivor<Cow>{ /∗ no change ∗/ }

class Sheep : public Herbivor<Sheep>{ /∗ no change ∗/ }

Page 12: Poss0502 slides

Techniques d'implémentation de la POO en C++ statique

Héritage statique : polymorphisme d'héritage par lepolymorphisme paramétrique contraint.

L'algorithme polymorphe :¥

void Farmer:: feed(Herbivor& thingy){Grass g;thingy . eat(g);

}

... devient : ¥template<typename Any>void Farmer:: feed(Herbivor<Any>& thingy){Grass h;thingy . eat(g);

}

Page 13: Poss0502 slides

Techniques d'implémentation de la POO en C++ statique

Avantages :

� l'expression des implémenta-tion n'est pas modi�ée

� on conserve la modélisation� le compilateur peut inliner

Inconvénients :

� mécanismes syntaxiques nonidiomatiques

� templates longs à compiler� multiplication cartésienne desinstances :(classesfilles)× (classesmeres)

Page 14: Poss0502 slides

Techniques d'implémentation de la POO en C++ statique

Le code � classique � :

¥struct IntFunc{

virtual int apply( int x) = 0;};

struct Annulate : IntFunc{

int apply( int x){ return 0; }

};

void many_applies(const unsigned N,IntFunc& f,int x)

{for(unsigned i = 0; i < N; ++i)

f . apply(x);};

Le code � statique � :¥

template<typename Exact>struct IntFunc{

int apply( int x){ return dispatch (apply)(x ); }// dispatch is a macro

};

struct Annulate : IntFunc<Annulate>{

int apply( int x){ return 0; }

};

template<typename Any>void many_applies(const unsigned N,

IntFunc<Any>& f,int x)

{for(unsigned i = 0; i < N; ++i)

f . apply(x);};

Page 15: Poss0502 slides

Techniques d'implémentation de la POO en C++ statique

Après optimisation par GCC :

0

1

2

3

4

5

6

7

0 2e+07 4e+07 6e+07 8e+07 1e+08

itera

tion

time,

in s

econ

ds

N

classic paradigmstatic paradigm

Page 16: Poss0502 slides

Techniques d'implémentation de la POO en C++ statique

Les vaches mangent de l'herbe...

Page 17: Poss0502 slides

Techniques d'implémentation de la POO en C++ statique

... alias la contravariance des arguments.

Food

Grass

Animal+eat(Food&)

Cow+eat(Grass&)

Implémentation classique :¥

struct Food { /∗ ... ∗/ };struct Grass : Food { /∗ ... ∗/ };

struct Animal{

virtual void eat(Food&) = 0;};

struct Cow : Animal{

// declaring eat(Grass&) violates// the contravariancevoid eat(Food& f){

// not safe , not e�cient :Grass& g =

dynamic_cast<Grass&>(f);/∗ ... ∗/

}};

Page 18: Poss0502 slides

Techniques d'implémentation de la POO en C++ statique

en utilisant l'héritage statique, ce code devient...

d'une part :

¥template<typename SubClass>struct Food{ /∗ ... ∗/ };

struct Grass : Food<Grass>{ /∗ ... ∗/ };

et d'autre part :¥

template<typename SubClass>struct Animal{

template<typename F>void eat(Food<F>& f){

dispatch (eat)(static_cast<F&>(f));}

};

struct Cow : Animal<Cow>{

void eat(Grass& g){cout � "Moo, I love grass."

� endl ;}

};

Cette version est correctement typée !

Page 19: Poss0502 slides

Techniques d'implémentation de la POO en C++ statique

Le compilateur rejette e�ectivement les cas invalides : ¥/∗ ... ∗/Meat m;Animal<Cow>& a = cow;a . eat(m);

donne : ¥contra .cc : In member function `void Animal<SubClass>::eat(Food<Any>&)

[with Any = Meat, SubClass = Cow]':contra .cc :35: instantiated from `void feed(Animal<AnyAnimal>&,

Food<AnyFood>&) [with AnyAnimal = Cow, AnyFood = Meat]'contra .cc :44: instantiated from herecontra .cc :18: no matching function for call to `Cow::eat(Meat&)'contra .cc :26: candidates are : void Cow::eat(Grass&)

Page 20: Poss0502 slides

Techniques d'implémentation de la POO en C++ statique

Séparation interface/dé�nition, classesabstraites

Page 21: Poss0502 slides

Techniques d'implémentation de la POO en C++ statique

Ce code n'est pas correct : ¥template<typename Exact>struct BaseClass{

void m(){

// dispatch to Exact ::mstatic_cast<Exact∗>(this)−>m();

}};

struct Deriv : BaseClass<Deriv>{

/∗ m not implemented ∗/};

⇒ au lieu d'un message d'erreur, lecompilateur génère une récursion

in�nie.

Celui-ci l'est :¥

template<typename Exact>struct BaseClass{

void m(){

// dispatch to Exact ::m_implstatic_cast<Exact∗>(this)−>m_impl();

}};

⇒ le compilateur peut détecter

l'oubli d'implémentation dans la

classe dérivée.

Deux e�ets : un problème corrigé et une séparation de nomsentre interface et implémentation.

Page 22: Poss0502 slides

Techniques d'implémentation de la POO en C++ statique

Ce code n'est pas correct : ¥template<typename Exact>struct AbstractClass{

void m();};

// [...]AbstractClass<Foo> foo;

// [...]

⇒ le compilateur permet d'instancierune classe abstraite

Celui-ci l'est : ¥template<typename Exact>struct AbstractClass{

void m();

protected:~AbstractClass ();

};

⇒ le compilateur interditl'instanciation de la classe

On simule les classes abstraites en protégeant le destructeur.

Page 23: Poss0502 slides

Techniques d'implémentation de la POO en C++ statique

Ajout de niveaux de hiérarchiesupplémentaires

Page 24: Poss0502 slides

Techniques d'implémentation de la POO en C++ statique

Pour la hiérarchie suivante :

Animal+eat(Food&)

Herbivor+eat(Grass&)

Cow+eat(Grass&)

Sheep+eat(Grass&)

On peut pratiquer troisméthodes :

� Méthode R⇒ la classe de base connaîtla classe immédiatement infé-rieure

� Méthodes S1 et S2

⇒ la classe de base connaît letype exact

Page 25: Poss0502 slides

Techniques d'implémentation de la POO en C++ statique

La méthode R d'héritage statique

Page 26: Poss0502 slides

Techniques d'implémentation de la POO en C++ statique

Avec la méthode R, on indique à chaque dérivation le typedérivé à la classe-mère :

Animal_+eat(Food<Any>&)#~Animal_()

Inf

Herbivor_+eat(Grass<Any>&)#~Herbivor_()

Inf

Animal_<Herbivor_<Inf>>

Cow_+eat(Grass<Any>&)

Inf

Herbivor_<Cow_<Inf>>

Sheep_+eat(Grass<Any>&)

Inf

Herbivor_<Sheep_<Inf>>

Page 27: Poss0502 slides

Techniques d'implémentation de la POO en C++ statique

Pour instancier une classe, on utilise un type bottom (⊥) :

¥typedef Cow_<bottom> Cow;

Cow = Cow_<bottom>

Herbivor_<Cow_<bottom>>

Animal_<Herbivor_<Cow_<bottom>>>

¥typedef Sheep_<bottom> Sheep;

Sheep = Sheep_<bottom>

Herbivor_<Sheep_<bottom>>

Animal_<Herbivor_<Sheep_<bottom>>>

Page 28: Poss0502 slides

Techniques d'implémentation de la POO en C++ statique

Le type exact de n'importe quel objet peut être retrouvé à lacompilation :

find exact(C) =

{C si inferior(C) = ⊥find exact( inferior(C) ) sinon.

Cette formule est implémentée par templates récursifs.

Page 29: Poss0502 slides

Techniques d'implémentation de la POO en C++ statique

la méthode RAvantages :

� le type des classes donne desinformations sur la hiérarchie

� l'instanciation des classes� terminales � est facile àl'aide de ⊥

Inconvénients :

� les messages d'erreur de ty-page sont très complexes

� l'implémentation de find exact

est coûteuse en temps decompilation

Page 30: Poss0502 slides

Techniques d'implémentation de la POO en C++ statique

La méthode S1 d'héritage statique

Page 31: Poss0502 slides

Techniques d'implémentation de la POO en C++ statique

Avec la méthode S1, on indique à chaque instanciation le typeexact à la hiérarchie :

Animal_+eat(Food<Any>&)#~Animal_()

Exact

Herbivor_+eat(Grass<Any>&)#~Herbivor_()

Exact

Animal_<Exact>

Cow_+eat(Grass<Any>&)

Exact

Herbivor_<Exact>

Sheep_+eat(Grass<Any>&)

Exact

Herbivor_<Exact>

Page 32: Poss0502 slides

Techniques d'implémentation de la POO en C++ statique

Pour instancier une classe de la hiérarchie, il faut la dériver :

¥class Cow : public Cow_<Cow>{ /∗ ... ∗/ }

Cow_<Cow>

Herbivor_<Cow>

Animal_<Cow>

Cow

¥class Sheep : public Sheep_<Sheep>{ /∗ ... ∗/ }

Sheep_<Sheep>

Herbivor_<Sheep>

Animal_<Sheep>

Sheep

Page 33: Poss0502 slides

Techniques d'implémentation de la POO en C++ statique

En C++, les constructeurs ne sont pas hérités :

¥template<typename Exact>class Cow_ : public Herbivor_<Exact>{public :

template<typename Any>Cow_(Cow_<Any>& mother,

Cow_<Any>& father){ /∗ birth code ∗/ }

// ...}

¥class Cow : public Cow_<Cow>{public :

template<typename Any>Cow(Cow_<Any>& mother,

Cow_<Any>& father)/∗ dispatch to parent class ∗/: Cow_<Cow>(mother, father)

{ }

}

Le(s) constructeur(s) doivent être recopiés à la dérivation.

Page 34: Poss0502 slides

Techniques d'implémentation de la POO en C++ statique

la méthode S1

Avantages :

� pas de coût pour retrouver letype exact dans la classe debase

� les messages d'erreur detypage sont (relativement)simples

Inconvénients :

� la création d'un type ins-tanciable implique la créationd'une nouvelle classe

� le compilateur n'empêche pasde dériver une classe abstraitede la hiérarchie pour créer untype instanciable

Page 35: Poss0502 slides

Techniques d'implémentation de la POO en C++ statique

La méthode S2 d'héritage statique

Page 36: Poss0502 slides

Techniques d'implémentation de la POO en C++ statique

Avec la méthode S2, on indique à chaque instanciation untraits vers le type exact à la hiérarchie :

Animal_+Exact = ExactFinder::ret+eat(Food<Any>&)#~Animal_()

ExactFinder

Herbivor_+eat(Grass<Any>&)#~Herbivor_()

ExactFinder

Animal_<ExactFinder>

Cow_+eat(Grass<Any>&)

ExactFinder

Herbivor_<ExactFinder>

Sheep_+eat(Grass<Any>&)

ExactFinder

Herbivor_<ExactFinder>

Page 37: Poss0502 slides

Techniques d'implémentation de la POO en C++ statique

Pour instancier une classe, il faut construire les traits adéquats :

¥struct IsCow{

typedef Cow_<IsCow> ret;};

typedef IsCow:: ret Cow;

IsCow+ret = Cow_<IsCow>

Animal_<IsCow>

Herbivor_<IsCow>

Cow = Cow_<IsCow>

¥struct IsSheep{

typedef Sheep_<IsSheep> ret;};

typedef IsSheep :: ret Sheep;

IsSheep+ret = Sheep_<IsSheep>

Animal_<IsSheep>

Herbivor_<IsSheep>

Sheep = Sheep_<IsSheep>

Page 38: Poss0502 slides

Techniques d'implémentation de la POO en C++ statique

On peut même automatiser la création de ExactFinder :

Make+ret = T<Make<T>>

T:template <class> class

Cow = Make<Cow_>::ret

Sheep = Make<Sheep_>::ret¥

template<template<class> class T>struct Make{

typedef T<Make<T> > ret;};

typedef Make<Sheep_>::ret Sheep;

typedef Make<Cow_>::ret Cow;

Page 39: Poss0502 slides

Techniques d'implémentation de la POO en C++ statique

Si une classe de la hiérarchie est � plus � paramétrée, il fautagrandir Make :

Make1+ret = T<Make1<T, Arg1>, Arg1>

T:template<class,class> classArg1

Make2+ret = T<Make2<T, Arg1, Arg2>, Arg1, Arg2>

T:template<class,class,class> classArg1Arg2

Page 40: Poss0502 slides

Techniques d'implémentation de la POO en C++ statique

la méthode S2

Avantages :

� pas de coût pour retrouver letype exact dans la classe debase

� la création d'un type instan-ciable est plus simple qu'avecla méthode S1

Inconvénients :

� les messages d'erreurs detypage sont moins simplesqu'avec la méthode S1

� on ne peut pas entièrementautomatiser la création duExactFinder

Page 41: Poss0502 slides

Techniques d'implémentation de la POO en C++ statique

Un détour chez les types virtuels

Page 42: Poss0502 slides

Techniques d'implémentation de la POO en C++ statique

Objectifs :

� équipper les hiérarchies avecles � types virtuels �

� garder une approche systé-matique de l'héritage statique

Mais...

� le typage du C++ empêcheune implémentation triviale

� la sémantique des types vir-tuels est encore mal dé�nie

Page 43: Poss0502 slides

Techniques d'implémentation de la POO en C++ statique

Modélisation souhaitée :

Container+iterator: virtual type+begin(): iterator+end(): iterator+contains(T&): bool

T

List+iterator = list_iterator<T>+begin(): list_iterator<T>+end(): list_iterator<T>+contains(T&): bool

TVector

+iterator = vector_iterator<T>+begin(): vector_iterator<T>+end(): vector_iterator<T>+contains(T&): bool

T

Page 44: Poss0502 slides

Techniques d'implémentation de la POO en C++ statique

Avec héritage statique :

Container_+Exact = to_exact(X)+iterator = virtual_type(Exact, iterator)+begin(): iterator+end(): iterator+contains(T&): bool#~Container()

XT

List_+iterator = list_iterator<T>+begin(): list_iterator<T>+end(): list_iterator<T>+contains(T&): bool

XT

Container_<exact_of(List, X, T) >

� to exact et exact of dépendent de la méthode d'héritage� virtual type reste à dé�nir

Page 45: Poss0502 slides

Techniques d'implémentation de la POO en C++ statique

Une implémentation triviale... ¥#de�ne virtual_type(ExactType, Name) typename ExactType::Name

template<typename Exact, typename T>struct Container_{

typedef virtual_type (Exact, iterator ) iterator ;

iterator begin(){ return dispatch (begin )(); }// ...

};

template<typename Exact, typename T>struct List_ : Container_<Exact, T>{

typedef list_iterator <T> iterator; // de�ned elsewhereiterator begin_impl(){ /∗ ... ∗/ }// ...

};

// derivation to instanciate the classtemplate<typename T> struct List : List_<List<T>, T> {};

Page 46: Poss0502 slides

Techniques d'implémentation de la POO en C++ statique

... ne fonctionne pas : ¥// [...]

List<int> l; // line 36

List<int>:: iterator i = l .begin ();// [...]

donne à la compilation : ¥triv_vt .cc : In instantiation of `Container_<List<int>, int>':triv_vt .cc :36: instantiated from `List_<List<int>, int>'triv_vt .cc :36: instantiated from ` List<int>'triv_vt .cc :36: instantiated from heretriv_vt .cc :9: no type named ` iterator ' in ` struct List<int>'triv_vt .cc :12: no type named ` iterator ' in ` struct List<int>'

À l'instanciation de Container , la dé�nition du type n'est pasencore connue.

Page 47: Poss0502 slides

Techniques d'implémentation de la POO en C++ statique

Pour résoudre le problème, on doit séparer la dé�nition destypes virtuels de la hiérarchie statique.

� Avec les méthodes R et S1,

on a recours à une hiérarchie

de traits parallèle

� Avec la méthode S2, on peutsoit :� recourir à une hiérarchieparallèle

� faire porter les types vir-tuels par le ExactFinder

Page 48: Poss0502 slides

Techniques d'implémentation de la POO en C++ statique

Avec une hiérarchie parallèle, on obtient la modélisationsuivante :

Container+Exact = to_exact(X)+iterator = vt_trait<Exact>::iterator

XT

List_

XT

Container_<exact_of(List, X, T)> vt_trait<Container_<...>>

vt_trait<List_<...>>+iterator = list_iterator<T>

XT

dépend de la méthode d’héritage

Page 49: Poss0502 slides

Techniques d'implémentation de la POO en C++ statique

Il n'y a qu'un traits, mais ses spécialisations sont hiérarchisées :

vt_trait<Cow_<Exact>>Exact

vt_trait<Herbivor_<Exact>>+FoodType = Grass

Exact

vt_trait<Animal<Exact>>Exact Ce mécanisme est nécessaire

pour pouvoir dé�nir les typesvirtuels à des niveaux

intermédiaires de la hiérarchie

Page 50: Poss0502 slides

Techniques d'implémentation de la POO en C++ statique

Avantages de cette méthode :

� permet d'exprimer les besoinscourants

� application systématique(donc automatisable)

Inconvénients :

� nécessite une séparationentre la dé�nition de la classeet celle de ses types virtuels

� on connaît mal la portée sé-mantique de cette construc-tion

Page 51: Poss0502 slides

Techniques d'implémentation de la POO en C++ statique

Conclusion