| Allegro CL version 6.2 Object described on page has changed in 6.2 |
Arguments: name-and-options args &key call-direct callback convention returning method-index release-heap release-heap-ignorable arg-checking optimize-for-space strings-convert error-value
This macro creates the specification which allows Lisp to correctly call non-Lisp code. Like other defining forms, its macroexpansion clearly shows what will occur and at what eval-when times. The execution of the expanded form always returns the Lisp name being defined. The definition that is installed is a Lisp function that serves as a wrapper and initiates the foreign call.
In release 6.0, more information is provided by the macroexpansion of a def-foreign-call form. See below.
The following table shows the arguments. The first two entries are the required arguments and the remainder are keyword arguments.
Argument | Value and Details | Notes |
name-and-options | A required argument.
Symbol or a list of a symbol and an external-name specification, which can either be: (1) a symbol naming a function of one argument that returns a string to be used as the foreign name or (2) a string which will be used as the foreign name. |
The symbol represents the lisp-name
for which the
foreign-call definition will be installed. The external name
specification can be either a string specifying a literal external
name, or it can be a symbol, which represents the name of a
conversion function. That conversion function must take one
required argument and at least the language
keyword argument, and must be defined at the time the macro
expansion is executed. At that time this conversion function will
be called and will receive the lisp-name specified, as well as the
arguments :language lang where lang
is the value of the convention keyword
argument to def-foreign-call. |
arglist | A required argument. A possibly
empty list of
argument specifications. |
nil implies
much the same as it does in C, that arguments are
not checked for type or number.
(:void) means 0 arguments are explicitly required (also as in C). Note that string-conversion is done automatically when
arglist is Otherwise a list of argument specifications. See Note 1: Argument Specifications after the table. See A note on foreign addresses
in foreign-functions.htm
for a discussion of foreign addresses and what is expected
when |
returning | Keyword argument. Default: the foreign type
A foreign type (defined by def-foreign-type) A list of a foreign type and a Lisp type (and an optional
third element which is not
used but may be in a later release). Example:
|
This argument specifies how the value returned
from the foreign function will be interpreted. If both foreign and
Lisp types are chosen, they will be checked for consistency and a
warning might be given. A common idiom is to use
to specify that the foreign value returned is to be simply shifted into a fixnum value, with no consing and simple truncation of the top two bits on overflow. If |
convention | Keyword argument. Default :c
Other possibilities listed in Note 2: Possible Values for Convention after the table. |
This argument allows the specialization of calling conventions due to language or operating-system distinctions. The default convention is :c, and is adequate for most situations. (Note that on NT and Windows95 the c/stdcall convention distinction is required for callbacks using defun-foreign-callable, but is not required in def-foreign-call). |
arg-checking | t or
nil (default is t ) |
If true, this argument causes
the Lisp wrapper function to first check the
Lisp types against the Lisp argument type specifications.
When nil , no
argument checking is done (although the number of arguments
might still be checked). If
true, the lisp-types specified or
implied in the argument specification
(provided with the arglist argument)
is used to check the actual arguments to the
wrapper; if a mismatch occurs, error is called. The default
value is t ,
unless arglist is nil . |
call-direct | t or nil
(default is nil ) |
The argument causes no changes to the
Lisp wrapper itself, but, when
specified true, allows for other Lisp functions to call
the foreign function directly when
compiled after the def-foreign-call
form is in effect. In order for the
compilation of a direct-call to be successful, the
argument and return types must imply
simple type conversions which the compiler can handle.
That list of direct-callable
conversions on a platform is constantly changing, but can be
examined by calling the
function list-call-direct-possibilities.
If
for any reason a call to the foreign function cannot be
compiled into a direct-call, a
warning is issued and a call to the wrapper is generated.
When error-value is non- |
method-index [Windows Only] | nil (the default)
or an index into C++ table, as described at
right. |
This argument allows for calling of
C++ style member-methods. The value,
if specified, must be an integer index into the virtual
table of the C++ class. Symbolic
index specifications are not directly supported.
See Note 6: More on the
:method-index argument for information on a
non- |
callback | t (the default and only allowed value) | The callback
keyword is non-operative, but is retained in the
hopes that its functionality can be revived in future versions.
A null value indicates a
promise by the programmer that the foreign function will never
call-back into lisp.
Unfortunately, due to the nature of OS threads implementations,
this promise is currently
impossible to keep. The value of this keyword is always
taken as t , and a
warning is issued if specified to nil . |
release-heap | Only used on platforms that use the :os-threads model for multiprocessing. See Note 3: the release-heap keyword argument below the table. | The release-heap
keyword allows the foreign function to operate
in native-OS threads (so :os-threads is on the
*features*
list), without causing
conflicting demands on the Lisp heap. The values for this
keyword are discussed in See Note 3:
the release-heap keyword argument below the table.
Note: the specification of :release-heap on any non-os-threads
implementation to any value other than :never
will generate a
warning, unless the user also specifies :release-heap-ignorable as
a non-nil value.
|
release-heap-ignorable | t or nil (default is nil ) |
This
argument, when t , tells the system to
ignore the release-heap keyword
argument,regardless of its value, on non-os-threads
implementations. The value on non-os-threads implementations
should be :never .
If the value of this argument
is nil , a warning will be generated if
the value of the release-heap is anything
other than :never . (The reason is such a value
is meaningless and the system wants to be sure that the programmer
understands that a meaningless value has been specified. Assuming
that the heap can be released on non-os-threads (it cannot be
released) may affect behavior or performance.) This argument is
provided to allow the same def-foreign-call forms to be
used on both os-threads and non-os-threads platforms without
warnings being signaled and without conditionalizing the value
of the release-heap argument.
|
optimize-for-space | t or nil (default is nil ) |
The optimize-for-space keyword provides for minimal space requirement for foreign-call wrappers. This option is best used in conjunction with the call-direct option. If true, optimize-for-space will ensure that the wrapper definition takes up very little room, usually as a closure. This usually comes at a cost of speed, and so only makes sense when call-direct is used to compile all actual calls to the foreign function directly, so that the Lisp wrapper is not called normally at all. |
strings-convert | t or
nil
(default is t ) |
This argument assists in having the
foreign-function
interface handle Allegro CL's 16-bit strings automatically.
When the strings-convert is true, then
when any of the specified
arguments at def-foreign-call time are declared directly or indirectly
as (* :char) , def-foreign-call augments the
function wrapping the low-level foreign function call so that for each
(* :char) declared argument, a check is made at
runtime to see if that declaration's corresponding value is a string.
If it is, then that value is converted at runtime to native-string
format using a dynamic-extent array, and this new array is passed in
place of the original string argument to low-level foreign function
call. See Foreign-Functions in
iacl.htm for full details and examples.
If arglist is |
error-value | nil ,
:errno , :os-specific ,
(default is nil ) |
nil causes normal operation of the foreign call
and a return of the one value that the foreign call itself
returns.
call-direct is
ineffective if error-value is
non- See Note 5: More on the error-value argument after the table for more information. |
Argument specifications are available with a rich set of syntax and defaults which allow for a C "feel" while still retaining the Lisp semantics and power.
The basic argument list is simply a list of names, e.g. (a b c d), whose types default to C int types converted in the usual manner from Lisp integer types. Any one argument can be specified more fully, as a list of the name optionally followed by the foreign type optionally followed by the Lisp type (optionally followed by a translation specification, which has not been implemented).
The
foreign type can either be a built-in foreign type or a type defined
by def-foreign-type, or it can be
one of the keywords :lisp
,
:foreign-address
,
or :single-float-no-proto
(deprecated).
The lisp type can be any
valid lisp type.
If the foreign type is not
specified, it defaults to
:int
(and the Lisp type thus defaults to integer).
If the
foreign type is specified but the Lisp type is not, a reasonable Lisp
type is chosen to correspond to the foreign type. If both foreign and
Lisp types are chosen, they will be checked for consistency and a
warning might be given. A foreign type specification that includes a
reference spec such as (& :int)
will be interpreted
as a pass-by-reference argument.
For boolean values, specify the argument (argname :int
boolean)
. Then any non-nil
Lisp
avlue (including 0) will be converted into a C value of 1, and nil
will be converted into a C value of 0. For
returned values, a C value of 0 is converted into nil
and a non-zero C value is converted into t
.
The special Lisp type specification :no-proto
is
provided for use with a :float
foreign type; it is
equivalent to the :single-float-no-proto
. Note that
both of these specifications are deprecated though they will
work. The preferred specification is (:double
single-float)
.
See
A note on foreign addresses
in foreign-functions.htm for a discussion of
foreign addresses and what is expected when
:foreign-address
is specified.
Other than :c
, (the default and suitable for most
purposes), the :fortran
convention is defined.
This convention generally causes a conversion of most atomic arguments
to pass-by-reference.
:fastcall
is a special convention used by NT and
Windows95 to speed up some calls, by passing two arguments in
registers. However, it does not work with Allegro CL. In Allegro CL
on X86 architectures, the first two arguments are usually passed in
registers anyway in Lisp, but they are different registers than used
in fastcall. Also, since the calling sequence itself overshadows the
speed that would be gained by saving a couple of push instructions,
the foreign call to a fastcall function would not in fact be very fast
at all.
The native-threads implementation of Allegro CL changes some basic assumptions of the foreign functions user interface. There is always exactly one native thread per Lisp Process, but there is not necessarily a Lisp process for every thread. Threads are free to run whenever they want; however, only one thread at a time can access the Lisp heap (for read or write); a thread cannot access the Lisp heap unless it has "acquired" the heap, which is only possible after another thread has "released" the heap.
See Releasing the heap when calling foreign functions in foreign-functions.htm for more information on this point.
def-foreign-call allows for the specification of whether to release the heap or not during a call. The possibilities for the release-heap keyword argument are:
:never
- This is the default and is
compatible to the original defforeign
interface; the caller necessarily has the lisp heap, this
call will not release it. Note
that if the foreign code being called spawns any new threads,
or if it allows another
thread to run, and the other thread attempts to call back
into lisp, it will have to wait
for the lisp heap. The danger is that the original thread
may be waiting for results from
its partner thread, but it has not yet given up the heap
(this constitutes a deadlock
situation). :always
- The foreign call always
gives up the heap before making the
transition into non-lisp execution. If the form is being
called within the dynamic context
of a without-interrupts
form, an error
is signaled. :when-ok
- The current dynamic context is
examined, and if
without-interrupts is in effect, the heap is not released;
otherwise the heap is released.
(ff:def-foreign-call foo ())
has no arguments specified, meaning that any number of
arguments of any type
will be passed. If any of these arguments are strings, it may be that
string conversion should be performed.
String conversion is done by default. The
above def-foreign-call form will
generate a warning message (to indicate that string arguments will be
converted). To suppress the warning but still convert the strings,
specify the value t
for the
strings-convert keyword argument. To suppress
the warning and to suppress all automatic strings conversion, specify
the value nil
for the
strings-convert keyword argument.
When considering specifying a non-nil
value
for the error-value argument, care should be
taken to assess whether or not the error value is needed. If an error
value will not be used, then it should not be specified in the def-foreign-call form,
because the foreign call is less efficient when retrieving the error
value. However, if the error value from a foreign call is needed,
then error-value is the only guaranteed option.
One could define a foreign call to GetLastError or to get the current
value of errno variable, but this cannot be guaranteed to work. The
last errors might be overewritten between the time of the foreign call
and the time of the retrieval. The wrong error code will make
debugging harder, so price of the overhead of a non-nil
value for the error-value
argument will be less than the price of wrong error codes.
There are two non-nil
value for
error-value: :errno
and
:os-specific
. All UNIX platforms are consistent in
always returning the error in or through the errno variable.
But many Windows calls (e.g. LoadLibrary()) require you to call
GetLastError() in order to get an error value. Other Windows
functions, particularly those which emulate libc.a/libc.so
functionality (e.g. open()), set errno values like
UNIX. When using the argument on Windows, be sure to specify the
appropriate value.
This argument is supported on Windows only. If a non-nil
value is specified for the
method-index keyword argument, then the value
must be an integer index into the virtual table of the C++ class. And
then, when the foreign function is called, the first argument of the
call must be an integer which is a pointer to the table of method
addresses. The arguments specified to def-foreign-call follow that
first argument. Note that the function name specified in the def-foreign-call form
is used only for the Lisp function name, and does not refer to any
function in a shared library. Instead, the function's address (as a
table address and an index into the table) is passed when the function
is called.
(def-foreign-call add2 (x y))
The symbol add2 will have a function definition calling the foreign function probably named "add2" in C, whose first arg is named "x" and is an integer in Lisp and which is converted to an int for passing to C. If the integer is larger than can be held in a C int, it is truncated. As with the first arg, the second arg named "y" is an integer converted to a C int. The return value is interpreted as a C int type, and is converted to a Lisp integer (which may either be a fixnum or consed as a bignum).
We say the foreign function is "probably" named "add2" because since no specific name or conversion function is specified, the default system conversion function is used. It depends on the platform and platform-specific rules but typically downcases the symbol name.
(def-foreign-call t_double ((x :double) (y :double single-float) (z :int fixnum)) :returning :double))
Call a function, probably named "t_double" in C (again "probably" because the actual name depends on platform-dependent defaults), whose first arg is a double-float both in Lisp and in C, and whose second arg is a single-float in Lisp but is converted to double float for passing into C (this is the calling convention used by some non ANSI C compilers and by others when the arguments are not prototyped), and the third argument is a fixnum Lisp passed as an integer to C. The function returns and boxes a double-float value to Lisp.
(def-foreign-call (t-float dash-to-underscore) ((x :double) (y (:float :no-proto)) (z :int fixnum) (w (* :char) string)) :returning #-(or (and sun4 (not svr4)) sun3q) :float #+(or (and sun4 (not svr4)) sun3q) (:double single-float) (def-foreign-call (t-float "t_float") ((x :double) (y (:float :no-proto)) (z :int fixnum) (w (* :char) string)) :returning #-(or (and sun4 (not svr4)) sun3q) :float #+(or (and sun4 (not svr4)) sun3q) (:double single-float)
These two examples do the same thing: call a function, named "t_float" in C (assuming in the first case proper conversion by dash-to-underscore, which must already be defined and should downcase the symbol name and replace dashes with underscores), whose first arg is a double-float both in Lisp and in C. Like the previous example, the second arg is a float in Lisp, and is converted to double float for passing into C. The third arg named "z" is a fixnum passed as an int, and "w" is a (null-terminated) Lisp string, whose first-character-address is passed to C (beware, the string may move if a gc is allowed). Depending on the architecture, the C function will return either a double (from older C compilers) or a float, each interpreted and boxed as a Lisp single-float value.
We give both examples to show how a lisp name (the symbol
t-float
) is converted to a foreign name
("t_float"). You can either specify a function that takes a
symbol as an argument and returns the correct string (so
(dash-to-underscore 't-float)
returns
"t_float"
) or you can simply specify the
correct string. Note again that dash-to-underscore
must be already defined when the def-foreign-call form is
evaluated.
Since def-foreign-call was created in Allegro CL 5.0, a
macroexpansion could be done that would indicate how it was
implemented, and imply how the resulting call would operate. However,
most of the information was only available as numeric flags or special
keywords such as :foreign-address
. The current
version of def-foreign-call has enhanced the macroexpansion to give
more information that not only makes it easier for the user to read,
but also allows the foreign call engine (called ff-funcall) and the
compiler to make better decisions about how to interpret the arguments
and conversion specifications. Compare the following examples, from
5.0.1 and from 6.2:
In 5.0.1:
user(1): (pprint (macroexpand '(ff:def-foreign-call foo (x (y (* :char)))))) (progn (eval-when (:compile-toplevel) (excl::check-lock-definitions-compile-time 'foo 'function 'foreign-functions:def-foreign-call (fboundp 'foo)) (push 'foo excl::.functions-defined.)) (eval-when (compile load eval) (remprop 'foo 'system::direct-ff-call)) (setf (fdefinition 'foo) (let ((excl::f (named-function foo (lambda (x y) (excl::check-args '(integer :foreign-address) 'foo x y) (system::ff-funcall (load-time-value (excl::determine-foreign-address '("foo" :language :c) 2 nil)) 0 x 0 y 'integer))))) (excl::set-func_name excl::f 'foo) excl::f)) (record-source-file 'foo) 'foo) user(2):
In 6.2:
cl-user(1): (pprint (macroexpand '(ff:def-foreign-call foo (x (y (* :char)))))) Warning: A runtime with-native-string call is being generated for argument `y' to the foreign-function `foo'. The with-native-string macro can be used for explicit string conversions around the foreign calls. This warning is suppressed when :strings-convert is specified in the def-foreign-call. (progn (eval-when (:compile-toplevel) (excl::check-lock-definitions-compile-time 'foo 'function 'foreign-functions:def-foreign-call (fboundp 'foo)) (push 'foo excl::.functions-defined.)) (eval-when (compile load eval) (remprop 'foo 'system::direct-ff-call)) (setf (fdefinition 'foo) (let ((excl::f (named-function foo (lambda (x y) (excl::check-args '((:int (integer * *)) ((* :char) (array character (*)))) 'foo x y) (with-native-string (#:g33506 (if* (stringp y) then y else (excl::warn-foreign-strings-convert 'y 'foo) "")) (unless (stringp y) (setq #:g33506 y)) (symbol-macrolet ((y #:g33506)) (system::ff-funcall (load-time-value (excl::determine-foreign-address '("foo" :language :c) 2 nil)) '(:int (integer * *)) x '((* :char) (array character (*))) y '(:int (integer * *))))))))) (excl::set-func_name excl::f 'foo) excl::f)) (record-source-file 'foo) 'foo) cl-user(12):
See ftype.htm for information on foreign types in Allegro CL and foreign-functions.htm for general information on foreign functions in Allegro CL.
Copyright (c) 1998-2002, Franz Inc. Oakland, CA., USA. All rights reserved.
Documentation for Allegro CL version 6.2. The object described on this page has been modified in the 6.2 release; see the Release Notes.
Created 2002.2.26.
| Allegro CL version 6.2 Object described on page has changed in 6.2 |