首页 > 代码库 > TCL和C的混合编程
TCL和C的混合编程
Tcl作为一种脚本语言具有非常高的效率,因此在实际的设计中我们可以采用TCl来完成一些对性能要求不是很高但是用C却需要很长开发周期的任务,而只把执行时间有很高的要求的部分采用C语言设计
1 在C中引用TCL时要包含头文件<tcl.h>编写,c中的所有tcl命令均通过TCL解释器Tcl_Interp完成。他是一个C结构体
主要的函数有
Tcl_Interp * Tcl_CreateInterp();//产生tcl解释器
Tcl_DeleteInterp(Tcl_Interp *interp); //删除tcl解释器
Tcl_InterpDeleted(Tcl_Interp *interp); //检测解释器是否已经被删除,未被删除则返回非零值
Tcl_Interp *Tcl_CreateSlave(Tcl_Interp *Interp,CONST char slavename, int issafe ); //创建从解释器,issafe决定是否创建安全的解释器
int Tcl_IsSafe(Tcl_Interp *interp);//检测是否为安全的解释器,安全返回1, 不安全返回0
int Tcl_MakeSafe(Tcl_Interp *interp); //产生安全的解释器
Tcl_Interp *Tcl_GetSlave(Tcl_Interp *Interp, const char *slavename);//返回从解释器
Tcl_Interp *Tcl_GetMaster(Tcl_Interp *Interp);//获取从解释器的主解释器
Tcl_Interp *Tcl_GetInterpPath(Tcl_Interp *askinterp, Tcl_interp *slaveInterp);//成功返回TCL_OK,否则返回TCL_ERROR,askinterp会被设为askinterp和slaveInterp之间的路径。
一.tcl中调用c/c++ 主要是利用c/c++处理复杂逻辑的能力,对于tcl来说,实际上就是一个扩展,因为你可以通过Tcl_CreateCommand函数,创建出一个新的tcl命令。 具体操作方法需要分情况:1.如果当前tcl版本支持load命令语法:load libpackage.so意义:在tcl中,当一个动态库libpackage.so被装载时,tcl会调用其中名为package_Init的函数,记住,名字一定不能错,包括大小写。这样,你就获得了一个入口,可以进入c/c++啦,你可以干任何事,当然,最重要的还是Tcl_CreateCommand了。函数原型:Tcl_Command Tcl_CreateCommand(interp, cmdName, proc, clientData, deleteProc)意义:创建一个新的tcl命令cmdName,对应的操作函数指针为proc。这里以一个阶乘算法为例:目的是提供一个名为myfract的tcl命令,只接收一个参数,例如:myfract 10表示计算10的阶乘。【fract.c】:#include "tcl.h"int Tcl_myfract(ClientData notUsed, Tcl_Interp *interp, int argc, char **argv){ int i, j; double res=1.0; char re[30]; if (argc > 2) { Tcl_SetResult(interp, "wrong args: should be myfract", TCL_VOLATILE); return TCL_ERROR; } if (Tcl_GetInt(interp, argv[1], &i) != TCL_OK) { return TCL_ERROR; } for (j=1;j<=i;j++) res *= j; sprintf(re,"%le",res); Tcl_SetResult(interp, re, TCL_VOLATILE); return TCL_OK;}int Fract_Init(Tcl_Interp *Interp) { Tcl_CreateCommand (Interp, "myfract", (Tcl_CmdProc *)Tcl_myfract, (ClientData)0, 0); return TCL_OK;}【Makefile】:t = libfract.soall: $tclean: rm -f $t corelibfract.so: fract.c gcc -I. -shared -o $@ fract.c【test.tcl】: #!/usr/bin/tclshload ./libfract.soset tcl_flag [catch {myfract 2} return_str]if {$tcl_flag != 0} { puts "OK:$return_str"} else { puts "Error:$return_str"}运行:linux:~/test/tcl # ./test.tcl2.000000e+00【小结】:这里有两点需要注意,第一,Fract_Init是由TCL到c/c++的入口,而Tcl_CreateCommand则是由c/c++到TCL的入口;第二,编译动态库时不必链接tcl的开发库,因为这里仅仅需要引用。还有swig可以使用【fract.i】:%module fractextern double myfract(int);swig的语法这里就不详述了,具体可参见www.swig.org。第一行表示模块名称叫做fract,对应前面的c/c++代码,就是说要创建的动态库叫做libfract.so;第二行表示将要导出的函数,这个函数是需要你自己定义的。【fract.c】:double myfract(int n){ double res=1.0; int j; for (j=1;j<=n;j++) { res *= j; } return(res);}【Makefile】:t = fract_wrap.c libfract.soall: $tclean: rm -f $t core *.doc *.ofract_wrap.c: fract.i swig -tcl fract.ilibfract.so:fract_wrap.c gcc -c fract.c fract_wrap.c运行:linux:~/test/tcl/swig # ./test.tcl2.02.如果当前tcl版本不支持load命令 我不知道哪个版本会这么原始,但即便如此,仍然有解决方法,那就是把扩展模块静态链接到执行文件中去,具体操作如下:【main.c】:#include <tcl.h>#include <stdio.h>int Tcl_AppInit(Tcl_Interp *interp);int main(int argc, char *argv[]) { Tcl_Main(argc, argv, Tcl_AppInit);}int Tcl_AppInit(Tcl_Interp *interp) { /* Initialize Tcl */ if (Tcl_Init(interp) == TCL_ERROR) { return TCL_ERROR; } /* Initialize our extension */ if (Fract_Init(interp) == TCL_ERROR) { return TCL_ERROR; } return TCL_OK;}【Makefile】:t = mainTCL_LIBS = -L/usr/lib -ltcl8.4all: $tclean: rm -f $t coremain:main.c fract.c gcc -I. ${TCL_LIBS} -o $@ main.c fract.c运行:linux:~/test/tcl # ./main% myfract 84.032000e+04% exitlinux:~/test/tcl # ldd main linux-gate.so.1 => (0xffffe000) libtcl8.4.so => /usr/lib/libtcl8.4.so (0x40030000) libc.so.6 => /lib/tls/libc.so.6 (0x400d8000) libdl.so.2 => /lib/libdl.so.2 (0x401f1000) libm.so.6 => /lib/tls/libm.so.6 (0x401f5000) /lib/ld-linux.so.2 (0x40000000) 我们发现,代码中重写了Tcl_AppInit函数,在tcl手册中该函数被形容为一个``hook‘‘ procedure,很奇妙的东西。因为最后会创建一个类似tclsh的执行文件,所以,tcl开发库必须链接上。二.c/c++中调用tcl 之所以会有这样的做法,主要是想利用tcl的客户化能力。 例如把流程写在tcl脚本中,c/c++代码中仅仅执行Tcl_EvalFile就可以了,对于不同的服务,我们可以借助某种手段,绑定一个流程脚本,这样,当增加新的服务时或者服务流程需要变更时,不必修改源码,只要增加或修改配置文件/脚本文件即可,这也就实现了我们所说的客户化。我们只讨论几个常用的函数:Tcl_CreateInterp - 创建一个tcl解释器Tcl_Eval - 执行一个tcl命令Tcl_VarEval - 类似Tcl_Eval,只不过这个命令是被参数串起来的Tcl_EvalFile - 执行一个tcl脚本Tcl_SetVar - 设置tcl变量Tcl_GetVar - 获取tcl变量的值下面是一个用法测试:【test.c】:#include <tcl.h>#include <string.h>#include <stdlib.h>#include <assert.h>int main(){ Tcl_Interp* interp; char *p; int tclres;//create interpreter interp = Tcl_CreateInterp();/* * Test Tcl_SetVar */ char *varname = "SERVICE_NAME"; char *varval = "MyServer"; p = (char *)Tcl_SetVar(interp, varname, varval, TCL_GLOBAL_ONLY); assert ( p!=NULL && !strncmp(p, "MyServer", 8) ); /* * Test Tcl_GetVar */ p = (char *)Tcl_GetVar(interp, "SERVICE_NAME", TCL_GLOBAL_ONLY); assert ( p!=NULL && !strncmp(p, "MyServer", 8) );/* * Test Tcl_Eval */ tclres = Tcl_eval_r(interp, "set CFG_val world"); assert ( tclres == TCL_OK) ; p = (char *)Tcl_GetVar(interp, "CFG_val", TCL_GLOBAL_ONLY); assert ( p!=NULL && !strncmp(p, "world", 5) );/* * Test Tcl_VarEval */ char *home = "./"; tclres = Tcl_Vareval_r(interp, "source ", home, "test.cfg", NULL) ; assert ( tclres == TCL_OK) ; p = (char *)Tcl_GetVar(interp, "CFG_val", TCL_GLOBAL_ONLY); assert ( p!=NULL && !strncmp(p, "hello", 5) );/* * Test Tcl_EvalFile */ char *path = "./test.cfg"; tclres = Tcl_EvalFile(interp, path); assert ( tclres == TCL_OK) ; p = (char *)Tcl_GetVar(interp, "CFG_val", TCL_GLOBAL_ONLY); assert ( p!=NULL && !strncmp(p, "hello", 5) );//END printf("OK/n"); }【Makefile】:t = testTCL_LIBS = -L/usr/lib -ltcl8.4all: $tclean: rm -f $t coretest: test.c gcc -I. ${TCL_LIBS} -o $@ test.clinux:~/test/tcl/test # ./testOK
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。