$Revision: 1.1 $
This document was provided to users of Allegro CL release 5.0/5.0.1 to assist in porting Allegro CL 3.0.1 for Windows applications to the new release. We provide it, essentially unedited, for any users of release 6.0 who might need it.
This document is for users porting code written for Allegro CL for Windows 3.0.x to Allegro CL 5.0.
The ACL 5.0 Foreign Function Interface differs from the ACLWin 3.x Foreign Function Interface. ACL 5.0 includes ACLWin 3.x FFI compatibility support to minimize the conversion effort developers with existing ACLWin 3.x applications must face. The new foreign function interface is described in foreign-functions.htm.
The document introduction.htm provides an overview of the Allegro CL documentation with links to all major documents. The document index.htm is an index with pointers to every documented object (operators, variables, etc.) The revision number of this document is below the title. These documents may be revised from time to time between releases.
Table of Contents
Accessing ACLWin 3.x FFI Compatibility Support
Areas Where You May Have to Change Your Code
Converting a C string into a Lisp string
ccallocate
cref: cpointers and handles
cref: cstructures
cref: floating point values
defun-callback
defun-dll
far-peek and far-poke
list-dll-libraries
common-lisp:null
rename-dll-libraries
unlink-dll
unlink-dll-functions
Improving performance
Table of functional equivalents
Accessing ACLWin 3.x FFI Compatibility Support
The :aclwffi module provides ACLWin 3.x FFI compatibility. To use this functionality, include a
(require :aclwffi)
form before compiling code containing ACLWin 3.x FFI calls. Remember that this module is required when generating a standalone application.
You should also evaluate the form
(setf ct::*default-allocation-type* nil)
before compiling code containing ACLWin 3.x FFI cref calls. See improving performance for further discussion.
Areas Where You May Have to Change Your Code
Converting a C string into a Lisp string
From ACLWin documentation:
Step c. does not work in ACL 5. Instead, use:
(ff:char*-to-string (cref (char 256) foo (0 &))) ; ACLWin syntax
or
(ff:char*-to-string (ff::fslot-address-typed '(:array :char 1) :foreign-static-gc foo 0)) ; ACL syntax
Currently, the FFI compatibility strlen function generates a warning, as in:
> (strlen foo) Warning: See ACLWin FFI Compatibility notes for important information about handling strings 16 > (ff:char*-to-string (ff::fslot-address-typed '(:array :char 1) :foreign-static-gc foo 0)) "this is a string" >
There is an optional no warn argument to strlen, for situations where you have verified desired behavior and wish to suppress the warning:
> (strlen foo t) 16
The ccallocate macro allocates an object each time it is called. This is different than in ACLWin, where a single allocation occurs during compile time.
Here is a code example that shows a problem that results from this difference:
> (defun ctest (obj) (let ((testobj (ccallocate (:long 10)))) (values testobj (eq obj testobj)))) ctest > (ctest (ctest t)) #(#(30272048 t 901 nil nil 40 nil) 34773 67541 1409474568 20 1931501856 1852404340 220799591 10 0 ...) nil
In ACLWin, a t result would have occurred.
Here is a suggested way to recode the above example to generate a t result:
> (let ((testobj (ccallocate (:long 10)))) (defun ctest (obj) (values testobj (eq obj testobj)))) ctest > (ctest (ctest t)) #(#(32577661 t 901 nil nil 40 nil) 178 55 993 57 0 0 0 0 0 ...) t >
cref: cpointers and handles
In ACLWin, C pointers are represented by cpointer objects. In ACL 5, they are represented by integers. The FFI compatibility package handles this difference transparently. However, if you have existing CLOS methods that specialize on cpointer objects or code that assumes that C pointers are cpointer objects, you must rewrite the code.
Here is an example:
ACLWin code:
(defun null-cpointer-p (object) (and object (typep object 'acl::cpointer) (= 0 (cpointer-value object))))
On ACL 5:
(defun null-cpointer-p (object) (and object (typep object 'integer) (= 0 object)))
The same issues apply to handles. In ACL 5, handles are integers.
In ACLWin, dereferencing a C pointer that contains a structure address results in the allocation of a cstructure on the Lisp heap, followed by a transparent copy. In ACL 5, dereferencing a pointer containing a structure address returns the address. The FFI compatibility package handles this difference transparently. However, you must rewrite code that depends on the ACLWin cstructure creation and copy.
Here is an example:
On ACLWin:
(defcstruct my-coord ((x :long) (y :long))) (defcstruct my-line ((start (my-coord *)) (end (my-coord *)))) (setf foo (cref (my-curve *) cobj (* :start *))) ;; cobj is a C pointer to a my-curve (do-a-c-free cobj) ;; calls a C function to free all memory (cref my-coord foo :x)
On ACL 5:
(defcstruct my-coord ((x :long) (y :long))) (defcstruct my-line ((start (my-coord *)) (end (my-coord *)))) (setf foo (cref (my-curve *) cobj (* :start *))) ;; cobj is a C pointer to a my-curve (cref my-coord foo :x) (do-a-c-free cobj) ;; don't free memory until the needed data is in Lisp
ACLWin has essentially only one internal floating point format - double float. ACL 5 has single float and double float. Currently you must insure that your code uses the correct format.
Examples:
On ACLWin:
(defcstruct my-coord ((x :double) (y :double))) (setf (cref my-coord cobj :x) 1.23)
On ACL 5:
(defcstruct my-coord ((x :double) (y :double))) (setf (cref my-coord cobj :x) 1.23d0) ;; the constant has to be a double float
If your code has a lot of constants, and all your C structures use double floats, you can avoid massive code changes by changing the default floating point read format:
(setf *read-default-float-format* 'double-float)
Here's another problem area example:
On ACLWin:
(setf (cref my-coord cobj :x) (float foo)) ;; foo is an integer
On ACL 5:
(setf (cref my-coord cobj :x) (float foo 1.d0)) ;; have to force conversion to double float
The ACLWin callback example's C code was incorrect. The correct code is:
int WINAPI call_callback(int (WINAPI *pcb) (int, int, int), int a, int b, int c) { return (*pcb) (a, b, c); }
(The difference is the 'WINAPI' included in the function pointer declaration.)
In ACLWin, under some circumstances, C code with the incorrect declaration style would still work - the example did work, even though it was incorrect. In other circumstances, incorrect results would occur or a segmentation violation would occur. In ACL 5, incorrectly specified C code that invokes a callback will always fail.
Changing the C code is the recommended way to correct this situation. If you cannot do that, you can change your lisp code in the following manner:
Replace
(defun-callback cb1 ((a :long) (b :long) (c :long)) (push (list 'cb1 a b c) *cb-stack*) (+ a b c))
with
(ff:register-function (ff:defun-foreign-callable (cb1 :c) ((a :long) (b :long) (c :long)) :unsigned-long (push (list 'cb1 a b c) *cb-stack*) (+ a b c)) :reuse t)
The :return-mode keyword is not supported. Code that depends on this keyword must be changed.
In ACLWin, the DLL specified in the defun-dll macro call is loaded the first time the function is called. In ACL 5, the DLL is loaded the first time the defun-dll macro invocation is evaluated, compiled, or the resulting compiled code is loaded.
These aren't implemented.
In ACLWin, this function returns a list of keyword package symbols. In ACL 5, it returns a list of strings. In ACL 5, the 'all argument does not affect the result, since DLL's are loaded at defun-dll eval, compile, or resulting compiled code load time.
This constant is not available in ACL 5. Use ct:hnull instead.
Because foreign loading occurs when defun-dll forms are processed, this function is not available in the compatibility package.
Because foreign loading occurs when defun-dll forms are processed, this function is not available in the compatibility package.
Because foreign loading occurs when defun-dll forms are processed, this function is not available in the compatibility package.
Evaluating the form
(setf ct::*default-allocation-type* nil)
before compiling code containing cref calls guarantees that memory accesses and sets will use the proper memory allocation model. The nil specifies that the memory allocation model will be determined at run time. This means that a lisp object will be examined each time the code containing the cref call is executed.
If you are certain that you know the proper allocation model, you can improve performance by setting the *default-allocation-type* variable at compile time. The relevant values are:
nil determine the
allocation model at runtime
:foreign a callocate or ccallocate call
generated the cref object argument
:c the cref
object argument was allocated in foreign code and returned to ACL
Here is an example:
(eval-when (compile) (setf ct::*default-allocation-type* :foreign)) (defun allocate-int-pointer (initial-value) (let ((new (callocate (:long *)))) (setf (cref (:long *) new *) initial-value) new)) (eval-when (compile) (setf ct::*default-allocation-type* nil))
For *default-allocation-type* values other than nil, a runtime error occurs when the run time allocation model doesn't match the model specified at compile time.
Note that the performance improvement only occurs when the cref access arguments can be determined at compile time. For example:
(cref (:long *) foo (integer i))
is not a candidate for this performance improvement because the offset value i is determined at run time.
ACLWIN 3.0.x source code can be changed piece by piece to the new interface. The following table shows the correspondences between the old and new functions. For functions that have no equivalent, the entry is (same) and the symbol exists in the ct package.
ACLWin symbol | ACL symbol |
---|---|
*export-c-names* variable | not applicable |
callocate macro | allocate-fobject |
defctype macro | def-foreign-type |
defun-callback macro | defun-foreign-callable |
defun-dll macro | def-foreign-call |
get-callback-procinst | register-foreign-callable |
ccallocate macro | allocate-fobject |
cpointer-value macro | (same) |
cref macro | (same) |
cset macro | (same) |
csets macro | (same) |
default-callback-style variable | not applicable |
defcstruct macro | def-foreign-type |
deflhandle macro | def-foreign-type |
defshandle macro | def-foreign-type |
defun-c-callback | defun-foreign-callable |
defun-pascal-callback | defun-foreign-callable |
dll-handle | (same) |
far-peek | not applicable |
far-poke | not applicable |
handle-value macro | (same) |
handle= macro | (same) |
list-dll-libraries | list-all-foreign-libraries |
null constant | ct:hnull |
null-cpointer-p macro | (same) |
null-handle macro | allocate-fobject |
null-handle-p macro | (same) |
rename-dll-libraries | not applicable |
sizeof macro | sizeof-fobject |
strlen | (same) |
unlink-dll | unload-foreign-library |
unlink-dll-functions | (same) |
Copyright (C) 1998-2000, Franz Inc., Berkeley, CA. All Rights Reserved.