def-foreign-call

Macro

Package: ff

Arguments: name-and-options args &key call-direct callback convention returning method-index release-heap arg-checking optimize-for-space strings-convert

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 argument. The first two entries are the required arguments and the remainder are keyword arguments.

ArgumentValueNotes
name-and-optionsA 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.
arglistA required argument.

A possibly empty list of argument specifications. nil and (:void) have special meanings.

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).

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 :foreign-address is specified.

returningKeyword argument.

Default: the foreign type :int, with a conversion to lisp type integer (with a possible overflow to a bignum). Or can be:

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: (:double single-float)

((* :char)), or ((* :char) string), etc. Causes native-to-string to be called automatically after the return. This is a safe change as far as compatibility goes, because pre-6.0 versions would error on such a specification. The alternative specification of defining the returning value as an integer and then to explicitly call with-native-string on the result, still works, and should be used if any external-format other than :default is desired.

:lisp, meaning a Lisp object. No conversion is done. If not actually a Lisp object, gc failure is possible.

:foreign-address

:single-float-from-double, a double is returned and it is converted to a single.This specification is deprecated; although it will work. The preferred specification is (:double single-float)

:void, nothing is returned, the Lisp function returns nil (:void is actually a foreign type).

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

:returning (:int fixnum)

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 :foreign-address is specified, the return value will be interpreted as an unsigned integer and converted to a positive Lisp integer. The system will not store this value into a foreign-pointer object. The discussion in A note on foreign addresses in foreign-functions.htm does not specifically apply to returning values. Programmers can add code to store the returned value as desired.

conventionKeyword 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-checkingt 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-directt 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.

method-indexnil (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.
callbackt (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-heapOnly 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.
optimize-for-spacet 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-convertt 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.

Note 1: Argument Specifications

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.

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, and although 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.

Note 2: Possible Values for Convention

Other than :c, (the default and suitable for most purposes), the current conventions defined are:

Note 3: the release-heap keyword argument

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:

Examples:

(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 take a symbol 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 in evaluated.

Macroexpansion of a def-foreign-call-form now provides more uneful information

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.0:

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.0:

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 (#:g123
                                             (if*
                                              (stringp y)
                                                then y
                                                else (excl::warn-foreign-strings-convert
                                                      'y
                                                      'foo)
                                                     ""))
                                            (unless
                                             (stringp y)
                                             (setq #:g123 y))
                                            (symbol-macrolet
                                             ((y #:g123))
                                             (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(2): 

See ftype.htm for information on foreign types in Allegro CL and foreign-functions.htm for general information on foreign functions in Allegro CL.

The documentation is described in introduction.htm and the index is in index.htm.

Copyright (c) 1998-2000, Franz Inc. Berkeley, CA., USA. All rights reserved.

Created 2000.10.5.