Guía de seguimiento dinámico de Solaris

Declaraciones de los traductores

Un traductor es una colección de instrucciones de asignación en D proporcionada por el proveedor de una interfaz que se puede usar para traducir una expresión de entrada en un objeto de tipo de estructura. Para comprender la necesidad de los traductores, utilizaremos como ejemplo las rutinas de la biblioteca estándar ANSI-C definidas en stdio.h. Estas rutinas funcionan en una estructura de datos llamada FILE, cuyos artefactos de implementación se abstraen de los programadores en C. Una técnica estándar para crear una abstracción de estructura de datos es proporcionar sólo una declaración hacia adelante de una estructura de datos en los archivos de encabezado públicos, a la vez que se conserva la definición de estructura correspondiente en un archivo de encabezado privado separado.

Si está escribiendo un programa en C y desea conocer el descriptor de archivo correspondiente a una estructura FILE, podrá usar la función fileno(3C) para obtener el descriptor en lugar de que un miembro de la estructura FILE deje de hacer referencia directamente. Los archivos de encabezado Solaris fuerzan esta regla definiendo FILE como una etiqueta de declaración de avance opaca, por lo que no puede dejar de hacer referencia directamente mediante programas escritos en C que incluyan <stdio.h>. En la biblioteca libc.so.1, puede imaginarse que fileno() está implementado en C de una forma parecida a ésta:

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

	return (ip->fd);
}

La función hipotética fileno() considera el puntero FILE como un argumento y lo convierte en un puntero hacia la estructura libc interna correspondiente, struct file_impl, y, a continuación, devuelve el valor del miembro fd de la estructura de implementación. ¿Por qué implementa Solaris las interfaces de esta forma? Al abstraer los detalles de la implementación actual de libc con respecto a los programas cliente, Sun puede mantener un compromiso de compatibilidad binaria sólida a la vez que continúa evolucionando y cambiando los detalles de implementación internos de libc. En nuestro ejemplo, el miembro fd podría cambiar de tamaño o posición dentro de struct file_impl, incluso en un parche, y los binarios existentes que llaman a fileno(3C) no se verían afectados por este cambio porque no dependen de estos artefactos.

Desafortunadamente, el software de observación, como por ejemplo DTrace, necesita mirar en la implementación con objeto de proporcionar unos resultados satisfactorios y no tiene el lujo de llamar a funciones arbitrarias en C definidas en las bibliotecas de Solaris o en el núcleo. Puede declarar una copia de struct file_impl en el programa escrito en D con la intención de instrumentar las rutinas declaradas en stdio.h, pero entonces el programa escrito en D se basaría en artefactos de implementación privados de la biblioteca que podrían aparecer en una microversión futura, en una versión menor o incluso en un parche. Nuestro objetivo es proporcionar una construcción para usarla en programas escritos en D, que esté enlazada a la implementación de la biblioteca y que se actualice en función de ello, pero también proporcionar una capa adicional de abstracción asociada con una mayor estabilidad.

Un traductor nuevo se crea usando una declaración con la forma:

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

output-type hace referencia a una estructura que será el tipo de resultado de la traducción. input-type hace referencia al tipo de la expresión de entrada; está rodeada de paréntesis angulares < > y seguida de input-identifier, que se puede usar en las expresiones del traductor como un alias para la expresión de entrada. El cuerpo del traductor está rodeado de llaves { } y termina con un punto y coma (;). Está formado por una lista de member-name e identificadores correspondientes a las expresiones de traducción. Cada una de las declaraciones miembro debe nombrar a un miembro único de output-type y debe estar asignado a una expresión de un tipo compatible con el tipo de miembro, según las reglas del operador de asignación de D (=).

Por ejemplo, podemos definir una estructura de información estable acerca de los archivos stdio basada en algunas de las interfaces libc disponibles:

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

Un hipotético traductor de D de FILE a file_info se podría declarar en D de la siguiente forma:

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

En este traductor hipotético, la expresión de entrada es del tipo FILE * y está asignada a input-identifier F. El identificador F se puede usar en las expresiones que son miembros del traductor como una variable del tipo FILE * que está visible sólo en el cuerpo de la declaración del traductor. Para determinar el valor del miembro file_fd de salida, el traductor realiza una conversión y deja de hacer referencia, de forma similar a la implementación hipotética de fileno(3C) mostrada anteriormente. Una traducción parecida se realiza para obtener el valor del indicador EOF.

Sun proporciona un conjunto de traductores para usarlos con las interfaces de Solaris que se pueden ejecutar desde los programas escritos en D. Asimismo, Sun se compromete a mantener estos traductores de acuerdo con las reglas de estabilidad de las interfaces definidas anteriormente, como la implementación de los cambios de interfaz correspondientes. Más adelante en el capítulo, encontrará información acerca de estos traductores, después de aprender cómo se ejecutan los traductores desde D. La utilidad de los traductores en sí misma también se proporciona para que la usen los desarrolladores de bibliotecas y aplicaciones que deseen ofrecer sus propios traductores con objeto de que los programadores de D puedan usarlos para estudiar el estado de sus paquetes de software.