4.2 KiB
Library Architecture
The functions exported in a library can be roughly divided into two categories: auxiliary functions and functional functions.
Auxiliary Functions
Auxiliary functions do not perform the main functionality of the library. They help the functional functions work properly.
Startup/Shutdown Functions
Startup/shutdown functions are a category of auxiliary functions, as shown in the following example:
#[unsafe(no_mangle)]
pub extern "C" fn WFStartup() -> CError {
// Function body omitted...
}
#[unsafe(no_mangle)]
pub extern "C" fn WFShutdown() -> () {
// Function body omitted...
}
Here WF is the <PREFIX>, indicating that these are startup/shutdown functions exclusive to this library. Usually the word startup is used as the <FUNC> of the startup function and the word shutdown is used as the <FUNC> of the shutdown function. Other names may be chosen according to the preference of the library author.
These functions usually have no input or output parameters, but this is not mandatory. The library author may add parameters as needed.
Among these functions, the return value of the startup function is CError, and the return value of the shutdown function is () (that is, void in C/C++). The return value types of these two cannot be changed. The reason for setting the return values this way is: when the startup function runs, it may return some errors to explicitly inform the user that initialization failed, so its return value is CError. As for the shutdown function, in order to more closely match the semantics of Rust's Drop and C++ destructors, its return value is ().
When implementing these functions on the Rust side and using them in C/C++, you need to guarantee:
- Any call to a functional function must be after the call to the startup function and before the call to the shutdown function. Otherwise these functional functions must return an error code to indicate this error.
- The startup/shutdown functions can be called repeatedly, but the startup and shutdown functions must appear in pairs. This is similar to Win32 COM's
CoInitializeandCoUninitialize.
You may use the library_lifecycle module provided by the crate to simplify the writing of the startup/shutdown functions.
Startup/shutdown functions are an optional kind of auxiliary function. If your library will not fail when initializing DLL-level resources, there is no need to add startup/shutdown functions. You can directly use Rust's lazy loading mechanism (for example LazyLock) so that the resources are loaded when the DLL is loaded and released when the DLL is released.
Error Information Functions
Error information functions are used to return the error information of the last call. Here is an example:
#[unsafe(no_mangle)]
pub extern "C" fn WFGetErrorMessage() -> CStrPtr {
// Function body omitted...
}
Here WF is the <PREFIX>, indicating that this is the error information function exclusive to this library. Usually the phrase "get last error" or "get error message" is used as the <FUNC> of the error information function. Other names may be chosen according to the preference of the library author.
The return value of the error information function is not CError but CStrPtr. CStrPtr is a type in the cstr_ffi module provided by the crate. This is among the few functions in the whole library whose return value is not CError.
When implementing the error information function, you should guarantee that its return value is based on the last call on the current thread rather than the last call on the process.
You may use the last_error module provided by the crate to simplify the writing of the error information function. Furthermore, by properly using cffi_wrapper!, you can simplify the writing of the error information function to the greatest extent, without even touching the last_error module.
Functional Functions
Functional functions are the main part of the exported functions. They are responsible for actually performing the main functionality of the library.
Functional functions can be ordinary functions or opaque struct functions, depending on the needs of the function itself.