Table of Contents
Getting Started
Getting the code
cl-ffi is not in Quicklisp. With stift,
require it in your project's stiftfile:
stift get https://fakehacker.cc/dxtr/cl-ffi.git
A project that uses a library built on cl-ffi, such as cl-vk, gets cl-ffi
from that library's own stiftfile, as an indirect entry. Without stift,
clone the repository where ASDF finds it, such as ~/common-lisp/.
cl-ffi needs SBCL on a 64-bit platform, and nothing else: no C compiler, no headers, no other Lisp library.
Loading
(asdf:load-system :cl-ffi)
(defpackage #:example
(:use #:cl)
(:local-nicknames (#:ffi #:cl-ffi)))
(in-package #:example)
The examples in this wiki write ffi: for cl-ffi.
A first binding
A library is defined once, by the files to look for on each platform, and loaded:
(ffi:define-foreign-library libz
(:windows "zlib1.dll")
(:unix (:or "libz.so.1" "libz.so")))
(ffi:use-foreign-library libz)
A C function becomes a Lisp function, typed:
(ffi:defcfun ("zlibVersion" zlib-version) :string
"Return the version of zlib that was loaded.")
(zlib-version) ; => "1.3.2"
defcfun declaims the function's ftype from the C types, so SBCL checks
its arguments, at compile time where it can. A :string result comes back
as a Lisp string.
Memory for an out-parameter or a struct comes from the stack, for the extent of a body:
(ffi:defcfun ("compressBound" compress-bound) :ulong (length :ulong))
(ffi:defcfun ("compress" compress) :int
(destination :pointer) (destination-length :pointer)
(source :pointer) (source-length :ulong))
(let ((text (map '(vector (unsigned-byte 8)) #'char-code "hello, hello, hello")))
(ffi:with-foreign-object (length :ulong)
(setf (ffi:mem-ref length :ulong) (compress-bound (length text)))
(ffi:with-foreign-pointer (output 128)
(ffi:with-pointer-to-vector-data (input text)
(compress output length input (length text)))
(ffi:mem-ref length :ulong)))) ; => the compressed length
C calls Lisp through a callback:
(ffi:defcallback compare :int ((a :pointer) (b :pointer))
(- (ffi:mem-ref a :int32) (ffi:mem-ref b :int32)))
(ffi:with-foreign-object (numbers :int32 4)
(loop for n in '(3 1 4 1)
for i from 0
do (setf (ffi:mem-aref numbers :int32 i) n))
(ffi:foreign-funcall "qsort" :pointer numbers :size 4 :size 4
:pointer (ffi:callback compare) :void)
(loop for i below 4 collect (ffi:mem-aref numbers :int32 i))) ; => (1 1 3 4)
Next: Types, or For cffi Users if you know cffi.