Next: Examining and Modifying, Previous: Basic C Types and Functions, Up: Foreign Functions [Contents][Index]
Calling FFI functions is a many-step process. The actual call of an external function is the last step in this chain but can be done repeatedly and almost as comfortable as elisp function calls thenceforth. The chain to FFI function calls can be summed up as following.
Hereby, step 1 is independent from the other steps. It can be interchanged with the other steps arbitrarily, but is mandatory at all.
Load library libname. Return a foreign object handle if successful, or indicate an error if the library cannot be loaded.
The argument libname should be the file-name string of a shared object library (usual extension is .so).
The library should reside in one of the directories specified by the $LD_LIBRARY_PATH environment variable or the more global ld.so.cache.
Note you cannot simply modify the ld.so.cache, instead use the
command ldconfig
on a suited configuration file. See your
vendor’s documentation how to do that.
Loading a library using ffi-load
additionally registers this
library in a list of already loaded libraries.
Alist of loaded libraries with elements of the form (LIB-NAME . FFIO)
.
There is a raw library loader function without the registration code
and without error handling. However, it is highly suggested to use
ffi-load
exclusively.
Load library libname.
Return a foreign object handle if successful, or nil
if the
library cannot be loaded.
The argument libname should be the file-name string of a shared object library (usual extension is .so).
The library should reside in one of the directories specified by the $LD_LIBRARY_PATH environment variable or the more global ld.so.cache.
The following example (like all other examples in this section) is taken from ffi-curl.el which comes with the SXEmacs distribution. We assume the library libcurl.so to exist and to reside in a directory searched by the dynamic loader.
(ffi-load "libcurl.so") ⇒ #<ffiobject type=(pointer void) size=4 fotype=2 foptr=0x8a1dad8> ffi-loaded-libraries ⇒ (("libcurl.so" . #<ffiobject type=(pointer void) size=4 fotype=2 foptr=0x8a1dad8>))
Declaring the signature of a function is quite like reading a
library’s include file. The main function to achieve this is
ffi-defun
.
Make and return a foreign object of type type and bind it to the external symbol sym.
The argument type should be a function type-cell. The argument sym should be a string naming a function in one of the loaded libraries.
If sym does not exist in any of the loaded libraries, an error is indicated.
This is like ffi-bind
but for function objects.
On the other hand, a library may contain useful variables. The main
directive to bind such variable objects is ffi-bind
.
Make and return a foreign object of type type and bind it to the external symbol sym.
The argument type can be any type-cell. The argument sym should be a string naming an arbitrary symbol in one of the loaded libraries.
If sym does not exist in any of the loaded libraries, nil
is returned.
Again, let’s look at an example taken from ffi-curl.el
(setq curl:curl_easy_getinfo (ffi-defun '(function int (pointer void) int) "curl_easy_getinfo")) ⇒ #<ffiobject type=(function int (pointer void) int) size=4 fotype=3 foptr=0x40bfa370>
The ffi-bind
function works similarly.
As seen in the previous example, external objects are assigned an (elisp-)internal object which refers to them. Following this abstraction process, it is kind of obvious that arguments for external functions cannot be passed as internal elisp objects, but have to be converted somehow.
The most user-friendly function to accomplish this task is
ffi-create-fo
, although it cannot catch all the cases
(especially compound types are missing as of November 2005).
Create a foreign object of type type and set its value to val. Return created FFI object.
Note that memory allocation and other administrative tasks are entirely performed within the FFI API without involving the user. That is why we can simply “convert” an elisp string to a C string, as the example below will demonstrate.
(ffi-create-fo 'c-string "foobar") ⇒ #<ffiobject type=c-string size=4 fotype=0 foptr=0x88e3fdc>
(ffi-create-fo 'unsigned-int 2299) ⇒ #<ffiobject type=unsigned-int size=4 fotype=0 foptr=0x89f648c>
The function ffi-create-fo
is written in lisp and decomposes
to more elementary functions. We are going to discuss them here
flatly since they provide a more sophisticated basis for the handling
of foreign objects.
Create a new FFI object of type type.
If optional argument size is non-nil
it should be an
integer, in this case additional storage size to hold data of at
least length size is allocated.
Set fo’s foreign value to val.
Note that currently ffi-set
does not work on compound
data types, nevertheless there are workaround functions.
The following example will demonstrate the use of
make-ffi-object
and, in conjunction, ffi-set
.
(setq xmpl-fo (make-ffi-object 'long)) ⇒ #<ffiobject type=long size=4 fotype=0 foptr=0x8937dcc> (ffi-set xmpl-fo 20000) ⇒ 20000 xmpl-fo ⇒ #<ffiobject type=long size=4 fotype=0 foptr=0x8937dcc>
(setq xmpl-fo (make-ffi-object 'c-string)) ⇒ #<ffiobject type=c-string size=4 fotype=0 foptr=0x890158c> (ffi-set xmpl-fo "some test string") ⇒ "some test string" xmpl-fo ⇒ #<ffiobject type=c-string size=4 fotype=0 foptr=0x890158c> (ffi-get xmpl-fo) ⇒ "some test string"
After using make-ffi-object
to create foreign objects,
always make sure that these were assigned a value before
requesting the object’s data, or simply always use
ffi-create-fo
. In the former case, FFI does not initialise the
object with default data, its value is therefore indefinite and may
cause a crash of SXEmacs when queried.
Also, check carefully to only assign data which is suited for the underlying C type. Passing, for example, strings to ‘unsigned-int’s or ‘long’ values to an object of type ‘int’ may not only result in unexpected behaviour but almost certainly a crash.
Like ffi-create-fo
the function ffi-set
is a higher
level lisp binding. It decomposes into several raw FFI API functions
which are presented here just for completeness. It is highly advised
to exclusively use ffi-set
.
Store and return the value val of type val-type in fo’s foreign space at offset.
val-type can be either a basic FFI type or an FFI pointer. If val-type is a basic FFI type, then val can be an ordinary, but suitable Emacs lisp object. If val-type is an FFI pointer then val must be an FFI object of the underlying type pointed to.
Now that function signatures are bound and argument data is
initialised, we can dare to actually apply functions and operations on
our data. The main function to achieve this is
ffi-call-function
.
Call a function referred to by fo with arguments args,
maybe return a foreign object with the result or nil
if there
is none.
fo should be a binding initiated by ffi-defun
, and
args should be foreign data objects or pointers to these.
Unlike with most Emacs lisp functional bindings foreign functions can be called by reference, this means a function may be passed a foreign object as argument and the function’s result will reside in that foreign object.
Before we come to an example, we shall discuss two further functions which “re-convert” foreign object data to internal Emacs lisp data.
Return fo’s value (converted to Emacs lisp compliant form).
Optional key :type may be used to cast fo to :type, it defaults to the object’s assigned type. Optional key :off may be used to specify an offset, it defaults to 0.
The backbone function of ffi-get
is ffi-fetch
, but it
is highly advised to exclusively use ffi-get
, which in contrast
also works for structs, arrays and pointers.
Return fo’s value (converted to Emacs lisp compliant form). fo is cast to type, and the value is aligned to offset.
Let us now look at the promised example.
(ffi-load "libcurl.so") ⇒ #<ffiobject type=(pointer void) size=4 fotype=2 foptr=0x8a1cc78> ;; we want: char *curl_escape(const char *string, int length); ;; this function takes a usual string and returns a version suitable ;; as URI (setq curl:curl_escape (ffi-defun '(function c-string c-string int) "curl_escape")) ⇒ #<ffiobject type=(function c-string c-string int) size=4 fotype=3 foptr=0x40bf2e50> ;; now prepare the funcall (let* ((url "http://foo.org/please escape this<$!=3>") (str (ffi-create-fo 'c-string url)) (len (ffi-create-fo 'int (length url)))) ;; call the function (setq result (ffi-call-function curl:curl_escape str len))) ⇒ #<ffiobject type=c-string size=4 fotype=0 foptr=0x8906af8> ;; now let's see what the escaped form is (ffi-get result) ⇒ "http%3A%2F%2Ffoo%2Eorg%2Fplease%20escape%20this%3C%24%21%3D3%3E" ;; and compare to (ffi-get result :off 13) ⇒ "foo%2Eorg%2Fplease%20escape%20this%3C%24%21%3D3%3E" ;; and to (ffi-get result :type 'char) ⇒ ?h
Next: Examining and Modifying, Previous: Basic C Types and Functions, Up: Foreign Functions [Contents][Index]