2.3 Compound Foreign Types
ffi2 type
(struct_t maybe-tag [field-id field-type] ...)
maybe-tag =
| id
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.
> (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
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.
> (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 | *
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.
> (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)
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
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)