模版
模版是一个简单的替换机制在nim的抽象语法树上操作。模版在编译器的语义分析阶段被处理。它和其他的语言整合的很好,分享没有c语言的预处理宏定义的缺陷。
调用一个模版,就像调用一个程序一样。
Example:
template `!=` (a, b: expr): expr =
# this definition exists in the System module
not (a == b)
assert(5 != 6) # the compiler rewrites that to: assert(not (5 == 6))
!=, >, >=, in, notin, isnot 操作都是模版。如果你重载==操作符这是很有益处的,!=操作符可以自动访问和做正确的事情。(除了IEEE浮点数-NaN打破基本的布尔逻辑)
a>b被转换成b<a。a in b被转换成contains(b,a)。notin和isnot有显而易见的含义。
模版对于懒惰评价的目的是非常有用的。 考虑一个简单的logging过程:
const
debug = true
proc log(msg: string) {.inline.} =
if debug: stdout.writeln(msg)
var
x = 4
log("x has the value: " & $x)
这个代码有一个缺点,如果debug被设置为false,$和&操作符依然执行。(对于过程的参数评估is eager)
将log函数转换成模版解决这个问题:
const
debug = true
template log(msg: string) =
if debug: stdout.writeln(msg)
var
x = 4
log("x has the value: " & $x)
模版的参数类型可以是普通类型或者元类型expr(代表表达式),stmt(代表声明),typedesc(代表类型描述)。如果模版没有显示的返回类型,声明用于过程和方法的一致性。
如果这是一个stmt参数,它应该被放到模版声明的最后面。原因是statements可以通过一个特殊的:语法传递给模版。
template withFile(f: expr, filename: string, mode: FileMode,
body: stmt): stmt {.immediate.} =
let fn = filename
var f: File
if open(f, fn, mode):
try:
body
finally:
close(f)
else:
quit("cannot open: " & fn)
withFile(txt, "ttempl3.txt", fmWrite):
txt.writeln("line 1")
txt.writeln("line 2")
例子中的两个writeln语句被绑定到body参数。withFile样板包含的样板代码有助于避免常见的错误:忘记关闭文件。注意:let fn=filename语句如何确保文件名只被计算一次。