передать аргументы макроса другому макросу - PullRequest
2 голосов
/ 01 августа 2020

Я новичок в ржавчине. Я пытаюсь создать макрос, который принимает буфер, а затем декодирует из него некоторые данные и создает список переменных. если возникает ошибка, он должен напечатать ошибку и продолжить, так как я буду вызывать ее в al oop, где я получаю буферы. примерно так: -

for bin_ref in bufs {

   extract!( bin_ref anime &str episodes u32 season u32);

   //if everything goes ok then do some cool stuff with
   //above variables otherwise take next buf_ref    
}

Как мне это сделать? Итак, я пришел с этим подходом: -

#[macro_export]
macro_rules! extract {

    ( $buf:ident $($var:ident $typ:ty),* ) => {
        $(
            ext_type!( $buf $var $typ );
        )*
    };
}

#[macro_export]
macro_rules! ext_type {
    ( $buf:ident $var:ident &str ) => {

        let mut $var : &str = ""; //some string specific function
        println!("doing cool things with '{}' which is string ",$var);        

    };
    ( $buf:ident $var:ident u32 ) => {
        let mut $var : u32 = 34; //some u32 specific function
        println!("doing cool things with '{}' which is u32",$var);
    }
}

У меня есть следующая тестовая функция: -

fn macro_test() {

    let mut bin_ref : &[u8] = &[0u8;100];

    ext_type!(bin_ref anime &str); // works
    ext_type!(bin_ref episodes u32 ); // works

    extract!( bin_ref username &str, password &str ); // does not work. why ??
}

Когда я компилирую это, я получаю следующую ошибку: -

error: no rules expected the token `&str`
  --> src/easycode.rs:11:34
   |
11 |             ext_type!( $buf $var $typ );
   |                                  ^^^^ no rules expected this token in macro call
...
19 | macro_rules! ext_type {
   | --------------------- when calling this macro
...
48 |     extract!( bin_ref username &str, password &str );
   |     ------------------------------------------------- in this macro invocation

Почему я не могу просто передать макрос $typ в ext_type!? работает при вызове с кода

1 Ответ

4 голосов
/ 01 августа 2020

Правила макроса ext_type! требуют наличия буквальных маркеров &str и u32 в конце. Эти буквальные токены не могут соответствовать совпавшему фрагменту $typ:ty в extract!. Чтобы успешно сопоставить буквальные токены с сопоставленным фрагментом, он должен быть tt, ident или lifetime.

Единственный вариант, который будет работать в этом случае is tt, что проще говоря, это просто токен парсера. Однако тип часто состоит из более чем одного токена; в пункте &str, который состоит из двух токенов & и str. Таким образом, мы должны использовать повторение, чтобы полностью захватить тип с tt s: $($typ:tt)+ подойдет.

Использование неограниченного повторения с tt требует затрат, однако - tt будет соответствовать почти всему, поэтому простая замена $typ:ty на $($typ:tt)+ не сработает, поскольку повторение $typ захватит все до конца вызова макроса! Чтобы этого не произошло, мы должны разделить дерево токенов типов в сопоставлении правил макросов, чтобы оно не потребляло все. Ценой того, что вызов становится немного более подробным, заключение повторения в круглые скобки сослужит нам хорошую службу и остановит соответствие дерева токенов именно там, где мы хотим. Измененный макрос выглядит следующим образом:

#[macro_export]
macro_rules! extract {
    ( $buf:ident $($var:ident ($($typ:tt)+)),* ) => {
        $(
            ext_type!( $buf $var $($typ)+);
        )*
    };
}

Обратите внимание на замену $typ:ty на ($($typ:tt)+) (которое представляет собой повторение дерева токенов, заключенное в круглые скобки) в сопоставлении и замену $typ на $($typ)+ в транскрибере.

Макроправило вызывается следующим образом:

extract!(bin_ref username (&str), password (&str), id (u32));

Rust Playground

...