2016-02-23 4 views
7

Ich habe ein Makro, das eine Liste von Funktionsdeklarationen nimmt und sie in verschiedene Deklarationen umwandelt.Wie generalisiere ich ein Rust-Makro über verschiedene Arten von Funktionen?

macro_rules! re_export { 
    ($(pub fn $i:ident($($arg:ident: $argty:ty)*) -> $ret:ty;)*) => ($(
     extern { 
      pub fn $i($($arg: $argty),*) -> $ret; 
     } 
    )*); 
    ($(pub fn $i:ident($($arg:ident: $argty:ty)*);)*) => ($(
     extern { 
      pub fn $i($($arg: $argty),*); 
     } 
    )*); 
} 

, die wie folgt verwendet:

re_export! { 
    pub fn abs(i: c_int) -> c_int; 
    pub fn rand() -> c_int; 
    pub fn foo(); 
    pub fn add(i: c_int, j: c_int) -> c_int; 
} 

Wie kann ich das Makro verallgemeinern, so dass ich es mehrere Funktionen mit oder ohne args geben kann und Rückgabetypen und haben es auf alle von ihnen zu arbeiten. Es ist einfach, ein Makro zu erstellen, das an mehreren Funktionen des gleichen Typs arbeitet, aber ich kann nicht herausfinden, wie es für verschiedene Typen funktioniert.

Antwort

9

Nun, es gibt zwei Möglichkeiten.

Wenn Sie diese genaue Syntax syntaktisch analysieren möchten, dann müssen Sie eine muncher verwenden. So etwas wie:

Dies beinhaltet Abbrechen einer Funktion Signatur zu einer Zeit, Verarbeitung sie rekursiv. Dies ist die flexibelste Möglichkeit zum Parsen von Dingen, aber bedeutet bedeutet, dass Sie gegen die Makro-Rekursionsgrenze stoßen können. Das Standardlimit ist 64. Wenn Sie also mehr Eingaben benötigen, benötigen Sie mehrere Makroaufrufe auf oberster Ebene oder Sie müssen das Rekursionslimit manuell erhöhen, indem Sie Ihrer Kiste ein #![recursion_limit="128"]-Attribut hinzufügen.

Die andere besteht darin, die Syntax so zu ändern, dass Sie die Signaturen in zwei Schritten aufteilen und dann verarbeiten. Um dies zu tun, müssen Sie eine normale Syntax auf oberster Ebene für die Signaturen haben. Zum Beispiel:

macro_rules! re_export { 
    ($({$($sigs:tt)*})*) => { 
     $(
      re_export! { @fn $($sigs)* } 
     )* 
    }; 

    (@fn pub fn $i:ident($($arg:ident: $argty:ty),*) -> $ret:ty) => { 
     extern { 
      pub fn $i($($arg: $argty),*) -> $ret; 
     } 
    }; 

    (@fn pub fn $i:ident($($arg:ident: $argty:ty),*)) => { 
     extern { 
      pub fn $i($($arg: $argty),*); 
     } 
    }; 
} 

Hier stellen wir jede Funktion Unterschrift in {...} s wickeln. Dies liegt daran, dass die Matcher-Gruppen ((...), [...] und {...}) macro_rules! erlauben, blind ihren Inhalt zu vergleichen, ohne sie zu verstehen. Dies ermöglicht es uns, die unregelmäßigen Funktionssignaturen auf eine reguläre Art und Weise anzupassen. Die Top-Level-Erweiterung leitet einfach jede einzelne Funktionssignatur zur eigentlichen Verarbeitung an sich selbst zurück. Die @fn ist nur ein internal rule Marker, um sicherzustellen, dass wir die richtige Regel während der Rekursion auswählen.

Diese haben nicht die gleichen Rekursion Grenzen, die die vorherige tut ... aber erfordert, dass Sie eine etwas stumpfe Syntax:

re_export! { 
    { pub fn abs(i: c_int) -> c_int } 
    { pub fn rand() -> c_int } 
    { pub fn foo() } 
    { pub fn add(i: c_int, j: c_int) -> c_int } 
} 
+0

ich mit Ihrem Benutzernamen annehmen werde, dass Sie das Buch schrieb, du hast verlinkt. Es ist recht gut! Ich wünschte, Google hätte es mir vorgeschlagen, als ich für diese Frage recherchierte. – Mastax

Verwandte Themen