r11214: 2006-10-10 Kevin Rosenberg (kevin@rosenberg.net)
[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 digitool) nil
21       #+allegro '(:void)
22       #+openmcl (values nil nil)
23
24       ;; args not null
25       #+(or lispworks allegro cmu sbcl scl digitool 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 digitool)
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     (declare (ignorable lisp-args processed))
141     (if (= (length out) 0)
142         `(%def-function ,names ,args 
143           ,@(if module (list :module module) (values))
144           ,@(if returning (list :returning returning) (values)))
145
146         #+(or cmu scl sbcl)
147         `(%def-function ,names ,args 
148           ,@(if returning (list :returning returning) (values)))
149         #+lispworks
150         `(%def-function ,names ,(convert-lispworks-args args) 
151           ,@(if module (list :module module) (values))
152           ,@(if returning (list :returning returning) (values)))
153         #-(or cmu scl sbcl lispworks)
154         (multiple-value-bind (name-pair fname lisp-name)
155             (preprocess-names names)
156           `(prog1
157             (%def-function ,name-pair ,processed 
158              :module ,module :returning ,returning)
159             ;(declaim (inline ,fname))
160             (defun ,lisp-name ,lisp-args
161               (with-foreign-objects ,out
162                 (values (,fname ,@(mapcar #'first args))
163                         ,@(mapcar #'(lambda (arg)
164                                       (list 'deref-pointer
165                                             (first arg)
166                                             (second arg))) out))))))
167         )))
168         
169
170 ;; name is either a string representing foreign name, or a list
171 ;; of foreign-name as a string and lisp name as a symbol
172 (defmacro %def-function (names args &key module returning)
173   #+(or cmu sbcl scl allegro openmcl digitool cormanlisp) (declare (ignore module))
174   
175   (let* ((result-type (convert-from-uffi-type returning :return))
176          (function-args (process-function-args args))
177          (foreign-name (if (atom names) names (car names)))
178          (lisp-name (if (atom names) (make-lisp-name names) (cadr names))))
179
180     ;; todo: calling-convention :stdcall for cormanlisp
181     #+allegro
182     `(ff:def-foreign-call (,lisp-name ,foreign-name)
183          ,function-args
184        :returning ,(allegro-convert-return-type result-type)
185        :call-direct t
186        :strings-convert nil)
187     #+(or cmu scl)
188     `(alien:def-alien-routine (,foreign-name ,lisp-name)
189          ,result-type
190        ,@function-args)
191     #+sbcl
192     `(sb-alien:define-alien-routine (,foreign-name ,lisp-name)
193          ,result-type
194        ,@function-args)
195     #+lispworks
196     `(fli:define-foreign-function (,lisp-name ,foreign-name :source)
197          ,function-args
198        ,@(if module (list :module module) (values))
199        :result-type ,result-type
200       :language :ansi-c
201        #+:win32 :calling-convention #+:win32 :cdecl)
202     #+digitool
203     `(eval-when (:compile-toplevel :load-toplevel :execute)
204        (ccl:define-entry-point (,lisp-name ,foreign-name)
205          ,function-args
206          ,result-type))
207     #+openmcl
208     (declare (ignore function-args))
209     #+(and openmcl darwinppc-target)
210     (setf foreign-name (concatenate 'string "_" foreign-name))
211     #+openmcl
212     (multiple-value-bind (params args) (process-function-args args)
213       `(defun ,lisp-name ,params
214          (ccl::external-call ,foreign-name ,@args ,result-type)))
215     #+cormanlisp
216     `(ct:defun-dll ,lisp-name (,function-args)
217        :return-type ,result-type
218        ,@(if module (list :library-name module) (values))
219        :entry-name ,foreign-name
220        :linkage-type ,calling-convention) ; we need :pascal
221     ))
222
223
224
225