How to generalize Rust macro for different types of functions?

I have a macro that takes a list of function declarations and turns them into different declarations.

macro_rules! re_export { ($(pub fn $i:ident($($arg:ident: $argty:ty)*) -> $ret:ty;)*) => ($( extern { pub fn $i($($arg: $argty),*) -> $ret; } )*); ($(pub fn $i:ident($($arg:ident: $argty:ty)*);)*) => ($( extern { pub fn $i($($arg: $argty),*); } )*); } 

What is used as follows:

 re_export! { pub fn abs(i: c_int) -> c_int; pub fn rand() -> c_int; pub fn foo(); pub fn add(i: c_int, j: c_int) -> c_int; } 

How can I generalize a macro so that I can give it several functions with or without arguments and return types and make them work on all of them. It's easy to make a macro that works on several functions of the same type, but I can't figure out how to make it work for different types.

+7
macros rust
source share
1 answer

Well, there are two ways.

If you want to parse this exact syntax, you will need to use the muncher . So something like:

 macro_rules! re_export { () => {}; ( pub fn $i:ident($($arg:ident: $argty:ty)*) -> $ret:ty; $($tail:tt)* ) => { extern { pub fn $i($($arg: $argty),*) -> $ret; } re_export! { $($tail)* } }; ( pub fn $i:ident($($arg:ident: $argty:ty)*); $($tail:tt)* ) => { extern { pub fn $i($($arg: $argty),*); } re_export! { $($tail)* } }; } 

This includes disabling one function signature at a time, processing them recursively. This is the most flexible way to parse things, but that means you might run into a macro recursion limit. The default limit is 64, so if you have more input, you will need several macro top-level macros, or you will have to manually raise the recursion limit by adding the #![recursion_limit="128"] attribute to your inbox.

Another is to change the syntax so that you split, then process the signatures in two steps. To do this, you must have some kind of regular top-level syntax for signatures. For example:

 macro_rules! re_export { ($({$($sigs:tt)*})*) => { $( re_export! { @fn $($sigs)* } )* }; (@fn pub fn $i:ident($($arg:ident: $argty:ty),*) -> $ret:ty) => { extern { pub fn $i($($arg: $argty),*) -> $ret; } }; (@fn pub fn $i:ident($($arg:ident: $argty:ty),*)) => { extern { pub fn $i($($arg: $argty),*); } }; } 

Here we complete each function signature in {...} s. This is because accompanying groups ( (...) , [...] and {...} ) allow macro_rules! accurately track their contents without understanding them. This allows us to regularly match signatures of irregular functions. The top-level extension simply translates each individual function signature back into itself for real processing. @fn is only an internal rule marker to make sure that we select the correct rule during recursion.

This does not have the same recursive constraints as the previous one, but a slightly dumb syntax is required:

 re_export! { { pub fn abs(i: c_int) -> c_int } { pub fn rand() -> c_int } { pub fn foo() } { pub fn add(i: c_int, j: c_int) -> c_int } } 
+9
source share

All Articles