This section gives you some examples on how to specify different C functions using define-foreign. The full specification of define-foreign is
(define-foreign t-function-name
(c-function-name (in type-spec opt-doc)
(in type-spec opt-doc)
...
)
ret-type-spec )
where type-spec is one of of the entries shown in Table .
opt-doc is any T symbol and is only used to document the name of the parameter. ret-type-spec can be one of the type-specs or ignore, in which case, the return value is discarded.
For example, consider the following definition
(define-foreign hack
(hackc (in rep/integer x)
(in rep/char y)
(in rep/double z))
rep/integer)
This corresponds to a C function defined as
int
hackc(x, y, z)
int x;
char y;
double z;
{
...
}
Next we consider how to pass strings to C functions.
T strings are not null-terminated, whereas C strings are.
Thus, before T strings are passed to C, they must be null terminated.
This is done by calling the T function tex2html_comment_mark>1 Unfortunately, this function modifies its argument, so you may want to use {\tt copy-string} to first make a new copy of the T string. \verb
tex2html_comment_mark>2
t-implementation-env so if you want to use it in another
locale, you might want to import it there.
Here is an example:
(define-foreign thirdchar4?
(checkthirdchar (in rep/string x))
rep/integer)
corresponds to the C function
int
checkthirdchar(x)
char *x;
{
if (*(x+2) == '4')
return (1);
else
return (0);
}
Now, say that we had a T string bound to the T symbol mystring, then we could call this function as
C does not have the capability of having multiple return values. To work around this, C programmers pass in a pointer to a variable and then have the called function use that pointer to write the result into the caller's variable. It should be noted that the caller has to allocate the memory required for this return value. Here is an example of how to do this.
(define-foreign xhack
(chack (in rep/extend xptr)
(in rep/extend yptr))
ignore)
and the corresponding C function is
void
chack(xptr, yptr)
int *xptr, yptr;
{
*xptr = 3; *yptr = 5;
}
Now, before calling xhack, we have to allocate storage for the two ints that will be returned. This memory is usually allocated as a T bytev. Also, the return value will be whatever the C function wrote into that bytev and will have to be converted into a value that T can accept. If we are dealing with C ints which are positive integers using less than 29 bits, then the function bref-32 does the job. This is done as follows.
(lset x (make-bytev 4)) ; allocate storage for x
(lset y (make-bytev 4)) ; allocate storage for y
(xhack x y) ; call xhack
(lset realx (bref-32 x 0)) ; get T value for x
(lset realy (bref-32 y 0)) ; get T value for y
A similar procedure may be used to return any value, including C structures. The important points to keep in mind are that the caller (i.e. the T function) should first allocate the required memory using make-bytev, then call the foreign function to fill in the bytev, and then the caller has to explicitly convert the bits in the bytev into a representation that T understands. All this can be encapsulated into another function. For example,
(define (xnothack)
(let ((x (make-bytev 4))
(y (make-bytev 4)))
(xhack x y)
(return (bref-32 x 0)
(bref-32 y 0))))
Returning strings is somewhat more problematic because their size cannot be known before calling the foreign function. Of course, one way to handle this is to have a maximum size string allocated, but this is often wasteful. Here is an example of how one might return a string from a C function.
(define-foreign retstr
(cstr)
rep/string)
and the corresponding C function is
char *
cstr ()
{
extern char *malloc();
char *strptr;
int strsize;
... find out how big the string is to be ...
strptr = malloc(strsize);
... fill in the string as required ...
return(strptr);
}
This function is called as follows
asciz->string
converts a null-terminated sequence of bytes into
a T string.
It also makes a copy of those bytes, so after it is done, there is no
connection between the C string and the T string.
Thus, the C string may be freed at this point.
asciz->string
is available in the t-implementation-env
and you may want to import
it into the locale you are using.
The type-spec rep/extend
is to be used when you are
dealing with a pointer into T space (like a bytev), but
rep/pointer
should be used when the pointer points into C
space.
In addition, rep/pointer
can only be used if no manipulations
are done to it in T.
In other words, use rep/pointer
if you are returning a pointer
into C space and if all you are going to do is to pass that pointer
back to a C function.
The c-function-name in the definition of the foreign function
can either be a T symbol or a T string.
If it is a T symbol (the normal case), the symbol is converted to a
string, then all the characters in that string are converted to
lower-case.
An underscore `` _
'' is prepended to this string to form the
name of that is searched for in the C symbol table.
Since most C functions have only lower-case characters in their names,
this works just fine.
However, in the cases when you need to call a C function which has
upper-case characters in its name, you will have to use a T string
rather than a T symbol for the C function name. If a string is provided as
the foreign name no conversions are done.
For example consider the definition of the XOpenDisplay
function for the X Window System.
(define-foreign xopendisplay
("
XOpenDisplay"
(in rep/string name))
rep/pointer)
It will have been noticed that it is not possible to directly pass or return 32-bit entities to/from C. This is because T uses the high-order two bits for tag information. A T fixnum can have a maximum magnitude of 29 bits. Most of the time, this will not be a problem, but sometimes it is desirable to have a full 32 bits passed back and forth between T and C. The way to do this is to use a four byte bytev to hold the 32 bits and then transfer a pointer to this bytev to C. For this to work, an auxiliary help function must be written in C that wiil do the appropriate conversions. For example, suppose there is a C function defined as follows:
unsigned long
cfun(x)
unsigned long x;
{
return ( x + 5L);
}
If all the bits are being used, then we cannot just use a
rep/integer
declaration in the define-foreign for the function.
First, we have to define a new ``help'' function that will do the
conversions and then call that function from T.
The ``help'' function can be defined as
void
helpcfun(y,ret)
unsigned long *y, *ret;
{
*ret = cfun(*y);
}
and the corresponding T function and the define-foreign for the C help function can be defined as
(define-foreign helpcfun
(helpcfun (in rep/extend y)
(in rep/extend ret))
ignore)
;;; expects a bytev as argument and returns a bytev
(define (cfun x)
(let ((rv (make-bytev 4)))
(helpcfun x rv)
rv))
You should also be aware that the function bref-32 only converts the low-order 30 bits to a T fixnum and so cannot be used when dealing with 32-bit entities. This 32-bit value could be made into a bignum by using bref-16-u to extract the high and low half and using generic arithmetic. Such code would be different on a Vax than a Sun.