FFI fails when return type is a pointer
Save following code block to "ffi-test.lisp", then in ECL do (load (compile-file "ffi-test.lisp"))
.
This example is adapted from documentation https://ecl.common-lisp.dev/static/manual/Foreign-Function-Interface.html#Foreign-Strings
(ext:with-backend :c/c++
(FFI:clines "extern char * readline(char *);"))
(ffi:def-function ("readline" c-readline)
((prompt :cstring))
:returning (* :char))
(ffi:def-function ("free" c-free)
((ptr :pointer-void))
:returning :void)
(ffi:load-foreign-library "/lib64/libreadline.so.8")
(defun readline (prompt)
"Reads a string from console with line-editing."
(ffi:with-cstring (c-prompt prompt)
(let* ((c-str (c-readline c-prompt))
(str (ffi:convert-from-foreign-string c-str)))
;;(ffi:free-foreign-object c-str)
(c-free c-str)
str)))
(setq x (readline "this is readine prompt:"))
Expected result is: you meet with a prompt: this is readine prompt:
, then you can type a string, end with ENTER
key, and get that string saved in variable x
.
Instead you get:
;;; 1.c: In function ‘L1c_readline’:
;;; 1.c:20:13: error: invalid operands to binary << (have ‘char *’ and ‘int’)
;;; 20 | value0 = ECL_CODE_CHAR(readline(ecl_base_string_pointer_safe(v2)));
;;; | ^~~~~~~~~~~~~
This looks like that ECL thinks readline
is returning a char
instead of char *
.
A grep ':returning.*\*'
shows that "FFI functions which return a pointer" is not used in the code base.
The following patch seems to fix the problem, however I'm not sure if this has other negative side effects.
diff --git a/src/lsp/ffi.lsp b/src/lsp/ffi.lsp
index 894c9642f..b2ae06b64 100644
--- a/src/lsp/ffi.lsp
+++ b/src/lsp/ffi.lsp
@@ -583,10 +583,7 @@ bound to this value during the execution of body."
)))
(defun %convert-to-return-type (type)
- (let ((type (%convert-to-ffi-type type)))
- (cond ((atom type) type)
- ((eq (first type) '*) (second type))
- (t type))))
+ (%convert-to-ffi-type type))
(defun produce-function-call (c-name nargs)
(declare (si::c-local))
The affected code dates back to 2004.
Another issue: the document uses free-foreign-object
but it doesn't work. Another part of the documentation says using C "free" works.
VERSION "23.9.9"
VCS-ID "UNKNOWN"
OS "Linux"
OS-VERSION "6.6.1-gentoo"
MACHINE-TYPE "x86_64"
FEATURES (:WALKER :CDR-6 :CDR-1 :CDR-5 :LINUX :FORMATTER :CDR-7 :ECL-WEAK-HASH :LITTLE-ENDIAN :ECL-READ-WRITE-LOCK :SSE2 :LONG-LONG :UINT64-T :UINT32-T :UINT16-T :COMPLEX-FLOAT ...)