利用Tcl C API开发过程中,为了访问C中的资源(resouce),需要传递该资源的指针给Tcl命令。
使用全局变量自然是最容易的方法。
如果始终只需要一个资源指针,则利用Tcl API中的ClientData
是一个不错的办法。
gearman_client_st client; int TclObjCmd_gearman_client(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { gearman_client_st &client = *(gearman_client_st *)clientData; return TCL_OK; } int Tclgearman_Init(Tcl_Interp *interp) { Tcl_CreateObjCommand(interp, "gearman::client", TclObjCmd_gearman_worker, &client, NULL); return TCL_OK; }
ClientData
本质上是一个void
类型的指针。
当需要区分多个不同的指针实例的时候,问题就开始变得复杂。比如典型的例子:
set client1 [gearman::client create] $client2 submit "reverse" "data 1" set client2 [gearman::client create] $client2 submit "reverse" "data 2"
这时TclObjCmd_gearman_client
函数就需要使用不同的client指针。无论具体如何实现,都可以确定的是TclObjCmd_gearman_client
一定都必须拿到一个Tcl_Obj
对象以作为不同实例的区分。
这个Tcl_Obj对象可以代表一个Tcl Command,也可以代表一个Tcl Variable参数。
如果是Tcl Command,则可以借助ClientData
存储对象指针。
如果是Tcl Variable,则需要在Tcl Variable和pointer之间进行转换。
主要是处理命令的删除。
定义新的变量类型。
指针和字符串之间的转换。(主要问题是没法自动实现资源的释放)
关键概念:entry即保存指针——确切说是用户数据结构——的地方。
/*--------------------------------------------------------------*/ /* User Part */ /*--------------------------------------------------------------*/ struct HandleEntry { void *pointer; }; void *userPtr; // pointer to user data structure #include <tclExtend.h> /*--------------------------------------------------------------*/ /* Init the table */ /*--------------------------------------------------------------*/ //# void_pt Tcl_HandleTblInit (const char *handleBase, int entrySize, int initEntries); void_pt headerPtr = Tcl_HandleTblInit("ptr", sizeof(HandleEntry), 24); /*--------------------------------------------------------------*/ /* Register the pointer */ /*--------------------------------------------------------------*/ char handlePtr[64]; //# void_pt Tcl_HandleAlloc (void_pt headerPtr, char *handlePtr); void_pt entryPtr = Tcl_HandleAlloc(headerPtr, handlePtr); ((HandleEntry *)entryPtr)->pointer = userPtr; /*--------------------------------------------------------------*/ /* Access the pointer */ /*--------------------------------------------------------------*/ //# void_pt Tcl_HandleXlate (Tcl_Interp *interp, void_pt headerPtr, const char *handle); void_pt entryPtr = Tcl_HandleXlate(interp, headerPtr, handlePtr); userPtr = ((HandleEntry *)entryPtr)->pointer; /*--------------------------------------------------------------*/ /* Traverse the table */ /*--------------------------------------------------------------*/ int walkKey = -1; while(1){ void_pt entryPtr = Tcl_HandleWalk(headerPtr, &walkKey); // get the entry if(entryPtr==NULL) break; // come to the end char handlePtr[64]; Tcl_WalkKeyToHandle(headerPtr, handlePtr); // get the handler Tcl_HandleFree(headerPtr, entryPtr); // delete the entry Tcl_HandleTblRelease(headerPtr); // release the table when goes to zero }