MacroPackage: ffToCDocOverviewCGDocRelNotesFAQIndexPermutedIndex
Allegro CL version 10.1
Moderately revised from 10.0.
10.0 version

def-foreign-call

Arguments: name-and-options arglist &key call-direct callback convention returning lisp-return-will-not-move method-index release-heap release-heap-ignorable arg-checking optimize-for-space strings-convert error-value pass-structs-by-value allow-gc release-heap-implies-allow-gc documentation

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.

Macroexpansion of a def-foreign-call form provides useful information about how the call is interpreted. 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 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).

Note that string-conversion is done automatically when arglist is nil unless strings-convert is specified nil. See Note 4: String conversion when no arguments are specified after the table.

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. The :aligned type passes a Lisp fixnum as if it were a machine integer (resulting in a value with the lowest 3 (64-bit) or 2 (32-bit) bits 0). This can serve as a pointer aligned on an 8 byte (64-bit) or 4 byte (32-bit) boundary and it avoids potential bignum calculations. See Aligned Pointers and the :aligned type in ftype.htm for more information on :aligned.

returning Keyword argument.

Default: the foreign type :int, with a conversion to Lisp type integer (a fixnum with a possible overflow to a bignum). See Note 7: Potential problems with foreign functions returning :int in 64-bit Lisps for possible problems with returning :int on 64-bit Lisps.

The value can also be:

A foreign type (defined by def-foreign-type)

A list of a foreign type and a Lisp type. Example: (:double single-float). See also Note 8: Returning booleans.

A list of a foreign type, a Lisp type, and a symbol naming a user-defined conversion function. Example: (:double single-float my-conversion-function). See section The user-conversion function in a complex-type-spec in foreign-functions.htm for more information on this option.

((* :char)), or ((* :char) string), etc. Causes native-to-string to be called automatically after the return. 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. See Note 9 on the :lisp return type below for more information on this choice.

:foreign-address (an integer which will be converted to a Lisp integer (fixnum or bugnum). The :aligned return type will return an address aligned on an 8 (64-bit) or 4 (32-bit) byte boundary (meaning the lowest 3 or 2 bits are 0) which will look to Lisp like a fixnum. See Aligned Pointers and the :aligned type in ftype.htm for more information. The (:aligned ftype) specification is not supported.

:single-float-from-double, a double is returned and it is converted to a single. This specification is deprecated though 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.

lisp-return-will-not-move Keyword argument. Default nil. If specified true, no warning will be signaled if the value of returning is :lisp. See Note 9 on the :lisp return type below.
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 Windows 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 unless arglist is nil) 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.

An argument specification (addr :int) is converted to (addr :int (integer -2147483648 2147483647)) so the argument checking mechanism will produce an error if the argument is anywhere outside of the range of a signed 32-bit integer.

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-nil, the call cannot be a direct-call so call-direct is ignored (but a warning is printed if call-direct is non-nil).

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-nil value for this argument.

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 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.
allow-gc :never, :always, or :when-ok (default is :never) This argument can have the same values as release-heap, and they roughly mean the same thing, except here applying to the garbage-collector is allowed to run either never (with :never), when interrupts are enabled (with :when-ok) or always (with :always). See Note 3: the release-heap keyword argument below the table. This argument is only used in an SMP Lisp.

It is planned to add as an allowable value :match-release-heap and eliminate the :release-heap-implies-allow-gc argument.

release-heap-implies-allow-gc t or nil (default is nil) If in an SMP Lisp the release-heap argument is given a value other than :never, then a warning is issued. This is because :release-heap is a confusing concept in an SMP world, and it is necessary to require users porting to SMP Lisps to explicitly state what is desired for SMP, regardless of the options used for os-threads or virtual-threads (where Lisp processes are managed within Lisp). So the user can elect to use conditional reading using #+os-threads, #+smp, etc., or these conditionalizations can be avoided with release-heap-implies-allow-gc which says "I understand that in an SMP Lisp we are really allowing the gc, but I want to use the release-heap argument to specify this".

It is the intention to remove this argumement and allow an additional value for allow-gc to replace it.

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 nil (meaning that no information about the type or the number of arguments is supplied) and this argument is unspecified, string conversion is enabled and a warning is printed stating that fact. See Note 4: String conversion when no arguments are specified after the table.

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.

:errno gets the most recent value of the errno variable and returns it as the second return value from the foreign call.

:os-specific gets an architecture/operating-system specific error value and returns it as the second return value from the foreign call. On Windows systems, the GetLastError() function is used to get this error value. Currently, all other architectures retrieve the value of the errno variable.

call-direct is ineffective if error-value is non-nil (a warning is printed and the call-direct argument is ignored if non-nil values are specified for both error-value and call-direct).

See Note 5: More on the error-value argument after the table for more information.

pass-structs-by-value nil or t, default is nil nil causes an argument structure not specified with a * to be passed by reference anyway. So an argument specification (point Point) with pass-structs-by-value nil will be passed by reference, as will (point (* Point)), while (point Point) with pass-structs-by-value t will be passed by reference. Structs can be passed by value or reference as arguments and returned by value or reference as well. See also the variable *pass-structs-by-value*, which is bound whenever def-foreign-call is invoked, either to the keyword value, or to its current value.

