January 24, 2008
Getting started with CFFI
I really like the Lisp approach of accessing foreign functions; it puts the programmer in charge (as usual) instead of making him wait for some bindings to appear or get updated.
Here’s a little recipe that shows how to get the load averages (the thing uptime shows) in Lisp, which will be useful later when we build a solid logging foundation with Gary’s log5 package.
In case you don’t know, the load average shows an approximation of the number of processes in the system’s task queue, thus serving as indication for machine load.
First, let’s do the initialization work for CFFI, as pointed out in its user guide:
(asdf:oos 'asdf:load-op 'cffi) (defpackage :cffi-user (:use :common-lisp :cffi)) (in-package :cffi-user) (define-foreign-library libc (:unix (:or "libc.so.6" "libc.so.5" "libc.so")) (t (:default "libc.so"))) (use-foreign-library libc)
Now we actually need to start thinking. How do we get at the numbers?
Let’s find the C function:
% apropos load | egrep -i "average|avg" getloadavg (3) - get system load averages [...] % man 3 getloadavg
The man page gives us this prototype:
int getloadavg(double loadavg[], int nelem);
It also tells us that the first parameter will be filled with nelem samples and notes that (at least on my Linux system) the maximum number of samples is three, denoting the load averages of the last 1, 5 and 15 minutes. Let’s say that we want all three.
Now unfortunately the CFFI manual doesn’t say anything about arrays. But we can rewrite the prototype as
int getloadavg(double* loadavg, int nelem);
leading to the following CFFI function spec:
(defcfun "getloadavg" :int (loadavg :pointer) (nelem :int))
Now we are able to use foreign-alloc to allocate a pointer of the correct size (i.e. 3*sizeof(double)), and mem-aref to access the resulting array.
Combined with matching LOOP and FORMAT programs and the manual garbage collection we get:
(defun load-averages () (let ((loadavg (foreign-alloc :double :count 3))) (getloadavg loadavg 3) ; note the imperative style we are forced to use (prog1 ; we need to clean up after producing the return value (format nil "~{~,2F~^ ~}" (loop for i from 0 to 2 collect (mem-aref loadavg :double i))) (foreign-free loadavg))))
If you have questions regarding any part of that last snippet, feel free to ask.
Note that we don’t do any error checking here; getloadavg will return -1 on failure, although I can’t imagine why it would do so.
You can access the full code at http://paste.lisp.org/display/54746.
I hope this post wasn’t overly verbose (read: boring) to you.
It was my intention to make this understandable for beginners.
Comments(5)
and what do i need to define if i want to call c-function with multi-dimensional lisp array?
something like this:
;;int some_c_function(double *, int dim1, int dim2);
(let ((ar (make-array (list *dim1* *dim2*) :element-type ‘double-float)))
(some-c-function ar *dim1* *dim2*))
Thanks for the snippet! It was precisely was I was looking for. I’m just beginning my Lisp journey, and right now I’m thinking about working on a project that requires access to C bindings.
Is there an equivalent for static (.a) libraries?
When dealing with arrays something like http://www.cliki.net/ffa is helpful, I think.
Mikael, how exactly do you imagine dynamic loading of static libraries would work? I suppose you can use Embeddable Common-Lisp from http://ecls.sourceforge.net/ which generates C code, so one can link it with anything C compiler can.
baleog, I recommend ffa for arrays with more than one dimension.
If you don’t want to use it, you need to keep track of N pointers, where N is your array’s dimension. I don’t think you can use Lisp arrays with CFFI (or UFFI, for that matter).
[...] one for the load averages, so we can see whether an error might have occurred due to heavy load (maybe a race condition): [...]