我正在编写一个宏来创建一个管理用户输入的结构。我正在使用包bitflags和sdl2。 返回是键返回的示例。
此宏列出所有可能的输入,然后
($($flag:ident = $value:expr;)+) => { ... }
使用输入名称
创建一个新的位标志bitflags!(
struct KeyType: u64 {
$(
const $flag = $value;// UPPER_CASE is the norm for globals: 'RETURN'
)+
}
);
使用Keycode
枚举检查是否按下了键。
match event {
$(Event::KeyDown { keycode: Some(Keycode::$flag), .. } => {
self.flags.insert($flag);
},)+
_ => ()
}// All enum fields start with a capital letter: 'Return'
创建一个getter函数:
$(
pub fn $flag(&self) -> bool { // lower_case is the norm for functions: 'return'
self.flags.contains($flag)
}
)+
我可以调整$flag
以满足所有三个要求(flag
,Flag
,FLAG
)吗?
答案 0 :(得分:3)
Macro_rules宏无法执行此操作。您可能需要将其实现为程序宏,该宏可以执行任意Rust代码以生成扩展代码。特别是,程序宏可以调用str::to_uppercase
和str::to_lowercase
。
// [dependencies]
// quote = "0.6"
// syn = "0.15"
extern crate proc_macro;
use self::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;
)*
}
);
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)
}
)*
}
})
}
使用宏:
use sleeping_panda::sleeping_panda;
sleeping_panda! {
Backspace = 8;
Tab = 9;
}
fn main() {}
答案 1 :(得分:3)
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;
}