2016-02-23 15 views
7

Tôi có một macro lấy danh sách các khai báo hàm và biến chúng thành các khai báo khác nhau.Làm cách nào để khái quát hóa macro Rust qua các loại chức năng khác nhau?

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

nào được sử dụng như thế này:

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

Làm thế nào tôi có thể khái quát vĩ mô để tôi có thể cung cấp cho nó nhiều chức năng có hoặc không có args và trở về chủng loại và có nó hoạt động trên tất cả trong số họ. Thật dễ dàng để tạo macro hoạt động trên nhiều chức năng cùng loại, nhưng tôi không thể tìm ra cách làm cho macro hoạt động với nhiều loại khác nhau.

Trả lời

9

Vâng, có hai cách.

Nếu bạn muốn phân tích cú pháp này chính xác, thì bạn sẽ cần sử dụng muncher. Vì vậy, một cái gì đó như:

macro_rules! re_export { 
    () => {}; 

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

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

Điều này liên quan đến việc phá vỡ một chữ ký chức năng tại một thời điểm, xử lý đệ quy. Đây là cách phân tích cú pháp linh hoạt nhất, nhưng không có nghĩa là bạn có thể chạy ngược lại giới hạn đệ quy macro. Giới hạn mặc định là 64, vì vậy nếu bạn có nhiều đầu vào hơn, bạn sẽ cần nhiều lệnh gọi macro cấp cao nhất hoặc bạn sẽ phải tăng giới hạn đệ quy theo cách thủ công bằng cách thêm thuộc tính #![recursion_limit="128"] vào thùng của bạn.

Cách khác là thay đổi cú pháp sao cho bạn chia nhỏ rồi xử lý chữ ký theo hai bước. Để làm điều này, bạn phải có một số loại cú pháp cấp cao nhất thường xuyên cho chữ ký. Ví dụ:

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

Ở đây, chúng tôi quấn từng chữ ký hàm vào {...} s. Điều này là do các nhóm đối sánh ((...), [...]{...}) cho phép macro_rules! khớp nội dung của chúng một cách mù quáng mà không cần phải hiểu chúng. Điều này cho phép chúng tôi kết hợp các chữ ký chức năng bất thường theo cách thông thường. Việc mở rộng cấp cao nhất chỉ đơn giản là chuyển tiếp mỗi chữ ký hàm riêng lẻ trở lại chính nó để xử lý thực tế. @fn chỉ là một dấu hiệu internal rule để đảm bảo chúng tôi chọn đúng quy tắc trong khi đệ quy.

này không có giới hạn đệ quy tương tự mà trước đó không ... nhưng đòi hỏi bạn phải sử dụng một cú pháp hơi tù:

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

tôi sẽ giả định bằng tên người dùng của bạn mà bạn đã viết cuốn sách mà bạn đã liên kết. Nó khá tốt! Tôi muốn Google sẽ đề xuất nó cho tôi khi tôi đang nghiên cứu câu hỏi này. – Mastax

Các vấn đề liên quan