简体   繁体   English

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

[英]How do I generalize a Rust macro over 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),*);
        }
    )*);
}

Which is used like this: 使用方式如下:

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 the macro so that I can give it multiple functions with or without args and return types and have it work on all of them. 我如何概括宏,以便我可以给它带有或不带args和返回类型的多个函数,让它适用于所有这些函数。 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. 很容易制作一个适用于同一类型的几个函数的宏,但我无法弄清楚如何使它适用于不同类型。

Well, there are two ways. 嗯,有两种方法。

If you want to parse this exact syntax, then you'll need to use a muncher . 如果要解析这种确切的语法,则需要使用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 involves breaking off one function signature at a time, processing them recursively. 这涉及一次断开一个函数签名,递归处理它们。 This is the most flexible way of parsing things, but does mean that you can run up against the macro recursion limit. 这是解析事物的最灵活方式,但确实意味着您可以遇到宏递归限制。 The default limit is 64, so if you have more input than that, you'll need multiple top-level macro invocations, or you'll have to manually raise the recursion limit by adding a #![recursion_limit="128"] attribute to your crate. 默认限制为64,因此如果您有更多输入,则需要多个顶级宏调用,或者您必须通过添加#![recursion_limit="128"]属性来手动提高递归限制到你的箱子里。

The other 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 the 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 wrap each function signature in {...} s. 在这里,我们将每个函数签名包装在{...} s中。 This is because matcher groups ( (...) , [...] , and {...} ) allow macro_rules! 这是因为匹配器组( (...)[...]{...} )允许macro_rules! to match their contents blindly, without having to understand them. 盲目地匹配他们的内容,而不必理解他们。 This allows us to match the irregular function signatures in a regular fashion. 这允许我们以常规方式匹配不规则函数签名。 The top-level expansion simply forwards each individual function signature back to itself for actual processing. 顶级扩展只是将每个单独的功能签名转发回自身以进行实际处理。 The @fn is just an internal rule marker to make sure we select the correct rule during recursion. @fn只是一个内部规则标记,以确保我们在递归期间选择正确的规则。

This doesn't have the same recursion limits that the previous one does... but requires you to use a slightly obtuse syntax: 这与前一个没有相同的递归限制...但要求您使用稍微钝的语法:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM