proc-macro-workshop:seq-9

审题

// 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 协议》,转载必须注明作者和本文链接
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!