Nim各類pragma使用方法

##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

相關文章
相關標籤/搜索