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支持和动态类型,可惜自己对编译原理了解太少,完全不会搞,就再没动过..源码在这里

另外闲着没事看了一下作者的简介,也是个闪瞎我双眼的大牛:法布里斯 贝拉