On this page:
foreign.type
type.macro
type_  meta.Parsed
type_  meta.After  Prefix  Parsed
type_  meta.After  Infix  Parsed
sizeof
offsetof
foreign.type
foreign.enum
system_  case
0.45+9.2.0.3

2.4 Defining Foreign Types🔗ℹ

definition

foreign.type id

 

definition

foreign.type id = parent_type

 

definition

foreign.type id:

  ~extends: parent_type

 

definition

foreign.type id:

  option

  ...

 

definition

foreign.type id(arg_id, ...):

  option

  ...

 

option

 = 

~extends: parent_type

 | 

~extends parent_type

 | 

~rhombus_to_c: rhombus_to_c_body; ...

 | 

~predicate: predicate_body; ...

 | 

~release: release_body; ...

 | 

~c_to_rhombus: c_to_rhombus_body; ...

 | 

~tag: tag_id

 | 

~tag tag_id

Defines a type id or a type constructor id. When not defining a type constructor, id is also defined as an annotation that recognizes Rhombus representations of the type.

When id is provided by itself, then id represents an opaque type whose C representations is unspecified. An opaque type is useful only with the * type operator to create a pointer type that is tagged using the name id with a * suffix.

When id = parent_type is provided, then id is defined as an alias of parent_type. The tags as name of the type for pointer-tagging purposes remains the same as for parent_type.

When an options block is provided, then ~extends is required. If only ~extends is provided, then id represents an opaque type that creates a pointer subtype relative to parent_type: a pointer representing a value of id* has the tags of parent_type* with an additional tag at the end formed by tag_id suffixed with *. If tag_id is not specified with a ~tag option, then id is used for tag_id. The ~tag option is allowed only in this case.

When an options block has multiple options, then it defines a new type that has the same C-side representation as parent_type, but its Rhombus-side representation can be different as determined by other options as described below.

If foreign.type is followed by id(arg_id, ...), then id is defined as a type constructor that receives expression arguments when applied in a type position, and the remainder of the foreign.type form can refer to the arg_ids to parameterize the definition over supplied values. Each use of id must be applied to any many expression arguments as arg_ids in the definition.

In all cases, the new type id (or the type that it constructs) has the same C representation as parent_type. The Rhombus representation can be adjusted via ~rhombus_to_c and ~c_to_racket options, which may need accompanying ~predicate and ~release functions:

  • ~predicate: Provides a predicate function as the result of the predicate_body sequence. This predicate is used when checks are enabled for Rhombus values to be converted to C for the type id, but the predicate can be skipped on request. The predicate determines whether a value is suitable as an argument to a function provided by ~rhombus_to_c. If predicate is not provided, then the predicate associated with parent_type is used.

  • ~racket_to_c: Provides a conversion function toward C as the result of racket_to_c_body sequence. This converter is applied to a Rhombus value that is supplied for a id type. The result of conversion should be a Rhombus representation for parent_type. If ~racket_to_c is not provided, conversion is the identity function.

  • ~release: Provides a function that finalizes conversion from Rhombus to C as the result of release_body sequence. The function is applied to the result of parent_type’s release function after the C value is delivered (e.g., passed in a foreign call that has returned). For base pointer types, the release function is Function.black_box, which is useful because it keeps a pointer live if it is subject to garbage collection. A release function could explicitly deallocate a pointer that was allocated by the ~racket_to_c function, but a release function is not called if control somehow escapes or the current thread is forcibly terminated.

  • ~c_to_racket: Provides a conversion function toward Rhombus as the result of the c_to_racket_body sequence. This converter is applied to a Rhombus representation of parent_type as extracted from a C representation. The result of conversion should be a Rhombus representation for id. If ~c_to_racket is not provided, conversion is the identity function.

foreign.type percentage_t:

  ~extends: double_t

  ~predicate: fun (v): v is_a Real.in(0.0, 100.0)

  ~rhombus_to_c: fun (v): v / 100.0

  ~c_to_rhombus: fun (v): v * 100.0

> def p = new double_t

> mem *(double_t *)p := 0.5

> mem *(percentage_t *)p

50.0

> mem *(percentage_t *)p := 0.25

