如何通过Tcl C API从Tcl_Channel获取FILE指针
获取Tcl_Channel关联的FILE指针
我希望通过Tcl C API在C语言中操作文件,Tcl脚本里的操作流程如下:
% set file [open "my_file"] file3 % myfunc::load $file
其中myfunc::load是一个C扩展实现的命令,代码如下:
#include <tcl/tcl.h> #include <stdio.h> #include <string.h> #include <errno.h> #include <stdlib.h> #define NS "myfunc" static int Load_Cmd(ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { if (objc != 2) { Tcl_SetObjResult(interp, Tcl_NewStringObj("wrong # args: should be \" " NS "::open file\"", -1)); return TCL_ERROR; } Tcl_Obj *o = objv[1]; char *chan_name = Tcl_GetString(o); Tcl_Channel chan = Tcl_GetChannel(interp, chan_name, NULL); if (chan == NULL) { return TCL_ERROR; } const Tcl_ChannelType * type = Tcl_GetChannelType(chan); if (!strcmp(type->typeName, "file")) { return TCL_ERROR; } return TCL_OK; } int DLLEXPORT Myfunc_Init(Tcl_Interp *interp) { Tcl_InitStubs(interp, TCL_VERSION, 0); Tcl_CreateNamespace(interp, NS, NULL, NULL); Tcl_CreateObjCommand(interp, NS "::load", Load_Cmd, NULL, NULL); Tcl_PkgProvide(interp, "myfunc", "1.0"); return TCL_OK; }
请问有没有办法获取与Tcl_Channel关联的FILE指针?
我尝试过以下几种方式,但都无法正常工作,最终导致段错误:
FILE *data = Tcl_GetChannelInstanceData(chan); FILE *fp = malloc(sizeof(FILE)); Tcl_GetChannelHandle(chan, TCL_READABLE, (ClientData *) fp); FILE *fp = malloc(sizeof(FILE)); Tcl_GetOpenFile(interp, chan_name, /*forWriting=*/ 0, /*checkUsage=*/1, (ClientData *) fp);
错误示例:
% myfunc::load $file Segmentation fault (core dumped)
解决方案
正确获取FILE指针的方法
对于Tcl的文件类型通道,Tcl_GetChannelHandle是正确的API,但你的用法有误:不需要提前mallocFILE指针,而是要传入一个指向FILE*的指针,让Tcl把已有的指针赋值给它。
修改后的代码片段如下:
// 先确认通道是文件类型 const Tcl_ChannelType *type = Tcl_GetChannelType(chan); if (strcmp(type->typeName, "file") != 0) { Tcl_SetObjResult(interp, Tcl_NewStringObj("channel is not a file", -1)); return TCL_ERROR; } // 正确获取FILE指针 FILE *fp; if (Tcl_GetChannelHandle(chan, TCL_READABLE, (ClientData *)&fp) != TCL_OK) { Tcl_SetObjResult(interp, Tcl_NewStringObj("failed to get file handle", -1)); return TCL_ERROR; } // 现在可以正常使用fp了,比如读取文件 char buf[1024]; size_t n = fread(buf, 1, sizeof(buf), fp); // ...
错误原因分析
Tcl_GetChannelInstanceData:返回的是通道类型的私有数据,对于文件通道来说,它不是直接的FILE*,直接强转必然出错。- 调用
Tcl_GetChannelHandle时错误malloc了FILE结构,Tcl期望接收的是FILE**,用来存储内部已打开的FILE*指针,多余的malloc会导致指针赋值错误,引发段错误。 Tcl_GetOpenFile是旧版API,现在推荐使用Tcl_GetChannelHandle,且你的用法同样错误,不该mallocFILE结构。
注意:Tcl的文件通道可能处于缓冲状态,直接用FILE*操作时,要避免和Tcl的通道操作冲突(比如不要同时用Tcl的read命令和C的fread操作同一个通道,除非手动同步缓冲)。
内容的提问来源于stack exchange,提问作者Lukas
相关产品推荐
相关产品推荐

