Using the newLISP FFI

One of newLisp’s out-of-the-box features is its foreign function interface. It is simple to use, requiring only a minimal knowledge of C and a willingness to read documentation.

Importing foreign functions

Loading a function from a shared library is done with the ‘import’ function. Once imported, the function now exists as a normal function in the namespace in which is was imported. From the newLisp documentation:

;; import in Linux
(import "libc.so.6" "printf")
;; import in Mac OS X
(import "libc.dylib" "printf")
;; import in CYGWIN
(import "cygwin1.dll" "printf")

printf is now a function that may be called as any other. Because functions evaluate to themselves and import returns the function value, it is simple to bind the function to a different name:

(setf print-f (import "libc.dylib" "printf"))

Calling foreign functions

Foreign functions are called in the same was any other function.  They can be applied, curried, or mapped.

(printf "%g %s %d %c\n" 1.23 "hello" 999 65)
1.23 hello 999 A

The values that the foreign function receives are pointers to the values that were passed to the function in newLisp. In most cases, this is not problematic, but it is something that must be kept in mind.

Return values

Often, a library function will not return a value directly. Often, functions return a pointer to a value or struct in memory. newLisp cannot guess at the size of the return value or its composition, so it is up to the programmer to know what kind of value is being returned.

For functions that return a pointer to a single value, the functions get-int, get-char, and get-float may be used to find the value to which the pointer points. It is important to check the return value of such a function, as passing an invalid value or null pointer to these functions may crash the interpreter.

(setf ptr (get-pointer-from-some-c-func))
(if-not (zero? ptr)
  (setf value (get-int ptr)))

Strings may be accessed using get-string, although functions that return string pointers often leave it to the consuming function to free the memory. To do so, the function ‘free’ (which frees memory) must first be imported from libc:

(import "libc.dylib" "free")
(setf ptr (get-string-pointer))
(if-not (zero? ptr)
  (setf str (get-string ptr)) ; copy the c string to a newLisp string
  (free ptr)) ; free the string allocated by the foreign function

Deconstructing structs

Functions in 3rd party libraries often return custom types that newLisp cannot introspect. In these cases, the programmer must tell newLisp how to unpack the values.

To that end, newLisp provides the pack and unpack functions. These can be used to pack newLisp values into a structure and vice versa. pack and unpack use a specially formated string that corresponds to the members of the struct. Take the following struct as an example:

struct foo {
  int int_value;
  double float_value;
}

It could be unpacked using the format, “lu lf” (see the documentation for pack and unpack for a full explanation of the format used). Unpacking with this format will return a list of the struct’s values.

(unpack "lu lf" foo-instance) ; returns (42 84.5)

A function that accepts such a struct can be passed the return value of the pack function:

(foofunction (pack "lu lf" 42 84.5))

This makes it simple to unpack complex types into newLisp values.

Putting it all together

It is helpful to create a base FOOP class to work with compound types. Specific types can be prototyped from this class, changing only the pack format to match.

(setf Pointer:pack-format nil)
 
(define (Pointer:Pointer addr)
  (list (context) addr))
 
(define (Pointer:pointer inst)
  (inst 1))
 
(define (Pointer:member inst n , unpacked)
  (setf unpacked (unpack Pointer:pack-format (:pointer inst)))
  (unpacked n))

The Pointer class gives us a few useful methods. :pointer returns the pointer address and :member returns the nth member of the struct. So, the foo class would look like this:

(new 'Pointer 'Foo)
(setf Foo:pack-format "lu lf")

It might also be helpful to add a couple of accessor functions:

(define (Foo:int-val inst , ptr)
  (setf ptr (:member inst 0))
  (if-not (zero? ptr)
    (get-int ptr)))
 
(define (Foo:float-val inst , ptr)
  (setf ptr (:member inst 1))
  (if-not (zero? ptr)
    (get-float ptr)))

Functions from a foreign library that work on ‘foo’ types can now be added directly to the Foo class as methods, and passed the return value of (:pointer my-foo). This makes working with foreign types reasonably trivial.

5 thoughts on “Using the newLISP FFI

  1. BTW, why Lutz doesn’t put an printf example for default win32 distribution (not cygwin one)? It is a common shame of unix programmers while using windows: not to know how their libc is called. ;-(

    > (import "msvcrt.dll" "printf")
    printf <77C4186A>
    > (printf "pi = %f\n" 3.1415926)
    pi = 3.141593
    14

    I hope WordPress will not squash my formatting…

  2. Pingback: The cost of soft/minor faults « cartesian product

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>