从Tcl Sript

时间:2019-05-30 20:13:17

标签: go tcl

我们使用第三方Tcl解析库来验证Tcl脚本的语法和语义检查。该驱动程序是用C编写的,并定义了一组实用程序功能。然后它调用Tcl_CreateObjCommand,因此脚本可以调用这些C函数。现在我们正在移植要运行的主程序,而我找不到执行此操作的方法。有谁知道从Tcl脚本调用golang函数的方法吗?

static int
create_utility_tcl_cmds(Tcl_Interp* interp)
{
    if (Tcl_CreateObjCommand(interp, "ip_v4_address",
                        ip_address, (ClientData)AF_INET, NULL) == NULL) {
        TCL_CHECKER_TCL_CMD_EVENT(0, "ip_v4_address");
        return -1;
    }

    .....
    return 0;
}

1 个答案:

答案 0 :(得分:1)

假设您已将相关功能设置为已导出,并像

中那样构建了项目的Go部分。
  

Using Go code in an existing C project

[…]

  

要注意的重要事项是:

     
      
  • 该软件包需要命名为main
  •   
  • 您需要具有main函数,尽管它可以为空。
  •   
  • 您需要导入软件包C
  •   
  • 您需要特殊的//export注释来标记要从C调用的函数。
  •   
     

我可以使用以下命令将其编译为C可调用静态库:

go build -buildmode=c-archive foo.go

然后要做的核心是从Tcl的API向您的Go代码编写C胶水功能。这将涉及到类似以下的功能:

static int ip_address_glue(
        ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) {
    // Need an explicit cast; ClientData is really void*
    GoInt address_family = (GoInt) clientData;

    // Check for the right number of arguments
    if (objc != 2) {
        Tcl_WrongNumArgs(interp, 1, objv, "address");
        return TCL_ERROR;
    }

    // Convert the argument to a Go string
    GoString address;
    int len;
    address.p = Tcl_GetStringFromObj(objv[1], &len);
    address.n = len; // This bit is hiding a type mismatch

    // Do the call; I assume your Go function is called ip_address
    ip_address(address_family, address);

    // Assume the Go code doesn't fail, so no need to map the failure back to Tcl

    return TCL_OK;
}

(向https://medium.com/learning-the-go-programming-language/calling-go-functions-from-other-languages-4c7d8bcc69bf致谢,以为我提供足够的信息来解决某些类型绑定。)

这就是您在Tcl中注册为回调的函数。

Tcl_CreateObjCommand(interp, "ip_v4_address", ip_address_glue, (ClientData)AF_INET, NULL);

理论上,命令注册可能会失败。实际上,只有在删除Tcl解释器(或其中的一些关键名称空间)时,这种情况才会发生。


如果在Go级别将其编码为枚举,则将故障映射到Tcl将是最容易的。可能最容易将成功表示为零。这样,您就可以这样做:

GoInt failure_code = ip_address(address_family, address);

switch (failure_code) {
case 0: // Success
    return TCL_OK;
case 1: // First type of failure
    Tcl_SetResult(interp, "failure of type #1", TCL_STATIC);
    return TCL_ERROR;
// ... etc for each expected case ...
default: // Should be unreachable, yes?
    Tcl_SetObjResult(interp, Tcl_ObjPrintf("unexpected failure: %d", failure_code));
    return TCL_ERROR;
}

也可以传递带有元组值(特别是成功指标和“实际”结果值的组合)的更复杂的返回类型,但是我没有Go开发环境来探究它们的方式在C级别映射。