3

我需要一个简单的 C 程序来创建 tcl 解释器,初始化 tcl 和 tk,然后加载给定的 tcl/tk 脚本。我想使用 tcl 和 tk 存根(以确保程序将在具有不同版本 tcl/tk 的计算机上运行)。我将使用这个程序而不是运行 wish(因为我有可移植性问题)。

#include <stdio.h>
#include <stdlib.h>
#include <tcl.h>
#include <tk.h>

int AppInit(Tcl_Interp *interp) {
  if(Tcl_Init(interp) == TCL_ERROR) return TCL_ERROR;
  if(Tk_Init(interp) == TCL_ERROR) return TCL_ERROR;
  Tcl_EvalFile(interp,"myscript.tcl");
  return TCL_OK;
}

int main(int argc, char *argv[]) {
  Tk_Main(argc, argv, AppInit);
  return 0;
}

我尝试使用以下命令编译(在 GNU/Linux 上)。程序编译没有错误,但随后因分段错误而停止。

gcc -I/usr/include/tcl8.5 -DUSE_TCL_STUBS -DUSE_TK_STUBS -o main.exe ../main.c /usr/lib/libtclstub.a /usr/lib/libtkstub.a
4

1 回答 1

3

tl;博士

使用 编译程序时main不应使用存根。相反,构建没有USE_TCL_STUBSandUSE_TK_STUBS定义并链接到libtcl.soand libtk.so(好吧,无论版本号附加到它们)。由于 Unix 链接器的特殊性,您应该将 Tk 库放在 Tcl 库之前(您可能还需要手动链接其他库,例如 X 库;链接有时是一种魔法)。

背景说明

存根机制旨在允许 Tcl 扩展库使用 Tcl(和 Tk)的 C API,而不必与 Tcl 库本身链接。这意味着该库与加载它的过程中存在的 Tcl 的确切版本无关,而是仅取决于特定版本的 Tcl API(Tcl 非常擅长管理长期 API 和 ABI 兼容性)。然而,这一切都取决于使用一种非常特殊的指针调用库初始化函数,该指针允许查找所有其他 API 函数。(一旦 Tcl 以这种方式引导,查找所有其他 API 就变得容易得多。)像您一样创建应用程序时,您会遇到一个问题,即没有现有的引导 Tcl 库实例供您的代码链接;Tcl_Main并且Tk_Main由于这个确切的原因是非存根函数)。

那些在家里阅读的人可能会认为这是 Tcl 重复了系统动态链接器所做的很多事情。你是对的。然而,系统动态链接器有许多方法可以做一些不太正确的事情(例如,当一个库有多个版本时,它会变得非常混乱),并且它在平台之间的功能略有不同。Tcl 使用它自己的机制,因为这使得它在任何地方都能精确地工作(对于 Tcl),让我们更好地控制长期 ABI 兼容性。

上面关于存根的规则有一个例外,那就是tclkit,它是单个文件中的完整 Tcl 和 Tk 运行时(加上一个小型 NoSQL DB)。不过,tclkit 的引导代码非常复杂。你不想处理那些你不需要的事情!如果你想要一个单文件的 Tcl 运行时,你可以使用 tclkit(或者其他几个可以做几乎相同事情的系统之一)。

于 2012-06-15T08:51:32.467 回答