如何静态链接最初打算用作Tcl扩展的库?

时间:2014-10-02 11:36:58

标签: tcl

我有一个库,最初是作为可加载的Tcl扩展而构建的。我现在正尝试将它用于稍微不同的目的(带有libtecla的交互式shell用于制表符完成和历史记录),目前我无法使Tcl_StaticPackage()load {} $lib组合起作用。奇怪的是,虽然不起作用,但它也不会产生错误。

我已将演示问题所需的代码减少到:

的main.cpp

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

extern "C" {
    int Demolib_Init(Tcl_Interp *);
    int Demolib_SafeInit(Tcl_Interp *);
}

int main(int argc, char *argv[])
{
    Tcl_Interp *interp;

    interp = Tcl_CreateInterp();

    if (!interp)
    {
        perror("Couldn't create interpreter");
        return 1;
    }

    if (TCL_OK != Tcl_Init(interp))
    {
        perror("Couldn't initialize Tcl");
        return 2;
    }

    Tcl_StaticPackage(interp, "Demolib", Demolib_Init, Demolib_SafeInit);

    printf("Via 'load'...\n");
    if (TCL_OK != Tcl_Eval(interp, "load {} Demolib"))
    {
        fprintf(stderr, "Err : %s\n", Tcl_GetStringResult(interp));
    }
    else
    {
        printf("Ok  : %s\n", Tcl_GetStringResult(interp));
    }

    printf("\nVia 'Demolib_Init()'...\n");
    Demolib_Init(interp);

    return 0;
}

demolib.cpp

#include <tcl.h>
#include <stdio.h>
#include "demolib.h"

#ifdef __cplusplus
extern "C" {
#endif

int DLLEXPORT Demolib_Init(Tcl_Interp *interp)
{
    printf("Pre-stubs\n");

#ifdef USE_TCL_STUBS
    if (Tcl_InitStubs(interp, TCL_VERSION, 0) == NULL)
    {
        return TCL_ERROR;
    }
#endif

    printf("Pre-provide\n");

    if (Tcl_PkgProvide(interp, "Demolib", "0.0") == TCL_ERROR)
    {
        return TCL_ERROR;
    }

    printf("Pre-return\n");

    return TCL_OK;
}

int DLLEXPORT Demolib_SafeInit(Tcl_Interp *interp)
{
    return Demolib_Init(interp);
}

#ifdef __cplusplus
}
#endif

(虽然演示不需要是.cpp,真正的代码确实如此)

如果以下"Demolib"load {} ...中的库名称不匹配,则会引发错误,因此Tcl_StaticPackage()具有load可以看到的效果。

    Tcl_StaticPackage(interp, "Demolib", Demolib_Init, Demolib_SafeInit);
    ...
    if (TCL_OK != Tcl_Eval(interp, "load {} Demolib"))

但是,Demolib_Init()Demolib_SafeInit()都没有被调用过。我可以直接调用它,它可以说是更整洁(没有Tcl_Eval(...)调用),但我想了解发生了什么(没有)。在最终的应用程序中,load {} ...的时间将由脚本确定,因此需要此功能。

请注意,我故意不调用Tcl_Main()或进入Tcl事件循环 - libtecla最终将提供提示。

我错过了什么?

1 个答案:

答案 0 :(得分:2)

使用MSVC 2013和Tcl 8.6在Windows上使用提供的两个文件尝试此操作:

C:\src\Files\DemoTcl>cl -nologo -I/opt/tcl/include -D_DEBUG -Od -Zi -MDd -c main.cpp
main.cpp

C:\src\Files\DemoTcl>cl -nologo -I/opt/tcl/include -D_DEBUG -Od -Zi -MDd -c demolib.cpp
demolib.cpp

C:\src\Files\DemoTcl>link -nologo -subsystem:console main.obj demolib.obj \opt\tcl\lib\tcl86.lib
   Creating library main.lib and object main.exp

C:\src\Files\DemoTcl>main.exe
Via 'load'...
Ok  :

Via 'Demolib_Init()'...
Pre-stubs
Pre-provide
Pre-return

如果我们将Tcl_StaticPackage调用更改为以下内容:

    Tcl_StaticPackage(NULL, "Demolib", Demolib_Init, Demolib_SafeInit);

然后新的可执行输出是:

Via 'load'...
Pre-stubs
Pre-provide
Pre-return
Ok  :

Via 'Demolib_Init()'...
Pre-stubs
Pre-provide
Pre-return

我认为您应该尽早添加Tcl_FindExecutable(argv[0]);,尽管这并不会影响所提出的问题。

Tcl_StaticPackage的文档巧妙地指出interp参数指向一个解释器,如果它不是NULL,那么它已经被加载了。