为tcl编写扩展
时间:2010-09-20 来源:阿尔法蕾特
绝大多数虚机运行的语言,为了提升速度或访问操作系统API等原因,都需要用能够链接二进制库的语言为其编写扩展。而扩展其实就是为了使链接二进制库的语言与最终调用的虚机语言能够互相理解彼此的类型系统及协调内存管理模式。很多人知道lua拥有一个非常易于扩展的CAPI,而与Perl同时代的Tcl早就具备了这种简练强大的CAPI(至少比起Perl的h2xs是要直观不少,这也是那时普遍认为tcl的胶水性比perl更强的原因之一),而且以DString类型提供了非常良好的中文支持,因此Tcl确实是嵌入、扩展开发的最理想语言之一,除了相较lua速度会较慢(这点对于高并发的游戏服务器领域比较敏感,所以Hipe erlang才显得如此重要)。下面我们就实际来编写一个简单的tcl扩展,作为编写tcl扩展快乐旅程的开始:
#######################
######## ex.c #########
#######################
#include <tcl.h>
static int
Hello_Cmd(ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
Tcl_SetObjResult(interp, Tcl_NewStringObj("Hello, World!", -1));
return TCL_OK;
}
int DLLEXPORT
Ex_Init(Tcl_Interp *interp)
{
if (Tcl_InitStubs(interp, TCL_VERSION, 0) == NULL) {
return TCL_ERROR;
}
/* changed this to check for an error - GPS */
if (Tcl_PkgProvide(interp, "Hello", "1.0") == TCL_ERROR) {
return TCL_ERROR;
}
Tcl_CreateObjCommand(interp, "hello", Hello_Cmd, NULL, NULL);
return TCL_OK;
}
至此我们创建了一个tcl解析器用于运行Hello_Cmd函数,并通过Tcl_SetObjResult设置该函数的返回值,然后通过Tcl_CreateObjCommand将Hello_Cmd注册给tcl,在tcl的世界中Hello_Cmd的名字叫hello,可通过调用hello命令(tcl中的函数称命令)来调用扩展中的Hello_Cmd函数。接下来我们用以下命令将上面的ex.c文件编译成动态库。
gcc -shared -o libex.so -DUSE_TCL_STUBS ex.c -I/usr/include/tcl8.6 -L/usr/lib -ltclstub8.6
不同机器的include和lib目录可能不同,请参考tclConfig.sh的内容,在我的Mandriva 2010 spring中它存在于/usr/lib/tclConfig.sh,成功后会在当前目录生成libex.so动态库。接下来我们来写一段使用这个扩展的tcl:
#######################
####### use.tcl #######
#######################
#!/usr/bin/tclsh
load ./libex
puts [hello]
运行一下tclsh use.tcl,如果你看到输出:Hello, World!,那么恭喜你成功完成了第一个tcl扩展的编写。很轻松而且很有趣不是么。