[fetter-devel] ECL

James Bielman jamesjb at jamesjb.com
Sat Jul 2 20:29:46 UTC 2005


Juan Jose Garcia Ripoll <jlr at mpq.mpg.de> writes:

> I have seen you have a single primitive, "%foreing-function-call",
> which can call a pointer with any number of types. Even though this
> may be implementable even in C (with a type cast), I do not very
> much like the idea. Personally I prefer wrappers. The reason is
> that, unlike other lisps, ECL can link its code against that of the
> C libraries. Currently, there is redundancy because we use not only
> the type declarations provided by the user in the FFI, but also
> those of the C headers of the code we are to link against. This
> means the C compiler can double-check the declarations made by the
> lisper and ensure everything will go right.

I've expected there to be some controversy about FOREIGN-FUNCALL.  The
main reason I like this operator is because it becomes very easy to
call variable-argument functions, and one of my long-term goals with
CFFI is to write a portable Objective-C bridge.

Using FOREIGN-FUNCALL you can define a primitive for calling ObjC
methods almost trivially:

(defmacro send (object method-name &rest args)
  "Call an Objective-C method METHOD-NAME on OBJECT.  If OBJECT is
a string, a method on the class of that name will be invoked."
  (when (stringp object)
    (setf object `(find-objc-class ,object)))
  `(foreign-funcall "objc_msgSend" :pointer ,object
                    :pointer (selector ,method-name) , at args))

? (send (send "NSAutoreleasePool" "alloc" :pointer) "init" :pointer)
#<A Mac Pointer #x305500>

It is also easy to experiment with calling foreign functions from the
REPL when you don't have to declare them first:

? (foreign-funcall "sqrtf" :float 16.0 :float)
4.0

Of course, CFFI does provide the DEFCFUN macro for generating Lisp
wrappers to call foreign functions, but these wrappers are implemented
portably in terms of FOREIGN-FUNCALL, and are able to do automatic
argument translation (Lisp to foreign strings, and vice versa).

(defcfun "puts" :void
  (s string))

==>

(defun puts (s)
  (foreign-funcall "puts" string s :void))

;; For fun, here's what this FOREIGN-FUNCALL macroexpands into.  This
;; really should stack-allocate the foreign string but it doesn't yet.
(let ((#:g8 (foreign-string-alloc s)))
  (unwind-protect
      (let ((#:g9 (%foreign-funcall "puts" :pointer #:g8 :void)))
        #:g9)
    (foreign-string-free #:g8)))

The user can also define his own type translators for arguments and
return values.  Here is how the STRING type translator looks (this
interface will probably change soon, it doesn't need to be this
complicated):

;; Define STRING as a typedef for :POINTER.
(defctype string :pointer)

(define-type-translator string :in (arg result-var)
  "Type translator for string input arguments."
  (values
    `(foreign-string-alloc ,arg)            ; result
    `((foreign-string-free ,result-var))))  ; unwinds

(define-type-translator string :result (arg result-var)
  "Type translator for string return values."
  (declare (ignore result-var))
  (values
    `(foreign-string-to-lisp ,arg)          ; result
    nil))                                   ; unwinds

James



More information about the fetter-devel mailing list