# hictor ## 0.1.5 提供三个辅助函数获取命令行参数: ```rust 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的差异在于: 1. 声明宏; ctor是过程宏实现. 2. 只支持函数; ctor还支持静态变量. 3. `ctor_args, ctor_custom`支持获取命令行输入参数; ctor未支持. 4. dtor完全等同__attribute__((destructor)),windows等平台不支持; ctor封装了atexit. 无论如何,业务代码不能依赖多个初始化函数的调用顺序,其无法保证. 注:仅在linux平台下测试通过. ## __attribute__((constructor)) 不关心命令行输入参数,则可以采用无参数的函数实现. 类型必须是`unsafe fn()`. ```rust unsafe fn init() { println!("init before main"); } hictor::ctor!(init); ``` 如果需要获取命令行输入,函数类型必须是`unsafe extern "C" fn(i32, *const *const i8)` ```rust 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)`. ```rust #[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(). 注意业务不应该依赖这个机制,因为应用程序可能因多种未知因素异常退出,这些函数都不会被调用到. 一个高可用性的软件应该为这种无法绝对避免的场景做准备,比如启动时做恢复检查或者额外的监控和容错处理程序等. ```rust unsafe fn fini() { println!("fini after main"); } hictor::dtor!(fini); ``` ## 获取命令行输入 如果不在main函数中,可以获取当前应用程序的命令行输入参数. ```rust fn foo() { let args: &'static [*const i8] = hictor::args(); } ``` ## 说明 其实现原理是定义一个全局变量,初始化为指定的函数指针,并指定在ELF的特殊段(linux下是`.init_array`)中. 需要定义一个静态变量实现这个功能. 但声明宏中无法同C的宏,组合行号等其他信息来生成一个变量名(`concat_idents`宏仅在nightly版本可用), 为了避免静态变量的重复定义,宏的实现中 利用函数名的唯一性定义了一个子模块,如果刚好有一个和函数同名的模块存在,则会存在冲突,这种情况下需要你指定一个唯一的模块名. ```rust mod init { pub(super) unsafe fn init() { println!("init before main"); } } use init::init; hictor::ctor!(init_mod, init); ```