標籤:style http 使用 檔案 io for cti 代碼
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 [email protected] 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.0 2.如果當前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 [email protected] 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 [email protected] test.clinux:~/test/tcl/test # ./testOK