1 个不稳定版本
0.1.0 | 2024年5月4日 |
---|
#3 在 #ctor
68,391 每月下载量
在 15 个 crate 中使用(通过 tiny-xlib)
16KB
184 行
ctor-lite
使用过程宏重写的 ctor
crate。
许可证
MIT/Apache2
lib.rs
:
使用过程宏重新实现的 ctor
crate。
在某些情况下,需要在程序的非常开始或非常结束时运行代码。此 crate 提供了一个宏,可用于在程序执行开始时运行代码,并提供一些额外功能。
相对于 ctor
的优势
- 完全无依赖,因为依赖于过程宏而不是 proc 宏。
- 支持与
ctor
crate 相同的所有用例。 - 支持与
ctor
crate 相同的所有平台。 - 修复了
ctor
API 中的几个问题,例如- 当使用时需要
unsafe
,请参阅下面的“安全性”部分。 - 全局变量必须为
Sync
。 - 全局变量使用
MaybeUninit
而不是Option
。 - 使用
ctor
或dtor
宏设置的函数不能在其他 Rust 代码中调用。
- 当使用时需要
缺点
- API 形式略有不同,在某些情况下可能不便。
- MSRV 提高到 1.36.0。
功能使用
ctor
宏可用于在程序启动时运行一个函数。
use std::sync::atomic::{AtomicUsize, Ordering};
static INITIALIZED: AtomicUsize = AtomicUsize::new(0);
ctor_lite::ctor! {
unsafe fn set_value() {
INITIALIZED.store(1, Ordering::Relaxed);
}
}
assert_eq!(INITIALIZED.load(Ordering::Relaxed), 1);
请注意,此宏是一个过程块而不是属性宏。如果您更喜欢使用宏的老方法,您可以使用 macro-rules-attribute
crate。
use macro_rules_attribute::apply;
use std::sync::atomic::{AtomicUsize, Ordering};
static INITIALIZED: AtomicUsize = AtomicUsize::new(0);
#[apply(ctor_lite::ctor!)]
unsafe fn set_value() {
INITIALIZED.store(1, Ordering::Relaxed);
}
assert_eq!(INITIALIZED.load(Ordering::Relaxed), 1);
静态使用
ctor
宏可用于创建初始化为默认值的静态变量。在启动时,使用函数初始化静态变量。
fn value() -> i32 {
6
}
ctor_lite::ctor! {
unsafe static VALUE: i32 = value();
}
assert_eq!(*VALUE, 6);
析构函数
此 crate 还可用于在程序退出时运行函数。可以使用 dtor
宏在程序结束时运行函数。
use macro_rules_attribute::apply;
#[apply(ctor_lite::dtor!)]
unsafe fn run_at_exit() {
do_some_cleanup();
}
安全性
本仓库中的宏必须谨慎使用。通常情况下,Rust代码假设程序启动前没有其他代码运行,程序关闭后也没有其他代码运行。具体来说,libstd
在main
函数之前设置了一些全局变量,并假设这些变量在其运行期间始终被设置。因此,调用使用这些变量的libstd
函数将导致未定义的行为。
通常情况下,可以在这些函数中安全地调用core
或alloc
中的函数。此外,libc
中的函数应该可以自由调用,以及大多数包含在rustix
中的函数。只有在理解它们包含的其他调用时,才应使用其他crate。
此外,ctor
或dtor
宏中运行的函数的顺序没有保证。
实现
ctor
宏通过创建一个具有链接器属性的函数来实现,该属性将其放置在文件中的特殊部分。当C运行时启动程序时,它会从该部分读取函数指针并运行它们。
这个函数调用...
ctor_lite::ctor! {
unsafe fn foo() { /* ... */ }
}
...被转换成类似以下的代码
#[used]
#[cfg_attr(any(target_os = "linux", target_os = "android"), link_section = ".init_array")]
#[cfg_attr(target_os = "freebsd", link_section = ".init_array")]
#[cfg_attr(target_os = "netbsd", link_section = ".init_array")]
#[cfg_attr(target_os = "openbsd", link_section = ".init_array")]
#[cfg_attr(target_os = "illumos", link_section = ".init_array")]
#[cfg_attr(any(target_os = "macos", target_os = "ios", target_os = "tvos"), link_section = "__DATA_CONST,__mod_init_func")]
#[cfg_attr(target_os = "windows", link_section = ".CRT$XCU")]
static FOO: extern fn() = {
#[cfg_attr(any(target_os = "linux", target_os = "android"), link_section = ".text.startup")]
extern fn foo() { /* ... */ };
foo
};
使用ctor
宏创建全局常量时,它会写入运行函数然后将值写入全局常量的代码。
这段代码...
ctor_lite::ctor! {
unsafe static FOO: i32 = foo();
}
...被转换成类似以下的代码,并通过修改允许从安全代码中使用FOO
static mut FOO: i32 = core::mem::uninitialized();
ctor_lite::ctor! {
unsafe fn init_storage() {
FOO = foo();
}
}
当函数被放入dtor
时,它会使用libc::atexit
函数运行ctor
,以确保在程序退出时运行该函数。
这段代码...
ctor_lite::dtor! {
unsafe fn foo() {
/* ... */
}
}
...被转换成类似以下的代码,并通过修改让我们避免对libc
仓库的依赖
unsafe fn foo() {
/* ... */
}
ctor_lite::ctor! {
unsafe fn run_dtor() {
libc::atexit(foo);
}
}