> mem *(double_t *)p

0.0025

foreign.type percentage_box_t:

  ~extends: ptr_t

  ~predicate: fun (bx): bx is_a Box && Box.value(bx) is_a percentage_t

  ~rhombus_to_c: fun (bx :~ Box):

                   let p = new ~immobile percentage_t

                   mem p[0] := bx.value

                   p

  ~c_to_rhombus: fun (ptr):

                   Box(mem *(percentage_t *)ptr)

> def p = new ~traced ptr_t

> mem *(percentage_box_t *)p := Box(50.5)

> mem *(percentage_box_t *)p

Box(50.5)

> mem *(double_t *)(mem *(ptr_t *)p)

0.505

foreign.type offset_double_t(delta):

  ~extends: double_t

  ~c_to_rhombus: fun (v): v + delta

  ~rhombus_to_c: fun (v): v - delta

foreign.struct posn_t (x :: offset_double_t(1.0),

                       y :: offset_double_t(2.0))

> def p = new posn_t(10.0, 20.0)

> mem (cast (double_t *)p)[0]

9.0

> mem (cast (double_t *)p)[1]

18.0

> p.x := 100.0

> p.x

100.0

> mem (cast (double_t *)p)[0]

99.0

Like expr.macro, but binds a macro that is expanded in type positions, instead of expression positions.

syntax class

syntax_class type_meta.Parsed

 

syntax class

syntax_class type_meta.AfterPrefixParsed(op_name):

  kind: ~group

  fields:

    group

    [tail, ...]

 

syntax class

syntax_class type_meta.AfterInfixParsed(op_name):

  kind: ~group

  fields:

    group

    [tail, ...]

Provided as meta.

Like expr.Parsed, expr.AfterPrefixParsed, and expr.AfterInfixParsed, but for type positions.

expression

sizeof(type)

Returns the number of bytes used for the C representation of type.

> sizeof(int32_t)

4

expression

offsetof(type, field_id)

Returns the number of bytes in the C representation of type that precede the field named field_id. The type must be have the C representation of a struct or union type; the result is always 0 in the case of a union type.

foreign.struct Point_t(x :: int_t,

                       y :: int_t)

> offsetof(Point_t, x)

0

> offsetof(Point_t, y)

4

Satisfied by values that are valid Racket representations of at_type.

A type name typically doubles as an annotation itself, but foreign.type can be used with more complex type forms, such as ptr_t*.

> def p = new double_t

> p is_a double_t

#false

> p is_a foreign.type double_t*

#true

definition

foreign.enum name parent_type

| enum_clause

| ...

 

enum_clause

 = 

id

 | 

id = literal_int

Like enum restricted to id cases, but also defines name as a type that extends parent_type, which must be an integer type. The C representation of the new type is the same as parent_type. The Rhombus representation is a symbol—the symbol form of one of the listed ids—except that conversion from C can produce an integer if it does not match the numeric value associated with one of the ids.

The numeric value of a id can be provided as a literal_int. If literal_int is not provided for an id, then the integer value is 0 if it is the first id, otherwise it is one more than the value for the preceding id.

When converting from C to Rhombus, if multiple ids have the same numeric value, the symbol form of the last listed id is used.

> foreign.enum shape_t int_t

  | circle

  | triangle = 3

  | square

> cast ~from (shape_t) ~to (int_t) #'circle

0

> cast ~from (shape_t) ~to (int_t) #'triangle

3

> cast ~from (shape_t) ~to (int_t) #'square

4

> cast ~from (int_t) ~to (shape_t) 3

#'triangle

foreign type

system_case key

| vals: then_type

| ~else: else_type

 

key

 = 

type

 | 

os

 | 

arch

 | 

word

 

vals

 = 

id

 | 

32

 | 

64

 | 

vals || vals

Describes a type with a platform-specific representation or a platform-specific choice of function ABI, enabling a compile-time (later than expand-time) choice. The symbol form of key corresponds to a method of system, and each val must be a potential result: an identifier for keys other than word, or either 32 or 64 in the case of word.

Each of then_type amd else_type must be a scalar type, such as int_t or float_t. A system_case type is also scalar, since it selects among scalar types.