#variables #cfg #macro

no-std cfgenius

Rust 中支持宏的条件编译

2 个版本

0.1.1 2024年4月22日
0.1.0 2023年5月28日

#491 in Rust 模式

每月 50 次下载
用于 3 个 crate (通过 jit-allocator)

MIT 许可证

20KB
233

CFGenius

一个宏,用于定义包含跨 crate 变量的条件编译 if-else 语句。

此宏类似于 cfg_if!——事实上非常相似,以至于我们将暂时抄袭其文档

此 crate 提供的宏 cond! 类似于 C 预处理器宏 if/elif,允许定义一系列 #[cfg] 情况,并输出匹配的第一个实现。

这允许你方便地提供一系列 #[cfg] 编译代码块,而无需多次重写每个条款。

示例

cfgenius::cond! {
    if cfg(unix) {
        fn foo() { /* unix specific functionality */ }
    } else if cfg(target_pointer_width = "32") {
        fn foo() { /* non-unix, 32-bit functionality */ }
    } else {
        fn foo() { /* fallback implementation */ }
    }
}

然而,新的功能是可以 define! 自定义条件编译变量,并在你的 cond! 命题中使用这些变量

// In `crate_1`...
cfgenius::define! {
    pub(super) is_32_bit_or_more = cfg(any(
        target_pointer_width = "32",
        target_pointer_width = "64",
    ));

    pub is_recommended = all(
        macro(is_32_bit_or_more),
        macro(is_supported),
        cfg(target_has_atomic),
    );
}

cfgenius::cond! {
    if all(cfg(windows), not(macro(is_32_bit_or_more))) {
        cfgenius::define!(pub is_supported = true());

        // windows-specific non-32-bit functionality
    } else if all(cfg(windows), macro(is_32_bit_or_more)) {
        cfgenius::define!(pub is_supported = true());

        // windows-specific non-32-bit functionality
    } else {
        cfgenius::define!(pub is_supported = false());
    }
}

pub const IS_SUPPORTED: bool = cfgenius::cond_expr!(macro(is_supported));

// In `crate_2`...
cfgenius::cond! {
    if any(
        macro(crate_1::is_recommended),
        all(cfg(feature = "force_crate_1_backend"), macro(crate_1::is_supported))
    ) {
        // (`crate_1` implementation)
    } else {
        // (fallback implementation)
    }
}

在常规的 #[cfg] 属性中无法实现

macro_rules! truthy {
    () => { all() };
}

#[cfg(truthy!())]
//          ^ Syntax Error: expected one of `(`, `,`, `::`, or `=`, found `!`
mod this_is_compiled {}

命题

在每个可能期望条件编译命题的地方,支持以下命题

  • true():始终为真

  • false():始终为假

  • cfg(<cfg 输入>):解析为具有相同输入的常规 cfg 属性 的结果。

  • not(<谓词>):否定提供的cfgenius谓词的解析。

  • all(<谓词 1>, <谓词 2>, ...):如果提供的所有cfgenius谓词都不失败,则解析为真。没有提供谓词的all()解析为真。

  • any(<谓词 1>, <谓词 2>, ...):如果提供的至少一个cfgenius谓词成功,则解析为真。没有提供谓词的any()解析为假。

  • macro(<宏的路径>):使用宏来决定谓词的真假。

  • macro(<宏的路径> => <宏参数>):使用提供的参数调用宏来决定谓词的真假。

自定义变量

大多数变量可以使用define!简洁地定义。然而,由于变量只是扩展以获得结果的宏,您可以通过遵循此协议定义自己的变量。

谓词macro(<宏的路径>)通过展开进行评估

path::to::macro! {
    yes { /* truthy tokens */ }
    no { /* falsy tokens */ }
}

...并且谓词macro(<宏的路径> => <宏参数>)通过展开进行评估

path::to::macro! {
    args { /* macro arguments */ }
    yes { /* truthy tokens */ }
    no { /* falsy tokens */ }
}

如果变量应该为真,则宏应展开为/* 真值标记 */以及更多。如果变量应该为假,则宏应展开为/* 假值标记 */以及更多。

这些宏应该在与环境无关的情况下是无效的和纯净的。您不应该依赖于该宏在谓词中出现一次时被评估一次,尽管这是当前的行为。

无运行时依赖