More details on define-foreign

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 [*].


Table: Type-specs and their corresponding C types
Type-spec Corresponding C type Number of bits
rep/integer int or long 30
rep/char unsigned char 8
rep/integer-8-s char 8
rep/integer-8-u unsigned char 8
rep/integer-16-s short 16
rep/integer-16-u unsigned short 16
rep/double double 64
rep/string (char *) 30
rep/extend (void *) 30
rep/pointer (void *) 30


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. \verbtex2html_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

(thirdchar4? (string->asciz! (copy-string mystring)))

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

(lset retval (asciz->string (retstr)))
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.