利用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
}