C/C++命令接口为Tclshell的工具如何开发?

How to develop tool in C/C++ whose command interface is Tcl shell?

假设需要开发一个用C/C++编写并具有Tcl命令行界面的工具X,步骤或方法是什么?

我知道 Tcl C API 可以通过为它编写 C 扩展来扩展 Tcl。

通常,SWIG(简化包装器和界面生成器)是必经之路。 SWIG HOMEPAGE

这样,您可以在C/C++中编写代码并定义要公开的接口。

假设您有一些要添加到 Tcl 中的 C 函数:

/* File : example.c */

 #include <time.h>
 double My_variable = 3.0;

 int fact(int n) {
     if (n <= 1) return 1;
     else return n*fact(n-1);
 }

 int my_mod(int x, int y) {
     return (x%y);
 }

 char *get_time()
 {
     time_t ltime;
     time(&ltime);
     return ctime(&ltime);
 }

现在,为了将这些文件添加到您喜欢的语言中,您需要编写一个 "interface file" 作为 SWIG 的输入。这些 C 函数的接口文件可能如下所示:

 /* example.i */
 %module example
 %{
 /* Put header files here or function declarations like below */
 extern double My_variable;
 extern int fact(int n);
 extern int my_mod(int x, int y);
 extern char *get_time();
 %}

 extern double My_variable;
 extern int fact(int n);
 extern int my_mod(int x, int y);
 extern char *get_time();

在 UNIX 提示符下,键入以下内容:

 unix % swig -tcl example.i
 unix % gcc -fpic -c example.c example_wrap.c \
        -I/usr/local/include 
 unix % gcc -shared example.o example_wrap.o -o example.so
 unix % tclsh
 % load ./example.so example
 % puts $My_variable
 3.0
 % fact 5
 120
 % my_mod 7 3
 1
 % get_time
 Sun Feb 11 23:01:07 2018

swig 命令生成一个文件 example_wrap.c,应编译该文件并将其与程序的其余部分链接。在这种情况下,我们已经构建了一个可动态加载的扩展,可以使用 'load' 命令将其加载到 Tcl 解释器中。

取自http://www.swig.org/tutorial.html

你想要做的是嵌入 Tcl(完全是一个受支持的用例;Tcl 记得它是一个 C 库)但仍在做一些事情 tclsh-like .最简单的方法是:

获取 tclAppInit.c 的副本(例如,this 是我写这篇文章时 Tcl 8.6 源代码树中的当前副本)并修改它,可能是通过放置代码来注册您的额外内容Tcl_AppInit() 函数中的命令、linked 变量等;你可能 trim 一堆东西就足够了。然后构建 link 直接针对 Tcl 库( 没有 存根)以有效地获得具有额外功能的自定义 tclsh。

如果您对交互式使用不感兴趣,您可以更广泛地使用 Tcl 的 API。 non-interactive使用的核心是:

// IMPORTANT: Initialises the Tcl library internals!
Tcl_FindExecutable(argv[0]);

Tcl_Interp *interp = Tcl_CreateInterp();
// Register your custom stuff here

int code = Tcl_Eval(interp, "your script");
// Or Tcl_EvalFile(interp, "yourScriptFile.tcl");

const char *result = Tcl_GetStringResult(interp);
if (code == TCL_ERROR) {
    // Really good idea to print out error messages
    fprintf(stderr, "ERROR: %s\n", result);
    // Probably a good idea to print error traces too; easier from in Tcl
    Tcl_Eval(interp, "puts stderr $errorInfo");
    exit(1);
}

// Print a non-empty result
if (result[0]) {
    printf("%s\n", result);
}

这就是您所需要的一切,除非您进行交互式使用,这就是 Tcl_Main() 变得真正有用​​的时候(它处理了很多额外的繁琐细节),示例 tclAppInit.c(提到上面)展示了如何使用。