doc: update design
This commit is contained in:
@@ -26,6 +26,19 @@ CError WFStartup(void);
|
||||
#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
|
||||
|
||||
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. |
|
||||
| `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.
|
||||
|
||||
Reference in New Issue
Block a user