Next: Foreign Structure Types, Previous: Foreign Type Translators, Up: Foreign Types
Being based on generic functions, the type translation mechanism described above can add a bit of overhead. This is usually not significant, but we nevertheless provide a way of getting rid of the overhead for the cases where it matters.
A good way to understand this issue is to look at the code generated
by defcfun
. Consider the following example using the
my-boolean
type defined above:
CFFI> (macroexpand-1 '(defcfun foo my-boolean (x my-boolean))) (DEFUN FOO (X) (MULTIPLE-VALUE-BIND (#:G3148 #:PARAM3149) (TRANSLATE-TYPE-TO-FOREIGN X #<FOREIGN-TYPEDEF MY-BOOLEAN>) (UNWIND-PROTECT (PROGN (TRANSLATE-TYPE-FROM-FOREIGN (%FOREIGN-FUNCALL "foo" :INT #:G3148 :INT) #<FOREIGN-TYPEDEF MY-BOOLEAN>)) (FREE-TYPE-TRANSLATED-OBJECT #:G3148 #<FOREIGN-TYPEDEF MY-BOOLEAN> #:PARAM3149))))
In order to get rid of those generic function calls, CFFI has
another set of extensible generic functions that provide functionality
similar to CL's compiler macros:
expand-to-foreign-dyn
, expand-to-foreign
and
expand-from-foreign
. Here's how one could define
my-boolean
with them:
(defmethod expand-to-foreign (value (type (eql 'my-boolean))) `(if ,value 1 0)) (defmethod expand-from-foreign (value (type (eql 'my-boolean))) `(not (zerop ,value)))
And here's what the macroexpansion of foo
now looks like:
CFFI> (macroexpand-1 '(defcfun foo my-boolean (x my-boolean))) (DEFUN FOO (X) (LET ((#:G3182 (IF X 1 0))) (NOT (ZEROP (%FOREIGN-FUNCALL "foo" :INT #:G3182 :INT)))))
Much better.
The expansion interface has no equivalent of
free-translated-object
; you must instead define a method on
expand-to-foreign-dyn
, the third generic function in this
interface. This is especially useful when you can allocate something
much more efficiently if you know the object has dynamic extent, as is
the case with function calls that don't save the relevant allocated
arguments. Consider the following example:
;;; This type inherits :string's translators. (defctype stack-allocated-string :string) (defmethod expand-to-foreign-dyn (value var body (type (eql 'stack-allocated-string))) `(with-foreign-string (,var ,value) ,@body))
To short-circuit expansion and use the translate-*
functions
instead, simply call the next method. Return its result in cases
where your method cannot generate an appropriate replacement for it.
The expand-*
methods have precedence over their
translate-*
counterparts and are guaranteed to be used in
defcfun
, foreign-funcall
, defcvar
and
defcallback
. If you define a method on each of the
expand-*
generic functions, you are guaranteed to have full
control over the expressions generated for type translation in these
macros.
They may or may not be used in other CFFI operators that need to
translate between Lisp and C data; you may only assume that
expand-*
methods will probably only be called during Lisp
compilation.
expand-to-foreign-dyn
has precedence over
expand-to-foreign
and is only used in defcfun
and
foreign-funcall
, only making sense in those contexts. If you
do not define a method on expand-to-foreign-dyn
, however,
please note that this expand method for the hypothetical type
my-string
is not the same as defining no method at all:
(defmethod expand-to-foreign (value-form (type-name (eql 'my-string)))
(call-next-method))
Without this method, your runtime translate-to-foreign
method
will be called, and its result will be passed to
free-translated-object
. However, if you define this method,
translate-to-foreign
will still be called, but its result will
not be passed to free-translated-object
. If you need to free
values with this interface, you must define an
expand-to-foreign-dyn
method.
Important note: this set of generic functions is called at
macroexpansion time. Methods are defined when loaded or evaluated,
not compiled. You are responsible for ensuring that your
expand-*
methods are defined when the foreign-funcall
or
other forms that use them are compiled. One way to do this is to put
the method definitions earlier in the file and inside an appropriate
eval-when
form; another way is to always load a separate Lisp
or FASL file containing your expand-*
definitions
before compiling files with forms that ought to use them. Otherwise,
they will not be found and the runtime translators will be used
instead.