ffi.stk 5.04 KB
Newer Older
1 2
;;;;
;;;; ffi.stk			-- FFI support
3
;;;;
4
;;;; Copyright © 2007-2011 Erick Gallesio - I3S-CNRS/ESSI <eg@essi.fr>
5 6
;;;;
;;;;
7 8 9 10
;;;; This program is free software; you can redistribute it and/or modify
;;;; it under the terms of the GNU General Public License as published by
;;;; the Free Software Foundation; either version 2 of the License, or
;;;; (at your option) any later version.
11
;;;;
12 13 14 15
;;;; This program is distributed in the hope that it will be useful,
;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;;;; GNU General Public License for more details.
16
;;;;
17 18
;;;; You should have received a copy of the GNU General Public License
;;;; along with this program; if not, write to the Free Software
19
;;;; Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
20
;;;; USA.
21
;;;;
22 23
;;;;           Author: Erick Gallesio [eg@essi.fr]
;;;;    Creation date: 14-Bun-2007 09:24 (eg)
24
;;;; Last file update:  4-Dec-2011 18:10 (eg)
25 26
;;;;

27 28
(define make-external-function #f)
(define make-callback	       #f)
29

30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
(let ((table '((:void 		0)
	       (:char 		1)
	       (:short 		2)
	       (:ushort 	3)
	       (:int 		4)
	       (:uint 		5)
	       (:long 		6)
	       (:ulong 		7)
	       (:lonlong	8)
	       (:ulonlong	9)
	       (:float 		10)
	       (:double 	11)
	       (:boolean 	12)
	       (:pointer 	13)
	       (:string		14)
45
	       (:int8		15)
46 47 48 49
	       (:int16		16)
	       (:int32		17)
	       (:int64		18)
	       (:obj		19))))
50

51 52 53 54 55 56 57 58
  (define (arg-type->number k argument?)
    (let ((info (assoc k table)))
      (if info
	  (let ((r (cadr info)))
	    (if (and (zero? r) argument?)
		(error 'define-external "parameter of type :void are forbidden")
		(cadr info)))
	  (error 'define-external "bad type name ~S" k))))
59

60 61 62 63 64 65 66 67 68 69 70 71 72
  (define (parse-parameters lst)
    (map (lambda (x)
	   (cond
	     ((keyword? x)
	      (arg-type->number x #t))
	     ((pair? x)
	      (if (and (symbol? (car x)) (keyword? (cadr x)) (null? (cddr x)))
		  (arg-type->number (cadr x) #t)
		  (error 'make-external-function
			 "bad parameter description: ~S" x)))
	     (else
	      (error 'make-external-function "bad parameter description: ~S" x))))
	 lst))
73 74


75 76
  ;; make-external-function
  (set! make-external-function
77 78 79 80
    (lambda (entry-name parameters return-type lib-name)
      (%make-ext-func entry-name
		      (parse-parameters parameters)
		      (arg-type->number return-type #f)
81 82 83 84 85 86
		      lib-name)))

  ;; make-callback
  (set! make-callback
    (lambda (proc types data)
      (%make-callback proc (parse-parameters types) data))))
87 88


89

90 91 92 93 94 95 96 97 98 99 100
#|
<doc EXT-SYNTAX define-external
 * (define-external name parameters option)
 *
 * The form |define-external| binds a new procedure to |name|.
 * The arity of this new procedure is defined by the typed list of
 * parameters given by |parameters|. This parameters list is a list
 * of keywords (as defined in the previous table) or couples whose first
 * element is the name of the parameter, and the second one is a type
 * keyword.  All the types defined in the above table, except
 * |:void|, are allowed for the parameters of a foreign function.
101
 * @linebreak
102 103 104
 * |Define-external| accepts several options:
 * ,(itemize
 * (item [
105
 * |:return-type| is used to define the type of the value returned
106 107 108 109 110 111 112 113 114 115
 * by the foreign function. The type returned must be chosen in the types specified
 * in the table. For instance:
 * @lisp
 * (define-external maximum(:int :int)
 *    :return-type :int)
 * @end lisp
 * defines the foreign function maximum which takes two C integers and
 * returns an integer result. Omitting this option default to a result
 * type equal to |:void| (i.e. the returned value is ,(emph "undefined")).
 * ])
116
 *
117 118 119 120
 * (item [
 * |:entry-name| is used to specify the name of the foreign
 * function in the C world. If this option is omitted, the entry-name is
 * supposed to be |name|. For instance:
121
 * @lisp
122 123 124 125 126 127 128 129
 * (define-external minimum((a :int) (b :int))
 *    :return-type :int
 *    :entry-name  "min")
 * @end lisp
 * defines the Scheme function |minimum| whose application
 * executes the C function called |min|.
 * ])
 * (item [
130 131 132 133
 * |:library-name| is used to specify the library which contains the
 * foreign-function. If necessary, the library is loaded before calling the
 * C function. So,
 * @lisp
134 135 136 137
 * (define-external minimum((a :int) (b :int))
 *    :return-type  :int
 *    :entry-name   "min"
 *     :library-name "libminmax")
138
 * @end lisp
139 140 141 142 143 144 145
 * defines a function which will execute the function |min|
 * located in the library |libminmax.xx| (where |xx| is the suffix used
 * for shared libraries on the running system (generally |so|))
 * ])
 * )
doc>
|#
146
(define-macro (define-external name parameters . args)
147 148 149 150 151 152 153 154
  (let* ((lib	     (key-get args :library-name ""))
	 (lib-name   (if (and (equal? lib "")
			      (equal? (running-os) 'cygwin-windows))
			 "cygwin1.dll"
			 lib))
	 (entry-name  (key-get args :entry-name (symbol->string name)))
	 (return-type (key-get args :return-type :void)))

155 156 157 158
    `(define ,name (make-external-function ,entry-name
					   ',parameters
					   ,return-type
					   ,lib-name))))
159

160
(provide "ffi")