2014-07-12 29 views
8

E 'possibile scrivere in una macro in ruggine che crea altre macro. Ad esempio, supponiamo che io definisco i seguenti due macro:Macro di ordine superiore

macro_rules! myprint(
    ($a:expr) => (print!("{}", $a)) 
) 

macro_rules! myprintln(
    ($a:expr) => (println!("{}", $a)) 
) 

Dal momento che le due macro ripetono un sacco di codice, mi consiglia di scrivere una macro per generare le macro.

Ho provato a generare un tale meta macro.

#![feature(macro_rules)] 

macro_rules! metamacro(
    ($i:ident) => (
     macro_rules! $i (
      ($a:expr) => ({println!("hello {}", $a)}) 
     ) 
    ); 
) 

metamacro!(foo) 

fn main() { 
    foo!(1i); 
} 

ma ottenere i seguenti errori:

<anon>:6:13: 6:14 error: unknown macro variable `a` 
<anon>:6    ($a:expr) => ({println!("hello {}", $a)}) 
        ^
playpen: application terminated with error code 101 
Program ended. 

Edit: Dopo aver suonato in giro con le macro un po ', ho scoperto che un ordine superiore macro funziona come previsto se la macro restituito non lo fa ricevere eventuali argomenti. Ad esempio, il seguente code

#![feature(macro_rules)] 

macro_rules! metamacro(
    ($i:ident) => (
     macro_rules! $i (
      () => ({println!("hello")}) 
     ) 
    ); 
) 

metamacro!(foo) 

fn main() { 
    foo!(); 
} 

stampe hello

+1

Perché non provare voi stessi? –

+2

Ho provato e non sono riuscito a trovare qualcosa che funzioni. Ho chiesto qui nel caso in cui ci fosse un meccanismo nascosto o meno noto nella ruggine che non avevo ancora provato. Aggiungerò questo alla mia domanda per renderla un po 'più chiara. – mwhittaker

+0

Non hai mostrato cosa hai provato o quali sono gli errori. Questi dettagli sono assolutamente necessari per poter dare qualsiasi tipo di risposta. –

risposta

10

Ciò è stato recentemente reso possibile in #34925. La risposta qui sotto si riferisce alle versioni precedenti di Rust.


Questo è #6795 e #6994, e non è direttamente possibile al momento. Per esempio. la prima cosa che si potrebbe provare è

#![feature(macro_rules)] 

macro_rules! define { 
    ($name: ident, $other: ident) => { 
     macro_rules! $name { 
      ($e: expr) => { 
       $other!("{}", $e) 
      } 
     } 
    } 
} 

define!{myprintln, println} 
define!{myprint, print} 

fn main() {} 

Ma questo non riesce con

so8.rs:6:13: 6:14 error: unknown macro variable `e` 
so8.rs:6    ($e: expr) => { 
        ^

(Archiviato #15640 sulla punta del punto di inserimento al ( invece del $e.)

Il motivo è l'espansione di macro è "stupido": ingenuamente interpreta e sostituisce tutti i token, anche all'interno di invocazioni di macro annidate (e macro_rules! è una macro invocazione). Quindi, non si può dire che lo macro_rules! interno sia in realtà macro_rules!: $e: sta solo cercando di sostituirli espandendo define.

La seconda cosa che si potrebbe provare sta passando argomenti extra per define di inserire nella definizione degli interni, in modo che il $e non appare direttamente nella invocazione nidificato:

#![feature(macro_rules)] 

macro_rules! define { 
    ($name: ident, $other: ident, $($interior: tt)*) => { 
     macro_rules! $name { 
      ($($interior)*: expr) => { 
       $other!("{}", $($interior)*) 
      } 
     } 
    } 
} 

define!{myprintln, println, $e} 
define!{myprint, print, $e} 

fn main() {} 

Cioè, ho aggiunto $interior: tt per consentire la cattura dello $e. tt indica token tree, che è un token non delimitatore (ad esempio $ o some_ident) o una sequenza di token circondata da delimitatori corrispondenti (ad esempio (foo + bar fn "baz")). Purtroppo, sembra espansione è abbastanza ansioso per l'espansione $e da espandere troppo:

so8.rs:8:13: 8:14 error: unknown macro variable `e` 
so8.rs:8    ($($interior)*: expr) => { 
        ^

Esso funziona quando la macro innestata ha alcun argomento stessa, perché non ci sono annidati $... non-terminali e quindi la fase di espansione non ha mai colpito il problema di definizione sopra descritto.


Infine, è possibile ottenere una parvenza di code sharing in quanto è possibile espandere a invocazioni macro per nome:

#![feature(macro_rules)] 

macro_rules! my { 
    ($name: ident, $e: expr) => { 
     $name!("{}", $e) 
    } 
} 

fn main() { 
    my!(print, 1i); 
    my!(println, 2i); 
} 

che stampa 12.

+0

Beh, direi, forse lo facciamo funzionare bene, o non permettiamo macro annidate tutte insieme ... – errordeveloper

+0

Wow, risposta eccezionale! Ti capiterà di sapere se le macro di ordine superiore sono pianificate per l'inclusione nella lingua qualche volta in futuro? – mwhittaker

+0

@mwhittaker, al momento non esiste un piano per questo, ma è certamente un fastidio che sarebbe bello risolvere. – huon

0

Ecco un esempio che funziona, forse un punto di partenza per voi:

#![feature(macro_rules)] 

macro_rules! metamacro(
    ($i:ident) => (
     macro_rules! $i (
      () => ({println!("hello!")}) 
     ) 
    ); 
) 

metamacro!(hello) 

fn main() { 
    hello!(); 
} 
Problemi correlati