4

I have a sophisticated library written in optimized c (library.c):

#include <stdio.h>
#include "library.h"

void make_fullname(char* fullname, char* name, int version) {
  sprintf(fullname, "%s-%d", name, version);
  printf("lib-name: %s\n", name);
  printf("lib-fullname: %s\n", fullname);
}

where library.h contains

void make_fullname(char* fullname, char* name, int version);

The library is compiled as follows:

gcc library.c -o library.so -shared

I am trying to make use of it from SBCL, here is my final take (the one on which I give up and turn to stackoverflow):

(load-shared-object "library.so")

(define-alien-routine make_fullname void
  (fullname (c-string :external-format :ascii))
  (name (c-string :external-format :ascii))
  (x int))


(defun print-name-version (name version)
  (with-alien ((fullname (c-string :external-format :ascii)))
    (setf fullname (make-alien char 100))
    (setf fullname "dummy-string")
    (make_fullname fullname name version)
    (format t "~a~%" fullname)))

Upon running, e.g., (print-name-version "Program" 1) I get this

lib-name: Program
lib-fullname: Program-1
dummy-string
NIL

So, everything works except for passing the string back into lisp. What is amiss in this example? Thanks, Andrei.

Update I have got my lisp code to work, but I still don't really get why the original snippet fails. Here is a working one:

(defun print-name-version (name version)
  (let ((fullname (make-alien char 100)))
    (make_fullname fullname name version)
    (with-alien ((fn-str-repr (c-string :external-format :ascii) fullname))
      (format t "~a~%" fn-str-repr))
    (free-alien fullname)))
Andrei
  • 2,585
  • 1
  • 14
  • 17
  • Just a point of information: You may want to keep in mind the fact that you have a potential buffer overflow here, if the caller of make_fullname doesn't provide enough storage for your formatted string. I also presume this code is just to make an example, and isn't your "sophisticated library"? Because in the latter case, you know you could do `(defun make-version (name version) (format nil "~a-~d" name version))`, right? :) – lindes Feb 22 '12 at 17:27
  • Of course :), these were just some tryouts to get familiar with ffi. – Andrei Mar 04 '12 at 23:13

1 Answers1

6

I have never used SBCL native FFI bindings, but I think I figured it out. For future reference, you would be more likely to get help about CFFI rather than implementation specific FFI bindings.

Variables of alien type c-string are automatically converted to Lisp strings when accessed from Lisp code. The top level function make_fullname is Lisp code, which in turns calls the alien routine, but by that time fullname has been converted to a Lisp string, which then gets converted to a new c-string, which is discarded once the call finishes.

You need to do what you did in the edit: allocate storage buffer and pass that to the alien function, and treat the associated c-string variable as a Lisp view on that storage.

Ramarren
  • 2,510
  • 1
  • 15
  • 11
  • Thanks for solving the puzzle (and for mentioning CFFI, I'm new to lisp and it's the first time I see that link :) – Andrei Feb 22 '12 at 08:37