r10608: update license
[uffi.git] / src / functions.lisp
1 ;;;; -*- Mode: Lisp; Syntax: ANSI-Common-Lisp; Base: 10; Package: UFFI -*-
2 ;;;; *************************************************************************
3 ;;;; FILE IDENTIFICATION
4 ;;;;
5 ;;;; Name:          function.lisp
6 ;;;; Purpose:       UFFI source to C function definitions
7 ;;;; Programmer:    Kevin M. Rosenberg
8 ;;;; Date Started:  Feb 2002
9 ;;;;
10 ;;;; $Id$
11 ;;;;
12 ;;;; This file, part of UFFI, is Copyright (c) 2005 by Kevin M. Rosenberg
13 ;;;;
14 ;;;; *************************************************************************
15
16 (in-package #:uffi)
17
18 (defun process-function-args (args)
19   (if (null args)
20       #+(or lispworks cmu sbcl scl cormanlisp (and mcl (not openmcl))) nil
21       #+allegro '(:void)
22       #+openmcl (values nil nil)
23
24       ;; args not null
25       #+(or lispworks allegro cmu sbcl scl (and mcl (not openmcl)) cormanlisp)
26       (let (processed)
27         (dolist (arg args)
28           (push (process-one-function-arg arg) processed))
29         (nreverse processed))
30       #+openmcl
31       (let ((processed nil)
32             (params nil))
33         (dolist (arg args)
34           (let ((name (car arg))
35                 (type (convert-from-uffi-type (cadr arg) :routine)))
36             ;;(when (and (listp type) (eq (car type) :address))
37             ;;(setf type :address))
38             (push name params)
39             (push type processed)
40             (push name processed)))
41         (values (nreverse params) (nreverse processed)))
42     ))
43
44 (defun process-one-function-arg (arg)
45   (let ((name (car arg))
46         (type (convert-from-uffi-type (cadr arg) :routine)))
47     #+(or cmu sbcl scl)
48     ;(list name type :in)
49     `(,name ,type ,@(if (= (length arg) 3) (list (third arg)) (values)))
50     #+(or allegro lispworks (and mcl (not openmcl)))
51     (if (and (listp type) (listp (car type)))
52         (append (list name) type)
53       (list name type))
54     #+openmcl
55     (declare (ignore name type))
56     ))    
57
58
59 (defun allegro-convert-return-type (type)
60   (if (and (listp type) (not (listp (car type))))
61       (list type)
62     type))
63
64 (defun funcallable-lambda-list (args)
65   (let ((ll nil))
66     (dolist (arg args)
67       (push (car arg) ll))
68     (nreverse ll)))
69
70 #|
71 (defmacro def-funcallable (name args &key returning)
72   (let ((result-type (convert-from-uffi-type returning :return))
73         (function-args (process-function-args args)))
74     #+lispworks
75     `(fli:define-foreign-funcallable ,name ,function-args
76       :result-type ,result-type
77       :language :ansi-c
78       :calling-convention :cdecl)
79     #+(or cmu scl sbcl)
80     ;; requires the type of the function pointer be declared correctly!
81     (let* ((ptrsym (gensym))
82            (ll (funcallable-lambda-list args)))
83       `(defun ,name ,(cons ptrsym ll)
84         (alien::alien-funcall ,ptrsym ,@ll)))
85     #+openmcl
86     (multiple-value-bind (params args) (process-function-args args)
87       (let ((ptrsym (gensym)))
88         `(defun ,name ,(cons ptrsym params)
89           (ccl::ff-call ,ptrsym ,@args ,result-type))))
90     #+allegro
91     ;; this is most definitely wrong
92     (let* ((ptrsym (gensym))
93            (ll (funcallable-lambda-list args)))
94       `(defun ,name ,(cons ptrsym ll)
95         (system::ff-funcall ,ptrsym ,@ll)))
96     ))
97 |#    
98
99 (defun convert-lispworks-args (args)
100   (loop for arg in args
101         with processed = nil
102         do
103         (if (and (= (length arg) 3) (eq (third arg) :out))
104             (push (list (first arg)
105                         (list :reference-return (second arg))) processed)
106             (push (subseq arg 0 2) processed))
107         finally (return processed)))
108
109 (defun preprocess-names (names)
110   (let ((fname (gensym)))
111     (if (atom names)
112         (values (list names fname) fname (uffi::make-lisp-name names))
113         (values (list (first names) fname) fname (second names)))))
114
115 (defun preprocess-args (args)
116   (loop for arg in args
117         with lisp-args = nil and out = nil and processed = nil
118         do
119         (if (= (length arg) 3)
120             (ecase (third arg)
121               (:in 
122                (progn
123                  (push (first arg) lisp-args)
124                  (push (list (first arg) (second arg)) processed)))
125               (:out
126                (progn
127                  (push (list (first arg) (second arg)) out)
128                  (push (list (first arg) (list '* (second arg))) processed))))
129             (progn
130               (push (first arg) lisp-args)
131               (push arg processed)))
132         finally (return (values (nreverse lisp-args) 
133                                 (nreverse out) 
134                                 (nreverse processed)))))
135
136
137 (defmacro def-function (names args &key module returning)
138   (multiple-value-bind (lisp-args out processed)
139       (preprocess-args args)
140     (if (= (length out) 0)
141         `(%def-function ,names ,args 
142           ,@(if module (list :module module) (values))
143           ,@(if returning (list :returning returning) (values)))
144
145         #+(or cmu scl sbcl)
146         `(%def-function ,names ,args 
147           ,@(if returning (list :returning returning) (values)))
148         #+lispworks
149         `(%def-function ,names ,(convert-lispworks-args args) 
150           ,@(if module (list :module module) (values))
151           ,@(if returning (list :returning returning) (values)))
152         #-(or cmu scl sbcl lispworks)
153         (multiple-value-bind (name-pair fname lisp-name)
154             (preprocess-names names)
155           `(prog1
156             (%def-function ,name-pair ,processed 
157              :module ,module :returning ,returning)
158             ;(declaim (inline ,fname))
159             (defun ,lisp-name ,lisp-args
160               (with-foreign-objects ,out
161                 (values (,fname ,@(mapcar #'first args))
162                         ,@(mapcar #'(lambda (arg)
163                                       (list 'deref-pointer
164                                             (first arg)
165                                             (second arg))) out))))))
166         )))
167         
168
169 ;; name is either a string representing foreign name, or a list
170 ;; of foreign-name as a string and lisp name as a symbol
171 (defmacro %def-function (names args &key module returning)
172   #+(or cmu sbcl scl allegro mcl cormanlisp) (declare (ignore module))
173   
174   (let* ((result-type (convert-from-uffi-type returning :return))
175          (function-args (process-function-args args))
176          (foreign-name (if (atom names) names (car names)))
177          (lisp-name (if (atom names) (make-lisp-name names) (cadr names))))
178
179     ;; todo: calling-convention :stdcall for cormanlisp
180     #+allegro
181     `(ff:def-foreign-call (,lisp-name ,foreign-name)
182          ,function-args
183        :returning ,(allegro-convert-return-type result-type)
184        :call-direct t
185        :strings-convert nil)
186     #+(or cmu scl)
187     `(alien:def-alien-routine (,foreign-name ,lisp-name)
188          ,result-type
189        ,@function-args)
190     #+sbcl
191     `(sb-alien:define-alien-routine (,foreign-name ,lisp-name)
192          ,result-type
193        ,@function-args)
194     #+lispworks
195     `(fli:define-foreign-function (,lisp-name ,foreign-name :source)
196          ,function-args
197        ,@(if module (list :module module) (values))
198        :result-type ,result-type
199       :language :ansi-c
200        #-macosx :calling-convention #-macosx :cdecl)
201     #+(and mcl (not openmcl))
202     `(eval-when (:compile-toplevel :load-toplevel :execute)
203        (ccl:define-entry-point (,lisp-name ,foreign-name)
204          ,function-args
205          ,result-type))
206     #+openmcl
207     (declare (ignore function-args))
208     #+(and openmcl darwinppc-target)
209     (setf foreign-name (concatenate 'string "_" foreign-name))
210     #+openmcl
211     (multiple-value-bind (params args) (process-function-args args)
212       `(defun ,lisp-name ,params
213          (ccl::external-call ,foreign-name ,@args ,result-type)))
214     #+cormanlisp
215     `(ct:defun-dll ,lisp-name (,function-args)
216        :return-type ,result-type
217        ,@(if module (list :library-name module) (values))
218        :entry-name ,foreign-name
219        :linkage-type ,calling-convention) ; we need :pascal
220     ))
221
222
223
224