Aggregate types are types such as arrays, strings and structures. The internal structure of an aggregate type is not transparent in the way that immediate types are. For example, two structures may have the same size of 8 bytes, but one might partition its bytes into two integers, whereas the other might be partitioned into a byte, an integer, and another byte. The FLI provides a number of functions to manipulate aggregate types. A feature of aggregate types is that they are usually accessed through the use of pointers, rather than directly.
The FLI has two predefined array types: the :c-array type, which corresponds to C arrays, and the :foreign-array type. The two types are the same in all aspects but one: if you attempt to pass a :c-array by value through a foreign function, the starting address of the array is what is actually passed, whereas if you attempt to pass a :foreign-array in this manner, an error is raised.
The :ef-mb-string converts between a Lisp string and an external format C multi-byte string. A maximum number of bytes must be given as a limit for the string size.
The :ef-wc-string converts between a Lisp string and an external format C wide character string. A maximum number of characters must be given as a limit for the string size.
For more information on converting Lisp strings to foreign language strings see the string types :ef-mb-string, :ef-wc-string, and the string functions convert-from-foreign-string, convert-to-foreign-string, and with-foreign-string.
To define types to interface with C structures, the FLI macro define-c-struct is provided. In the next example it is used to define a FLI structure,
The various elements of a structure are known as slots, and can be accessed using the FLI foreign slot functions foreign-slot-names, foreign-slot-type and foreign-slot-value, and the macro with-foreign-slots. For example, the next commands set
point equal to an instance of
tagPOINT, and set the Lisp variable
names equal to a list of the names of the slots of
(setq point (fli:allocate-foreign-object :type 'tagpoint))
(setq names (fli:foreign-slot-names point))
(setq name-type (fli:foreign-slot-type point (car names)))
The above example demonstrates some of the functions used to manipulate FLI structures. The FLI :union type is similar to the :struct type, in that the FLI slot functions can be used to access instances of a union. The convenience FLI function define-c-union is also provided for the definition of specific union types.
Vector types are types that correspond to C vector types. These are handled by the C compiler in a special way, and therefore when you pass or return them to/from foreign code by value you must declare them correctly.
The names of the FLI types are designed to best match the types that are defined by Clang, which is used on Mac OS X, iOS and FreeBSD and is optionally available on other operating systems. For every C/Objective-C type of the form
vector_<type><count>, there is an FLI type of the form
fli:vector-<scalar fli type><count>. For example, the C/Objective-C type
vector_double8 is matched by the FLI type
The count can be 2, 3, 4, 8, 16 (for elements of 32 bits or less) or 32 (for elements of 16 bits or less). The restrictions mean that the maximum size of a vector is 64 bytes and the maximum count is 32.
vector-float<count>, the Lisp vector must either have element type
single-float, or have element type
tand contain elements of type
tand contain elements that fit into the FLI vector.
When a FLI vector type is passed into Lisp, either because it is a returned value from a foreign function or an argument to a foreign callable, it is automatically converted to a Lisp vector of the correct length and element type. This also occurs when accessing a value using foreign-slot-value, foreign-aref and dereference.
When you have a foreign pointer to a vector type, you can access individual elements using foreign-aref, or convert the vector into a Lisp vector using dereference. The reverse operations can be performed using the
setf form or foreign-aref and dereference. For example:
Normally there is no reason to allocate a foreign object for a vector type as in the example above. You would, however, encounter such a pointer if you have foreign code that calls into Lisp passing it an argument that is a pointer to a vector type, and your Lisp code needs to set the values in it. In this case, you will need to declare the argument type as
(:pointer vector-double4) and then set it like this:
Note that if you call a function that takes a pointer to a vector type, you can use the FLI types :reference, :reference-pass and :reference-return to pass and return values without having to explicitly allocate a foreign pointer. For example, if the C function
my_function takes a pointer to
vector_double2 and fills it like this:
vector_sizeattribute, for example,
vector_double4would be defined by:
On 32-bit x86, vector types can be passed either with or without using SSE2. The Lisp FLI definitions must pass/receive arguments in the same way as the C compiler that was used to compile the foreign code. On Mac OS X, this is always with SSE2, so this is not an issue, but on other platforms (Linux, FreeBSD, Solaris) the situation is not clear. What the Lisp definitions do is controlled by *use-sse2-for-ext-vector-type*.
vector-uchar2 on x86_64 platforms and the C compiler is Clang or a derivative, you need to check that you have the latest version of the C compiler, because earlier versions of Clang compiled these types differently from later versions. This affects Mac OS X too because the Xcode C compiler is based on Clang. You can check the version of the C compiler by executing
cc -v in a shell. On Mac OS X, you need to check that you have LLVM 8.0 or later. If you have Clang, you need to check that you have version 3.9 or later.
On Mac OS X x86_64, the treatment of
vector_uchar2 changed between LLVM 6.0 and 8.0. LispWorks is compatible with LLVM 8.0. You can check which version of LLVM you have by executing
cc -v in a shell.
When a structure is passed by value and it contains one of more fields whose types are vector types, it is also important to declare the type correctly in Lisp, otherwise the wrong data may be passed. That is because the machine registers that are used to pass such structures may be different from the registers that are used to pass seemingly equivalent structures that are defined without vector types. Such structures are commonly used to represent matrices.
LispWorks Foreign Language Interface User Guide and Reference Manual - 29 Sep 2017