C Orienté Objet

, par MiKaël Navarro

Les prototypes des fonctions sont un peu la carte de visite d’un programme,
c’est-à-dire son interface, qui est l’ensembles des fonctionnalités qu’il
propose. Nous allons nous concentrer sur cette notion d’interface afin de
construire des bibliothèques de fonctions constituées de deux parties :
l’interface et la réalisation.

Paquetage

Le paquetage permet de séparer de manière logique les composantes d’un logiciel
de manière à les rendre les plus indépendantes possibles. Cette manière de
programmer utilise des concepts provenant des langages à objects, bien que la
ressemblance reste éloignée car le langage C n’est pas orienté objet.

 L’implémentation

  • La déclaration de la structure de donnée
  • Le constructeur et le destructeur
  • Les sélecteurs

 Les utilitaires

Rassemble un certain nombre de fonctions de facilité utilisant directement les
fonctionnalités de la partie implémentation.

 L’utilisateur

Correspond aux fonctions qui utilisent les deux parties précédentes.

La bibliothèque sera constitué des deux premières parties : une couche de bas
niveau et une couche de haut niveau. La couche utilisateur se sert de l’ensemble
des fonctionnalités des deux premières.

 Nombres rationnels

Fort de nos connaissances, nous souhaitons concevoir une bibliothèque pour
les nombres rationnels.

  • Primitives
    • Structure de donnée

Le premier champ est le numérateur et le second le dénominateur.

    • Constructeur et destructeur
    • Selecteurs
  • Utilitaires
    • Addition
    • Affichage
  • Utilisation

Nous avons maintenant de quoi réaliser un petit programme :

Pour faire fonctionner ce programme, il faut rasembler toutes les fonctions dans
un même fichier et include #include <stdio.h> (pour printf),
le compiler et exécuter le binaire produit.

Cette structure peut sembler lourde. Elle a l’avantage d’être complètement générique
et les sections utilitaires et utilisation ne font absolument aucune hypothèse sur
la nature du type de donnée.

Ceci est la base de la notion de paquetage : un paquetage fournit des
fonctionnalités agissant sur des noms de données.

Programme C multi-fichiers

Un fichier d’en-tête est un fichier source C (.h) contenant principalement des
déclarations de types de données et des prototypes de fonctions.

Nous pouvons maintenant préparer un fichier d’en-tête rationnel.h :

Nous créons ensuite le fichier _rationnel.c contenant nos primitives.
Par convention en C, ce qui est primitif est préfixé par le caractère de soulignement.

Et, nous écrivons le fichier rationnel.c contenant les fonctions de haut niveau de
la même manière.

Enfin le programme principal sera placé dans un fichier main.c avec une directive
d’inclusion sur notre en-tête rationnel.h.

Pour compiler l’ensemble :

Ainsi nous sommes capable de rassembler les fichiers _rationnel.c et rationnel.c
dans une bibliothèque rationnel.a|.so afin de cacher la façon dont la bibliothèque est
implémentée ; Et le fichier rationnel.h devient alors notre interface , c-à-d la
partie publique de notre bibliothèque.

Le résultat de notre paquetage est satisfaisant, mais la structure interne des
données est publique. L’objectif sera donc de cacher la structure de donnée
en utilisant le passage par référence et la compilation séparée.

Paquetage et interface

Dans les sections qui vont suivre, nous allons améliorer notre paquetage en
créant une véritable interface "opaque". Le but étant de séparer l’interface de
la bibliothèque du code réalisant cette interface.

Tout d’abord nous devons compiler nos fichiers sources séparéments :

Ce qui produit les objets _rationnel.o et rationnel.o.

Maintenant il est possible de rassembler les fichiers objets dans une archive, appelée
bibliothèque :

Considérons maintenant notre fichier main.c :

On voit qu’il est maintenant possible de distribuer la bibliothèque avec le fichier
d’en-tête.
Cependant, cette méthode de distribution impose de recompiler leur programme
pour prendre en compte une nouvelle version de la bibliothèque. Pour palier à ce
problème nous allons créer une bibliothèque dynamique.

Une bibliothèque dynamique ressemble à une bibliothèque statique, mais la liaison
au programme utilisateur est effectuée au moment de l’exécution et non lors de
l’édition des liens : il est alors possible de remplacer la bibliothèque par une autre
respectant la même interface.

Pour cela, au lieu d’utiliser l’archiveur nous allons utiliser gcc :

Et nous le lions à notre exécutable :

Enfin pour exécuter notre programme nous devons renseigner la localisation de
notre nouvelle bibliothèque avec la variable d’environnement :
$ export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH

Pointeurs

Grâce aux pointeurs nous alllons rendre la structure de donnée opaque à l’utilisateur :

Nous déclarons la structure de données _Rationnel, avec un souligné devant, et
la première lettre en majuscule. Puis un pointeur est déclaré de la même façon sans
le souligné devant.

L’impacte sur le fichier main.c se limite à l’usage du type Rationnel en lieu et
place de rationnel.

Attachons-nous maintenant à notre implémentation :

Nous sommes donc passé de l’allocation statique à l’allocation dynamique sans
que les utilisateurs ne soient obliger de changer leur code.

Enfin, à part le lettre en majuscule, aucun changement du coté de notre fichier
utilitaire rationnel.c.

Un petit détail reste encore, pour rendre l’interface complètement opaque nous
devons cacher la structure de données. Cela est maintenat possible en déplaçant
simplement la définition de la structure _Rationnel vers le fichier _rationnel.c
(la définition du pointeur Rationnel suffit au compilateur C pour effectuer ses
contrôles).

Ce type de programmation s’apparente en partie à la programmation orientée objet.
Les bibliothèques proposent des objets abstraits et des fonctions pour les manipuler.

Exemples

Exemples d’applications sur les types de données liste, pile et tables de
hachage
 : TADs

Application au projet ANTs permettant le tri des éléments d’un
couvain (oeufs, larves, cocons, ...) par des fourmis informatiques.

P.-S.

Merci à Guilhem de Wailly pour ces articles parus dans Linux Magazine #6-19,21-30