170 lines
7.7 KiB
Markdown
170 lines
7.7 KiB
Markdown
# FFI Function Signatures
|
|
|
|
Under this section we discuss the signature requirements of the exported FFI
|
|
functions.
|
|
|
|
## C-style Name Export
|
|
|
|
FFI functions must be exported under C names, that is, they must not go through the
|
|
C++ name mangling mechanism.
|
|
|
|
In Rust, you need to decorate the function with the `#[unsafe(no_mangle)]` attribute
|
|
together with `extern "C"`, for example:
|
|
|
|
```rust
|
|
#[unsafe(no_mangle)]
|
|
pub extern "C" fn WFStartup() -> CError {
|
|
// Function body omitted...
|
|
}
|
|
|
|
// More FFI functions omitted...
|
|
```
|
|
|
|
## Export Name Style
|
|
|
|
In this subsection, we discuss the naming style of exported functions.
|
|
|
|
In this design, there are exactly two styles to choose from. We call them the
|
|
Windows style and the POSIX style. Either style can be chosen freely, but within a
|
|
single library the two styles must not be mixed.
|
|
|
|
We recommend the following selection rule: if your library is Windows-only, choose
|
|
the Windows style; otherwise, choose the POSIX style.
|
|
|
|
### Windows Style
|
|
|
|
The Windows style format is `<PREFIX><FUNC>` for ordinary functions, and
|
|
`<PREFIX><STRUCT><FUNC>` for opaque struct functions.
|
|
|
|
In the format, `<STRUCT>` is the name of the opaque struct and `<FUNC>` is the name
|
|
of the function. Both of them must use PascalCase naming.
|
|
|
|
For `<PREFIX>`, it is a prefix identifier that is specific to this library. As we
|
|
all know, C has no namespace concept, and all functions are exported into the same
|
|
space. Therefore, avoiding name collisions between functions is an important task.
|
|
By prepending `<PREFIX>` to the function name, name duplication can be avoided as
|
|
much as possible. For this reason, you need to choose `<PREFIX>` carefully and try
|
|
to pick a prefix that is unlikely to collide.
|
|
|
|
In the Windows style, there are two spellings for `<PREFIX>`. One is the
|
|
all-uppercase style and the other is the style in which only the first letter is
|
|
uppercase and all remaining letters are lowercase. Choose according to your own
|
|
needs. In addition, the length of `<PREFIX>` must not exceed 5 characters, and
|
|
it must not contain anything other than letters and digits (that is, underscores are
|
|
not allowed).
|
|
|
|
The following examples show some legal and illegal Windows style function names:
|
|
|
|
| Name | Verdict | Reason |
|
|
|------|---------|--------|
|
|
| `WFStartup` | Legal | An ordinary (non opaque struct) function. |
|
|
| `WFIconGetHandle` | Legal | An opaque struct function whose `<STRUCT>` is `Icon`. |
|
|
| `WFWordHandleGetHandle` | Legal | An opaque struct function whose `<STRUCT>` is `WordHandle`. |
|
|
| `FlGetMessage` | Legal | Uses a `<PREFIX>`, which is `Fl`, that capitalizes only its first letter. |
|
|
| `MyRustGetMessage` | Illegal | `<PREFIX>`, which is `MyRust`, does not keep all remaining letters lowercase. |
|
|
| `My_RustGetMessage` | Illegal | `<PREFIX>` contains an underscore. |
|
|
| `IconGetHandle` | Illegal | Missing `<PREFIX>`. |
|
|
| `QwertyuiopGetData` | Illegal | `<PREFIX>`, which is `Qwertyuiop`, is too long. |
|
|
| `WFget_message` | Illegal | `<FUNC>`, which is `get_message`, is not PascalCase. |
|
|
| `WFicon_handleGetMessage` | Illegal | `<STRUCT>`, which is `icon_handle`, is not PascalCase. |
|
|
|
|
### POSIX Style
|
|
|
|
The POSIX style format is `<PREFIX>_<FUNC>` for ordinary functions, and
|
|
`<PREFIX>_<STRUCT>_<FUNC>` for opaque struct functions.
|
|
|
|
The `<PREFIX>`, `<STRUCT>` and `<FUNC>` components in the format have the same
|
|
meaning as in the Windows style, but the format is different. `<STRUCT>` and `<FUNC>`
|
|
must use snake_case naming. `<PREFIX>` must be all lowercase, and it must not contain
|
|
anything other than letters and digits (that is, underscores are not allowed). Its
|
|
length and selection requirements are the same as in the Windows style.
|
|
|
|
The following examples show some legal and illegal POSIX style function names:
|
|
|
|
| Name | Verdict | Reason |
|
|
|------|---------|--------|
|
|
| `wf_startup` | Legal | An ordinary (non opaque struct) function. |
|
|
| `wf_icon_get_handle` | Legal | An opaque struct function whose `<STRUCT>` is `icon`. |
|
|
| `wf_word_handle_get_handle` | Legal | An opaque struct function whose `<STRUCT>` is `word_handle`. |
|
|
| `Fl_get_message` | Illegal | `<PREFIX>`, which is `Fl`, is not all lowercase. |
|
|
| `my_rust_get_message` | Illegal | `<PREFIX>`, which is `my_rust`, contains an underscore. |
|
|
| `icon_get_handle` | Illegal | Missing `<PREFIX>`. |
|
|
| `qwertyuiop_get_data` | Illegal | `<PREFIX>`, which is `qwertyuiop`, is too long. |
|
|
| `wf_GetMessage` | Illegal | `<FUNC>`, which is `GetMessage`, is not snake_case. |
|
|
| `wf_Icon_get_message` | Illegal | `<STRUCT>`, which is `Icon`, is not snake_case. |
|
|
|
|
## Parameters and Return Value
|
|
|
|
Under this section we discuss the parameters and return value of the exported
|
|
functions. A standard FFI function looks like the following on the Rust side:
|
|
|
|
```rust
|
|
#[unsafe(no_mangle)]
|
|
pub extern "C" fn FBAdd(
|
|
in_a: in_param_ty!(u32),
|
|
in_b: in_param_ty!(u32),
|
|
out_rv: out_param_ty!(u32),
|
|
out_overflow: out_param_ty!(bool),
|
|
) -> CError {
|
|
// Function body omitted...
|
|
}
|
|
```
|
|
|
|
As shown in the example, this function has four parameters. The first two are input
|
|
parameters and the last two are output parameters. The return value of the function
|
|
is `CError`.
|
|
|
|
### Parameter Rules
|
|
|
|
To follow the best practice of C for the signature of functions with any number of
|
|
input parameters and output parameters, we must design the function signature this
|
|
way. To implement such a function that accepts any number of inputs and outputs, we
|
|
need to put both the input parameters and the output parameters into the parameter
|
|
list. All input parameters must precede any output parameter. The return value,
|
|
`CError`, is used only to indicate whether the function succeeded or failed.
|
|
|
|
The naming style of the function parameters (both input and output) must be
|
|
snake_case and must not start with an underscore.
|
|
|
|
#### Input Parameters
|
|
|
|
Input parameters are passed directly by value (for example, integers, floats or
|
|
pointers). In the example, `in_a` and `in_b` are both of type `u32`. You can use
|
|
the `in_param_ty!` macro provided by the crate to conveniently mark this.
|
|
|
|
By convention, input parameters are prefixed with `in_`, but this is not enforced.
|
|
|
|
#### Output Parameters
|
|
|
|
Output parameters are passed as pointers. In the example, `out_rv` is actually of
|
|
type `*mut u32` and `out_overflow` is actually of type `*mut bool`. To avoid
|
|
repeatedly writing these pointer types and to avoid writing them incorrectly, you
|
|
can use the `out_param_ty!` macro provided by the crate, which is symmetric with
|
|
`in_param_ty!`.
|
|
|
|
At the same time, you can use the `deref_out_param!` macro provided by the crate to
|
|
conveniently dereference an output parameter for assignment, or directly use the
|
|
`set_out_param!` macro provided by the crate for assignment. But you usually do not
|
|
need to do that, because in later chapters you will be guided on how to correctly
|
|
use `cffi_wrapper!` to maximize the efficiency of writing FFI functions.
|
|
|
|
By convention, output parameters are prefixed with `out_`, but this is not enforced.
|
|
|
|
### The Return Value
|
|
|
|
The return value of an FFI function must be of the `CError` type.
|
|
|
|
`CError` is defined in `last_error::CError` as a plain `u32`. It is not an enum with
|
|
a fixed set of members. Instead, `CERROR_OK` is the single value that represents
|
|
absolute, unambiguous success. Every other value's meaning is decided by the
|
|
consuming project: it may denote a failure, or, like some Win32 functions, a
|
|
partial success.
|
|
|
|
#### Migrating from a bool Return Value
|
|
|
|
Projects that previously returned a plain `bool` can adopt the error-code scheme
|
|
with minimal change: define exactly one non-success code, for example
|
|
`const CERROR_FAIL: CError = 1;`, and map every error type to it. The result is
|
|
equivalent to a `bool`: `CERROR_OK` for success and `CERROR_FAIL` for any failure,
|
|
while leaving room to introduce finer-grained codes later.
|