Tcl/Tk Insight: C/C++ 扩展

风行水上 @ 2009-07-04 09:59:57
标签:
    «目录»

    Tcl 作为一种脚本语言,有其优点,因而在C/C++程序中加入执行Tcl脚本的能力,可以提供脚本。
    Tcl 作为一种脚本语言,终有其局限性。比如速度和某些功能。在Tcl中调用 C/C++ 使得 Tcl 可以扩展。

    Tcl/Tk 和 C/C++ 的关系可以概括为三种情况:

    1. 在C/C++程序里面执行 Tcl 语句。 (Tcl Interpreter)
    2. 在 Tcl 中调用 C/C++ 实现的功能。 (Tcl Package)
    3. C/C++程序本身是基于 Tcl/Tk 的程序。(Big Wish)

    在C/C++程序里面执行 Tcl 语句 (Tcl Interpreter)

    由于Tcl语言是解释器执行语言,可以想象问题的关键是两部分:

    • 创建Tcl解释器(Tcl_Interp)
    • 执行Tcl命令

    具体实现可以参考 Tcl解释器(Tcl Interpreter)的创建和管理

    在 Tcl 中调用 C/C++ 实现的功能 (Tcl Package)

    这个常用于扩展包。

    在Tcl中读入编译好的C/C++程序模块(.so 文件或者.dll文件)。

    • Tcl 通过命令 load 加载编译好的程序模块
    load filename.so pkgName
    # Tcl解释器将会调用C/C++ 模块种特定的函数(Pkgname_Init/Pkgname_SafeInit) 作为程序入口。
    # 该入口函数通常用来添加Tcl命令。
    
    # 默认是读入当前的解释器。也可以指定解释器如下
    load filename.so pkgName interp
    
    • C/C++
    /************************************************ 
    * 以下两者之一是函数原型
    * 后者用于safe intepreter
    *************************************************/
    int Pkgname_Init(Tcl_Interp *interp);
    int Pkgname_SafeInit(Tcl_Interp *interp);
    
    //e.g.
    int Pkgname_Init(Tcl_Interp *interp){
      return TCL_OK;
    }
    
    • 关于如何在C中创建新的Tcl命令,稍后介绍。

    C/C++程序本身是基于 Tcl/Tk 的程序

    这种程序的好处是,在拥有C的强大计算能力的同时,提供给用户一个交互式界面。
    如果加上Tk,还可以实现GUI。这样的程序有时也被称作 //bigwish//

    • Hello World 程序。执行起来就像是一个Tcl Shell。
    #include <tk.h>
    #include <tcl.h>
    #include <iostream>
    
    typedef int Tcl_AppInitProc(Tcl_Interp *interp);
    
    int appInitProc(Tcl_Interp *interp){
        std::cout << "App Init" << std::endl;
        Tcl_Init(interp);
        Tk_Init(interp);
    
        Tk_Window topwin = Tk_MainWindow(interp);
        Tk_SetAppName(topwin,"NOYESNO");
        Tcl_Eval(interp,"wm title . NOYESNO");
        //Tcl_EvalFile(interp,"debug.tcl");
        return 0;
    }
    
    int main(int argc, char** argv){
        Tk_Main(argc, argv, appInitProc);
        //Tk_MainLoop();
        return 0;
    }
    
    

    实际应用中常主要的问题是初始化。包括下面几项任务

    设置环境变量 TCL_LIBRARY 和 TK_LIBRARY

    • 这两个变量是用于帮助定位 init.tcl 和 tk.tcl

    方法一:

        const char *argv0 = Tcl_GetNameOfExecutable();
        
        char exec_dir[255];
        strcpy(exec_dir,argv0);
        char *pos = strrchr(exec_dir,'/');
        *pos = '\0';
        
        
        char buf[255];
        sprintf(buf,"TCL_LIBRARY=%s/lib/tcl8.5",exec_dir);
        putenv(buf); 
    

    方法二:

        const char *argv0 = Tcl_GetNameOfExecutable();
        const char *tcl_init = 
          "set t [file dirname [info nameofexecutable]] \n"
          "set env(TCL_LIBRARY) [file join $t lib tcl8.5] \n"
          "set env(TK_LIBRARY)  [file join $t lib tk8.5] \n"
          "unset t"
          ;
          
        if (Tcl_Eval(interp,tcl_init) != TCL_OK){
            return TCL_ERROR;
        }
        
    

    应该说还是第一种方法作为C/C++程序来说更纯粹些。

    设置默认字符编码

    Tcl_SetSystemEncoding(interp,"utf-8");
    Tcl_SetDefaultEncodingDir("/some/path/lib/tcl8.5/encoding");
    

    执行初始化脚本

        const char *nyno_init_file = "noyesno.tcl";
        if(access(nyno_init_file,R_OK) == 0){
            Tcl_EvalFile(interp,nyno_init_file);
        }
    

    参考资源

    标签:

      分享到:
      comments powered by Disqus

      31/35ms