doc: update design
This commit is contained in:
@@ -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.
|
||||||
|
|||||||
Reference in New Issue
Block a user