Wrapping a C library in lisp (part 2)

Once the library has been defined and loaded into the Lisp image, work can begin on defining C routines in Lisp.

Before that can be done, however, a note for those who do not know C. Functions in C have a declared type, as do all of their arguments. When reading the docs for a particular library, you will want to know description conventions for C routines. Let’s use the example of mysql_init() to begin with.

MYSQL *mysql_init(MYSQL *mysql)

The first part, MYSQL *mysql_init, defines the function’s return type and name. MYSQL is a type created by the library that stores a struct. This is actually completely irrelevant at this point, because of the asterisk. The asterisk means that the function is actually allocating a pointer. Although we can pass an already existing MYSQL object pointer (that’s the second part), generally we will just use this function to create the internal struct needed to run the program. We will need to store the pointer that is returned.

The second part, MYSQL *mysql, states this- namely, that a pointer (named mysql, and we know it’s a pointer because it’s labelled with an asterisk- *mysql) will be returned. We store it because this will be passed to most of the other routines in the library.

Technically, we do not really need to define the routine in Lisp. The function, foreign-funcall, allows us to call the foreign function directly.

(foreign-funcall "mysql_init" :pointer (null-pointer) :pointer)

In this, we apply mysql_init to a null pointer (an empty pointer argument), created using the CFFI function (null-pointer). The last symbol states we will get a pointer back.

In order to wrap this as a pretty Lisp function, we do:

(defcfun ("mysql_init" libmysql-init) :pointer
  (mysql :pointer))

This points the Lisp symbol libmysql-init to “mysql_init” in the C library. The next argument, :pointer, states that this returns a pointer. Next, we can have an &rest list of (parameter-name :type), to define the parameters to pass to the function, in this case, (mysql :pointer). Available types are listed in the CFFI documentation.

However, we don’t want to have to run (null-pointer) every time we use this function. We also need to store the return value for future use. So let’s wrap it in another function to make it more expressive:

(defun mysql-init ()
  (setq *mysql* (libmysql-init (null-pointer)))
  (or *mysql* (setq *mysql* nil)))

Now, we can simply run (mysql-init) and we will have the symbol *mysql* pointing to the returned pointer or nil in the event of a failure.

If we do have a failure, we can use the following definitions to get at the error text:

(defcfun ("mysql_error" libmysql-error) :string
    (mysql :pointer))
 
(defun mysql-error ()
    (libmysql-error *mysql*))

Of course, we will want to add error handling and some other niceties, but having these declared makes writing and testing those completed functions that much easier.

Leave a comment | Trackback
Jul 3rd, 2007 | Posted in Programming
Tags: , ,
No comments yet.