简体   繁体   English

macro_rules! macro采用字符串文字“...”,扩展为“...”和b“......”

[英]macro_rules! macro takes string literal “…”, expands to both “…” and b“…”

I would like to write a macro_rules! 我想写一个macro_rules! macro whose arguments are one ident and a list of string literals. 其参数是一个ident和一个字符串文字列表的宏。 The expansion of the macro needs to contain both the string literals, and the corresponding byte literals. 宏的扩展需要包含字符串文字和相应的字节文字。 The macro is for tests, and will only ever be used with strings containing only characters in the U+0000 ... U+007F range. 该宏用于测试,并且仅用于仅包含U + 0000 ... U + 007F范围内的字符的字符串。 It is OK if supplying anything other than string literals to the macro causes a compile-time error. 如果向宏提供除字符串文字之外的任何内容都会导致编译时错误。

If this isn't currently possible without resorting to procedural macros, just tell me so, don't bother actually writing the procedural macro ;-) 如果目前不能使用程序宏,那么请告诉我,不要打扰实际编写程序宏;-)

The desired invocation and expansion is like this: 所需的调用和扩展如下:

all_s! isalpha [ "abcdefghijklmnopqrstuvwxyz" /* , ... */ ];

=> =>

assert!(isalpha("abcdefghijklmnopqrstuvwxyz"));
assert!("abcdefghijklmnopqrstuvwxyz".chars().all(|b| isalpha(b));
assert!(isalpha(b"abcdefghijklmnopqrstuvwxyz"));
assert!(b"abcdefghijklmnopqrstuvwxyz".iter().all(|b| isalpha(b)));
/* ... */

This is as far as I've gotten: 这是我得到的:

macro_rules! all_s {
    ($what: ident, $( $str: tt ),* ) => {{
        $(
            assert!($what($str));
            assert!($str.chars().all(|b| $what(b));
            assert!($what(BYTE_LITERAL!($str)));
            assert!(BYTE_LITERAL!($str).iter().all(|b| $what(b)));
        )*
    }}
}

but I don't know what to put where it says BYTE_LITERAL! 但是我不知道在什么地方BYTE_LITERAL! , and also I'm getting error messages that suggest that I haven't written the match pattern correctly, eg "macro all_s! expects no ident argument, given 'isalpha'" when '$what:ident' is right there. ,而且我收到的错误消息表明我没有正确编写匹配模式,例如“宏all_s!期望没有ident参数,给'isalpha'”当'$ what:ident'就在那里。

I also don't know how to reject tt s that aren't string literals. 我也不知道如何拒绝不是字符串文字的tt

If you make use of the fact that the arguments are strings, then the typechecker will take care of producing errors. 如果你利用参数是字符串的事实,那么typechecker将负责产生错误。

macro_rules! all_s {
    ($what: ident, $( $str: tt ),* ) => {{
        $(
            assert!($what($str));
            assert!(str::chars($str).all(|c| char::is_alphabetic(c)));
            assert!(str::chars($str).all(|c| char::is_alphabetic(c)));
        )*
    }}
}

In your question code, you are trying to use the same isalpha with values of both char and u8 . 在你的问题代码中,你试图使用相同的isalphacharu8值。 You'll have to convert the char s to u8 s first: 你必须先将char转换为u8

assert!(str::chars($str)
            .map(|c| c as u8) // this is safe given the assumption you stated
            .all(|b| b < 0x7F));

You cannot convert a literal to another type of literal; 您无法将文字转换为其他类型的文字; it's just not how macros work. 它不是宏如何工作。

You can convert a &str to a &[u8] via str::as_bytes : 可以通过str::as_bytes&str转换为&str &[u8]

fn is_alpha<T>(_: T) -> bool { true }

macro_rules! all_s {
    ($what: ident, $str: tt) => {{
        assert!($what($str));
        assert!($str.chars().all(|b| $what(b)));
        assert!($what($str.as_bytes()));
        assert!($str.as_bytes().iter().all(|b| $what(b)));
    }};
}

fn main() {
    all_s!(is_alpha, "abcdefghijklmnopqrstuvwxyz");
}

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

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