doc: update design

This commit is contained in:
2026-08-09 22:12:10 +08:00
parent 0c21166e53
commit 8eb24a0b71
2 changed files with 88 additions and 0 deletions
@@ -26,6 +26,19 @@ CError WFStartup(void);
#endif // __cplusplus #endif // __cplusplus
``` ```
## Parameter Declarations
A declaration of an exported FFI function in the C header file generally looks like
the following:
```c++
CError FBAdd(
OMRF_IN_PARAM_TY(uint32_t, in_a),
OMRF_IN_PARAM_TY(uint32_t, in_b),
OMRF_OUT_PARAM_TY(uint32_t, out_rv),
OMRF_OUT_PARAM_TY(bool, out_overflow));
```
## Required Header Files ## Required Header Files
The generated C/C++ header files must include the appropriate header files for the The generated C/C++ header files must include the appropriate header files for the
@@ -92,3 +92,78 @@ The following examples show some legal and illegal POSIX style function names:
| `qwertyuiop_get_data` | Illegal | `<PREFIX>`, which is `qwertyuiop`, is too long. | | `qwertyuiop_get_data` | Illegal | `<PREFIX>`, which is `qwertyuiop`, is too long. |
| `wf_GetMessage` | Illegal | `<FUNC>`, which is `GetMessage`, is not snake_case. | | `wf_GetMessage` | Illegal | `<FUNC>`, which is `GetMessage`, is not snake_case. |
| `wf_Icon_get_message` | Illegal | `<STRUCT>`, which is `Icon`, 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.