##Pragmas(编译指示) 编译指示"{."为开始, ".}"为结束, ","号为分隔符, 例如{.cdecl, importc.}javascript
###deprecated pragma (弃用(分解?)指示) deprecated指示用来标记为弃用.html
proc p() {.deprecated.} var x {.deprecated.}: char
也能够在声明时使用, 须要定义一个重命名列表.java
type File = object Stream = ref object {.deprecated: [TFile: File, PStream: Stream].}
###noSideEffect pragma (无反作用指示) noSideEffect指示用于标记proc(函数)/iterator(迭代器)为无反作用, 好象是在该函数里使用影响效率的函数或者修改某些变量内容就会出错, 如echo
将来的发展方向: func可能成为无反作用函数的关键字或语法糖(就是说之后更新正式版可能会加入func这个关键字来声明无反作用函数.)node
func `+` (x, y: int): int
###procvar pragma(过程变量指示) procvar指示用于标记函数, 让它能够被传递给一个过程(函数)变量.linux
###compileTime pragma(编译时指示) compileTime指示标记的函数和变量仅能在编译时使用, 不会生成代码, 辅助宏来使用.c++
###noReturn pragma(无返回值指示) noReturn标记函数没有返回值.后端
###discardable pragma(丢弃返回值指示) 使用discardable标记的函数能够不写返回值或discard.数组
###noInit pragma(无初始化指示) 使用该指示的变量不会被初始化.安全
###requiresInit pragma(显式初始化指示) 使用该指示的变量需显式的初始化.bash
type MyObject = object {.requiresInit.} proc p() = # the following is valid: var x: MyObject if someCondition(): x = a() else: x = b() use x
###acyclic pragma(非周期指示) acyclic指示用来标记那些看起来周期循环(其实就是递归使用自身的类型, 像链表什么的)的object(相似c++类)类型为非周期类型, 主要是为了优化效率不让GC(垃圾处理)把这种类型的对象当循环周期部分来处理.
type Node = ref NodeObj NodeObj {.acyclic, final.} = object left, right: Node data: string
这个例子把node类型定义为一个树结构, 注意像这种定义为递归的类型 GC会假设这种类型会造成一个周期图, 使用acyclic指示的话 GC将会无视它, 若是你把acyclic指标用于真正的循环周期类型, GC将形成内存泄漏, 固然不会有其它更糟糕的状况(我以为内存泄漏就很糟糕拉:).
将来的发展方向: acyclic将成为ref类型属性.
type Node = acyclic ref NodeObj NodeObj = object left, right: Node data: string
###final pragma(最终指示) 相似于c++11的final关键字功能, 其实就是说这个类型不能被继承.
###shallow pragma(浅拷贝指示) 浅拷贝指示影响类型的语义让编译器容许浅拷贝, 这可能会致使严重的语义问题打破内存安全, 然而, 它能够很是快速的完成做业, 由于nim的语义要求深拷贝序列(seq)和字符串(string), 这是很是费时的, 特别是用序列来创建一个树结构.
type NodeKind = enum nkLeaf, nkInner Node {.final, shallow.} = object case kind: NodeKind of nkLeaf: strVal: string of nkInner: children: seq[Node]
###pure pragma(抽象指示) 对象(object)类型可标记抽象指示, 以便该类型字段在运行时省略类型识别, 主要是为了二进制兼容其它编译语言.
也能够标记一个枚举(enum)类型, 以后必须写完整才能访问其字段.
type MyEnum {.pure.} = enum valueA, valueB, valueC, valueD echo valueA # 错误, 必须类型字段写完整. echo MyEnum.valueA # 正确
###asmNoStackFrame pragma(无堆栈帧汇编指示) 函数(proc)可标记asmNoStackFrame指示告诉编译器该函数不生成堆栈帧, 也没有退出声明 如return返回值, 其生成的函数为C语言的 declspec(naked)或__attribute((naked))属性函数(取决于使用的C语言编译器).
注意: 这个指示只能在该函数里使用汇编语句.
###error pragma(错误指示) error指示标记用于使编译器输出一个错误消息的内容, 使其在编译发生错误时不必定会终止.
error指示也能够用来标注一个符号(如一个迭代器(iterator)或函数(proc)), 若是使用符号则发出一个编译时错误, 对于排除那些有效的重载和类型转换的操做是很是有用的.
## 若是使用这个函数就会弹出编译时错误. proc `==`(x, y: ptr int): bool {.error.}
###fatal pragma(致命指示) fatal指示标记用于使编译器输出一个错误消息的内容, 相对于error指示, fatal指示放哪就在哪里出错.
when not defined(objc): {.fatal: "Compile this program with the objc command!".}
###warning pragma(警告指示) warning指示标记用于使编译器输出一个警告消息的内容, 警告后继续编译.
###hint pragma(提示指示) hint指示标记用于使编译器输出一个提示消息的内容, 提示后继续编译.
###line pragma(行指示) line指示标记能够影响注释语言在堆栈回溯跟踪时的可见信息.
template myassert*(cond: expr, msg = "") = if not cond: # change run-time line information of the 'raise' statement: {.line: InstantiationInfo().}: raise newException(EAssertionFailed, msg)
若是行指示想使用参数, 则参数需为一个元组(tuple [filename: string, line: int]), 若是无参, 则须使用system.InstantiationInfo()函数.
###linearScanEnd pragma(直线扫描到最后?) linearScanEnd指示用来告诉编译器如何编译case语句, 须在case语句里声明:
case myInt of 0: echo "most common case" of 1: {.linearScanEnd.} echo "second most common case" of 2: echo "unlikely: use branch table" else: echo "unlikely too: use branch table for ", myInt
我的理解多是在使用case语言里的某个分支使用了linearScanEnd指示就会让该分支之上全部分支的时间效率为O(1), 具体不是很懂, 请参考原句linearScanend pragma, 懂的请帮忙解惑留言.
###computedGoto pragma(跳转计算指示) computedGoto指示用来告诉编译器在while循环里如何编译case语句, 须在循环内声明:
type MyEnum = enum enumA, enumB, enumC, enumD, enumE proc vm() = var instructions: array [0..100, MyEnum] instructions[2] = enumC instructions[3] = enumD instructions[4] = enumA instructions[5] = enumD instructions[6] = enumC instructions[7] = enumA instructions[8] = enumB instructions[12] = enumE var pc = 0 while true: {.computedGoto.} let instr = instructions[pc] case instr of enumA: echo "yeah A" of enumC, enumD: echo "yeah CD" of enumB: echo "yeah B" of enumE: break inc(pc) vm()
如例如示computedGoto很是适合做为解释器来使用, 若是使用的C语言编译器不支持跳转计算扩展功能 该指示将被忽略.
###immediate pragma(当即指示) immediate指示用于使模板不参与重载解析, 能够在参数调用前不检查语义, 因此能够接收未声明的标识符.
# 普通模板 template declareInt(x: expr) = var x: int declareInt(x) # 错误: 未知的标识符: 'x' #当即指示的模板 template declareInt(x: expr) {.immediate.} = var x: int declareInt(x) # 正确
###compilation option pragmas(编译选项指示) 这些列出的指示能够用来覆盖proc/method/converter的代码生成选项.
目前实现了下列选项(之后可能添加更多的选项)
编译指示 | 可用值 | 描述 |
---|---|---|
checks | on/off | 代码生成时是否打开运行时检测 |
boundChecks | on/off | 代码生成时是否检测数组边界 |
overflowChecks | on/off | 代码生成时是否检测下溢和溢出 |
nilChecks | on/off | 代码生成时是否检测nil指针 |
assertions | on/off | 代码生成时断言是否生效 |
warnings | on/off | 是否打开编译器的警告消息 |
hints | on/off | 是否打开编译器的提示消息 |
optimization | none/speed/size | 优化代码的速度或大小, 或关闭优化功能 |
patterns | on/off | 是否打开改写术语的模板和宏? |
callconv | cdecl/stdcall/... | 对全部的函数(proc)和函数类型(proc type)指定默认的调用协议 |
示例:
{.checks: off, optimization: speed.} # 编译时不会进行运行时检测(checks)和速度优化(optimization)
###push and pop pragmas(推入/弹出指示) 这两个指示需成对使用, 功能相似于选项指示, 做用是临时覆盖那些设置, 例如:
{.push checks: off.} # 保存旧的设置 # 编译push/pop区域内的代码时不进行运行时检测. # speed critical # ... 一些代码 ... {.pop.} # 恢复旧的设置
###register pragma(寄存器指示) 寄存器指示只能用于变量, 它会声明一个寄存器变量, 告诉编译器该变量应该使用硬件寄存器来提供更快的访问速度, C语言编译器常常会忽略这个功能, 由于它优化的比你快.
在高度特定的状况下(例如字节码解释器的消息循环)可能会更有效率, 虽然通常没什么卵用。
###global pragma(全局指示) global指示相似于c/c++的static关键字功能, 在函数(proc)里使用变量加上global指示可一直使用此变量, 该变量只会在程序运行时初始化一次.
proc isHexNumber(s: string): bool = var pattern {.global.} = re"[0-9a-fA-F]+" result = s.match(pattern)
每一个函数(proc)里的global变量只相对该函数是惟一的, 其它函数里global变量同名不受影响.
###deadCodeElim pragma(死代码消除) deadCodeElim指示只应用于整个模块, 它告诉编译器是否对模块激活死代码消除功能.
在编译时加上--deadCodeElim:on命令时, 全部模块都带有{.deadCodeElim:on}死码消功能.
{.deadCodeElim: on.}
###pragma pragma(pp指示) pp指示能够用来声明用户定义的指示, 这很是有用, 由于模板和宏不会影响指示, 它们不能从模块导入.
when appType == "lib": {.pragma: rtl, exportc, dynlib, cdecl.} else: {.pragma: rtl, importc, dynlib: "client.dll", cdecl.} proc p*(a, b: int): int {.rtl.} = result = a+b
在这个例子中一个被命名为rlt的指示能够从任意动态库中导入其中的符号(函数或变量)或为动态库生成导出符号.
###Disabling certain messages(禁止某些消息) 某些用户可能不喜欢nim生成的一些很长的警告和提示, 可用它来关闭那些警告和提示.
{.hint[LineTooLong]: off.} # 禁止太长的提示.
比挨个设置禁止不一样的警告要方便多了.
###experimental pragma(实验性指示) 让你可使用那些实验性的语言功能, 这些不稳定的功能可能会在出如今将来的稳定版中也有可能永远删除, 喜欢尝鲜的小白鼠能够一试.
{.experimental.} proc useUsing(dest: var string) = using dest add "foo" add "bar"
##Foreign function interface(外部函数接口) Nim的外部函数接口是很是普遍的, 只有部分将来扩展的后端(如LLVM/Javascript)记录在这里.
###Importc pragma(C语言导入指示) 该指示表示从外部文件导入符号(函数或变量)
proc printf(formatstr: cstring) {.header: "<stdio.h>", importc: "printf", varargs.}
上面是导入C语言的符号, 若是想导入C++的就是importcpp, objc的就是importobjc.
###Exportc pragma(C语言导出指示) 与importc的功能相反, 主要是做为库导出符号让人调用.
#fib.nim proc fib(a: cint): cint {.exportc.} = if a <= 2: result = 1 else: result = fib(a - 1) + fib(a - 2)
//maths.c #include "fib.h" #include <stdio.h> int main(void) { NimMain(); for (int f = 0; f < 10; f++) printf("Fib of %d is %d\n", f, fib(f)); return 0; }
直接编译要加入nimcache目录
> nim c --noMain --noLinking --header:fib.h fib.nim > gcc -o m -Inimcache -Ipath/to/nim/lib nimcache/*.c maths.c
也能够编译成静态库给c/c++用, linux下可能要加-ldl
> nim c --app:staticLib --noMain --header fib.nim > gcc -o m -Inimcache -Ipath/to/nim/lib libfib.nim.a maths.c
也能够编译成javascript给html用.
<html><body> <script type="text/javascript" src="fib.js"></script> <script type="text/javascript"> alert("Fib for 9 is " + fib(9)); </script> </body></html>
nim js -o:fib.js fib.nim
详细内容请参考:backends
###Extern pragma(外部指示) 相似importc或exportc, extern指示会影响名字识别, 字符串转给extern能够是格式字符串.
proc p(s: string) {.extern: "prefix$1".} = echo s
例中把p设为外部名prefix$1(1参数), 相似于win的函数符号协议.
###Bycopy pragma(被复制指示) Bycopy该指示可应用于对象和元组, 经过编译器把类型的值转给函数(proc).
###Byref pragma(被引用指示) Byref指示可应用于对象和元组, 经过编译器把该类型做为引用传给函数.
###Varargs pragma(可变参数指示) varargs指示可让使用的函数(proc)最后一个参数转变为可变参数(相似c语言printf的fmt), 这个函数使用的nim字符串会自动转换为cstring.
proc printf(formatstr: cstring) {.nodecl, varargs.} printf("hallo %s", "world") # "world" 将转换为cstring
###Union pragma(联合指示) union看起来像c++的union用法, 可应用于任何对象(object)类型, 共享字段, 暂不支持GC和继承.
将来发展的方向: 将容许GC检测和控制内存.
###Packed pragma(封包指示) packed指示可应用于任何对象(object)类型, 它确保对象的字段头尾相接连续保存在一段内存内, 这适用于网络包传输和硬件驱动, 封包指示目前还不能使用继承和GC管理内存. 将来发展的方向: 将支持继承, 若是封闭的内部有使用gc的话将会在编译时提示错误.
###Unchecked pragma(无检测指示) Unchecked指示可让该数组不对边界检测, 这个很是有用, 适合在你想要一个灵活大小却又不肯定大小的数组时使用.
type ArrayPart{.unchecked.} = array[0..0, int] MySeq = object len, cap: int data: ArrayPart
相似C语言的这个代码.
typedef struct { NI len; NI cap; NI data[]; } MySeq;
无检测的数组类型没有GC内存管理.
将来的发展方向: 无检测的数组将支持GC内存管理.
###Dynlib pragma for import(动态库的导入指示) dynlib指示可结合importc指示从动态连接库里导入符号(函数,变量等)(.dll是win的, .so是linux/unix的动态库扩展名)
proc gtk_image_new(): PGtkWidget {.cdecl, dynlib: "libgtk-x11-2.0.so", importc.}
一样支持版本控制.
proc Tcl_Eval(interp: pTcl_Interp, script: cstring): int {.cdecl, importc, dynlib: "libtcl(|8.5|8.4|8.3).so.(1|0)".}
运行时会依照这个循序搜索动态库文件.
libtcl.so.1 libtcl.so.0 libtcl8.5.so.1 libtcl8.5.so.0 libtcl8.4.so.1 libtcl8.4.so.0 libtcl8.3.so.1 libtcl8.3.so.0
动态库导入不只支持常数字符串, 一样支持通常的字符串表达式.
import os proc getDllName: string = result = "mylib.dll" if existsFile(result): return result = "mylib2.dll" if existsFile(result): return quit("could not load dynamic library") proc myImport(s: cstring) {.cdecl, importc, dynlib: getDllName().}
注意: 像libtcl(|8.5|8.4).so只支持在常数字符串, 由于它是预编译.
注意: 由于初始化循序的问题, 运行时传递变量给该指示将会失败.
注意: 动态库导入能够被 --dynlibOverride:name 命令行选项覆盖, 查看用户编译手册了解相关信息.
###Dynlib pragma for export(动态库的导出指示) dynlib指示也能能把符号导出给他人使用, 该指示没有参数且必须结合exportc指示使用.
proc exportme(): int {.cdecl, exportc, dynlib.}
动态库导出只在程序经过命令行选项 --app:lib 编译成动态连接库才有用.
##参考资料:nim manual-pragmas