我正在编写一个宏,它创建一个管理用户输入的结构。我现在用的包装箱bitflags和SDL2。Return是 key 的一个例子Return。
此宏获取所有可能输入的列表,然后
($($flag:ident = $value:expr;)+) => { ... }
Run Code Online (Sandbox Code Playgroud)
使用输入的名称创建一个新的位标志
bitflags!(
struct KeyType: u64 {
$(
const $flag = $value;// UPPER_CASE is the norm for globals: 'RETURN'
)+
}
);
Run Code Online (Sandbox Code Playgroud)使用Keycode枚举检查键是否被按下。
match event {
$(Event::KeyDown { keycode: Some(Keycode::$flag), .. } => {
self.flags.insert($flag);
},)+
_ => ()
}// All enum fields start with a capital letter: 'Return'
Run Code Online (Sandbox Code Playgroud)创建一个 getter 函数:
$(
pub fn $flag(&self) -> bool { // lower_case is the norm for functions: 'return'
self.flags.contains($flag)
}
)+
Run Code Online (Sandbox Code Playgroud)我$flag可以适应所有三个要求 ( flag, Flag, FLAG) 吗?
dto*_*nay 15
该paste板条箱有助于在 Macro_rules 宏中进行标识符大小写转换,用于[<$ident:lower>]小写和[<$ident:upper>]大写。
像这样的东西:
// [dependencies]
// paste = "0.1"
macro_rules! sleeping_panda {
($($flag:ident = $value:expr;)+) => {
paste::item! {
bitflags::bitflags! (
struct KeyType: u64 {
$(
const [<$flag:upper>] = $value;
)*
}
);
pub struct SleepingPanda {
flags: KeyType,
}
impl SleepingPanda {
pub fn receive_event(&mut self, event: sdl2::event::Event) {
match event {
$(
sdl2::event::Event::KeyDown {
keycode: Some(sdl2::keyboard::Keycode::$flag),
..
} => {
self.flags.insert(KeyType::[<$flag:upper>]);
}
)*
_ => {}
}
}
$(
pub fn [<$flag:lower>](&self) -> bool {
self.flags.contains(KeyType::[<$flag:upper>])
}
)*
}
}
};
}
sleeping_panda! {
Backspace = 8;
Tab = 9;
}
Run Code Online (Sandbox Code Playgroud)
Macro_rules 宏无法做到这一点。您需要将它实现为一个过程宏,它允许执行任意 Rust 代码以生成扩展代码。特别是,程序宏能够调用str::to_uppercase和str::to_lowercase。
// [dependencies]
// quote = "1.0"
// syn = "1.0"
use proc_macro::TokenStream;
use quote::quote;
use syn::parse::{Parse, ParseStream, Result};
use syn::{parse_macro_input, Expr, Ident, Token};
struct Input {
flags: Vec<Ident>,
values: Vec<Expr>,
}
// $( $flag:ident = $value:expr; )*
impl Parse for Input {
fn parse(input: ParseStream) -> Result<Self> {
let mut flags = Vec::new();
let mut values = Vec::new();
while !input.is_empty() {
flags.push(input.parse()?);
input.parse::<Token![=]>()?;
values.push(input.parse()?);
input.parse::<Token![;]>()?;
}
Ok(Input { flags, values })
}
}
#[proc_macro]
pub fn sleeping_panda(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as Input);
let camelcase_flags = &input.flags; // assume CamelCase in the input
let bitflag_values = &input.values;
let uppercase_flags = input
.flags
.iter()
.map(|ident| Ident::new(&ident.to_string().to_uppercase(), ident.span()));
// Some copies because these need to appear multiple times in the generated code.
let uppercase_flags2 = uppercase_flags.clone();
let uppercase_flags3 = uppercase_flags.clone();
let lowercase_flags = input
.flags
.iter()
.map(|ident| Ident::new(&ident.to_string().to_lowercase(), ident.span()));
TokenStream::from(quote! {
bitflags::bitflags! (
struct KeyType: u64 {
#(
const #uppercase_flags = #bitflag_values;
)*
}
);
pub struct SleepingPanda {
flags: KeyType,
}
impl SleepingPanda {
pub fn receive_event(&mut self, event: sdl2::event::Event) {
match event {
#(
sdl2::event::Event::KeyDown {
keycode: Some(sdl2::keyboard::Keycode::#camelcase_flags),
..
} => {
self.flags.insert(KeyType::#uppercase_flags2);
}
)*
_ => {}
}
}
#(
pub fn #lowercase_flags(&self) -> bool {
self.flags.contains(KeyType::#uppercase_flags3)
}
)*
}
})
}
Run Code Online (Sandbox Code Playgroud)
使用宏:
use sleeping_panda::sleeping_panda;
sleeping_panda! {
Backspace = 8;
Tab = 9;
}
fn main() {}
Run Code Online (Sandbox Code Playgroud)