On this page:
struct_  t
union_  t
array_  t
gcable_  t
list_  t
vector_  t
9.2.0.3

2.3 Compound Foreign Types🔗ℹ

ffi2 type

(struct_t maybe-tag
  [field-id field-type]
  ...)
 
maybe-tag = 
  | id
Describes a type that is represented by a struct declaration on the C side and a pointer object in the Racket side. If maybe-tag is an identifier, the symbol form of the identifier with a * suffix added is used as a tag for pointers that represent instances of the struct_t type, otherwise a generic ptr_t pointer is used.

Each field-id must be distinct, and the corresponding field-type describes the field’s representation on the C side and the representation used on the Racket side if the field’s value is extracted from a representation of the struct_t type.

When struct_t is used as the parent-type in a define-ffi2-type definition of name without any options (such as #:tag or #:racket->c), then struct_t and define-ffi2-type influence each other:

  • name is used as a maybe-tag identifier if maybe-tag is not present.

  • name* is also defined as a pointer type using maybe-tag* or name* as its tag.

  • name*/gcable is also defined like name*, but it treats a C-to-Scheme conversion like ptr_t/gcable by treating the C-side pointer as (potentialy) referencing memory that is managed by Racket’s garbage collector.

  • name*? is defined to recognize suitably tagged Racket pointer representations.

  • name-field-id is defined for each field-id as an accessor: it takes a pointer for a struct_t instance and extracts a representation of the corresponding field value based on the conversion implied by the associated field-type.

  • set-name-field-id! is defined for each field-id as a mutator: it takes a pointer for a struct_t instance and a representation of the corresponding field value, and it installs a converted value (based the associated field-type) into the struct_t instance.

  • name is defined both as a type and as an expression form. As an expression, it accepts as may subexpressions as field-ids, it allocates an instance of the struct_t type via ffi2-malloc, and it installs each subexpression’s result into the allocated memory in the same way as set-name-field-id!. An optional allocation mode can be provided before the field subexpressions, and the default allocation mode is #:gcable. The name expression can also be used not in a application position, in which case it returns a procedure that accepts field values and allocates an instance.

Note that the Racket-side representation is the same for name and name*, even though the C-side representation differs.

Examples:
> (define-ffi2-type point_t (struct_t
                              [x double_t]
                              [y double_t]))
> (ffi2-sizeof point_t)

16

> (ffi2-sizeof point_t*)

8

> (define p1 (point_t 1.0 2.0))
> p1

#<point_t*/gcable>

> (point_t*? p1)

#t

> (point_t-x p1)

1.0

> (point_t-y p1)

2.0

> (point_t-x (ffi2-malloc 16))

point_t-x: contract violation

  expected: point_t*?

  given: #<ptr_t/gcable>

With this example’s definition of point_t, a field in another struct_t type would take up 16 bytes, while a point_t* field would take up 8 bytes. Accessing the field in either case would produce a Racket representation that is a pointer tagged as point_t*. In the case of a point_t field, the returned pointer would refer to memory within the accessed struct_t instance.

Along similar lines, a pointer tagged with point_t* is suitable as an argument to a C function that has either a point_t or point_t* argument. In the case of a point_t argument, the C function receives a copy of the content of the pointer. In the case of a point_t* argument, the C function receives the same address as encapsulated by the pointer.

ffi2 type

(union_t maybe-tag
  [field-id field-type]
  ...)
 
maybe-tag = 
  | id
Similar to struct_t, but for a type that uses union on the C side.

The interaction of define-ffi2-type and union_t is like the interaction of define-ffi2-type and struct_t, except for the way the defined name is bound as an expression form:

  • name an expression expects a single field name followed by a single field subexpression, and it installs that field’s value after allocating the union_t representation. An optional allocation mode can be provided before the field name.

Examples:
> (define-ffi2-type grade_t (union_t
                              [score double_t]
                              [pass-fail bool_t]))
> (ffi2-sizeof grade_t)

8

> (define g1 (grade_t score 93.0))
> (grade_t-score g1)

93.0

> (define g2 (grade_t pass-fail #t))
> (grade_t-pass-fail g2)

#t

> (define g3 (grade_t score 0.0))
> (grade_t-pass-fail g3)

#f

ffi2 type

(array_t elem_type count)

 
count = exact-nonnegative-integer
  | *
Describes a type that is represented by an array or pointer declaration on the C side and a pointer object in the Racket side. The array’s count must be a literal nonnegative exact integer for a C array declaration, or it can be literally * to indicate a C pointer.

The Racket-side pointer representation uses a tag formed by adding a * suffix on the name of elem_type, as long as it has a name. If elem-type is an immediate struct_t, union_t, array_t, or -> form, then it has no name, and the Racket-side representation is a generic pointer.

When array_t is used as the parent-type in a define-ffi2-type definition of name without any options (such as #:racket->c) other than #:tag, then array_t and define-ffi2-type influence each other:

  • The #:tag option of define-ffi2-type can replace the tag used for pointer representations of the array, which is normally * added as suffix on the name of elem_type. If the #:tag option is not present, then name is added to the end of the Racket pointer representation’s tag to create a pointer subtype, where the array type is a subtype of an elem_type-pointer type.

  • name/gcable is also defined like name* if count is *. It treats a C-to-Scheme conversion like ptr_t/gcable by treating the C-side pointer as (potentialy) referencing memory that is managed by Racket’s garbage collector.

  • name? is defined to recognize suitably tagged Racket pointer representations.

  • name-ref is defined as an accessor: it takes a pointer for a array_t instance and an exact integer, and it extracts a representation of the corresponding element value based on the conversion implied by elem-type. If count is not *, the integer passed to name-ref must be in the range 0 (inclusive) to count (exclusive).

  • name-ref is defined as a mutator: it takes a pointer for a array_t instance, an exact integer, and a field value; it installs a converted value (based elem-type) into the array_t instance. If count is not *, the integer passed to name-set! is constrained in the same way as for name-ref.

Examples:
> (define-ffi2-type triple_t (array_t double_t 3))
> (ffi2-sizeof triple_t)

24

> (define p (ffi2-malloc triple_t))
> p

#<double_t*:triple_t/gcable>

> (triple_t-set! p 0 0.0)
> (triple_t-set! p 1 10.0)
> (triple_t-set! p 2 20.0)
> (triple_t-set! p 3 30.0)

triple_t-set!: contract violation

  expected: (integer-in 0 2)

  given: 3

> (triple_t-ref p 1)

10.0

ffi2 type

(gcable_t ptr-type)

Describes a type that is the same as ptr-type, which must describe a pointer type, except that conversion from C to Scheme creates a reference to an address that is managed by the Racket garbage collector. A gcable_t adjustment has no effect on conversion from Scheme to C or on predicates formed with ffi2-is-a?.

The type (gcable_t ptr_t) is equivalent to ptr_t/gcable. More generally, when defining a pointer type with define-ffi2-type, a type name with a /gcable suffix is defined, and that name describes the same type as using gcable_t. The predicate ptr_t/gcable? is not the same as (lambda (x) (ffi2-is-a? x ptr_t/gcable?)), because ptr_t/gcable? checks specifically for a pointer into a region managed by the Racket garbage collector.

ffi2 type

(list_t maybe-mode elem-type maybe-length)

 
maybe-length = 
  | #:length len-expr
Constructs a type that is like (array_t elem-type *), but where the Racket-side representation is a list of array values. Conversion from Racket to C allocates an array based on the list’s length. For conversion from C, the array length must be specified by maybe-length, which default to a 0 length.

If maybe-mode is non-empty, it is used like ffi2-malloc when allocating for a Racket to C conversion.

ffi2 type

(vector_t maybe-mode elem-type maybe-length)

Like list_t, but for a Racket representation as a vector instead of a list.