2015-12-11 3 views
6

Dies ist nur Pseudo-Code:Wie passe ich den Typ eines Ausdrucks in einem Rust-Makro an?

macro_rules! attribute { 
    $e: expr<f32> => { /* magical float stuff */ }; 
    $e: expr<i64> => { /* mystical int stuff */ }; 
}; 

ich je nach Typ einen unterschiedlich erweitert Makro haben möchte, die ich an das Makro übergeben.

Dies ist, wie es das ist funktionieren würde in nicht in der Lage zu tun, 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 */ } 
} 
+0

Makros nur Versand auf Syntax, traits OTOH Versand kann die Art basiert. Ziehen Sie in Erwägung, ein Merkmal zu verwenden oder ein Merkmal in Ihrem Makro zu verwenden. – bluss

Antwort

11

Rust Makros. Makros operieren auf der syntaktischen Ebene, nicht auf der semantischen Ebene. Das bedeutet, obwohl der Compiler weiß, dass er einen Ausdruck (Syntax) hat, weiß er nicht, was der Wert des Ausdrucks (semantisch) in dem Moment ist, in dem das Makro expandiert wird.

Eine Abhilfe wäre die erwartete Art an das Makro weitergeben müssen:

macro_rules! attribute { 
    ($e:expr, f32) => { /* magical float stuff */ }; 
    ($e:expr, i64) => { /* mystical int stuff */ }; 
} 

fn main() { 
    attribute!(2 + 2, i64); 
} 

Oder, einfacher gesagt, mehrere Makros definieren.


Wenn Sie statische (kompilieren Zeit) Versendung auf dem Typ eines Ausdrucks basierend tun möchten, können Sie Merkmale verwenden. Definieren Sie ein Merkmal mit den erforderlichen Methoden und implementieren Sie dann das Merkmal für die von Ihnen benötigten Typen. Sie können ein Merkmal für beliebigen Typ (einschließlich Grundelemente und Typen aus anderen Bibliotheken) implementieren, wenn der impl Block in der gleichen Kiste wie die Merkmaldefinition ist.

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); 
} 

Hinweis: Sie können auch $e.process() im Makro Körper schreiben könnte, aber dann das Makro möglicherweise eine unabhängige process Methode aufrufen.

+1

Also in diesem Zusammenhang Rost-Makros sind weniger mächtig als C++ - Vorlagen? – Arne

+0

@Arne Wenn das die Art ist, wie du daran denken willst, dann sicher. Rust-Makros und C++ - Templates sind nur in groben Zügen vergleichbar und haben unterschiedliche Fähigkeiten. Beide * Sprachen * geben Ihnen hier jedoch die gleiche Fähigkeit, wie das Merkmalsbeispiel zeigt. Meine voreingenommene Meinung ist, dass die Rust-Version * besser * ist, da Sie das Metaprogrammieren eines Templates/Makros überhaupt nicht benötigen - ich würde wahrscheinlich das Makro verlassen und einfach '(2 + 2) .attribute()' aufrufen in "Haupt". – Shepmaster

+1

@Shempmaster Ich brauche keinen statischen oder dynamischen Versand, ich brauche das Ergebnis zur Kompilierzeit. – Arne

1

Wie bereits erläutert, können Sie je nach Typ eines expr nicht unterschiedlich expandieren. Aber wie dieses Problem zu umgehen, können Sie die any module verwenden und versuchen, aus dem Any Charakterzug niedergeschlagenen:

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); 
} 

Displays:

`0` is f32. 
`0` is f64. 
I dunno what is `0` :(
Verwandte Themen