我们可以在过程宏属性中获取调用者的源代码位置吗? [英] Can we get the source code location of the caller in a procedural macro attribute?
问题描述
我需要获取每个方法的调用者的源位置.我正在尝试创建一个 proc_macro_attribute
来捕获位置并打印它.
I have requirement to get the source location of the caller of every method. I am trying to create a proc_macro_attribute
to capture the location and print it.
#[proc_macro_attribute]
pub fn get_location(attr: TokenStream, item: TokenStream) -> TokenStream {
// Get and print file!(), line!() of source
// Should print line no. 11
item
}
#[get_location]
fn add(x: u32, y: u32) -> u32 {
x + y
}
fn main() {
add(1, 5); // Line No. 11
}
推荐答案
可以使用现成的解决方案(请参阅 @timotree 的评论).如果你想自己做这件事,有更多的灵活性或学习,你可以编写一个程序宏来解析回溯(从被调用的函数内部获得)并打印你需要的信息.这是 lib.rs
中的一个过程宏:
Ready to use solutions are available (see @timotree 's comment). If you want to do this yourself, have more flexibility or learn, you can write a procedural macro that will parse a backtrace (obtained from inside the function that is called) and print the information that you need. Here is a procedural macro inside a lib.rs
:
extern crate proc_macro;
use proc_macro::{TokenStream, TokenTree};
#[proc_macro_attribute]
pub fn get_location(_attr: TokenStream, item: TokenStream) -> TokenStream {
// prefix code to be added to the function's body
let mut prefix: TokenStream = "
// find earliest symbol in source file using backtrace
let ps = Backtrace::new().frames().iter()
.flat_map(BacktraceFrame::symbols)
.skip_while(|s| s.filename()
.map(|p|!p.ends_with(file!())).unwrap_or(true))
.nth(1 as usize).unwrap();
println!(\"Called from {:?} at line {:?}\",
ps.filename().unwrap(), ps.lineno().unwrap());
".parse().unwrap(); // parse string into TokenStream
item.into_iter().map(|tt| { // edit input TokenStream
match tt {
TokenTree::Group(ref g) // match the function's body
if g.delimiter() == proc_macro::Delimiter::Brace => {
prefix.extend(g.stream()); // add parsed string
TokenTree::Group(proc_macro::Group::new(
proc_macro::Delimiter::Brace, prefix.clone()))
},
other => other, // else just forward TokenTree
}
}).collect()
}
分析回溯以找到源文件中最早的符号(使用另一个宏 file!()
检索).我们需要添加到函数中的代码定义在一个字符串中,然后将其解析为 TokenStream
并添加到函数体的开头.我们本可以在最后添加这个逻辑,但是返回一个没有分号的值将不再起作用.然后,您可以在 main.rs
中使用过程宏,如下所示:
The backtrace is parsed to find the earliest symbol inside the source file (retrieved using file!()
, another macro). The code we need to add to the function is defined in a string, that is then parsed as a TokenStream
and added at the beginning of the function's body. We could have added this logic at the end, but then returning a value without a semicolon wouldn't work anymore. You can then use the procedural macro in your main.rs
as follow:
extern crate backtrace;
use backtrace::{Backtrace, BacktraceFrame};
use mylib::get_location;
#[get_location]
fn add(x: u32, y: u32) -> u32 { x + y }
fn main() {
add(1, 41);
add(41, 1);
}
输出为:
> Called from "src/main.rs" at line 10
> Called from "src/main.rs" at line 11
不要忘记通过将这两行添加到您的 Cargo.toml
来指定您的 lib
crate 提供程序宏:
Don't forget to specify that your lib
crate is providing procedural macros by adding these two lines to your Cargo.toml
:
[lib]
proc-macro = true
这篇关于我们可以在过程宏属性中获取调用者的源代码位置吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!