On this page:
->
foreign.fun
foreign.def
foreign.linker
default_  abi
cdecl_  abi
stdcall_  abi
system_  case
0.45+9.2.0.3

2.6 Foreign Functions🔗ℹ

foreign type

args_tuple -> result_tuple

 

foreign type

args_tuple -> result_tuple:

  arrow_option

  ...

  body

  ...

 

args_tuple

 = 

arg_type

 | 

(arg, ...)

 | 

(arg, ..., ~varargs, arg, ...)

 

arg

 = 

arg_type

 | 

arg_id :: arg_type

 | 

arg_type = auto_expr

 | 

arg_id :: arg_type = auto_expr

 

result_tuple

 = 

result_type

 | 

(result)

 | 

(result, errno)

 

result

 = 

result_type

 | 

result_id :: result_type

 | 

result_id :~ result_type

 

errno

 = 

~errno

 | 

~get_last_error

 | 

errno_id :: ~errno

 | 

errno_id :: ~get_last_error

 

arrow_option

 = 

~abi abi

 | 

~atomic

 | 

~collect_safe

 | 

~allow_callback_exn

 | 

~in_original

Describes the type of a function. The C representation of a function is an address, while the Rhombus representation is a Rhombus function.

When a function type is used to convert a C function pointer to Rhombus, then calling the Rhombus function is a foreign callout. When a function type is use to convert a Rhombus function to a C function pointer, then it represents a foreign callback.

The args_tuple component of a function type describes the argument types for a C procedure. The tuple can be a single type, or it can be a parenthesized sequence of argument descriptions. If ~varargs appears in the argument sequence, it corresponds to a point in the argument sequence where the remainder is represented by just ... in a C function prototype; the Racket representation of the function expects additional arguments described after ~varargs as required arguments (so, a C function that has variable arguments must be converted through a different type for each different combination of arguments to be supplied).

Each argument is represented by an arg_type, an optional arg_id, and an optional auto_expr. If an argument has an arg_id, it can be referenced by later auto_exprs as well as in result_tuple and a body sequence. If an argument has a auto_expr, then auto_expr supplies the Rhombus representation of an argument to be passed to a C function for a foreign callout, while the Rhombus representation of a callout omits the argument; an auto_expr is not used for a foreign callback.

The result_tuple component describes the result protocol for the C function. It has at least a result_type, and it may have an errno. The C representation of the result_type describes the result from the C function. If an errno is present, then the Rhombus representation for a foreign callout returns two values (when no body sequence is present): a converted value for the C result and an integer for the value of the C library’s errno or Windows’s GetLastError() just after the C function returns. The C function result can have a result_id, and an errno result can have an optional errno_id; those identifiers can be referenced in a body sequence. An errno component is not used for a foreign callback.

If a non-empty body sequence is present, then it determines the result of the Rhombus representation of the function for a foreign callout. A body sequence can refer to the original arguments and the results via arg_ids, result_id, and/or errno_id. A body sequence is not used for a foreign callback.

Additional arrow_options affects the way a C function is called or how a callback is handled:

  • ~abi abi: Uses abi as the ABI for a foreign callout or foreign callback. See default_abi, cdecl_abi, and stdcall_abi.

  • ~atomic: Adjusts a foreign callout to potentially improve performance. The foreign function must not invoke any callbacks or otherwise reach the Rhombus run-time system, so it can be considered an atomic operation from the perspective of Rhombus. This option has no effect on foreign callbacks.

  • ~collect_safe: Adjusts a foreign callout to allow Rhombus garbage collection concurrent with the call, or adjusts a foreign callback to re-enable synchronization with the garbage collector during the callback (i.e., only collect-safe callbacks are allowed to be invoked via a collect-safe procedure call; a collect-safe callback can be invoked through a non-collect-safe foreign procedure call). Note that a collect-safe call makes sense only when arguments to the foreign procedure are not managed by the Rhombus garbage collector or are immobile and reliably retained.

  • ~allow_callback_exn: Adjusts a foreign callout so that a foreign callback is allowed to raise an exception that escape the foreign-function call. This option has no effect on foreign callbacks themselves. A foreign callback must never raise an exception unless it is invoked via foreign function call using this option.

  • ~in_original: Adjusts a foreign callout to take place in a coroutine thread within Rhombus’s main place (which is useful if the foreign function is not thread-safe), or adjusts a foreign callback invocation so that it takes place in a coroutine thread within the current place (which can be useful if the callback might otherwise run in a thread not created by Rhombus). The callout or callback happens in the context of an unspecified Rhombus coroutine thread, so it must not raise an exception.

