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.