指令碼新選擇——用C做指令碼

網事已瘋發表於2014-11-15

blog文章連結:http://www.godebug.org/index.php/archives/130/ 很早之前就知道了有tcc這麼個玩意,不過當時對這玩意興趣不大,因為他官網上主要是在說tcc的編譯速度很快甩GCC好幾條街之類的,當時覺得似乎意義不是很大就沒再關心過,可是沒想到這個小玩意給了我不少的驚訝,之前閒著麼事逛陳皓的blog發現它能把C當指令碼使,感覺有點驚訝,不過覺得C比起bash或者python什麼的毫無優勢可言,於是還是隻當是個玩具。再後來因為專案需要用指令碼來實現一些邏輯經常需要改動的地方,就一直在用lua+luabridge來搞的,不過習慣了類C語言的語法,總覺得lua的語法很彆扭,於是想換一個,思來想去的,考慮過python,考慮過V8搞js,可惜python和V8,要麼比較大,一堆沒用的庫;要麼編譯麻煩,還沒什麼基礎庫。正煩艹中想到了tcc,記得當時用了下他是可以呼叫C的標準庫,也可以呼叫系統API,而且還是C語言的,正合我意。

當時還想是要自己改tcc的原始碼,搞出來介面供我的程式呼叫,沒想到又仔細看了下tcc發現人家本來就提供了個libtcc,是可以直接拿來用的,這下可把我樂壞了,於是就開始學習這玩意怎麼用。到現在用libtcc當指令碼引擎也快半年了,感覺相當不錯,於是寫一篇文章來介紹下。

其實想想C++和指令碼的互動,無非就是個C++程式碼能呼叫指令碼的函式,指令碼能呼叫C++的函式,這就夠了,既然指令碼是C,而且幾乎沒有什麼語言不支援C的介面,於是libtcc的介面相當的簡單粗暴,就tcc_add_symbol和tcc_get_symbol兩個函式,add是把宿主語言中的symbol“匯出”給指令碼用,get是從指令碼中獲取symbol。這個symbol可以是變數,也可以是函式指標(函式指標本來就是一個指標型別的變數)。不過如果是變數的話可能會涉及到生命週期或者變數的size是不是比sizeof(void*)大什麼的,比較麻煩,所以我一直都是匯出的函式,要匯出變數也是弄一個get或者set函式,然後匯出函式。

互動的話,就是這麼簡單,具體使用方式請參考以下程式碼: https://github.com/avdbg/libtcc_example 裡面兩個專案,一個是直接用了libtcc自帶的的一個example,一個是把hello_win當成指令碼來用,然後中間顯示的字改成了從宿主語言中獲取。 可以看出來使用方法相當簡單,tcc_new建立一個TCCState,然後根據這個state新增程式碼字串(tcc_compile_string),新增程式碼檔案或者庫檔案(tcc_add_file),新增希望在指令碼中訪問到的函式(tcc_add_symbol),然後獲取要執行的指令碼中的函式(先tcc_relocate然後tcc_get_symbol),完事後清理現場(tcc_delete),就是這麼簡單。

至於執行速度,雖然沒跟V8、luajit或者pypy這些專門jit優化過的引擎相比,但是libtcc也是jit執行的,而且tcc作者打破圓周率世界記錄的程式就是這玩意編譯的,這玩意還能“在15秒內從原始碼編譯並啟動Linux系統”,想必不會慢到哪去..

不過C做指令碼的話有很多時候還是會覺得缺少許多指令碼語言應該有的特性,沒有lambda、靜態型別還不支援泛型什麼的不過相比起來上面的優點這些都瑕不掩瑜。

之前為了研究tcc,還把他的原始碼加了個VS的工程,也好方便除錯和學習。本來是想自己改造下libtcc加上lambda支援和動態型別,可惜自己對編譯原理了解太少,完全不會搞,就再沒動過..原始碼在這裡

另外閒著沒事看了一下作者的簡介,也是個閃瞎我雙眼的大牛:法布里斯 貝拉

相關文章