Miguel Moquillon wrote:
Laurent Deniau wrote:
C supporte nativement le polymosphisme de soustypage au sens de Liskov.
Ce qu'il faut pour faire ce polymorphisme, c'est (implicitement ou
explicitement) la notion de pointeur et la possibilite de reinterpreter
une aggregation sur une 'sous-aggregation', c'est-a-dire etre autorise
par le langage a appliquer le principe de substitution. Et la norme du C
l'autorise.
Tu le peux effectivement si tu écris toi même le mécanismes le permettant :
c'est à dire écrire une relation entre des structures de données qui
permettent ceci (le plus simple : champs communs entre les deux structures
de données "parentes"). Ce n'est pas fournit par défaut par un mécanisme du
langage, tu utilises juste les possibilités du langage pour implémenter ou
simuler un tel mécanisme.
Cette relation de *polymorphisme* est necessaire meme dans les langages OO, ce n'est donc pas un critere.
C:
struct Derived { struct Base; ... };
C++:
struct Derived : Base { ... };
En revanche la relation de C++ est plus riche, elle integre egalement l'heritage qui inclu une regle pour la variance du type derive.
Application au polymorphisme:
typedef struct Base {
char *name;
} *Base;
typedef struct Derived {
struct Base Base;
int val;
} *Derived;
void print(Base base) // polymorphique
{
printf(base->name);
}
int main(void)
{
struct Base base[1] = {{ "Base" }};
struct Derived derived[1] = {{{ "Derived" }, 10 }};
print(base);
print((Base)derived); // non support de type variant -> cast
}
Ce que C ne supporte pas, c'est une regle pour autoriser la variance des type selon les regles de l'heritage et oblige a utiliser un cast. Cela parait evident puisque le C n'integre pas de notion d'heritage, seulement de polymorphisme (au moins deux cas dans la norme: le recouvrement d'aggregation a l'offset zero et les flexibles arrays). Les extension de gcc offre d'avantage de cas de polymorphismes.
Si par exemple on va plus loin (cas de OOC) et que toutes les methodes de classes sont virtuelles et que la definition d'une classe utilise quelques macros, alors le cast n'est plus utile. On est dans le cas que tu dis, un support 'non-natif' de la variance de type.
- conception par contrat, c'est plus une discipline de programmation
qu'un probleme de support syntaxique.
Je ne suis pas tout à fait d'accord là dessus. La conception par contrat est
avant tout un support à la fois syntaxique mais aussi sémantique. Par
exemple, le support de la conception par contrat (qui est autre chose que
de simples assertions) permet de penser différemment la gestion des
exceptions puisque ces dernières ne sont plus alors gérées de la façon
"classique" : en gros les exceptions sont provoquées par des violations de
contrat, à partir de là, on pense les exceptions différemment.
J'ai lu le Meyer, mais je n'ai pas assez d'experience dans l'utilisation de la programmation par contrat. Je suis cependant tout a fait d'accord que ce ne sont pas de simples assertions mais plutot des ensembles d'assertions (au sens mathematique du terme) avec les notions de commutativite et de distributivite qui vont avec.
Qu'est ce que tu entends par "on pense les exceptions différemment".
- closures, c'est essentiellement un probleme de gestion memoire qui se
resout par un comptage de reference sur objet dynamique.
Je ne crois pas car lorsque tu passes d'un appel à un autre, entre temps ton
compteur de référence peut-être à 0 et là bye bye. Une implémentation
Je me suis mal exprime:
1) si les objects sont alloues sur le tas (ou statiques/globaux mais pas automatiques), c'est ce que j'entendais par dynamique.
2) si chaque objet supporte le comptage de reference (ou le langage utilise un GC)
et si le langage supporte les aggregation et les pointeurs de fonction (comme le C) alors les closures sont triviales a implementer. Et effectivement dans le cas non-GC, elles incrementent le compteur de reference des objets qu'elles referencent pour prolonger leur duree de vie au dela du contexte lexical courant.
1) est lies a une politique systematique de la gestion de la memoire des objets
2) est lies a une politique systematique de la gestion de la duree de vie des objets qui dans le cas du GC releve uniquement de la gestion memoire.
Deux concepts present dans beaucoup de langage OO, et tout a fait implementable en C (et present dans OOC), c'est ce que j'ai voulu dire.
classique des closures est la gestion d'une structure de données qui
contient un pointeur sur le code de la fonction et l'ensemble des
variables/valeurs de la fonction crée au moment de son appel. En C, on peut
simuler les closures en utilisant le mot-clé static.
C'est seulement un cas particulier non reentrant d'implementation donc non recommande.
a+, ld.