Macros 如何在不同类型的函数上泛化Rust宏?

Macros 如何在不同类型的函数上泛化Rust宏?,macros,rust,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

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

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),*);
        }
    )*);
}
它是这样使用的:

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;
}

如何对宏进行泛化,使其具有或不具有参数和返回类型的多个函数,并使其能够处理所有这些函数。制作一个可以在同一类型的多个函数上工作的宏很容易,但我不知道如何使它在不同类型上工作。

嗯,有两种方法

如果您想解析这个确切的语法,那么您需要使用一个。比如说:

macro\u规则!再出口{
() => {};
(
发布fn$i:ident($($arg:ident:$argty:ty)*)->$ret:ty;
$($tail:tt)*
) => {
外行{
发布fn$i($($arg:$argty),*)->$ret;
}
再出口!{$($tail)*}
};
(
发布fn$i:ident($($arg:ident:$argty:ty)*);
$($tail:tt)*
) => {
外行{
发布fn$i($($arg:$argty),*);
}
再出口!{$($tail)*}
};
}
这涉及到一次中断一个函数签名,递归地处理它们。这是最灵活的解析方式,但确实意味着您可能会遇到宏递归限制。默认限制为64,因此,如果输入多于64,则需要多次顶级宏调用,或者必须通过添加
#手动提高递归限制![recursion\u 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),*);
        }
    };
}
这里,我们将每个函数签名包装在
{…}
s中。这是因为匹配器组(
(…)
[…]
{…}
)允许
宏规则盲目地匹配它们的内容,而不必理解它们。这允许我们以常规方式匹配不规则函数签名。顶级扩展只是将每个单独的函数签名转发回自身进行实际处理。
@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 }
}

我会根据你的用户名假设你写了你链接的那本书。非常好!我希望谷歌在我研究这个问题时能给我建议一下。