2014-07-12 30 views
8

Czy w rdzeniu jest możliwe napisanie makra, które tworzy inne makra. Na przykład, załóżmy zdefiniować następujące dwa makra:Makra wyższego rzędu

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

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

Ponieważ dwa makra powtórzyć dużo kodu, może chcę napisać makro do generowania makr.

Próbowałem wygenerować taki meta macro.

#![feature(macro_rules)] 

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

metamacro!(foo) 

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

ale uzyskać następujące błędy:

<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: Po zabawy z makrami trochę więcej, odkryłem, że wyższego rzędu prac makro zgodnie z oczekiwaniami, jeśli nie wrócił makro odbierać jakiekolwiek argumenty. Na przykład, następujący code

#![feature(macro_rules)] 

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

metamacro!(foo) 

fn main() { 
    foo!(); 
} 

wydruki hello

+1

Dlaczego nie spróbować samemu? –

+2

Próbowałem i nie udało mi się znaleźć czegoś, co działa. Zapytałem tutaj, na wypadek, gdyby istniał ukryty lub mniej znany mechanizm rdzy, którego jeszcze nie próbowałem. Dodam to do mojego pytania, aby było trochę jaśniej. – mwhittaker

+0

Nie pokazałeś, co próbowałeś lub jakie są błędy. Te dane są absolutnie niezbędne, aby móc udzielić jakiejkolwiek odpowiedzi. –

Odpowiedz

10

ten został niedawno możliwe #34925. Poniższa odpowiedź dotyczy starszych wersji Rusta.


To #6795 i #6994, a nie jest bezpośrednio dostępny w tej chwili. Na przykład. Pierwszą rzeczą, jaką można by spróbować jest

#![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() {} 

Ale to się nie powiedzie z

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

(złożone #15640 o skierowaną karetki na ( zamiast $e.)

Powodem jest makr jest "głupi": naiwnie interpretuje i zamienia wszystkie tokeny, nawet wewnątrz wywoływanych makr wbudowanych (i macro_rules! jest makropoleceniem). Dlatego nie można stwierdzić, że $e wewnątrz wnętrza macro_rules! są w rzeczywistości przeznaczone do pozostawienia jako $e: po prostu próbują je zastąpić podczas rozszerzania define.

Drugą rzeczą może próbować przechodzi w dodatkowych argumentów define umieścić we wnętrzu definicji, tak że $e nie pojawia się bezpośrednio w zagnieżdżonej inwokacji:

#![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() {} 

To znaczy, ja dodanej $interior: tt, aby umożliwić przechwytywanie $e. tt oznacza drzewo tokena, które jest nieprzetworzonym tokenem nieprzeterminującym (np. $ lub some_ident) lub sekwencją tokenów otoczonych dopasowanymi ogranicznikami (na przykład (foo + bar fn "baz")). Niestety, wydaje się ekspansja jest wystarczająco chętny do ekspansji $e zostać rozszerzona również:

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

To działa dobrze, gdy zagnieżdżona makro ma samego argumentu, ponieważ nie ma zagnieżdżonych $... non-zaciski i tak faza ekspansji nigdy nie dotyka problemu definicji opisanego powyżej.


Wreszcie, można uzyskać pewne pozory udostępniania kodu ponieważ można rozszerzyć do wywołania makr według nazwy:

#![feature(macro_rules)] 

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

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

która drukuje 12.

+0

Cóż, powiedziałbym, być może albo sprawimy, żeby działało dobrze, albo nie pozwalają na zagnieżdżone makra ... – errordeveloper

+0

Wow, wspaniała odpowiedź! Czy zdarzyło Ci się wiedzieć, czy makra o wyższym porządku są planowane do włączenia do języka w przyszłości? – mwhittaker

+0

@mwhittaker, teraz nie ma obecnego planu, ale na pewno jest to uciążliwe rozwiązanie. – huon

0

Oto jeden przykład, który działa, być może punkt wyjścia dla Ciebie:

#![feature(macro_rules)] 

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

metamacro!(hello) 

fn main() { 
    hello!(); 
} 
Powiązane problemy