如何在不同类型的函数上推广Rust宏?

Mas*_*tax 8 macros rust

我有一个宏,它接受一个函数声明列表,并将它们转换为不同的声明.

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),*);
        }
    )*);
}
Run Code Online (Sandbox Code Playgroud)

使用方式如下:

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;
}
Run Code Online (Sandbox Code Playgroud)

我如何概括宏,以便我可以给它带有或不带args和返回类型的多个函数,让它适用于所有这些函数.很容易制作一个适用于同一类型的几个函数的宏,但我无法弄清楚如何使它适用于不同类型.

DK.*_*DK. 11

嗯,有两种方法.

如果要解析这种确切的语法,则需要使用muncher.所以,像:

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)* }
    };
}
Run Code Online (Sandbox Code Playgroud)

这涉及一次断开一个函数签名,递归处理它们.这是解析事物的最灵活方式,但确实意味着您可以遇到宏递归限制.默认限制为64,因此如果您有更多输入,则需要多个顶级宏调用,或者您必须通过向包中添加#![recursion_limit="128"]属性来手动提高递归限制.

另一种是更改语法,以便您拆分然后分两步处理签名.为此,您必须为签名提供某种常规顶级语法.例如:

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),*);
        }
    };
}
Run Code Online (Sandbox Code Playgroud)

在这里,我们将每个函数签名包装在{...}s中.这是因为匹配器组((...),[...]{...})允许macro_rules!盲目匹配其内容,而不必理解它们.这允许我们以常规方式匹配不规则函数签名.顶级扩展只是将每个单独的功能签名转发回自身以进行实际处理.这@fn只是一个内部规则标记,以确保我们在递归过程中选择正确的规则.

这与前一个没有相同的递归限制...但要求您使用稍微钝的语法:

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 }
}
Run Code Online (Sandbox Code Playgroud)