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
。
小结
seq
是proc-macro-workshop唯一一道函数式宏的题目。
它的调用方式和macro_rules
类似,但是解析过程完全可以自由发挥。
很多的自定义语法(当然,在sql
中更为常见),我们都可以自由扩展。
再也不用担心看不见,看不懂底层的实现而担惊受怕。
本作品采用《CC 协议》,转载必须注明作者和本文链接
推荐文章: