Manuel de suivi dynamique Solaris

Déclarations du translateur

Un translateur est un ensemble d'instructions d'affectation en D proposées par le fournisseur d'une interface permettant de convertir une expression d'entrée en un objet de type struct. Afin de comprendre la nécessité des translateurs et de leur utilisation, examinons les routines de bibliothèque standard ANSI-C définies dans stdio.h. Ces routines fonctionnent sur une structure de données nommée FILE dont les artefacts d'implémentation sont retirés par les programmeurs en langage C. Une technique standard de création d'une abstraction de structure de données ne consiste qu'à faire suivre une déclaration de structure de données dans des fichiers d'en-tête publics tout en conservant la définition struct correspondante dans un fichier d'en-tête privé.

Si vous écrivez un programme en C et souhaitez connaître le descripteur de fichier correspondant à une struct FILE, vous pouvez utiliser la fonction fileno(3C) pour obtenir le descripteur au lieu de déréférencer directement un membre de la struct FILE. Les fichiers d'en-tête Solaris appliquent cette règle en définissant FILE comme une balise de suivi de déclaration opaque de manière à empêcher son déréférencement direct par les programmes en C qui intègrent <stdio.h>. Dans la bibliothèque libc.so.1, vous pouvez imaginer que la fonction fileno() est implémentée dans C de la manière suivante :

int
fileno(FILE *fp)
{
	struct file_impl *ip = (struct file_impl *)fp;

	return (ip->fd);
}

Notre exemple de fonction fileno() utilise en argument un pointeur FILE et le transmet à un pointeur sur une structure libc interne correspondante, struct file_impl, puis retourne la valeur du membre fd de la structure d'implémentation. Pourquoi Solaris implémente-t-il des interfaces de cette manière ? La suppression des détails de l'implémentation actuelle de libc des programmes client permet à Sun de préserver une compatibilité binaire forte tout en continuant à évoluer et à modifier les détails d'implémentation interne de libc. Dans notre exemple, le membre fd peut changer de taille ou de position dans struct file_impl, y compris dans un correctif, alors que les codes binaires existants appelant fileno(3C) ne sont pas affectés par cette modification, car ils ne dépendent pas de ces artefacts.

Malheureusement, les logiciels d'observation comme DTrace doivent inspecter l'implémentation de l'intérieur pour fournir des résultats utiles sans se permettre le luxe d'appeler des fonctions arbitraires en C définies dans les bibliothèques de Solaris ou dans le noyau. Vous pouvez déclarer une copie de struct file_impl dans vos programmes en D pour instrumenter les routines déclarées dans stdio.h. Cependant, votre programme en D risque de reposer sur des artefacts d'implémentation privés de la bibliothèque qui ne seront peut-être plus valables dans une future micro-version ou version mineure, voire même dans un prochain correctif. Nous souhaitons, dans l'idéal, fournir une construction à utiliser dans les programmes en D, construction qui est liée à l'implémentation de la bibliothèque et mise à jour en conséquence tout en continuant de fournir une couche supplémentaire d'abstraction associée à une meilleure stabilité.

Un nouveau translateur est créé à l'aide d'une déclaration se présentant comme suit :

translator output-type < input-type input-identifier > {
	member-name = expression ;
	member-name = expression ;
	...
};	

output-type nomme une struct qui sera le type de résultat de la conversion. input-type spécifie le type de l'expression d'entrée et figure entre crochets angulaires < >. Il est suivi de input-identifier que vous pouvez utiliser en tant qu'alias pour l'expression d'entrée dans les expressions du translateur. Le corps du translateur figure entre accolades { } et se termine par un point-virgule (;). Il consiste en une liste de member-name et d'identificateurs correspondant aux expressions de conversion. Chaque déclaration de membre doit nommer un membre unique de output-type et doit se voir affecter une expression dont le type est compatible avec celui du membre, conformément aux règles de l'opérateur d'affectation en D (=).

Par exemple, nous pouvons définir une struct d'informations stables sur les fichiers stdio en fonction de quelques-unes des interfaces libcdisponibles :

struct file_info {
	int file_fd;   /* file descriptor from fileno(3C) */
	int file_eof;  /* eof flag from feof(3C) */
};

Il est ensuite possible de déclarer en langage D un translateur en D hypothétique de FILE vers file_info comme suit :

translator struct file_info < FILE *F > {
	file_fd = ((struct file_impl *)F)->fd;
	file_eof = ((struct file_impl *)F)->eof;
};

Dans notre translateur hypothétique, l'expression d'entrée est de type FILE * et input-identifier F lui est affecté. Il est ensuite possible d'utiliser l'identificateur F dans les expressions du membre du translateur comme une variable de type FILE * qui n'est visible que dans le corps de la déclaration du translateur. Pour déterminer la valeur du membre de sortie file_fd, le translateur exécute un forçage de type et un déréférencement identiques à l'implémentation hypothétique de fileno(3C), illustrée ci-dessus. Une conversion similaire est réalisée pour obtenir la valeur de l'indicateur EOF.

Sun fournit un ensemble de translateurs à utiliser avec les interfaces Solaris que vous pouvez invoquer à partir de vos programmes en D et promet de conserver ces translateurs conformément aux règles sur la stabilité des interfaces définies précédemment, au moment des changements dans l'implémentation de l'interface correspondante. Nous étudierons ces translateurs un peu plus loin dans ce chapitre, une fois que vous saurez invoquer des translateurs à partir du langage D. La fonction de conversion elle-même est également fournie par l'application et les développeurs de bibliothèque qui souhaitent proposer leurs propres translateurs que les programmeurs en C peuvent utiliser pour observer l'état de leurs packages logiciels.