proc-macro-workshop:seq-9

godme發表於2022-07-05
// Suppose we wanted a seq invocation in which the upper bound is given by the
// value of a const. Both macros and consts are compile-time things so this
// seems like it should be easy.
//
//     static PROCS: [Proc; NPROC] = seq!(N in 0..NPROC { ... });
//
// In fact it isn't, because macro expansion in Rust happens entirely before
// name resolution. That is, a macro might know that something is called NPROC
// but has no way to know which of many NPROC values in the dependency graph
// this identifier refers to. Or maybe the NPROC constant doesn't exist yet when
// the macro runs because it is only emitted by a later macro that hasn't run
// yet. In this compilation model it isn't possible to support a query API where
// a macro can give it the name of a constant and receive back the value of the
// constant.
//
// All hope is not lost; it just means that our source of truth for the value of
// NPROC must be a macro rather than a constant. The code in this test case
// implements this workaround.
//
// This test case may or may not require code changes in your seq macro
// implementation depending on how you have implemented it so far. Before
// jumping into any code changes, make sure you understand what the code in this
// test case is trying to do.

use seq::seq;

// Source of truth. Call a given macro passing nproc as argument.
//
// We want this number to appear in only one place so that updating this one
// number will correctly affect anything that depends on the number of procs.

// 傳入某個宏,處理256
macro_rules! pass_nproc {
    ($mac:ident) => {
        $mac! { 256 }
    };
}

// 直接返回傳入的字面量
macro_rules! literal_identity_macro {
    ($nproc:literal) => {
        $nproc
    };
}

// Expands to: `const NPROC: usize = 256;`
// 處理某個宏,展開後返回一個定義的字面量
const NPROC: usize = pass_nproc!(literal_identity_macro);

struct Proc;

impl Proc {
    const fn new() -> Self {
        Proc
    }
}

// 傳入某個字面量,然後進行宏展開
macro_rules! make_procs_array {
    ($nproc:literal) => {
        seq!(N in 0..$nproc { [#(Proc::new(),)*] })
    }
}

// Expands to: `static PROCS: [Proc; NPROC] = [Proc::new(), ..., Proc::new()];`
// 將處理定義為宏,然後透過其他宏呼叫,並且傳入指定數值,完成宏展開,相當於先定義方法call(f) {f(n)}
static PROCS: [Proc; NPROC] = pass_nproc!(make_procs_array);

fn main() {}

該有的註釋我也加在裡面了,這裡主要是對我們展示了一個trick

首先值得高興的是我們自定義的宏和其他的macro_rules正常進行互動,內心的確是有一些小激動。
其次,主要是因為過程宏展開的時候無法引入常量,透過這種方式我們可以繞開這種限制。
最後,我們發現所謂的macro並不孤立於rust,宏也可以作為處理元素進行傳入。

類比於函式引數,宏之間也可以存在這種方式,傳入之後invoke

seqproc-macro-workshop唯一一道函式式宏的題目。
它的呼叫方式和macro_rules類似,但是解析過程完全可以自由發揮。
很多的自定義語法(當然,在sql中更為常見),我們都可以自由擴充套件。
再也不用擔心看不見,看不懂底層的實現而擔驚受怕。

本作品採用《CC 協議》,轉載必須註明作者和本文連結