Макросы Rust: вызов функции зависит от выражения - PullRequest
0 голосов
/ 23 апреля 2019

У меня есть три разные функции, из которых я хочу вызвать одну на основе аргумента макроса.Этот аргумент должен быть предварительно обработан, поэтому я подумал, что мне нужно записать его как expr.Тем не менее, я не могу найти способ различать разные случаи для expr в макросе.Вот мой код:

fn func_100(){
    println!("Func 100!");
}
fn func_200(){
    println!("Func 200!");
}
fn func_300(){
    println!("Func 300!");
}

macro_rules! generate_func_call {
    (100) => {
        func_100();
    };
    (200) => {
        func_200();
    };
    (300) => {
        func_300();
    }
}

macro_rules! generate_func_call_wrapper {
    ($func: ident, $number: expr) => {
        fn $func(){
            println!("{:?}", $number / 100);
            generate_func_call!($number);
        }
    };
}

generate_func_call_wrapper!(f1,100);
generate_func_call_wrapper!(f2,200);
generate_func_call_wrapper!(f3,300);

fn main(){
    f1();
}

, который генерирует следующую ошибку времени компиляции:

    generate_func_call!($number);
                        ^^^^^^^ no rules expected this token in macro call

Как я могу исправить эту программу так, чтобы она вызывала другую функцию, основанную на выражении $number

Ответы [ 2 ]

2 голосов
/ 23 апреля 2019

Вы можете увидеть расширение макроса, набрав cargo +nightly rustc --profile=check -- -Zunstable-options --pretty=expanded или используя cargo-expand

fn f1() {
    {
        ::std::io::_print(::std::fmt::Arguments::new_v1(
            &["", "\n"],
            &match (&(100 / 100),) {
                (arg0,) => [::std::fmt::ArgumentV1::new(arg0, ::std::fmt::Debug::fmt)],
            },
        ));
    };
    ();
}

Вы можете увидеть последние ();, которые должны были быть func_100()

Это потому что нет правила токена в generate_func_call типа ($number: expr), то есть нет правила, соответствующего расширению. Это потому, что $number не заменяется на 100, как вы ожидаете от функции. Макрос просто создает больше кода ржавчины на основе типов фрагментов , которые он получает, он не пытается ничего оценить.

Измените код на:

macro_rules! generate_func_call {
    ($number: expr) => {
        match $number {
            100 => func_100(),
            200 => func_200(),
            300 => func_300(),
            _ => (),
        }
    };
}

И, наконец, (); меняется на:

match 300 {
    100 => func_100(),
    200 => func_200(),
    300 => func_300(),
    _ => (),
};

0 голосов
/ 23 апреля 2019

Похоже, это просто не поддерживается.

Simmilar Issue # 1 Simmilar Issue # 2

Возможный обходной путь - сопоставление с литералом в первом макросе, как на детской площадке . Хотя это просто вводит больше повторений кода.

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

Прямо сейчас я бы посоветовал просто сопоставить значение во время выполнения, используя обычный match.

...