Arr*_*rrr 8 macros templates nim-lang
我最近一直在使用模板和宏,但我不得不说我几乎找不到有关这些重要类型的信息.这是我肤浅的理解:
这是一种非常含糊的概念.我想对它们有一个更好的解释,包括哪些类型应该用作返回.
zah*_*zah 11
这些不同参数类型的目标是在指定编译器应该接受宏作为参数的内容时为您提供几个不断提高的精度.
让我们设想一个可以解决数学方程的假设宏.它会像这样使用:
solve(x + 10 = 25) # figures out that the correct value for x is 15
Run Code Online (Sandbox Code Playgroud)
这里,宏只关心提供的AST树的结构.它不要求同一个树是当前范围内的有效表达式(即已x定义的等等).宏只是利用了已经可以解码大多数数学方程的Nim解析器,使它们更容易处理AST树.这就是untyped参数的用途.他们没有得到语义检查,你得到原始的AST.
精度阶梯的下一步是typed参数.它们允许我们编写一个接受任何表达式的通用宏,只要它在当前作用域中具有正确的含义(即可以确定其类型).除了早期捕获错误之外,这还有一个优点,即我们现在可以使用宏体内表达式的类型(使用macros.getTypeproc).
通过要求表达特定类型(具体类型或类型类/概念),我们可以更加精确.宏现在可以像常规proc那样参与重载决策.重要的是要理解宏仍将接收AST树,因为它将接受可在编译时评估的表达式和只能在运行时评估的表达式.
最后,我们可以要求宏接收在编译时提供的特定类型的值.宏可以使用此值来参数化代码生成.这是静态参数的领域.在宏的主体内,它们不再是AST树,而是普通的良好类型值.
到目前为止,我们只讨论过表达式,但Nim的宏也接受并生成块,这是我们可以控制的第二个轴.expr通常表示单个表达式,而stmt表示表达式列表(历史上,它的名称来自StatementList,它在表达式和语句在Nim中统一之前作为单独的概念存在).
使用模板的返回类型可以很容易地说明这种区别.考虑newException系统模块中的模板:
template newException*(exceptn: typedesc, message: string): expr =
## creates an exception object of type ``exceptn`` and sets its ``msg`` field
## to `message`. Returns the new exception object.
var
e: ref exceptn
new(e)
e.msg = message
e
Run Code Online (Sandbox Code Playgroud)
甚至认为构造异常需要几个步骤,通过指定expr模板的返回类型,我们告诉编译器只有最后一个表达式才会被视为模板的返回值.其余的语句将被内联,但巧妙地隐藏在调用代码中.
作为另一个例子,让我们定义一个特殊的赋值运算符,它可以模拟C/C++的语义,允许在if语句中赋值:
template `:=` (a: untyped, b: typed): bool =
var a = b
a != nil
if f := open("foo"):
...
Run Code Online (Sandbox Code Playgroud)
指定具体类型与使用具有相同的语义expr.如果我们使用了默认的stmt返回类型,编译器就不允许我们传递"表达式列表",因为if语句显然需要单个表达式.
.immediate.当模板和宏没有参与重载解析时,这是过去很久以来的遗产.当我们第一次让他们意识到类型系统时,大量的代码需要当前的untyped参数,但是很难重构编译器从一开始就引入它们而是我们添加了.immediate.pragma作为强制向后兼容行为的方法对于整个宏/模板.
使用typed/untyped,您可以更精细地控制宏的各个参数,并且.immediate.pragma将逐渐被淘汰并弃用.