1 ;;;; -*- Mode: lisp; indent-tabs-mode: nil -*-
3 ;;; functions.lisp --- High-level interface to foreign functions.
5 ;;; Copyright (C) 2005-2006, James Bielman <jamesjb@jamesjb.com>
6 ;;; Copyright (C) 2005-2007, Luis Oliveira <loliveira@common-lisp.net>
8 ;;; Permission is hereby granted, free of charge, to any person
9 ;;; obtaining a copy of this software and associated documentation
10 ;;; files (the "Software"), to deal in the Software without
11 ;;; restriction, including without limitation the rights to use, copy,
12 ;;; modify, merge, publish, distribute, sublicense, and/or sell copies
13 ;;; of the Software, and to permit persons to whom the Software is
14 ;;; furnished to do so, subject to the following conditions:
16 ;;; The above copyright notice and this permission notice shall be
17 ;;; included in all copies or substantial portions of the Software.
19 ;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20 ;;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21 ;;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22 ;;; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
23 ;;; HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
24 ;;; WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25 ;;; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26 ;;; DEALINGS IN THE SOFTWARE.
31 ;;;# Calling Foreign Functions
33 ;;; FOREIGN-FUNCALL is the main primitive for calling foreign
34 ;;; functions. It converts each argument based on the installed
35 ;;; translators for its type, then passes the resulting list to
36 ;;; CFFI-SYS:%FOREIGN-FUNCALL.
38 ;;; For implementation-specific reasons, DEFCFUN doesn't use
39 ;;; FOREIGN-FUNCALL directly and might use something else (passed to
40 ;;; TRANSLATE-OBJECTS as the CALL-FORM argument) instead of
41 ;;; CFFI-SYS:%FOREIGN-FUNCALL to call the foreign-function.
43 (defun translate-objects (syms args types rettype call-form
&optional indirect
)
44 "Helper function for FOREIGN-FUNCALL and DEFCFUN. If 'indirect is T, all arguments are represented by foreign pointers, even those that can be represented by CL objects."
46 (expand-from-foreign call-form
(parse-type rettype
))
49 #'expand-to-foreign-dyn-indirect
50 #'expand-to-foreign-dyn
)
52 (list (translate-objects (cdr syms
) (cdr args
)
53 (cdr types
) rettype call-form indirect
))
54 (parse-type (car types
)))))
56 (defun parse-args-and-types (args)
57 "Returns 4 values: types, canonicalized types, args and return type."
58 (let* ((len (length args
))
59 (return-type (if (oddp len
) (lastcar args
) :void
)))
60 (loop repeat
(floor len
2)
61 for
(type arg
) on args by
#'cddr
62 collect type into types
63 collect
(canonicalize-foreign-type type
) into ctypes
64 collect arg into fargs
65 finally
(return (values types ctypes fargs return-type
)))))
67 ;;; While the options passed directly to DEFCFUN/FOREIGN-FUNCALL have
68 ;;; precedence, we also grab its library's options, if possible.
69 (defun parse-function-options (options &key pointer
)
70 (destructuring-bind (&key
(library :default libraryp
)
72 (calling-convention cconv calling-convention-p
)
73 (convention calling-convention
))
76 (warn-obsolete-argument :cconv
:convention
))
77 (when calling-convention-p
78 (warn-obsolete-argument :calling-convention
:convention
))
82 (let ((lib-options (foreign-library-options
83 (get-foreign-library library
))))
84 (getf lib-options
:convention
)))
86 ;; Don't pass the library option if we're dealing with
87 ;; FOREIGN-FUNCALL-POINTER.
89 (list :library library
)))))
91 (defun structure-by-value-p (ctype)
92 "A structure or union is to be called or returned by value."
93 (let ((actual-type (ensure-parsed-base-type ctype
)))
94 (or (and (typep actual-type
'foreign-struct-type
)
95 (not (bare-struct-type-p actual-type
)))
96 #+cffi
::no-long-long
(typep actual-type
'emulated-llong-type
))))
98 (defun fn-call-by-value-p (argument-types return-type
)
99 "One or more structures in the arguments or return from the function are called by value."
100 (or (some 'structure-by-value-p argument-types
)
101 (structure-by-value-p return-type
)))
103 (defvar *foreign-structures-by-value
*
105 (declare (ignore args
))
107 (error "Unable to call structures by value without cffi-libffi loaded.")
108 (load-cffi-libffi () :report
"Load cffi-libffi."
109 (asdf:operate
'asdf
:load-op
'cffi-libffi
))))
110 "A function that produces a form suitable for calling structures by value.")
112 (defun foreign-funcall-form (thing options args pointerp
)
113 (multiple-value-bind (types ctypes fargs rettype
)
114 (parse-args-and-types args
)
115 (let ((syms (make-gensym-list (length fargs
)))
116 (fsbvp (fn-call-by-value-p ctypes rettype
)))
118 ;; Structures by value call through *foreign-structures-by-value*
119 (funcall *foreign-structures-by-value
*
128 syms fargs types rettype
129 `(,(if pointerp
'%foreign-funcall-pointer
'%foreign-funcall
)
130 ;; No structures by value, direct call
132 (,@(mapcan #'list ctypes syms
)
133 ,(canonicalize-foreign-type rettype
))
134 ,@(parse-function-options options
:pointer pointerp
)))))))
136 (defmacro foreign-funcall
(name-and-options &rest args
)
137 "Wrapper around %FOREIGN-FUNCALL that translates its arguments."
138 (let ((name (car (ensure-list name-and-options
)))
139 (options (cdr (ensure-list name-and-options
))))
140 (foreign-funcall-form name options args nil
)))
142 (defmacro foreign-funcall-pointer
(pointer options
&rest args
)
143 (foreign-funcall-form pointer options args t
))
145 (defun promote-varargs-type (builtin-type)
146 "Default argument promotions."
149 ((:char
:short
) :int
)
150 ((:unsigned-char
:unsigned-short
) :unsigned-int
)
153 ;; If cffi-sys doesn't provide a %foreign-funcall-varargs macros we
154 ;; define one that use %foreign-funcall.
155 (eval-when (:compile-toplevel
:load-toplevel
:execute
)
156 (unless (fboundp '%foreign-funcall-varargs
)
157 (defmacro %foreign-funcall-varargs
(name fixed-args varargs
158 &rest args
&key convention library
)
159 (declare (ignore convention library
))
160 `(%foreign-funcall
,name
,(append fixed-args varargs
) ,@args
)))
161 (unless (fboundp '%foreign-funcall-pointer-varargs
)
162 (defmacro %foreign-funcall-pointer-varargs
(pointer fixed-args varargs
163 &rest args
&key convention
)
164 (declare (ignore convention
))
165 `(%foreign-funcall-pointer
,pointer
,(append fixed-args varargs
) ,@args
))))
167 (defun foreign-funcall-varargs-form (thing options fixed-args varargs pointerp
)
168 (multiple-value-bind (fixed-types fixed-ctypes fixed-fargs
)
169 (parse-args-and-types fixed-args
)
170 (multiple-value-bind (varargs-types varargs-ctypes varargs-fargs rettype
)
171 (parse-args-and-types varargs
)
172 (let ((fixed-syms (make-gensym-list (length fixed-fargs
)))
173 (varargs-syms (make-gensym-list (length varargs-fargs
))))
175 (append fixed-syms varargs-syms
)
176 (append fixed-fargs varargs-fargs
)
177 (append fixed-types varargs-types
)
179 `(,(if pointerp
'%foreign-funcall-pointer-varargs
'%foreign-funcall-varargs
)
181 ,(mapcan #'list fixed-ctypes fixed-syms
)
184 (mapcar #'promote-varargs-type varargs-ctypes
)
185 (loop for sym in varargs-syms
186 and type in varargs-ctypes
188 collect
`(float ,sym
1.0d0
)
190 (list (canonicalize-foreign-type rettype
)))
193 (defmacro foreign-funcall-varargs
(name-and-options fixed-args
195 "Wrapper around %FOREIGN-FUNCALL that translates its arguments
196 and does type promotion for the variadic arguments."
197 (let ((name (car (ensure-list name-and-options
)))
198 (options (cdr (ensure-list name-and-options
))))
199 (foreign-funcall-varargs-form name options fixed-args varargs nil
)))
201 (defmacro foreign-funcall-pointer-varargs
(pointer options fixed-args
203 "Wrapper around %FOREIGN-FUNCALL-POINTER that translates its
204 arguments and does type promotion for the variadic arguments."
205 (foreign-funcall-varargs-form pointer options fixed-args varargs t
))
207 ;;;# Defining Foreign Functions
209 ;;; The DEFCFUN macro provides a declarative interface for defining
210 ;;; Lisp functions that call foreign functions.
212 ;; If cffi-sys doesn't provide a defcfun-helper-forms,
213 ;; we define one that uses %foreign-funcall.
214 (eval-when (:compile-toplevel
:load-toplevel
:execute
)
215 (unless (fboundp 'defcfun-helper-forms
)
216 (defun defcfun-helper-forms (name lisp-name rettype args types options
)
217 (declare (ignore lisp-name
))
220 `(%foreign-funcall
,name
,(append (mapcan #'list types args
)
224 (defun %defcfun
(lisp-name foreign-name return-type args options docstring
)
225 (let* ((arg-names (mapcar #'first args
))
226 (arg-types (mapcar #'second args
))
227 (syms (make-gensym-list (length args
)))
228 (call-by-value (fn-call-by-value-p arg-types return-type
)))
229 (multiple-value-bind (prelude caller
)
232 (defcfun-helper-forms
233 foreign-name lisp-name
(canonicalize-foreign-type return-type
)
234 syms
(mapcar #'canonicalize-foreign-type arg-types
) options
))
237 (defun ,lisp-name
,arg-names
238 #+cmucl
(declare (notinline alien
::%heap-alien
))
239 ,@(ensure-list docstring
)
242 ,(cons foreign-name options
)
243 ,@(append (mapcan #'list arg-types arg-names
)
246 syms arg-names arg-types return-type caller
)))))))
248 (defun %defcfun-varargs
(lisp-name foreign-name return-type args options doc
)
249 (with-unique-names (varargs)
250 (let ((arg-names (mapcar #'car args
)))
251 `(defmacro ,lisp-name
(,@arg-names
&rest
,varargs
)
253 `(foreign-funcall-varargs
254 ,'(,foreign-name
,@options
)
255 ,,`(list ,@(loop for
(name type
) in args
256 collect
`',type collect name
))
260 (defgeneric translate-underscore-separated-name
(name)
261 (:method
((name string
))
262 (values (intern (canonicalize-symbol-name-case (substitute #\-
#\_ name
)))))
263 (:method
((name symbol
))
264 (substitute #\_
#\-
(string-downcase (symbol-name name
)))))
266 (defun collapse-prefix (l special-words
)
268 (multiple-value-bind (newpre skip
) (check-prefix l special-words
)
269 (cons newpre
(collapse-prefix (nthcdr skip l
) special-words
)))))
271 (defun check-prefix (l special-words
)
272 (let ((pl (loop for i from
(1- (length l
)) downto
0
273 collect
(apply #'concatenate
'simple-string
(butlast l i
)))))
274 (loop for w in special-words
275 for p
= (position-if #'(lambda (s) (string= s w
)) pl
)
276 when p do
(return-from check-prefix
(values (nth p pl
) (1+ p
))))
277 (values (first l
) 1)))
279 (defgeneric translate-camelcase-name
(name &key upper-initial-p special-words
)
280 (:method
((name string
) &key upper-initial-p special-words
)
281 (declare (ignore upper-initial-p
))
282 (values (intern (reduce #'(lambda (s1 s2
)
283 (concatenate 'simple-string s1
"-" s2
))
284 (mapcar #'string-upcase
286 (split-if #'(lambda (ch)
287 (or (upper-case-p ch
)
291 (:method
((name symbol
) &key upper-initial-p special-words
)
294 (loop for str in
(split-if #'(lambda (ch) (eq ch
#\-
))
297 for first-word-p
= t then nil
298 for e
= (member str special-words
299 :test
#'equal
:key
#'string-upcase
)
301 ((and first-word-p
(not upper-initial-p
))
302 (string-downcase str
))
304 (t (string-capitalize str
)))))))
306 (defgeneric translate-name-from-foreign
(foreign-name package
&optional varp
)
307 (:method
(foreign-name package
&optional varp
)
308 (declare (ignore package
))
309 (let ((sym (translate-underscore-separated-name foreign-name
)))
311 (values (intern (format nil
"*~A*"
312 (canonicalize-symbol-name-case
313 (symbol-name sym
)))))
316 (defgeneric translate-name-to-foreign
(lisp-name package
&optional varp
)
317 (:method
(lisp-name package
&optional varp
)
318 (declare (ignore package
))
319 (let ((name (translate-underscore-separated-name lisp-name
)))
321 (string-trim '(#\
*) name
)
324 (defun lisp-name (spec varp
)
325 (check-type spec string
)
326 (translate-name-from-foreign spec
*package
* varp
))
328 (defun foreign-name (spec varp
)
329 (check-type spec
(and symbol
(not null
)))
330 (translate-name-to-foreign spec
*package
* varp
))
332 (defun foreign-options (opts varp
)
334 (funcall 'parse-defcvar-options opts
)
335 (parse-function-options opts
)))
337 (defun lisp-name-p (name)
338 (and name
(symbolp name
) (not (keywordp name
))))
340 (defun %parse-name-and-options
(spec varp
)
343 (values (lisp-name spec varp
) spec nil
))
345 (assert (not (null spec
)))
346 (values spec
(foreign-name spec varp
) nil
))
347 ((and (consp spec
) (stringp (first spec
)))
348 (destructuring-bind (foreign-name &rest options
)
352 (keywordp (first options
)))
353 (values (lisp-name foreign-name varp
) foreign-name options
))
355 (assert (lisp-name-p (first options
)))
356 (values (first options
) foreign-name
(rest options
))))))
357 ((and (consp spec
) (lisp-name-p (first spec
)))
358 (destructuring-bind (lisp-name &rest options
)
362 (keywordp (first options
)))
363 (values lisp-name
(foreign-name spec varp
) options
))
365 (assert (stringp (first options
)))
366 (values lisp-name
(first options
) (rest options
))))))
368 (error "Not a valid foreign function specifier: ~A" spec
))))
370 ;;; DEFCFUN's first argument has can have the following syntax:
374 ;;; 3. \( string [symbol] options* )
375 ;;; 4. \( symbol [string] options* )
377 ;;; The string argument denotes the foreign function's name. The
378 ;;; symbol argument is used to name the Lisp function. If one isn't
379 ;;; present, its name is derived from the other. See the user
380 ;;; documentation for an explanation of the derivation rules.
381 (defun parse-name-and-options (spec &optional varp
)
382 (multiple-value-bind (lisp-name foreign-name options
)
383 (%parse-name-and-options spec varp
)
384 (values lisp-name foreign-name
(foreign-options options varp
))))
386 ;;; If we find a &REST token at the end of ARGS, it means this is a
387 ;;; varargs foreign function therefore we define a lisp macro using
388 ;;; %DEFCFUN-VARARGS. Otherwise, a lisp function is defined with
390 (defmacro defcfun
(name-and-options return-type
&body args
)
391 "Defines a Lisp function that calls a foreign function."
392 (let ((docstring (when (stringp (car args
)) (pop args
))))
393 (multiple-value-bind (lisp-name foreign-name options
)
394 (parse-name-and-options name-and-options
)
395 (if (eq (lastcar args
) '&rest
)
396 (%defcfun-varargs lisp-name foreign-name return-type
397 (butlast args
) options docstring
)
398 (%defcfun lisp-name foreign-name return-type args options
401 ;;;# Defining Callbacks
403 (defun inverse-translate-objects (args types declarations rettype call
)
404 `(let (,@(loop for arg in args and type in types
405 collect
(list arg
(expand-from-foreign
406 arg
(parse-type type
)))))
408 ,(expand-to-foreign call
(parse-type rettype
))))
410 (defun parse-defcallback-options (options)
411 (destructuring-bind (&key
(cconv :cdecl cconv-p
)
412 (calling-convention cconv calling-convention-p
)
413 (convention calling-convention
))
416 (warn-obsolete-argument :cconv
:convention
))
417 (when calling-convention-p
418 (warn-obsolete-argument :calling-convention
:convention
))
419 (list :convention convention
)))
421 (defmacro defcallback
(name-and-options return-type args
&body body
)
422 (multiple-value-bind (body declarations
)
423 (parse-body body
:documentation t
)
424 (let ((arg-names (mapcar #'car args
))
425 (arg-types (mapcar #'cadr args
))
426 (name (car (ensure-list name-and-options
)))
427 (options (cdr (ensure-list name-and-options
))))
429 (%defcallback
,name
,(canonicalize-foreign-type return-type
)
430 ,arg-names
,(mapcar #'canonicalize-foreign-type arg-types
)
431 ,(inverse-translate-objects
432 arg-names arg-types declarations return-type
433 `(block ,name
,@body
))
434 ,@(parse-defcallback-options options
))
437 (declaim (inline get-callback
))
438 (defun get-callback (symbol)
441 (defmacro callback
(name)