How to recursively take the last argument of a macro?

泄露秘密 提交于 2020-01-06 16:25:07

问题


Using a simple recursive macro like the example below, its common to take the first argument, then glob the rest.

macro_rules! count_tts {
    () => {0usize};
    ($_head:tt $($tail:tt)*) => {1usize + count_tts!($($tail)*)};
}

Is there a way to recursively take the last argument?

This makes it possible to:

  • Handle the arguments in reverse.
  • Take all the previous arguments into account (count them for example, see related question)

Something like ($($head:tt)* $tail:tt) ... but this doesn't work.


回答1:


There is no "backtracking" in the macro parser, so no you can't do this directly with $($head:tt)* $tail:tt. But you can do it by reversing it yourself.

macro_rules! concat_reverse {
    ([] $($reversed:tt)*) => { 
        concat!($(stringify!($reversed)),*)  // base case
    };
    ([$first:tt $($rest:tt)*] $($reversed:tt)*) => { 
        concat_reverse!([$($rest)*] $first $($reversed)*)  // recursion
    };
}

fn main() {
    println!("{}", concat_reverse!([e d c b a]))
    // output: abcde
}

The macro trace looks like:

   concat_reverse!([e d c b a])
== concat_reverse!([d c b a] e)
== concat_reverse!([c b a] d e)
== concat_reverse!([b a] c d e)
== concat_reverse!([a] b c d e)
== concat_reverse!([] a b c d e)
== concat!(stringify!(a), stringify!(b), stringify!(c), stringify!(d), stringify!(e))

You could do some "map" and "reduce" operation (e.g. for counting) in the recursion phase.

Note that this method will eat your recursion depth, you may need to raise your #![recursion_limit="..."].



来源:https://stackoverflow.com/questions/42171483/how-to-recursively-take-the-last-argument-of-a-macro

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!