3

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.

Community
  • 1
  • 1
ideasman42
  • 42,413
  • 44
  • 197
  • 320

1 Answers1

10

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="..."].

kennytm
  • 510,854
  • 105
  • 1,084
  • 1,005
  • Thanks works perfectly, used this to answer my own question here: http://stackoverflow.com/a/42176533/432509 . The `apply_args_reverse` macro might be useful to others, since it allows reversing arguments for existing macros. – ideasman42 Feb 11 '17 at 13:46