2. Techniques d'implémentation de la POO en C++ statique
Classes, objets et messages
dénition d'une classe envoi de message :
convention d'appel
messages reconnus : réception de message :
implémentation des mé-
déclaration des méthodes thodes
3. Techniques d'implémentation de la POO en C++ statique
Pour la modélisation suivante : On écrit traditionnellement :
¤
class Cow
{
Cow public :
// `Cow' objects can receive
// ` eat ' messages with an
// argument of type ` Food'
+eat(Food) }
void eat(Food);
4. Techniques d'implémentation de la POO en C++ statique
Envoi d'un message : Réception par une méthode : ¤
void Cow::eat(Food f)
¤
// send message `eat ' {
// to object ` cow', cout Moo, I like that
// with argument ` grass ' f endl ;
cow . eat ( grass ); }
5. Techniques d'implémentation de la POO en C++ statique
Héritage et polymorphisme
dénition d'une relation d'hé- voir comme :
ritage références et coercion
polymorphisme d'héritage :
liaison retardée : agir sur une référence vers
méthodes virtuelles une classe de base
6. Techniques d'implémentation de la POO en C++ statique
Pour la modélisation suivante : On écrit traditionnellement : ¤
class Herbivor
{
public :
virtual void eat(Food) = 0;
};
class Cow : public Herbivor
{
public :
Herbivor void eat(Food);
+eat(Food) }
class Sheep : public Herbivor
{
public :
Cow Sheep void eat(Food);
+eat(Food) +eat(Food) }
7. Techniques d'implémentation de la POO en C++ statique
Démonstration de la liaison Alorithme polymorphe : ¤
retardée : void Farmer:: feed( Herbivor thingy)
¤
// ` thingy ' is a reference to {
// an object of base class Grass g;
// ` Herbivor ' thingy . eat(g);
}
thingy . eat ( grass ); // [...]
farmer . feed(cow);
farmer . feed(sheep );
// [...]
L'appel est résolu par liaison
retardée.
8. Techniques d'implémentation de la POO en C++ statique
Mais...
1 invocation d'une méthode les règles de typage im-
virtuelle ⇒ 1 indirection pour posent une coercion dangeu-
obtenir son adresse avant reuse pour des cas de modé-
l'appel lisation courants
le compilateur n'a pas la pos- on voudrait des types vir-
sibilité d'inliner les méthodes tuels
9. Techniques d'implémentation de la POO en C++ statique
Objectifs : Moyens mis en ÷uvre :
résoudre statiquement la liai- la méta-programmation
son retardée le polymorphisme paramé-
conserver, si possible, le trique
sucre syntaxique du
C++ ⇒ utiliser les templates du
le code doit exprimer la mo- C++ pour faire de la
délisation théorique Méta-POO
11. Techniques d'implémentation de la POO en C++ statique
Objectif : instruire la classe de base.
Première modélisation : Implémentation : ¤
templatetypename Exact
class Herbivor
SubClass {
Herbivor public :
void eat(Food);
}
¤
templatetypename Exact
HerbivorCow void HerbivorExact::eat(Food f)
{
// emulate method dispatching
// with controlled coercion
Cow static_castExact∗(this)−eat(f);
}
¤
HerbivorSheep class Cow : public HerbivorCow
{ /∗ no change ∗/ }
class Sheep : public HerbivorSheep
Sheep { /∗ no change ∗/ }
12. Techniques d'implémentation de la POO en C++ statique
Héritage statique : polymorphisme d'héritage par le
polymorphisme paramétrique contraint.
L'algorithme polymorphe : ... devient : ¤
templatetypename Any
¤
void Farmer:: feed( Herbivor thingy) void Farmer:: feed( HerbivorAny thingy)
{ {
Grass g; Grass h;
thingy . eat(g); thingy . eat(g);
} }
13. Techniques d'implémentation de la POO en C++ statique
Avantages : Inconvénients :
mécanismes syntaxiques non
idiomatiques
l'expression des implémenta-
templates longs à compiler
tion n'est pas modiée multiplication cartésienne des
on conserve la modélisation instances :
le compilateur peut inliner (classesf illes) × (classesmeres)
14. Techniques d'implémentation de la POO en C++ statique
Le code classique : Le code statique : ¤
templatetypename Exact
struct IntFunc
{
¤ int apply( int x)
struct IntFunc { return dispatch (apply )(x ); }
{ // dispatch is a macro
virtual int apply( int x) = 0; };
};
struct Annulate : IntFuncAnnulate
struct Annulate : IntFunc {
{ int apply( int x)
int apply( int x) { return 0; }
{ return 0; } };
};
templatetypename Any
void many_applies(const unsigned N, void many_applies(const unsigned N,
IntFunc f, IntFuncAny f,
int x) int x)
{ {
for (unsigned i = 0; i N; ++i) for (unsigned i = 0; i N; ++i)
f . apply(x ); f . apply(x );
}; };
15. Techniques d'implémentation de la POO en C++ statique
Après optimisation par GCC :
7
classic paradigm
static paradigm
6
5
iteration time, in seconds
4
3
2
1
0
0 2e+07 4e+07 6e+07 8e+07 1e+08
N
17. Techniques d'implémentation de la POO en C++ statique
... alias la contravariance des arguments.
Food Implémentation classique :
¤
struct Food { /∗ ... ∗/ };
struct Grass : Food { /∗ ... ∗/ };
struct Animal
Grass {
virtual void eat(Food) = 0;
};
struct Cow : Animal
Animal {
// declaring eat(Grass) violates
+eat(Food) // the contravariance
void eat(Food f)
{
// not safe , not ecient :
Grass g =
dynamic_castGrass(f);
Cow /∗ ... ∗/
}
+eat(Grass) };
18. Techniques d'implémentation de la POO en C++ statique
en utilisant l'héritage statique, ce code devient...
d'une part : et d'autre part : ¤
templatetypename SubClass
struct Animal
{
templatetypename F
void eat(FoodF f)
{
dispatch (eat)( static_castF(f));
}
};
struct Cow : AnimalCow
¤{
templatetypename SubClass void eat(Grass g)
struct Food {
{ /∗ ... ∗/ }; cout Moo, I love grass.
endl ;
struct Grass : FoodGrass }
{ /∗ ... ∗/ }; };
Cette version est correctement typée !
19. Techniques d'implémentation de la POO en C++ statique
Le compilateur rejette eectivement les cas invalides : ¤
/∗ ... ∗/
Meat m;
AnimalCow a = cow;
a . eat(m);
donne : ¤
contra .cc : In member function ` void AnimalSubClass::eat(FoodAny)
[ with Any = Meat, SubClass = Cow]':
contra .cc :35: instantiated from ` void feed(AnimalAnyAnimal,
FoodAnyFood) [with AnyAnimal = Cow, AnyFood = Meat]'
contra .cc :44: instantiated from here
contra .cc :18: no matching function for call to ` Cow::eat(Meat)'
contra .cc :26: candidates are : void Cow::eat(Grass)
21. Techniques d'implémentation de la POO en C++ statique
Ce code n'est pas correct : ¤
Celui-ci l'est :
templatetypename Exact ¤
struct BaseClass templatetypename Exact
{ struct BaseClass
void m() {
{ void m()
// dispatch to Exact :: m {
static_castExact∗(this) // dispatch to Exact :: m_impl
−m(); static_castExact∗(this)
} −m_impl();
}; }
};
struct Deriv : BaseClassDeriv
{
/∗ m not implemented ∗/
};
⇒ le compilateur peut détecter
⇒ au lieu d'un message d'erreur, le
l'oubli d'implémentation dans la
compilateur génère une récursion
innie. classe dérivée.
Deux eets : un problème corrigé et une séparation de noms
entre interface et implémentation.
22. Techniques d'implémentation de la POO en C++ statique
Ce code n'est pas correct : ¤
Celui-ci l'est : ¤
templatetypename Exact templatetypename Exact
struct AbstractClass struct AbstractClass
{ {
void m(); void m();
};
protected:
// [...] ~AbstractClass ();
AbstractClass Foo foo; };
// [...]
⇒ le compilateur permet d'instancier ⇒ le compilateur interdit
une classe abstraite l'instanciation de la classe
On simule les classes abstraites en protégeant le destructeur.
24. Techniques d'implémentation de la POO en C++ statique
Pour la hiérarchie suivante : On peut pratiquer trois
méthodes :
Méthode R
Animal
+eat(Food) ⇒ la classe de base connaît
la classe immédiatement infé-
Herbivor
+eat(Grass)
rieure
Méthodes S1 et S2
Cow Sheep ⇒ la classe de base connaît le
+eat(Grass) +eat(Grass) type exact
26. Techniques d'implémentation de la POO en C++ statique
Avec la méthode R, on indique à chaque dérivation le type
dérivé à la classe-mère :
Herbivor_Cow_Inf
Inf
Animal_
+eat(FoodAny) Inf
#~Animal_() Cow_
+eat(GrassAny)
Animal_Herbivor_Inf
Herbivor_Sheep_Inf
Inf
Herbivor_ Inf
+eat(GrassAny) Sheep_
#~Herbivor_() +eat(GrassAny)
27. Techniques d'implémentation de la POO en C++ statique
Pour instancier une classe, on utilise un type bottom (⊥) :
¤ ¤
typedef Cow_bottom Cow; typedef Sheep_bottom Sheep;
Animal_Herbivor_Cow_bottom Animal_Herbivor_Sheep_bottom
Herbivor_Cow_bottom Herbivor_Sheep_bottom
Cow = Cow_bottom Sheep = Sheep_bottom
28. Techniques d'implémentation de la POO en C++ statique
Le type exact de n'importe quel objet peut être retrouvé à la
compilation :
find exact (C) =
C si inferior (C) = ⊥
find exact ( inferior (C) ) sinon.
Cette formule est implémentée par templates récursifs.
29. Techniques d'implémentation de la POO en C++ statique
la méthode R
Avantages : Inconvénients :
le type des classes donne des les messages d'erreur de ty-
informations sur la hiérarchie page sont très complexes
l'instanciation des classes l'implémentation de find exact
terminales est facile à est coûteuse en temps de
l'aide de ⊥ compilation
31. Techniques d'implémentation de la POO en C++ statique
Avec la méthode S1, on indique à chaque instanciation le type
exact à la hiérarchie :
Exact Herbivor_Exact
Animal_
+eat(FoodAny)
#~Animal_() Exact
Cow_
+eat(GrassAny)
Animal_Exact
Herbivor_Exact
Exact
Herbivor_ Exact
+eat(GrassAny) Sheep_
#~Herbivor_()
+eat(GrassAny)
32. 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 class Sheep : public Sheep_Sheep
{ /∗ ... ∗/ } { /∗ ... ∗/ }
Animal_Cow
Animal_Sheep
Herbivor_Cow Herbivor_Sheep
Cow_Cow Sheep_Sheep
Cow Sheep
33. Techniques d'implémentation de la POO en C++ statique
En C++, les constructeurs ne sont pas hérités :
¤ ¤
templatetypename Exact class Cow : public Cow_Cow
class Cow_ : public Herbivor_Exact {
{ public :
public : templatetypename Any
templatetypename Any Cow(Cow_Any mother,
Cow_(Cow_Any mother, Cow_Any father)
Cow_Any father) /∗ dispatch to parent class ∗/
{ /∗ birth code ∗/ } : Cow_Cow(mother, father)
{}
// ...
} }
Le(s) constructeur(s) doivent être recopiés à la dérivation.
34. Techniques d'implémentation de la POO en C++ statique
la méthode S1
Avantages : Inconvénients :
la création d'un type ins-
pas de coût pour retrouver le
tanciable implique la création
type exact dans la classe de d'une nouvelle classe
base le compilateur n'empêche pas
les messages d'erreur de de dériver une classe abstraite
typage sont (relativement) de la hiérarchie pour créer un
simples type instanciable
36. Techniques d'implémentation de la POO en C++ statique
Avec la méthode S2, on indique à chaque instanciation un
traits vers le type exact à la hiérarchie :
ExactFinder
Animal_
Herbivor_ExactFinder
+Exact = ExactFinder::ret
+eat(FoodAny) ExactFinder
#~Animal_()
Cow_
+eat(GrassAny)
Animal_ExactFinder
Herbivor_ExactFinder
ExactFinder
Herbivor_
+eat(GrassAny) ExactFinder
#~Herbivor_() Sheep_
+eat(GrassAny)
37. Techniques d'implémentation de la POO en C++ statique
Pour instancier une classe, il faut construire les traits adéquats :
¤ ¤
struct IsCow struct IsSheep
{ {
typedef Cow_IsCow ret; typedef Sheep_IsSheep ret;
}; };
typedef IsCow:: ret Cow; typedef IsSheep :: ret Sheep;
IsCow
+ret = Cow_IsCow IsSheep
+ret = Sheep_IsSheep
Animal_IsCow
Animal_IsSheep
Herbivor_IsCow Herbivor_IsSheep
Cow = Cow_IsCow Sheep = Sheep_IsSheep
38. Techniques d'implémentation de la POO en C++ statique
On peut même automatiser la création de ExactFinder :
T:template class class
Make
+ret = TMakeT
Cow = MakeCow_::ret
Sheep = MakeSheep_::ret
¤
templatetemplateclass class T
struct Make
{
typedef TMakeT ret;
};
typedef MakeSheep_::ret Sheep;
typedef MakeCow_::ret Cow;
39. Techniques d'implémentation de la POO en C++ statique
Si une classe de la hiérarchie est plus paramétrée, il faut
agrandir Make :
T:templateclass,class class
Arg1
Make1
+ret = TMake1T, Arg1, Arg1
T:templateclass,class,class class
Arg1
Arg2
Make2
+ret = TMake2T, Arg1, Arg2, Arg1, Arg2
40. Techniques d'implémentation de la POO en C++ statique
la méthode S2
Avantages : Inconvénients :
pas de coût pour retrouver le les messages d'erreurs de
type exact dans la classe de typage sont moins simples
base qu'avec la méthode S1
la création d'un type instan- on ne peut pas entièrement
ciable est plus simple qu'avec automatiser la création du
la méthode S1 ExactFinder
42. Techniques d'implémentation de la POO en C++ statique
Objectifs : Mais...
équipper les hiérarchies avec le typage du C++ empêche
les types virtuels une implémentation triviale
garder une approche systé- la sémantique des types vir-
matique de l'héritage statique tuels est encore mal dénie
43. Techniques d'implémentation de la POO en C++ statique
Modélisation souhaitée :
T
Container
+iterator: virtual type
+begin(): iterator
+end(): iterator
+contains(T): bool
T T
List Vector
+iterator = list_iteratorT +iterator = vector_iteratorT
+begin(): list_iteratorT +begin(): vector_iteratorT
+end(): list_iteratorT +end(): vector_iteratorT
+contains(T): bool +contains(T): bool
44. Techniques d'implémentation de la POO en C++ statique
Avec héritage statique :
Container_exact_of(List, X, T)
X
T
Container_ X
T
+Exact = to_exact(X) List_
+iterator = virtual_type(Exact, iterator)
+begin(): iterator +iterator = list_iteratorT
+end(): iterator +begin(): list_iteratorT
+contains(T): bool +end(): list_iteratorT
#~Container() +contains(T): bool
to exact et exact of dépendent de la méthode d'héritage
virtual type reste à dénir
45. Techniques d'implémentation de la POO en C++ statique
Une implémentation triviale... ¤
#dene virtual_type(ExactType, Name) typename ExactType::Name
templatetypename Exact, typename T
struct Container_
{
typedef virtual_type (Exact, iterator ) iterator ;
iterator begin()
{ return dispatch (begin )(); }
// ...
};
templatetypename Exact, typename T
struct List_ : Container_Exact, T
{
typedef list_iterator T iterator; // dened elsewhere
iterator begin_impl()
{ /∗ ... ∗/ }
// ...
};
// derivation to instanciate the class
templatetypename T struct List : List_ListT, T {};
46. 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_Listint, int':
triv_vt .cc :36: instantiated from `List_Listint, int'
triv_vt .cc :36: instantiated from ` List int'
triv_vt .cc :36: instantiated from here
triv_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 pas
encore connue.
47. Techniques d'implémentation de la POO en C++ statique
Pour résoudre le problème, on doit séparer la dénition des
types virtuels de la hiérarchie statique.
Avec les méthodes R et S1, Avec la méthode S2, on peut
soit :
recourir à une hiérarchie
on a recours à une hiérarchie
parallèle
faire porter les types vir-
de traits parallèle tuels par le ExactFinder
48. Techniques d'implémentation de la POO en C++ statique
Avec une hiérarchie parallèle, on obtient la modélisation
suivante :
X
T
Container
+Exact = to_exact(X)
+iterator = vt_traitExact::iterator
Container_exact_of(List, X, T) vt_traitContainer_...
X
X T
T vt_traitList_...
List_ +iterator = list_iteratorT
dépend de la méthode d’héritage
49. 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 :
Exact
vt_traitAnimalExact Ce mécanisme est nécessaire
pour pouvoir dénir les types
Exact
virtuels à des niveaux
vt_traitHerbivor_Exact intermédiaires de la hiérarchie
+FoodType = Grass
Exact
vt_traitCow_Exact
50. Techniques d'implémentation de la POO en C++ statique
Avantages de cette méthode : Inconvénients :
nécessite une séparation
permet d'exprimer les besoins entre la dénition de la classe
et celle de ses types virtuels
courants
on connaît mal la portée sé-
application systématique mantique de cette construc-
(donc automatisable) tion