If not specified, a warning is issued. See Passing structures by value in foreign-functions.htm for details.

documentation nil or a string, default is nil Supplies a documentation string for the foreign function. The string can be accessed with (documentation name 'function). You can set the documentation after definition with (setf documentation). If excl:*load-documentation* is nil, documentation string is effectively ignored.

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 elements of an argument list can be:

The allowable keywords for the foreign type (second list element) are:

Here is an example:

  (a (b :int fixnum) (c :lisp))

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 value (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 (but its use is deprecated); it is equivalent to the :single-float-no-proto foreign type (not listed above because it is deprecated). The preferred specification is (name :double single-float).

Specifying (* :float) or (* :double) as the type of an argument is not recommended. The function will expect a foreign address (not a Lisp address) and it will not pass the address of a Lisp float if a Lisp float is given as a value.

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 :fortran convention is defined. This convention generally causes a conversion of most atomic arguments to pass-by-reference.

:fastcall convention on Windows does not work

:fastcall is a special convention used by some Windows operating systems 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.

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:

Garbage collections in an SMP Lisp

There is a big difference in dealing with other-thread and garbage collection (gc) action intermingled with foreign calls in all of virtual-threads (processes are managed by Lisp), os-threads (processes are managed by the OS, but only one hardware processor is used for Lisp code), and SMP (multiple hardware processors can be used for Lisp and foreign code) Lisps. All three are different. This makes designing functionality which will run correctly on all three complicated.

The release-heap argument was added for os-threads, and has options :never, :always, and :when-ok. These are still the options. They pertain to whether the thread calling a foreign-function wants to allow another thread to access the Lisp heap during the call (note that amonst other things, this could lead to a garbage-collection).

The virtual-threads Lisps are simpler. Because they are more constrained, :release-heap :never is the only option that makes sense, since there is no concept of releasing the heap by a thread separate from pausing the thread.

If the user is coding for both virtual and os-threads, he can code for os-threads, and then add the :release-heap-ignorable t option to allow virtual-threads calls to compile without warning (and with the implied descriptive value of :never for the :release-heap option).

SMP Lisps are more complicated As with virtual threads, there is again no concept of heap ownership, in this case not because the heap is automatically assigned to the running thread, but because there are no longer any heap management constraints except one (explained later) which means that any thread can access the heap at any time. So the concept of releasing the heap makes even less sense in an SMP Lisp than it does in a virtual-threads Lisp.

In a pure SMP Lisp, there would be no restrictions at all on foreign calls. But, as mentioned above, although the heap itself is no longer a one-at-a-time resource needing gating, the garbage-collector, which is a stop-the-world collector still, must have complete and exclusive access to the heap when it runs, and so threads must specify whether to allow the gc to run.

def-foreign-call has two new keyword arguments: allow-gc and release-heap-implies-allow-gc to deals with these issues.

allow-gc,defaults to :never, to keep it in line with the release-heap argument. It can have the same values as release-heap, and they roughly mean the same thing (with the difference being that the garbage-collector is allowed to run either never (with :never), when interrupts are enabled (with :when-ok) or always (with :always). Note that as with os-threads, in an SMP Lisp, :allow-gc :never does not lock out the gc if the foreign code performs a callback to Lisp. So appropriae measures must still be taken, if a callback is possible, to reacquire Lisp objects after a possible gc, because they may have moved.

Note 4: String conversion when no arguments are specified

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

Note 5: More on the error-value argument

Getting an error value from a foreign call is somewhat complicated, so users should not casually use this feature, but instead consider whether the information is necessary and be aware of various ways in which the information may be incorrect.

First note the following:

The two acceptable non-nil values for error-value are :errno and :os-specific.

On UNIX and UNIX-like platforms

The two non-nil values have the same effect: the value of errno is stored immediately after the actual foreign function returns (and before additional wrapper code is run), and then returned as the second return value of the foreign call.

On Windows

The values have different effects.

If you specify the value :os-specific, then GetLastError() is called immediately after the actual foreign function returns (and before additional wrapper code is run), and then returned as the second return value of the foreign call. This is the standard Windows procedure, and works for all standard Windows routines, for example LoadLibrary() (see the description of that function in the MSDN where it says that you get error information by calling GetLastError()).

If you specify the value :errno, then the value of errno is stored immediately after the actual foreign function returns (and before additional wrapper code is run), and then returned as the second return value of the foreign call. This is not standard Windows procedure, but is how some routines work, particularly those which are provided in UNIX-like libraries. This is often how Windows versions of open() works, for example, (open() is not a standard Windows function).

On Windows, you must specify the appropriate way to get the error value for the routine you are calling. Specifying :errno will give a wrong value for LoadLibrary(). Specifying :os-specific will give a wrong value for (many, perhaps all implementations of) open().

Note 6: More on the method-index argument

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 a vector whose first value is the 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 the vector whose first element is 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.

Note 7: Potential problems with foreign functions returning :int in 64-bit Lisps

def-foreign-call is defined to default its :returning keyword argument (specifying the expected type of value returned by the foreign call) to :int. :int corresponds to C's int type. However, if the foreign function does not actually return an int, subtle bugs could be introduced in programs, particularly if the C function returns a long, an unsigned long, or a pointer of some sort. In 32-bit Lisps, returning those values is not a problem (when :returning :int is specified or defaulted to) because int is always 32 bits on every architecture we support. But on 64-bit Lisps, if a 64 bit value is returned, the upper 32 bits are lost. If the value was not correctly sign-extended by the foreign code, a negative value in the foreign code could be seen by Lisp as a large positive value. Also, the :long or :unsigned-long type is an inadequate specfication because on Windows long types are always 32 bits. So on either 32 or 64 bit lisps, for portability, use :returning :unsigned-nat when the return value is some kind of pointer. (See The :nat and :unsigned-nat types in implementation.htm for a description of the :nat and :unsigned-nat types.) When the returned value is an integer value, be sure to use the correct type and be sure that the foreign code actually produces that type.

Note 8: Returning booleans

def-foreign-call can accept :returning :boolean as a return type, and it will be automatically translated to a canonical form which is :returning (:int boolean). This works with some foreign languages (where 0 is considered false and anything else is considered true - it translates 0 into nil on the lisp side and anything else to the value t), but in some cases (C++, for instance) the boolean type is equivalent to signed-char type, and so it must be specified in the def-foreign-call as :returning (:char boolean).

Note 9 on the :lisp return type

The :lisp return type means that the foreign code will return a Lisp object. For most Lisp objects (other than characters, fixnums, and a few others), this means returning a pointer to the object. If for whatever reason the object has been moved before the pointer is in a place where it can be seen by the garbage collector and properly forwarded, the pointer will be invalid and the result may be Lisp failure because of gc failure. For this reason, a warning is signaled when :lisp is specified as a value for :returning.

The warning can be avoided by specifying :aligned instead of :lisp as the :returning value. Although aligned pointers could also represent objects that move during gc, it is much less likely so, because they tend to only be used when the objects they represent are static, like stack-based objects or allocated in aclmalloc space. Note associating the actual Lisp value with an :aligned value is complicated. See Aligned Pointers and the :aligned type in ftype.htm for more information.

If you are sure the item will not move (because it is located in statis space, for example), you can give t as the value of the lisp-return-will-not-move keyword argument. That supresses any warning about :returning :lisp.

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

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

def-foreign-call has enhanced the macroexpansion to give useful information for users. Here is an example:

cl-user(11): (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 (#:g34798
                                             (if*
                                              (stringp y)
                                                then y
                                                else ""))
                                            (unless
                                             (stringp y)
                                             (setq #:g34798 y))
                                            (symbol-macrolet
                                             ((y #:g34798))
                                             (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): 

The effects of the new, longer array implementation on def-foreign-call

The new array implementation is discussed in the Arrays and short arrays section in implementation.htm. In brief, standard Common Lisp arrays now can be significantly larger than in earlier releases, while the new short arrays implement the old arrays (the same size limitations but also the same type codes and structure).

In this discussion, `array' refers to the newly-implemented arrays, while `short array' refers to the old implementation, preserved as short arrays in 7.0.

Foreign calls are made with arrays as arguments by passing the address of the first value. In the new implementation, simple-arrays always have exactly the same first element offset (but some short arrays are aligned to the next higher word boundary so that the elements within are naturally aligned). This sometimes-difference between arrays and short-arrays poses an extra burden on the ff interface, in that the arrays must be distinguished between themselves at runtime.

It is now possible to declare an argument a (short-simple-array ... (*)) and the interface will generate code as it did before for normal arrays, passing the address of the first argument.

For 7.0, a declaration of (simple-array ... (*)) actually generates code that tests at runtime whether the argument is a short array or a normal array. So in effect, a short simple-array passed in as if it were a normal simple-array will be properly passed.

Note that with this setup, if argument checking is specified and a short-array is passed in, the check will fail, because a short-array is not a subtype of simple-array. But for 7.0, if you suppress this argument checking, the interface will pass either array correctly.

However, programmers are urged to provide correct declarations and pass the correct type of array even though 7.0 allows sloppiness. In a future release, we anticipate adding a (dual-simple-array ... (*)) declaration to the direct-call foreign interface, and to move that functionality from its current place in the (simple-array ... (*)) declaration. This would then mean that the (simple-array ... (*)) declaration would only pass simple-arrays, and not short simple-arrays. This change will also make more consistent the arg-checking feature of the direct-call interface, since the dual-simple-array type is defined as an or of simple-array and short-simple-array, so a short-simple-array being passed will pass the test of being a dual-simple-array.

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


Copyright (c) 1998-2022, Franz Inc. Lafayette, CA., USA. All rights reserved.
This page has had moderate revisions compared to the 10.0 page.
Created 2019.8.20.

ToCDocOverviewCGDocRelNotesFAQIndexPermutedIndex
Allegro CL version 10.1
Moderately revised from 10.0.
10.0 version