#ctor #macro #proc-macro

无 std ctor-lite

在程序启动或关闭时运行代码

1 个不稳定版本

0.1.0 2024年5月4日

#3#ctor

Download history 154/week @ 2024-05-02 6/week @ 2024-05-09 1897/week @ 2024-05-23 5729/week @ 2024-05-30 14700/week @ 2024-06-06 23697/week @ 2024-06-13 20862/week @ 2024-06-20 20919/week @ 2024-06-27 14109/week @ 2024-07-04 17781/week @ 2024-07-11 18077/week @ 2024-07-18 16727/week @ 2024-07-25 14530/week @ 2024-08-01 16378/week @ 2024-08-08

68,391 每月下载量
15 个 crate 中使用(通过 tiny-xlib

MIT/Apache

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
    • 使用 ctordtor 宏设置的函数不能在其他 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代码假设程序启动前没有其他代码运行,程序关闭后也没有其他代码运行。具体来说,libstdmain函数之前设置了一些全局变量,并假设这些变量在其运行期间始终被设置。因此,调用使用这些变量的libstd函数将导致未定义的行为。

通常情况下,可以在这些函数中安全地调用corealloc中的函数。此外,libc中的函数应该可以自由调用,以及大多数包含在rustix中的函数。只有在理解它们包含的其他调用时,才应使用其他crate。

此外,ctordtor宏中运行的函数的顺序没有保证。

实现

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);
    }
}

无运行时依赖