2 Types
dxtr edited this page 2026-10-18 15:16:26 +02:00

Types

A type specifier names how a value is laid out in C and what Lisp sees of it. A constant specifier is parsed at compile time, so the code that uses it holds only the memory access and the conversion; a specifier known only at run time works too, more slowly.

Scalars

Specifier Size Lisp values
:int8 :uint8 :int16 :uint16 :int32 :uint32 :int64 :uint64 1 to 8 integers
:char :unsigned-char :uchar 1 integers; :char is signed, as in cffi
:short :unsigned-short :ushort 2 integers
:int :unsigned-int :uint 4 integers
:long :unsigned-long :ulong 8, or 4 on Windows integers
:long-long :llong :unsigned-long-long :ullong 8 integers
:size :ssize :intptr :uintptr :ptrdiff 8 integers
:float :double 4, 8 single and double floats
:pointer 8 pointers
:string 8 Lisp strings in UTF-8, in calls only (see Strings)
(:string :encoding encoding) 8 Lisp strings in :utf-16le, :utf-16be or :latin-1, in calls only
:boolean, (:boolean integer-type) that of the integer type, :int by default generalized booleans: 0 is false, any other value true, and true is written as 1
:void none a function's result only

Each scalar is aligned to its size.

Typedefs

(ffi:defctype ft-pos :long "A coordinate, in font units or 26.6 pixels.")

defctype names a type that is stored and converted as its base type.

Enums

(ffi:defcenum render-mode (:normal 0) :light :mono)
(ffi:defcenum (small :int8) (:minus -1) :zero)

An enum's values are keywords to Lisp. Each item is a keyword, or (keyword value); a keyword without a value takes the one after the previous keyword's, the first 0. Without a base type an enum is :uint32, or :int32 if a value is negative, as in cffi.

A call, mem-ref and foreign-slot-value convert keywords to values and back. An integer passes as itself. Reading a value no keyword has signals an ffi-error; of a value two keywords share, the last defined is returned.

foreign-enum-value, foreign-enum-keyword and foreign-enum-keyword-list convert by hand; the first two take :errorp nil to return nil in place of an error.

Bitfields

(ffi:defbitfield (load-flags :int32) (:no-scale 1) (:no-hinting 2) :render)
(ffi:foreign-bitfield-value 'load-flags '(:no-scale :render))   ; => 5
(ffi:foreign-bitfield-symbols 'load-flags 3)                    ; => (:NO-SCALE :NO-HINTING)

A bitfield's values are lists of symbols. Each item is a symbol, or (symbol mask); a symbol without a mask takes the next bit above the highest single-bit mask so far, the first 1. Writing takes a list of symbols, one symbol, or an integer; reading gives the symbols of the single bits set, in the order of the bits, and leaves out masks of several bits. foreign-bitfield-symbol-list lists the symbols in the order of their definition.

Structs and unions

(ffi:defcstruct ft-vector (x :long) (y :long))
(ffi:defcstruct glyph-metrics
  "Some of FT_Glyph_Metrics."
  (width :long) (height :long)
  (bearing (:struct ft-vector))         ; held by value
  (tags :uint8 :count 4))               ; an array of 4
(ffi:defcunion value (i :int64) (d :double))

Type specifiers name them (:struct name) and (:union name). Slots are laid out as C lays them out, the same on x86-64 System V, Windows x64 and AArch64: each slot at the next multiple of its alignment, the struct aligned as its most aligned slot and its size rounded up to a multiple of that. A union's slots all start at 0. A struct held by value must be defined before the struct that holds it.

Operator Gives
(foreign-slot-value pointer type slot) the value of a scalar slot; setf writes it
(foreign-slot-pointer pointer type slot) a pointer to a slot: to the first element of an array, or to a struct
(foreign-slot-offset type slot) the slot's offset in bytes
(foreign-slot-type type slot) the type the slot was defined with; an array's element type
(with-foreign-slots (variables pointer type) body...) variables that stand for slots: a symbol for the slot of its name, (variable slot) for another
(foreign-type-size type), (foreign-type-alignment type) the size, padding included, and the alignment
(foreign-type-lisp-type type &optional direction) the Lisp type a call takes for an argument of type, or with :result the values type it returns, as defcfun declaims them

foreign-slot-value reads scalar slots only. An array, struct or union slot has no value to read, and asking for one is refused, at compile time when the type and slot are constants: take its pointer with foreign-slot-pointer.

C bit-fields have no slot kind of their own: define their storage unit as an integer slot and take the fields out with ldb.