为Tcl编写C的扩展库

Tcl是一个比较简洁的脚本语言,官方地址 http://www.tcl.tk.

tcl脚本加载C实现的动态库非常方便。

1. 为Tcl编写一个用C实现的扩展函数。

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

extern "C" {
    // extern for C++.
    int Myimpltcl_Init(Tcl_Interp *Interp);
    int Myimpltcl_Unload(Tcl_Interp *Interp);
}

int Action_FuncA(int notUsed, Tcl_Interp *interp, int argc, char **argv) {
    if (argc != 3) {
        // check args, same as main function args.
        Tcl_SetResult(interp, "Usage::Action_FuncA arg1 arg2",
            TCL_VOLATILE);
        return TCL_ERROR;
    }
    printf("argv[1] is %s.
", argv[1]);
    printf("argv[2] is %s.
", argv[2]);
    // return string.
    Tcl_SetResult(interp, "return of Action_FuncA", TCL_VOLATILE);
    return TCL_OK;
}

int Action_FuncB(int notUsed, Tcl_Interp *interp, int argc, char **argv) {
    if (argc != 2) {
        // check args, same as main function args.
        Tcl_SetResult(interp, "Usage::Action_FuncB arg1",
            TCL_VOLATILE);
        return TCL_ERROR;
    }
    printf("argv[1] is %s.
", argv[1]);
    // return string.
    Tcl_SetResult(interp, "return of Action_FuncB", TCL_VOLATILE);
    return TCL_OK;
}

int Myimpltcl_Init(Tcl_Interp *Interp) {
    // initialize operation.
    Tcl_CreateCommand (Interp, "Action_FuncA", (Tcl_CmdProc *)Action_FuncA, 0, 0);
    Tcl_CreateCommand (Interp, "Action_FuncB", (Tcl_CmdProc *)Action_FuncB, 0, 0);
    return TCL_OK;
}

int Myimpltcl_Unload(Tcl_Interp *Interp, int flags) {
    // destroy operation.
    return TCL_OK;
}

分析:

tcl.h是加载tcl需要头文件。

初始化函数 Myimpltcl_Init

  使用Tcl_CreateCommand函数创建一个可以在tcl脚本中调用的函数,函数的实现指向C实现的函数。

创建方法 Tcl中可以调用的函数名称 C中实现的函数名称
Tcl_CreateCommand 
Action_FuncA
int Action_FuncA(int notUsed, Tcl_Interp *interp, int argc, char **argv)
Tcl_CreateCommand 
Action_FuncB
int Action_FuncB(int notUsed, Tcl_Interp *interp, int argc, char **argv)


退出函数 Myimpltcl_Unload

  tcl卸载动态库时会调用的函数,用于是否内存和其他的资源。

2. 编写Makefile文件

CC = gcc -g -O3 -w
SHARED_FLAG = -fPIC -shared
PROJECT = libmyimpltcl.so

INC  = -I./
INC += -I$(TCL_HOME)/include
LIB = -L$(TCL_HOME)/lib -ltcl8.5

all : $(PROJECT)

$(PROJECT) :
	$(CC) myimpltcl.cpp ${SHARED_FLAG} -o $(PROJECT) $(INC) $(LIB)

clean:
	rm -rf *.o *.a *.so

分析:

生成的动态库名称必须是libmyimpltcl.so,为什么呢?

Tcl加载C编写的so库的规则是。

void *handle = dlopen("libmyimpltcl.so", RTLD_NOW | RTLD_GLOBAL);

将so库的名称去掉lib前缀

 libmyimpltcl.so 

把去掉前缀的第一个字母变成大写并增加后缀_Init

myimpltcl --> Myimpltcl_Init

拼接成新的字符串作用动态库的入库函数,用dlsym系统调用得到so中的C函数地址,并执行

dlsym(handle, "Myimpltcl_Init");

3. 测试

[user@host tcl]# tclsh
% load libmyimpltcl.so
% # 加载编译好的so库
% info loaded
% # 查看加载过的库信息
{libmyimpltcl.so Myimpltcl}
% set ret [Action_FuncA param1 param2]
% # 调用so中的C函数Action_FuncA
argv[1] is param1.
argv[2] is param2.
return of Action_FuncA
% puts $ret
return of Action_FuncA
% set retB [Action_FuncB 123]
% # 调用so中的C函数Action_FuncB
argv[1] is 123.
return of Action_FuncB
% puts $retB
return of Action_FuncB

  

Done.

原文地址:https://www.cnblogs.com/voipman/p/5284646.html