Cette semaine, c'était le dernier TD de la partie C du module CSH. Comme je n'interviens pas dans la partie Shell du module, c'était ma dernière intervention. Comme j'avais à peu près couvert le programme du module, j'ai refait un point sur le préprocesseur, et j'ai fini mon discours sur la programmation objet en C.
Le préprocesseur est toujours embêtant à caser dans un cours de C car il ne s'agit pas vraiment de programmation C, c'est une sur-couche, une verrue, sur le langage C lui-même pas forcément hyper-esthétique. D'ailleurs, la plupart des bouquins de C ne parlent pas du préprocesseur, ou pas bien, je trouve. Là, tout à la fin du cours, les étudiants ont le bagage pour comprendre les manques du compilo C que le préprocesseur corrige. D'où l'intérêt de prendre une heure dans un guide de référence comme ça.
La seconde partie du TD portait donc sur les concepts avancés de programmation objet en C. Il y a différentes façons de faire, puisque le langage ne fait rien et qu'il faut tout faire soi-même.
Pour le polymorphisme, j'ai fait léger : je n'ai montré que la possibilité de mettre des pointeurs sur fonction dans les objets. Cette façon n'est pas la plus efficace: il vaut mieux mettre les pointeurs dans une structure spécifique représentant la classe. De cette façon, à la création de l'objet, on n'a qu'un seul pointeur (sur la classe) à initialiser. À l'usage, ça va un poil moins vite puisqu'il faut 2 déréférencements de pointeur pour trouver l'adresse de la fonction à exécuter au lieu d'un seul, mais c'est assez négligeable en pratique. Bon, c'est un tradeoff: la durée de vie des objets influe sur les performances relatives des approches : vaut-il mieux passer un chouille plus de temps à créer les objets à l'initialisation, puis aller un micro-chouille plus vite à les utiliser ou le contraire? Dans SimGrid, on a choisi la première approche mais la seconde serait sans doute assez équivalente.
Pour l'héritage, j'ai montré la façon consistant à mettre en premier champ de la structure-objet fille la structure représentant l'ancêtre. C'est sans doute l'une des façons les plus simples. Une autre solution est de mettre dans la fille un pointeur vers la structure mère, ce qui permet d'avoir des champs privés dans la mère que les filles ne peuvent pas tripatouiller. Mais ce qui est beau (je trouve) dans l'approche que j'ai présenté, c'est qu'on peut transtyper une classe fille en sa classe mère sans soucis puisque l'alignement des champs de données est préservé en mémoire. Et ça, je dois dire que c'est tellement inattendu que je trouve ça bô, tout simplement.
Bon, ceci dit, force est de constater que mes étudiants n'ont pas partagé mon exaltation devant la beauté de ces constructions. Je comprend pas. Ces jeunes n'ont vraiment aucun goût esthégeek, c'est pas possible.
Le dernier tableau est un petit retour sur le Makefile, histoire de remettre une couche (répétition est mère de pédagogie), de reparler des variables que j'avais vraiment introduit très très vite la dernière fois, et d'introduire des notions comme .PHONY. Rien de bien méchant, je reconstruirais automake avec eux une autre fois