Lisp Knowledgebase

Title: Passing and returning strings to and from foreign code

ID: 17004


Product: LispWorks
Version: All
OS: All

Description: You can get an error message "Cannot pass foreign aggregate type :EF-MB-STRING..." when you use the :EF-MB-STRING foreign type directly in FLI:DEFINE-FOREIGN-FUNCTION or FLI:DEFINE-FOREIGN-CALLABLE. The error occurs because :EF-MB-STRING is an array type, not a pointer type and the FLI cannot handle such array (i.e. aggregate) types as function arguments. To convert the array into a pointer, wrap the type with :REFERENCE or one of its derivatives such as :REFERENCE-PASS.

Example 1: passing a constant string as an argument

C prototype:
int count_upper(const char *string);

Calling from C:
{ char buffer[256];
 int count;
 ... place string into buffer ...
 count = count_upper(&buffer[0]);
 ... use count ...
}

Lisp code for defining and calling:

;; Use :LISP-TO-FOREIGN-P NIL since we do not need to convert the Lisp
;; string back to a foreign string on return.
;; :LIMIT is needed in some versions of LispWorks to work around a bug.
(fli:define-foreign-callable ("count_upper" :result-type :int)
   ((string (:reference (:ef-mb-string :limit 256)
             :lisp-to-foreign-p nil)))
 (count-if 'upper-case-p string))


;; :REFERENCE-PASS means that we convert the Lisp string to a foreign
;; string when calling but do not convert the string back again on return.
(fli:define-foreign-function (count-upper "count_upper" :source)
   ((string (:reference-pass :ef-mb-string)))
 :result-type :int)


(count-upper "AbcDEf")  ==>  3
(count-upper "Abcdef")  ==>  1

(count-upper "AbcdeF")  ==>  2


Example 2: passing a string as an argument which is modified

C prototype:
void foreign_string_upcase(char *string);

Calling from C:
{ char buffer[256];
 ... place string into buffer ...
 foreign_string_upcase(&buffer[0]);
 ... use contents of buffer ...
}

Lisp code for defining and calling:

;; Use :REFERENCE so that the foreign string is converted to a Lisp string
;; on entry and back to a foreign string on return.  The :LIMIT is needed
;; to put a bound on the conversion back to the foreign string (the caller
;; has allocated the foreign memory).
(fli:define-foreign-callable ("foreign_string_upcase" :result-type :void)
   ((string (:reference (:ef-mb-string :limit 256))))
 (nstring-upcase string))


;; Use :REFERENCE so that the Lisp to string is converted to a foreign
;; string when calling and back to a new Lisp string on return.
;; :LIMIT is needed in some versions of LispWorks to work around a bug.
(fli:define-foreign-function (foreign-string-upcase
                             "foreign_string_upcase" :source)
                ((string (:reference (:ef-mb-string :limit 256))))
              :result-type :void)

(let ((str "Hello there")) (foreign-string-upcase str))
 ==>  "HELLO THERE"

Example 3: returning a string via a buffer

C prototype:
int random_string(int length, char *string);

Calling from C:
{ char buffer[256];
 random_string(256, &buffer[0]);
 ... use contents of buffer ...
}

Lisp code for defining and calling:

;; Use a pointer type and copy the converted lisp string back to the
;; pointer explicitly using FLI:REPLACE-FOREIGN-OBJECT.  Using
;; FLI:WITH-FOREIGN-STRING to do the conversion takes care of any external
;; format issues.
(fli:define-foreign-callable ("random_string" :result-type :void)
   ((length :int)
    (return-string-pointer (:pointer :char)))
 (let ((return-string (make-string length)))
   (loop for index below length
         do (setf (schar return-string index)
                  (code-char (+ (char-code #\a) (random 26)))))
   (fli:with-foreign-string (temp elts bytes) return-string
     (declare (ignore elts))
     (fli:replace-foreign-object return-string-pointer temp
                                 :nelems bytes))))


;; Use :REFERENCE-RETURN so that no Lisp string is needed when calling.
;; Use :LAMBDA-LIST so that the caller doesn't have to pass a dummy
;; argument for the return-string.
(fli:define-foreign-function (random-string "random_string" :source)
  ((length :int)
   (return-string (:reference-return (:ef-mb-string :limit 256))))
:result-type nil
:lambda-list (length &aux return-string))


(random-string 4)  ==>  "vpsh"
(random-string 6)  ==>  "goqquf"

See Also:
Workaround:
Patch:

Hardware:N/A
Summary:
Bug#:
Patch Enhancement#:
Reported:

Company     Contact     Privacy Policy     Terms of Use