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