Bridge for parsing c2ffi JSON into CFFI
Common Lisp C
Switch branches/tags
Nothing to show
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Failed to load latest commit information.


This project is deprecated in favor of cl-autowrap which does a whole lot more with a lot less work.


This is a bridge between c2ffi and CFFI. It reads the JSON output produced by c2ffi and produces very readable input for CFFI. Example:

#define FOO (1 << 2)

#define QUUX "abc"

const int BAR = FOO + 10;

extern SomeExtern;

void blah(char *x[]);

extern char *foo;

typedef struct my_point {
  int x;
  int y;
  int odd_value[BAR + 1];
} my_point_t;

typedef struct {
  int a, b;
} anonymous_t;

union my_union {
  char c;
  int i;
  double d;

enum some_values {

void do_something(my_point_t *p, int x, int y);

This produces the following two files:

;;;; example.lisp:

(in-package :common-lisp-user)

;; example.h:5:11
(defconstant +bar+ 14)

;; example.h:7:12
(cffi:defcvar (someextern "SomeExtern") :int)

;; example.h:9:6
(cffi:defcfun (blah "blah")
  (x (:pointer :string)))

;; example.h:11:14
(cffi:defcvar (foo "foo") :string)

;; example.h:13:16
(cffi:defcstruct my-point
  (x :int)
  (y :int)
  (odd-value :int :count 15))

;; example.h:17:3
(cffi:defctype my-point-t my-point)

;; example.h:21:3
 (eval-when (:compile-toplevel :load-toplevel :execute)
   (cffi:defcstruct #1=#:anon-type-1587
     (a :int)
     (b :int)))
 (cffi:defctype anonymous-t #1#))

;; example.h:23:7
(cffi:defcstruct my-union
  (c :char)
  (i :int)
  (d :double))

;; example.h:29:6
(cffi:defcenum some-values
  (:a-value 0)
  (:another-value 1)
  (:yet-another-value 2))

;; example.h:35:6
(cffi:defcfun (do-something "do_something")
  (p (:pointer my-point-t))
  (x :int)
  (y :int))

 '(do-something some-values my-union anonymous-t my-point-t my-point foo blah
                someextern +bar+))

;;;; macros.lisp:
(common-lisp:in-package :c2ffi-another-package)

;; macros.h:7:13
(common-lisp:defvar +quux+ "abc")

;; macros.h:8:18
(common-lisp:defconstant +foo+ 4)

(common-lisp:export '(+foo+ +quux+))


First, produce a spec file using c2ffi:

$ cd example/
$ c2ffi -M macros.h -o example.spec example.h
$ c2ffi -o macros.spec macros.h

Now you can generate a file manually like the following:

(c2ffi-cffi:parse-file-to-file "example.spec" "example.lisp")
(c2ffi-cffi:parse-file-to-file "macros.spec" "macros.lisp")

Note this probably didn't work, unless you specified the full path to the files. Easier, especially for projects, is to use the provided ASDF source-file type:

(eval-when (:compile-toplevel :load-toplevel :execute)
  (asdf:load-system :c2ffi-cffi))

(defsystem :my-system
  :components ((c2ffi-cffi:spec "example")
               (c2ffi-cffi:spec "macros")))

This will, at load/compile time, generate and load example.lisp and macros.lisp from the example.spec and macros.spec. You may also specify a :package PACKAGE, which will put an in-package at the top of the generated files. Otherwise this will default to :COMMON-LISP-USER, which is probably not what you want.

Note that the package must exist at the time the file is loaded, or you will get an error. This is necessary to write fully-qualified symbols.


Because some things are not supported by CFFI, like bitfields, and some are occasionally even unsupported by c2ffi (vector literals?), you can now specify exclusions as follows:

(let ((c2ffi-cffi:*exclude-sources*
       '("/path/to/bad-stuff/.*" ...))
       '("SomeBadCFun1" "SomeBad.*Funs")))

Both use cl-ppcre regexps to pre-exclude entire files or specific definitions. You will simply see the file/line and nil in the output.

This is also supported with ASDF:

  :components ((c2ffi-cffi:spec "example"
                  :exclude-sources ("/path/.*" ...)
                  :exclude-definitions ("Bad.*Defs" ...)))