Hi!

Attached is a hasty patch to enable CFFI-SYS:%FOREIGN-FUNCALL and
CFFI-SYS:%FOREIGN-FUNCALL-POINTER for LispWorks.  The idea is to
create one foreign funcallable per signature and cache those in a hash
table.  I took a quick glance at the source code of CMUCL's
ALIEN-FUNCALL and my impression (not really understanding the details)
was that CMUCL actually does something similar internally - it creates
a "stub" if necessary, depending on the types.  As you can observe at
the REPL it compiles /something/ when ALIEN-FUNCALL is called.

Granted, it is kind of ugly to call FLI:DEFINE-FOREIGN-FUNCALLABLE in
non-top-level position but I think this solution is better than
nothing.  It looks to me as if FOREIGN-FUNCALL is mostly intended for
interactive use and for "one-shot" invocations - correct me if I'm
wrong.

I don't know if CMUCL's "stubs" will ever be garbage-collected, the
foreign funcallables in my patch certainly won't.  I don't expect this
to be a big problem, though.

I tried the CFFI test suite and it basically ran through with three
exceptions - FUNCALL.FLOAT, DEREF.FLOAT.2, and DEREF.FLOAT.3.  My
guess is that these are due to LispWorks identifying FLOAT and
DOUBLE-FLOAT and are not related to my patch.

Two minor quibbles, unrelated:

1. The documentation says it is only about the cffi-luis branch but it
   seems to me that the main branch and cffi-luis are almost identical
   - or did I miss something?

2. This form

    (define-foreign-library libcurl
      (:unix (:or "libcurl.so.3" "libcurl.so"))
      (t (:default "libcurl")))

   in the (very nice, BTW) tutorial didn't work for me on Linux,
   neither with LispWorks nor with CMUCL.  I had to remove the second
   (:UNIX) line to make it work.

     [EMAIL PROTECTED]:~$ ls -l /usr/lib/libcurl*
     lrwxrwxrwx  1 root root     16 Dec 28 19:29 /usr/lib/libcurl.so.3 -> 
libcurl.so.3.0.0
     -rw-r--r--  1 root root 196632 Dec  7 12:39 /usr/lib/libcurl.so.3.0.0

Cheers,
Edi.

diff -ru cffi.orig/src/cffi-lispworks.lisp cffi/src/cffi-lispworks.lisp
--- cffi.orig/src/cffi-lispworks.lisp	2006-01-12 16:38:28.000000000 +0100
+++ cffi/src/cffi-lispworks.lisp	2006-01-15 00:00:05.000000000 +0100
@@ -41,6 +41,7 @@
    #:foreign-free
    #:with-foreign-pointer
    #:%foreign-funcall
+   #:%foreign-funcall-pointer
    #:%foreign-type-alignment
    #:%foreign-type-size
    #:%load-foreign-library
@@ -61,7 +62,7 @@
 (eval-when (:compile-toplevel :load-toplevel :execute)
   (mapc (lambda (feature) (pushnew feature *features*))
         '(;; Backend features.
-
+          cffi-features:foreign-funcall
           ;; OS/CPU features.
           #+darwin  cffi-features:darwin
           #+unix    cffi-features:unix
@@ -182,6 +183,11 @@
 
 ;;;# Calling Foreign Functions
 
+(defvar *foreign-funcallable-cache* (make-hash-table :test 'equal)
+  "Caches foreign funcallables created by %FOREIGN-FUNCALL or
+%FOREIGN-FUNCALL-POINTER.  We only need to have one per each
+signature.")
+
 (defun foreign-funcall-type-and-args (args)
   "Returns a list of types, list of args and return type."
   (let ((return-type :void))
@@ -191,9 +197,44 @@
        else do (setf return-type (convert-foreign-type type))
        finally (return (values types fargs return-type)))))
 
+(defmacro %%foreign-funcall (foreign-function &rest args)
+  "Does the actual work for %FOREIGN-FUNCALL-POINTER and
+%FOREIGN-FUNCALL.  Checks if a foreign funcallable which fits ARGS
+already exists and creates and caches it if necessary.  Finally calls
+it."
+  (multiple-value-bind (types fargs rettype)
+      (foreign-funcall-type-and-args args)
+    (with-unique-names (signature internal-name create-ff get-ff)
+      `(let ((,signature (list ,rettype ,@types)))
+         (labels ((,create-ff ()
+                    "Creates a foreign funcallable for the signature."
+                    (format t "~&Creating foreign funcallable for signatur ~S -> ~S~%"
+                            ',types ,rettype)
+                    ;; yes, ugly, this most likely wants to be a
+                    ;; top-level form...
+                    (fli:define-foreign-funcallable ,internal-name
+                        ,(loop for type in types
+                               collect (list (gensym) type))
+                      :result-type ,rettype)
+                    ',internal-name)
+                  (,get-ff ()
+                    "Returns a foreign funcallable for the signature -
+either from the cache or newly created."
+                    (or (gethash ,signature *foreign-funcallable-cache*)
+                        ;; (SETF GETHASH) is supposed to be thread-safe
+                        (setf (gethash ,signature *foreign-funcallable-cache*)
+                                (,create-ff)))))
+           ;; now call it
+           (funcall (,get-ff) ,foreign-function ,@fargs))))))
+
 (defmacro %foreign-funcall (name &rest args)
-  "Call a foreign function NAME passing arguments ARGS."
-  `(format t "~&;; Calling ~A with args ~S.~%" ,name ',args))
+  "Calls a foreign function named NAME passing arguments ARGS."
+  `(%%foreign-funcall (fli:make-pointer :symbol-name ,name)
+                      ,@args))
+
+(defmacro %foreign-funcall-pointer (ptr &rest args)
+  "Calls a foreign function pointed at by PTR passing arguments ARGS."
+  `(%%foreign-funcall ,ptr ,@args))
 
 (defun defcfun-helper-forms (name lisp-name rettype args types)
   "Return 2 values for DEFCFUN. A prelude form and a caller form."
_______________________________________________
cffi-devel mailing list
cffi-devel@common-lisp.net
http://common-lisp.net/cgi-bin/mailman/listinfo/cffi-devel

Reply via email to