7 个版本
0.1.6 | 2023年11月17日 |
---|---|
0.1.5 | 2023年11月17日 |
0.1.4 | 2023年10月15日 |
#1 in #destructor
被 3 个 crate 使用
13KB
159 代码行数(不包括注释)
hictor
0.1.5
提供三个辅助函数来获取命令行参数
fn args() -> &'static [*const u8];
fn program_invocation_name() -> &'static str;
fn program_invocation_short_name() -> &'static str;
功能介绍
基于声明宏实现,支持 gcc 的 __attribute__((constructor)) 和 __attribute__((destructor)) 功能。
与 ctor crate 的不同之处在于
- 声明宏;ctor 是过程宏实现。
- 只支持函数;ctor 还支持静态变量。
ctor_args, ctor_custom
支持获取命令行输入参数;ctor 未支持。- dtor 完全等同于 __attribute__((destructor)),Windows 等平台不支持;ctor 封装了 atexit。
无论如何,业务代码不能依赖于多个初始化函数的调用顺序,这无法保证。
注:仅在 Linux 平台下进行过测试。
attribute((constructor))
如果不需要关心命令行输入参数,则可以使用无参数的函数实现。函数类型必须是 unsafe fn()
。
unsafe fn init() {
println!("init before main");
}
hictor::ctor!(init);
如果需要获取命令行输入,函数类型必须是 unsafe extern "C" fn(i32, *const *const i8)
unsafe extern "C" fn init(argc: i32, argv: *const *const i8) {
println!("argc = {argc}, program = {:?}", std::ffi::CStr::from_ptr(*argv));
}
hictor::ctor_args!(init);
某些平台下支持更多的参数,例如 Linux 平台下还可以获取环境变量,其函数类型为 unsafe extern "C" fn(u32, *const *const u8, *const *const u8)
。
#[cfg(target_os = "linux")]
unsafe extern "C" fn init(argc: i32, argv: *const *const i8, envs: *const *const i8) {
println!("argc = {argc}, program = {:?} env.0 = {:?}", std::ffi::CStr::from_ptr(*argv), std::ffi::CStr::from_ptr(*envs));
}
#[cfg(target_os = "linux")]
hictor::ctor_custom!(init);
attribute((destructor))
函数类型必须是 unsafe fn(). 业务不应该依赖于这个机制,因为应用程序可能因多种未知因素异常退出,这些函数可能不会被调用。高可用性软件应该为这种无法绝对避免的场景做准备,例如启动时进行恢复检查或额外的监控和容错处理程序等。
unsafe fn fini() {
println!("fini after main");
}
hictor::dtor!(fini);
获取命令行输入
如果不在 main 函数中,可以获取当前应用程序的命令行输入参数。
fn foo() {
let args: &'static [*const i8] = hictor::args();
}
说明
其实现原理是定义一个全局变量,初始化为指定的函数指针,并指定在 ELF 的特殊段(Linux 下是 .init_array
)中。需要定义一个静态变量来实现这个功能。但是声明宏中无法像 C 的宏那样,通过组合行号等其他信息来生成一个变量名(concat_idents
宏仅在 nightly 版本可用),为了避免静态变量的重复定义,宏的实现中利用函数名的唯一性定义了一个子模块。如果恰好有一个和函数同名的模块存在,则会出现冲突。在这种情况下,需要指定一个唯一的模块名。
mod init {
pub(super) unsafe fn init() {
println!("init before main");
}
}
use init::init;
hictor::ctor!(init_mod, init);