如何实现自定义 Tcl 事件循环?

How to implement custom Tcl event loop?

我看到了一些其他帖子,但它们都在 TCL 中,我希望在我的嵌入式解释器中使用 C++ 来执行此操作。我遇到的问题是我需要自己的事件循环,这样我就可以从网络上读取信息,在某些地方我可以检查套接字。我看到 Tcl_SetMainLoop() 但不确定它是如何工作的。

我的应用程序结构如下。

int Tcl_AppInit(Tcl_Interp *interp)
{

    if (Tcl_Init(interp) == TCL_ERROR) 
        return TCL_ERROR;

    
    if (MyTcl_Init(interp) == TCL_ERROR) 
        return TCL_ERROR;

    return TCL_OK;
}

int main(int argc, char* argv[])
{
    //...

    Tcl_Main(argc, argv, Tcl_AppInit);
    
    /* Replaced Tcl_Main for this, didn't work.
    Tcl_SetMainLoop([]() {
        Tcl_DoOneEvent(0);
    });
    */
}

我不使用 Tk。关于如何设置自定义事件循环有什么想法吗?

如何在 Tcl 中进行事件循环的经典示例是 vwait 命令。其核心是这段代码:

    done = 0;
    foundEvent = 1;
    while (!done && foundEvent) {
        foundEvent = Tcl_DoOneEvent(TCL_ALL_EVENTS);
        if (Tcl_Canceled(interp, TCL_LEAVE_ERR_MSG) == TCL_ERROR) {
            break;
        }
        if (Tcl_LimitExceeded(interp)) {
            Tcl_ResetResult(interp);
            Tcl_SetObjResult(interp, Tcl_NewStringObj("limit exceeded", -1));
            break;
        }
    }

done变量由结束循环触发事件发生时的回调设置(写入变量),您自己的代码中可能会省略带有取消和限制管理的子句.精简版是:

    done = 0;
    foundEvent = 1;
    while (!done && foundEvent) {
        foundEvent = Tcl_DoOneEvent(TCL_ALL_EVENTS);
    }

是的,它将大部分工作委托给 Tcl_DoOneEvent(通知层的一部分)。如果您想将您喜欢的套接字插入其中,最简单的方法是编写您自己的事件处理程序并使用 Tcl_CreateFileHandler or Tcl_CreateChannelHandler 安装它;可能是前者,假设您不在 Windows 上(因为它依赖于文件描述符的 POSIX 概念)并且在 Windows 上您需要做一些工作来制作频道类型(因为底层通知系统在该平台上的工作原理略有不同)。一旦你这样做了,你就可以使用标准的事件循环;您的自定义处理程序(在 C 或 C++ 中)将在正确的时间被调用。 (ClientData 参数实际上只是一个任意指针,它将通过 Tcl 不加解释地传递给您的回调;您可能会将其转换回指向真实对象类型的指针,作为您在回调中做的第一件事;这就是其他人所做的。)


可以使用Tcl_SetNotifier安装您自己的低级事件处理引擎——如果您愿意,请尽早非常想那样做——但这通常是个坏主意。特别是,您不需要自定义事件循环来处理自定义套接字类型。自定义事件循环的更好用途是将 Tk 与其他一些 GUI 工具包集成,但这是一个 far 更复杂的用例!