すべてのメソッドの呼び出し元のソースの場所を取得する必要があります。proc_macro_attribute
場所をキャプチャして印刷するためのを作成しようとしています。
#[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
:内の手続き型マクロです。
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!()
別のマクロを使用して取得)。関数に追加する必要のあるコードは文字列で定義され、文字列はaとして解析されTokenStream
、関数の本体の先頭に追加されます。このロジックを最後に追加することもできますが、セミコロンなしで値を返すことはできなくなります。次に、次のようにで手続き型マクロを使用できmain.rs
ます。
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
lib
次の2行を次の行に追加して、クレートが手続き型マクロを提供していることを指定することを忘れないでくださいCargo.toml
。
[lib]
proc-macro = true
TL; DR
ここで使用している手続きのマクロであるsyn
とquote
あなたが説明してきた何をすべきかは:
// print_caller_location/src/lib.rs
use proc_macro::TokenStream;
use quote::quote;
use syn::spanned::Spanned;
// Create a procedural attribute macro
//
// Notably, this must be placed alone in its own crate
#[proc_macro_attribute]
pub fn print_caller_location(_attr: TokenStream, item: TokenStream) -> TokenStream {
// Parse the passed item as a function
let func = syn::parse_macro_input!(item as syn::ItemFn);
// Break the function down into its parts
let syn::ItemFn {
attrs,
vis,
sig,
block,
} = func;
// Ensure that it isn't an `async fn`
if let Some(async_token) = sig.asyncness {
// Error out if so
let error = syn::Error::new(
async_token.span(),
"async functions do not support caller tracking functionality
help: consider returning `impl Future` instead",
);
return TokenStream::from(error.to_compile_error());
}
// Wrap body in a closure only if function doesn't already have #[track_caller]
let block = if attrs.iter().any(|attr| attr.path.is_ident("track_caller")) {
quote! { #block }
} else {
quote! {
(move || #block)()
}
};
// Extract function name for prettier output
let name = format!("{}", sig.ident);
// Generate the output, adding `#[track_caller]` as well as a `println!`
let output = quote! {
#[track_caller]
#(#attrs)*
#vis #sig {
println!(
"entering `fn {}`: called from `{}`",
#name,
::core::panic::Location::caller()
);
#block
}
};
// Convert the output from a `proc_macro2::TokenStream` to a `proc_macro::TokenStream`
TokenStream::from(output)
}
必ずそれを木枠に入れて、これらの行をその上に追加してくださいCargo.toml
:
# print_caller_location/Cargo.toml
[lib]
proc-macro = true
[dependencies]
syn = {version = "1.0.16", features = ["full"]}
quote = "1.0.3"
proc-macro2 = "1.0.9"
マクロは、最初から手作業で記述できるコードにのみ拡張できます。これを知っていると、ここに2つの質問があります。
簡単な答え:関数が呼び出される場所を取得するには、関数にマークを付けて本体で
#[track_caller]
使用std::panic::Location::caller
します。
手続き型マクロが必要です
#[track_caller]
、Location::caller
。たとえば、次のような関数を変換します。
fn foo() {
// body of foo
}
に
#[track_caller]
fn foo() {
println!("{}", std::panic::Location::caller());
// body of foo
}
以下に、その変換を正確に実行する手続き型マクロを示します。ただし、後のバージョンで見られるように、おそらく別のものが必要になるでしょう。このコードを試すには、以前のTL; DRセクションのように、コードを独自のクレートに入れ、依存関係をに追加しCargo.toml
ます。
// print_caller_location/src/lib.rs
use proc_macro::TokenStream;
use quote::quote;
// Create a procedural attribute macro
//
// Notably, this must be placed alone in its own crate
#[proc_macro_attribute]
pub fn print_caller_location(_attr: TokenStream, item: TokenStream) -> TokenStream {
// Parse the passed item as a function
let func = syn::parse_macro_input!(item as syn::ItemFn);
// Break the function down into its parts
let syn::ItemFn {
attrs,
vis,
sig,
block,
} = func;
// Extract function name for prettier output
let name = format!("{}", sig.ident);
// Generate the output, adding `#[track_caller]` as well as a `println!`
let output = quote! {
#[track_caller]
#(#attrs)*
#vis #sig {
println!(
"entering `fn {}`: called from `{}`",
#name,
::core::panic::Location::caller()
);
#block
}
};
// Convert the output from a `proc_macro2::TokenStream` to a `proc_macro::TokenStream`
TokenStream::from(output)
}
使用例:
// example1/src/main.rs
#![feature(track_caller)]
#[print_caller_location::print_caller_location]
fn add(x: u32, y: u32) -> u32 {
x + y
}
fn main() {
add(1, 5); // entering `fn add`: called from `example1/src/main.rs:11:5`
add(1, 5); // entering `fn add`: called from `example1/src/main.rs:12:5`
}
残念ながら、その単純なバージョンで逃げることはできません。そのバージョンには少なくとも2つの問題があります。
async fn
sでどのように構成するか:
#[print_caller_location]
)が呼び出された場所を出力します。例えば:// example2/src/main.rs
#![feature(track_caller)]
#[print_caller_location::print_caller_location]
async fn foo() {}
fn main() {
let future = foo();
// ^ oops! prints nothing
futures::executor::block_on(future);
// ^ oops! prints "entering `fn foo`: called from `example2/src/main.rs:5:1`"
let future = foo();
// ^ oops! prints nothing
futures::executor::block_on(future);
// ^ oops! prints "entering `fn foo`: called from `example2/src/main.rs:5:1`"
}
それ自体の他の呼び出し、または一般的には、どのように機能するか#[track_caller]
:
#[print_caller_location]
は、特定の関数の直接の呼び出し元ではなく、ルート呼び出し元の場所を出力します。例えば:// example3/src/main.rs
#![feature(track_caller)]
#[print_caller_location::print_caller_location]
fn add(x: u32, y: u32) -> u32 {
x + y
}
#[print_caller_location::print_caller_location]
fn add_outer(x: u32, y: u32) -> u32 {
add(x, y)
// ^ we would expect "entering `fn add`: called from `example3/src/main.rs:12:5`"
}
fn main() {
add(1, 5);
// ^ "entering `fn add`: called from `example3/src/main.rs:17:5`"
add(1, 5);
// ^ "entering `fn add`: called from `example3/src/main.rs:19:5`"
add_outer(1, 5);
// ^ "entering `fn add_outer`: called from `example3/src/main.rs:21:5`"
// ^ oops! "entering `fn add`: called from `example3/src/main.rs:21:5`"
//
// In reality, `add` was called on line 12, from within the body of `add_outer`
add_outer(1, 5);
// ^ "entering `fn add_outer`: called from `example3/src/main.rs:26:5`"
// oops! ^ entering `fn add`: called from `example3/src/main.rs:26:5`
//
// In reality, `add` was called on line 12, from within the body of `add_outer`
}
async fn
秒async fn
を使用してsの問題を回避することは可能-> impl Future
です。たとえば、async fn
反例を正しく機能させたい場合は、代わりに次のように記述できます。
// example4/src/main.rs
#![feature(track_caller)]
use std::future::Future;
#[print_caller_location::print_caller_location]
fn foo() -> impl Future<Output = ()> {
async move {
// body of foo
}
}
fn main() {
let future = foo();
// ^ prints "entering `fn foo`: called from `example4/src/main.rs:15:18`"
futures::executor::block_on(future);
// ^ prints nothing
let future = foo();
// ^ prints "entering `fn foo`: called from `example4/src/main.rs:19:18`"
futures::executor::block_on(future);
// ^ prints nothing
}
この変換をマクロに適用する特別なケースを追加できます。ただし、その変換により、関数のパブリックAPIがからに変更async fn foo()
さfn foo() -> impl Future<Output = ()>
れるだけでなく、返されるfutureが持つことができる自動特性にも影響します。
したがって、ユーザーが必要に応じてその回避策を使用できるようにし、マクロがで使用されている場合は単にエラーを発行することをお勧めしますasync fn
。これを行うには、次の行をマクロコードに追加します。
// Ensure that it isn't an `async fn`
if let Some(async_token) = sig.asyncness {
// Error out if so
let error = syn::Error::new(
async_token.span(),
"async functions do not support caller tracking functionality
help: consider returning `impl Future` instead",
);
return TokenStream::from(error.to_compile_error());
}
#[print_caller_location]
関数のネストされた動作の修正この事実にダウン問題行動の最小化:#[track_caller]
機能、foo
直接別に呼び出して#[track_caller]
、機能bar
、Location::caller
にそれらのアクセスの両方与えるfoo
の発信者を。言い換えると、Location::caller
ネストされた#[track_caller]
関数の場合、ルート呼び出し元へのアクセスを提供します。
#![feature(track_caller)]
fn main() {
foo(); // prints `src/main.rs:4:5` instead of the line number in `foo`
}
#[track_caller]
fn foo() {
bar();
}
#[track_caller]
fn bar() {
println!("{}", std::panic::Location::caller());
}
遊び場リンク
これを改善するには、#[track_caller]
呼び出しのチェーンを切断する必要があります。bar
クロージャ内にネストされた呼び出しを非表示にすることで、チェーンを切断できます。
#![feature(track_caller)]
fn main() {
foo();
}
#[track_caller]
fn foo() {
(move || {
bar(); // prints `src/main.rs:10:9`
})()
}
#[track_caller]
fn bar() {
println!("{}", std::panic::Location::caller());
}
遊び場リンク
#[track_caller]
関数のチェーンを壊す方法がわかったので、この問題に対処できます。ユーザーが実際#[track_caller]
に意図的に関数にマークを付けた場合は、クロージャーを挿入したりチェーンを切断したりしないようにする必要があります。
これらの行をソリューションに追加できます。
// Wrap body in a closure only if function doesn't already have #[track_caller]
let block = if attrs.iter().any(|attr| attr.path.is_ident("track_caller")) {
quote! { #block }
} else {
quote! {
(move || #block)()
}
};
これらの2つの変更の後、次のコードになりました。
// print_caller_location/src/lib.rs
use proc_macro::TokenStream;
use quote::quote;
use syn::spanned::Spanned;
// Create a procedural attribute macro
//
// Notably, this must be placed alone in its own crate
#[proc_macro_attribute]
pub fn print_caller_location(_attr: TokenStream, item: TokenStream) -> TokenStream {
// Parse the passed item as a function
let func = syn::parse_macro_input!(item as syn::ItemFn);
// Break the function down into its parts
let syn::ItemFn {
attrs,
vis,
sig,
block,
} = func;
// Ensure that it isn't an `async fn`
if let Some(async_token) = sig.asyncness {
// Error out if so
let error = syn::Error::new(
async_token.span(),
"async functions do not support caller tracking functionality
help: consider returning `impl Future` instead",
);
return TokenStream::from(error.to_compile_error());
}
// Wrap body in a closure only if function doesn't already have #[track_caller]
let block = if attrs.iter().any(|attr| attr.path.is_ident("track_caller")) {
quote! { #block }
} else {
quote! {
(move || #block)()
}
};
// Extract function name for prettier output
let name = format!("{}", sig.ident);
// Generate the output, adding `#[track_caller]` as well as a `println!`
let output = quote! {
#[track_caller]
#(#attrs)*
#vis #sig {
println!(
"entering `fn {}`: called from `{}`",
#name,
::core::panic::Location::caller()
);
#block
}
};
// Convert the output from a `proc_macro2::TokenStream` to a `proc_macro::TokenStream`
TokenStream::from(output)
}
特徴的なスターのコリン・エッグレスフィールドは、RomaDrama Liveでのスリル満点のファンとの出会いについて料理しました!加えて、大会での彼のINSPIREプログラム。
ノーザンエクスポージャーが90年代の最も人気のある番組の1つになった理由を確認するには、Blu-rayまたはDVDプレーヤーをほこりで払う必要があります。
ドミニカのボイリング湖は、世界で2番目に大きいボイリング湖です。そこにたどり着くまでのトレッキングは大変で長いですが、努力する価値は十分にあります。
今日のホワイトハウスの記者会見で、報道官のサラ・ハッカビー・サンダースは、スポーツセンターのホストであるイェメル・ヒルのドナルド・トランプに関する最近のツイートについてコメントするよう求められ、大統領と彼の政策を白人至上主義者として説明した。ヒルは彼女のつぶやきのためにESPNによって公に叱責されました。
基本的に頭のあらゆる部分から髪の毛を整えたり、ブロードライしたり、まっすぐにしたり、脱毛したりする必要がある場合は、このレミントンゴールドボックスが最適です。今日だけ、Amazonは、すでに人気のあるShortcut Pro Self-HaircutKitやPearlPro Ceramic Flat Ironのように、グルーミングをはるかに簡単にするヘアツールをマークダウンしています。
トゥパックシャクール(ティムモーゼンフェルダー/ゲッティイメージズ); マヤアンジェロウ(マーティンゴッドウィン/ゲッティイメージズ)移動、テイラースウィフト。ケンドールとカイリーは、必要な数の座席を持っています。
写真:Tesla Motorsフロリダに住んでいて、Tesla Model S、Model X 60、またはModel 60Dを使用している場合、車の自律性は50 km(約30マイル)高くなります。これは失敗ではなく、ハリケーンイルマによる避難作業を容易にするために会社自身が命じた自治権の一時的な延長です。
Zendaya shared a sweet photo in honor of boyfriend Tom Holland's 26th birthday Wednesday
シーレン「Ms.JuicyBaby」ピアソンは、先月脳卒中で入院した後、「もう一度たくさんのことをする方法を学ばなければならない」ため、言語療法を受けていることを明らかにしました。
オスカー受賞者の世紀半ばの家には、3つのベッドルーム、2つのバス、オーシャンフロントの景色があります。
Bioscoutは、農家を運転席に置くという使命を負っています。Artesian(GrainInnovate)やUniseedと並んで、最新のシードラウンドでチームを支援できることをうれしく思います。問題真菌症による重大な作物の損失は、農民にとって試練であることが証明されています。
遠隔医療は、パンデミック後の時代では新しいものではなく、時代遅れの分野でもありません。しかし、業界を詳しく見ると、需要と供給の強力な持続可能性と、米国で絶え間ない革命となる強力な潜在的成長曲線を示しています。
2021年は、世界的なベンチャーキャピタル(VC)の資金調達にとって記録的な年でした。DealStreetAsiaによると、東南アジアも例外ではなく、この地域では年間で記録的な25の新しいユニコーンが採掘されました。
計算に対する私たちの欲求とムーアの法則が提供できるものとの間には、指数関数的に増大するギャップがあります。私たちの文明は計算に基づいています—建築と想像力の現在の限界を超える技術を見つけなければなりません。