A translator is a collection of D assignment
statements provided by the supplier of an interface. Translators
can be used to translate an input expression into an object of the
struct
type. To understand the need for using
translators, consider as an example the ANSI C standard library
routines that are defined in stdio.h
. These
routines operate on a data structure named
FILE
, which contains implementation artifacts
that are abstracted away from C programmers. A standard technique
for creating a data structure abstraction is to provide only a
forward declaration of a data structure in public header files,
while keeping the corresponding struct
definition in a separate and private header file.
If you are writing a C program and want to know the file
descriptor corresponding to a FILE
struct
, use the fileno()
function to obtain the descriptor rather than dereferencing a
member of the FILE
struct
directly. The Oracle Linux header files enforce this rule by defining
FILE
as an opaque forward declaration tag so
that it cannot be dereferenced directly by C programs that include
<stdio.h>
.
Inside the /lib/libc.so.6
library, consider the
following hypothetical example where fileno
is
implemented in C, noting that a real-life implementation would not
be at all similar to this example:
int fileno(FILE *fp) { struct file_impl *ip = (struct file_impl *)fp; return (ip->fd); }
In the example, the hypothetical fileno
takes a
FILE
pointer as an argument and casts it to a
pointer that corresponds to the internal libc
structure, struct file_impl
, then returns the
value of the fd
member of the implementation
structure.
Unfortunately, observability software like DTrace requires the
ability to peer inside the implementation in order to provide
useful results. DTrace cannot call arbitrary C functions that are
defined in Oracle Linux libraries or in the kernel. You could declare a
copy of struct file_impl
in your D program to
instrument the routines that are declared in
stdio.h
, but then your D program would rely on
Private implementation artifacts of the library that might break
in a future micro or minor release, or even in a patch. Ideally,
you want to provide a construct for use in D programs that is
bound to the implementation of the library and is updated
accordingly, yet still provides an additional layer of abstraction
associated with greater stability.
A new translator is created by using a declaration of the following form:
translatoroutput-type
<input-type
input-identifier
> {member-name
=expression
;member-name
=expression
; ... };
The output-type
names a
struct
that will be the result type for the
translation. The input-type
specifies
the type of the input expression, is surrounded in angle brackets
<>
, and followed by an
input-identifier
that can be used in
the translator expressions as an alias for the input expression.
The body of the translator is surrounded in braces
{}
and terminated with a semicolon
(;
), and consists of a list of
member-name
s and identifiers that
correspond to translation expressions. Each member declaration
must name a unique member of the
output-type
and must be assigned an
expression of a type that is compatible with the member type,
according to the rules for the D assignment (=
)
operator.
For example, you could define a struct
of
stable information about stdio
files based on
some of the available libc
interfaces:
struct file_info { int file_fd; /* file descriptor from fileno() */ int file_eof; /* eof flag from feof() */ };
Then, you could define a hypothetical D translator from
FILE
to file_info
:
translator struct file_info < FILE *F > { file_fd = ((struct file_impl *)F)->fd; file_eof = ((struct file_impl *)F)->eof; };
In this hypothetical translator, the input expression is of type
FILE *
and is assigned the
input-identifier
F
.
The identifier F
can then be used in the
translator member expressions as a variable of type FILE
*
that is only visible within the body of the translator
declaration. To determine the value of the output
file_fd
member, the translator performs a cast
and dereference similar to the hypothetical implementation of
fileno()
shown in the previous example. A
similar translation is performed to obtain the value of the
EOF
indicator.