使用Tcl C API从Tcl_Channel获取文件指针

lvjbypge  于 2023-04-29  发布在  其他
关注(0)|答案(2)|浏览(147)

我想使用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频道关联的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);

但是这些似乎都不起作用并且以分段故障结束。e.

% myfunc::load $file
Segmentation fault (core dumped)
qv7cva1a

qv7cva1a1#

Tcl不在任何平台上使用C stdio;stdio不太擅长处理异步I/O。相反,Tcl在任何可能的地方直接调用系统;在某些平台上,这可以避免一些非常讨厌的bug。
在所有POSIX平台(包括Linux和macOS)上,底层句柄实际上总是一个文件描述符,其类型为int。在Windows上,它可能是许多东西中的一个;通常是某种类型的HANDLE,有时更复杂。

int fd;

if (Tcl_GetChannelHandle(chan, TCL_READABLE, (ClientData *) &fd) != TCL_OK) {
     // Some sort of failure...
     return TCL_ERROR;
}

***仅在POSIX平台上,***您还可以can do

FILE *file;

if (Tcl_GetOpenFile(interp, channelName, 0, 1, (ClientData *) &file) != TCL_OK) {
     // Some sort of failure...
     return TCL_ERROR;
}

这有一个有点奇怪的类型签名;最后一个参数 should 是一个FILE **,但这并不是为了避免将大量的stdio直接绑定到Tcl的API中。Tcl在内部仍然不对通道使用FILE *,但是在这种情况下,它会将文件描述符打包成一个(使用fdopen())。

vbkedwbf

vbkedwbf2#

我假设有三种不同的方法来获取文件指针。我只有最后一个函数的信息,我认为这是对函数Tcl_GetOpenFile的手册页的误读
你需要做的是:

FILE *fp = 0;
Tcl_GetOpenFile(interp, chan_name, 0, 1, (ClientData *) &fp);

...并且不要忘记检查函数的返回代码。如果你阅读了这个函数的源代码,那么你可以看到它的实现:
函数的最后一个参数:void **filePtr
其被指定为:*filePtr = f;,其中f是fdopen的结果。
也就是说,它只是将FILE *分配给传入的内容。

相关问题