> def strlen_ptr = Lib.load(#false).find("strlen")

> strlen_ptr

#<ptr_t>

> (cast (string_t -> int_t)strlen_ptr)("hello")

5

definition

foreign.fun id args_tuple result_tuple result_annot

 

definition

foreign.fun id args_tuple result_tuple result_annot:

  option

  ...

  body

  ...

 

result_annot

 = 

:: annot

 | 

:~ annot

 | 

:: values(annot, ...)

 | 

:~ values(annot, ...)

 | 

ϵ

 

option

 = 

~lib: lib_expr

 | 

~c_id: c_id

 | 

~fail: fail_handler_expr

 | 

~wrap: wrapper_expr

 | 

arrow_option

Defines id by combining a ->-like description of a function type with Lib.find to extract a function pointer from the foreign library represented by the result of lib_expr. The export located in the foreign library is the symbol form of c_id if provided, otherwise it is the symbolic form of id. The ~lib form of option is required.

When a result_annot is present, it provides an annotation for the result of the Racket function defined as id. The result value is checked against the annotation when :: is used (as opposed to :~).

When ~fail is provided, then fail_handler_expr should produce a function that expects one argument. When a relevant export is not found in the result of lib_expr, then the result of fail_handler_expr is called with the name that was not found (i.e., c_id or id).

When ~wrap is provided, then wrapper_expr should produce a function that expects one argument. In that case, the value that id would be bound to is passed to the result of wrapper_expr, and id is defined as the result of the wrapper.

foreign.fun strlen(string_t) :: int_t:

  ~lib: Lib.load(#false) // => C library

> strlen("hello")

5

foreign.fun verbose_strlen(s :: string_t) :: (len :: int_t) :: String:

  ~lib: Lib.load(#false) // => C library

  ~c_id: strlen

  @str{The string @repr(s) has @len bytes in its UTF-8 encoding}

> println(verbose_strlen("π day"))

The string "π day" has 6 bytes in its UTF-8 encoding

definition

foreign.def ref_or_id :: type

 

definition

foreign.def ref_or_id :: type:

  option

  ...

 

ref_or_id

 = 

id

 | 

& id

 

option

 = 

~lib: lib_expr

 | 

~c_id: c_id

 | 

~fail: fail_handler_expr

Similar to foreign.fun, but defines id to the Rhombus representation of a foreign-library export.

If & appears before id, then id is defined as a pointer object with static information to indicate type as the content type.

If & does not appear before id, then id is defined as a the conversion of the content at a C library export (which is an address) to the Rhombus representation of type.

definition

foreign.linker id:

  option

  ...

 

option

 = 

~lib: lib_expr

 | 

~default_fail: fail_handler_expr

 | 

~default_wrap: wrapper_expr

Defines id to be a namespace that provides a definition form id.def that’s like foreign.def, and a definition form id.fun that’s like foreign.fun, but using the result of lib_expr automatically. That is, id.def and id.fun disallow a ~lib option, because it is provided to foreign.linker.

If ~default_fail is specified, then the result of fail_handler_expr is used for a ~fail clause if a use of id.def or id.fun does not include one.

If ~default_wrap is specified, then the result of wrapper_expr is used for a ~wrap clause if a use of id.fun does not include one.

foreign ABI

default_abi

 

foreign ABI

cdecl_abi

 

foreign ABI

stdcall_abi

 

foreign ABI

system_case key

| vals: abi

| ~else: abi

An ABI specifies a calling convention to use for a foreign call or callback. On most platforms, the only meaningful ABI is default_abi, because most platforms have only a single standard ABI for C procedures. Windows for 32-bit x86 defines multiple procedure ABIs, and cdecl_abi and stdcall_abi specify alternative ABIs for that platform; on other platforms, cdecl_abi and stdcall_abi are treated the same as default_abi.

The system_case ABI form is analogous to system_case for types.