Могу ли я создать свои собственные атрибуты условной компиляции? - PullRequest
2 голосов
/ 18 февраля 2020

Есть несколько способов сделать что-то в моем ящике, некоторые из которых приводят к быстрому выполнению, некоторые с низким двоичным размером, некоторые имеют другие преимущества, поэтому я предоставляю пользовательские интерфейсы для всех из них. Неиспользуемые функции будут оптимизированы компилятором. Внутренние функции в моей корзине также должны использовать эти интерфейсы, и я хотел бы, чтобы они уважали выбор пользователя во время компиляции.

Существуют атрибуты условной компиляции, такие как target_os, которые хранят значение, подобное * 1004. * или windows. Как я могу создать такой атрибут, например prefer_method, чтобы я и пользователь могли использовать его, как в следующих фрагментах кода?

Мой ящик:

#[cfg(not(any(
    not(prefer_method),
    prefer_method = "fast",
    prefer_method = "small"
)))]
compile_error("invalid `prefer_method` value");

pub fn bla() {
    #[cfg(prefer_method = "fast")]
    foo_fast();

    #[cfg(prefer_method = "small")]
    foo_small();

    #[cfg(not(prefer_method))]
    foo_default();
}

pub fn foo_fast() {
    // Fast execution.
}

pub fn foo_small() {
    // Small binary file.
}

pub fn foo_default() {
    // Medium size, medium fast.
}

Пользователь crate:

#[prefer_method = "small"]
extern crate my_crate;

fn f() {
    // Uses the `foo_small` function, the other `foo_*` functions will not end up in the binary.
    my_crate::bla();

    // But the user can also call any function, which of course will also end up in the binary.
    my_crate::foo_default();
}

Я знаю, что есть атрибуты --cfg, но AFAIK они представляют только логические флаги, а не значения перечисления, которые позволяют устанавливать несколько флагов, когда допустимо только одно значение перечисления.

1 Ответ

3 голосов
/ 19 февраля 2020

Во-первых, флаг --cfg поддерживает пары ключ-значение с использованием синтаксиса --cfg 'prefer_method="fast"'. Это позволит вам написать код вроде:

#[cfg(prefer_method = "fast")]
fn foo_fast() { }

Вы также можете установить эти параметры cfg из скрипта сборки . Например:

// build.rs
fn main() {
    println!("cargo:rustc-cfg=prefer_method=\"method_a\"");
}
// src/main.rs
#[cfg(prefer_method = "method_a")]
fn main() {
    println!("It's A");
}

#[cfg(prefer_method = "method_b")]
fn main() {
    println!("It's B");
}

#[cfg(not(any(prefer_method = "method_a", prefer_method = "method_b")))]
fn main() {
    println!("No preferred method");
}

Приведенный выше код приведет к выполнению исполняемого файла, который напечатает «Это А».

Нет синтаксиса, подобного тому, который вы предлагаете указать в настройках cfg. Лучше всего представить эти параметры пользователям ваших ящиков через Автомобиль go Функции .

Например:

# Library Cargo.toml
# ...
[features]
method_a = []
method_b = []
// build.rs
fn main() {
    // prefer method A if both method A and B are selected
    if cfg!(feature = "method_a") {
        println!("cargo:rustc-cfg=prefer_method=\"method_a\"");
    } else if cfg!(feature = "method_b") {
        println!("cargo:rustc-cfg=prefer_method=\"method_b\"");
    }
}
# User Cargo.toml
# ...
[dependencies.my_crate]
version = "..."
features = ["method_a"]

Однако в этом случае я бы рекомендовал просто использовать функции Car go непосредственно в вашем коде (например, #[cfg(feature = "fast")]), а не добавлять сценарий сборки, так как между компонентами car go имеется однозначное соответствие. и добавляется ржавчина c -cfg.

...