Next: , Previous: , Up: Foreign Functions   [Contents][Index]


65.2 Calling Foreign Functions

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.

  1. incorporate external library contents
  2. declare function signatures
  3. initialise function arguments
  4. call the function

Hereby, step 1 is independent from the other steps. It can be interchanged with the other steps arbitrarily, but is mandatory at all.

65.2.1 Incorporate External Library Contents

Function: ffi-load libname

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.

Variable: ffi-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.

Function: ffi-load-library libname

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>))

65.2.2 Declaring Function Signatures

Declaring the signature of a function is quite like reading a library’s include file. The main function to achieve this is ffi-defun.

Function: ffi-defun type sym

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.

Function: ffi-bind type sym

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.

65.2.3 Initialising Function Arguments

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).

Function: ffi-create-fo type val

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.

Function: make-ffi-object type &optional size

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.

Function: ffi-set fo val

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.

Function: ffi-store fo offset val-type val

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.

65.2.4 Calling Functions

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.

Function: ffi-call-function fo &rest args

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.

Function: ffi-get fo &keys type off

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.

Function: ffi-fetch fo offset type

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: , Previous: , Up: Foreign Functions   [Contents][Index]