-
Notifications
You must be signed in to change notification settings - Fork 124
FFI (interop with C)
(Work in progress)
Pixie has very good support for interop with C libraries, these features are based of a fairly basic set of features, but are composed via pixie.ffi-infer
into some rather easy to use utilites.
C Libraries can be loaded via pixie.stdlib/ffi-library
, a library with the given name will be searched for in every directory found in pixie.stdlib/*load-paths*
. The library once found will be returned as a pixie.stdlib.ExternalLib
object that can be handed to one of two symbol loading functions.
pixie.stdlib/ffi-fn
is used to load a function from an external library, while pixie.stdlib/ffi-voidp
is used to load void*
symbols.
Although these primitives are fairly basic, getting the arguments to these functions correct can be rather complex. Instead it is recommended that developers use the namespace known as ffi-infer
The ffi-infer
namespace contains a simple but competent inference engine for creating C interop libraries. The user of this namespace simply specifies symbols to import and where they can be found, and the engine determines the proper way to import them. The inference process can take some time, as it involves compiling a C++ template file that makes heavy use of template meta-programming. But the resulting data can be cached by compiling a given .pxi
file to a .pxic
file. Thus the macros are only compiled once, and the result of the macros can be re-used on each subsequent invocation of the Pixie program.
Here is a basic example of how to use ffi-infer:
(ns my-sdl-example
(require pixie.ffi-infer :refer :all))
(with-config {:library "SDL"
:cxx-flags ["`sdl2-config --cflags --libs`"]
:includes ["SDL.h"]
}
(defconst SDL_INIT_EVERYTHING)
(defcfn SDL_Init)
(defconst SDL_WINDOW_SHOWN))
When this example is executed, with-config
will create a small C++ program that links against flags found in :cxx-flags
. The program will start with a import of SDL.h
. The body of the program will then be a collection of template definitions that make use of Boost Type Traits to infer and emit the types of the requested symbols. This program will emit the type data as EDN. That EDN data will be loaded by with-config and used to generate a bunch of type defnintions. The resulting macro-expanded code will look something like this:
(binding [*library* (ffi-library "SDL.dylib")]
(def SDL_INIT_EVERYTHING 1)
(def SDL_Init (ffi-fn *library* "SDL_Init" [CUInt32] CUInt64))
(def SDL_WINDOW_SHOWN 44))
There are several instructions that can be handed to with-config
that handle everything from ffi callbacks, to structs, to global variables, but in this example, the value of this interop method can be clearly seen. The user simply specifies how to compile a C program with the given library, and the symbols that need to be imported, along with the generic type (function, global, constant, etc). And ffi-infer
does all the heavy lifting.
Pixie itself makes heavy use of ffi-infer
and so it is fairly robust and the upside is that the runtime penalty for precompiled files is very low.
** TODO **