これは単なる擬似コードです。
macro_rules! attribute {
$e: expr<f32> => { /* magical float stuff */ };
$e: expr<i64> => { /* mystical int stuff */ };
};
マクロに渡したタイプに応じて、異なる展開のマクロが欲しいのですが。
これはC++でどのように機能するかです
template <typename T>
struct Attribute{ void operator(T)() {} };
template <>
struct Attribute<float> {
void operator(float)(float) { /* magical float stuff */ }
};
template <>
struct Attribute<long> {
void operator()(long) { /* mystical int stuff */ }
}
Rustマクロはそれを行うことができません。マクロは、セマンティックレベルではなく、構文レベルで動作します。つまり、コンパイラは式(構文)があることを認識していても、マクロが展開された時点で式の値のタイプ(セマンティック)が何であるかを認識していません。
回避策は、予期される型をマクロに渡すことです。
_macro_rules! attribute {
($e:expr, f32) => { /* magical float stuff */ };
($e:expr, i64) => { /* mystical int stuff */ };
}
fn main() {
attribute!(2 + 2, i64);
}
_
または、もっと簡単に言えば、複数のマクロを定義します。
式のタイプに基づいて静的(コンパイル時)ディスパッチを実行する場合は、トレイトを使用できます。必要なメソッドを使用してトレイトを定義してから、必要なタイプのトレイトを実装します。 impl
ブロックがトレイト定義と同じクレートにある場合は、anyタイプ(他のライブラリのプリミティブとタイプを含む)のトレイトを実装できます。
_trait Attribute {
fn process(&self);
}
impl Attribute for f32 {
fn process(&self) { /* TODO */ }
}
impl Attribute for i64 {
fn process(&self) { /* TODO */ }
}
macro_rules! attribute {
($e:expr) => { Attribute::process(&$e) };
}
fn main() {
attribute!(2 + 2);
}
_
注:マクロの本体に$e.process()
を書き込むこともできますが、その場合、マクロは無関係のprocess
メソッドを呼び出す可能性があります。
すでに説明したように、expr
のタイプに応じて異なる方法で展開することはできません。ただし、回避策として、 any
module を使用して、Any
トレイトからダウンキャストしてみることができます。
use std::any::Any;
macro_rules! attribute {
( $e:expr ) => {
if let Some(f) = (&$e as &Any).downcast_ref::<f32>() {
println!("`{}` is f32.", f);
} else if let Some(f) = (&$e as &Any).downcast_ref::<f64>() {
println!("`{}` is f64.", f);
} else {
println!("I dunno what is `{:?}` :(", $e);
}
};
}
fn main() {
attribute!(0f32);
attribute!(0f64);
attribute!(0);
}
表示:
`0` is f32.
`0` is f64.
I dunno what is `0` :(