1. Introduction项目中需要测试一组C API,除了开发人员那边的单元测试,我们可能还需要对这组API进行一些功能
测试. 对于我这样好久没写C程序的懒人来说,写C程序来测试API实在是在很痛苦的事情. 感觉写
C程序必须小心翼翼,不然一步小心就内存泄露. 如果可以用perl/python等脚本语言来直接
调用C函数, 问题就解决了.
2. Solution在Perl中,有三种方式可以实现这样的想法:
1. XS
XS是perl的接口编程语言,可以方便的写出perl的扩展.
2. Inline
此perl模块可以让用户在perl程序中直接内嵌其他语言的代码,这些其他语言的代码会自动编译,然后在
运行的时候自动加载.本质上Inline也是基于XS的.
3. SWIG
SWIG是个帮助使用C或者C++编写的软件能与其它各种高级编程语言进行嵌入联接的开发工具。SWIG能应
用于各种不同类型的语言包括常用脚本编译语言例如Perl, PHP, Python, Tcl, Ruby and PHP.
上述3者的关系可以用下图表示:
_________________________
| C/C++ Code |
|________________________|
| Inline | |
|________|_______________|
| XS | SWIG |
|___________|____________|
| Perl Core |
|________________________|
3. Inline Example首先尝试下第二种Inline::C, 相比XS来说,inline的学习曲线可能小些. 但是你还是得研究下列文档:
1)perlapi 2)perlguts 2)perldoc Inline. 内联的一个非常实用的地方是可以依据 C语言的库
编写出快速包装代码并将在 Perl上使用它,这样(就我而言)就可以将 Perl变成一个很好的测试平台.
内联得到您的 C
语言代码,然后在它基础上构建一个 XS
文件,就像一个人工的扩展模块编写器所执行
的一样,接着创建那个模块,然后装载它。接下来的代码调用就会很容易找到以前构建过的那个模块,然
后直接装载它.
总的来说,大体步骤分为步:
a. 在Inline中设置C API的库路径和头文件路径. 这个跟gcc的用法的一致. 例如:
use Inline C => Config => MYEXTLIB => "/home/ray/study/perl/inline/share/lib/libmy_ctags.so",
INC => "-I/home/ray/study/perl/inline/share/include";
# LIBS => "-L/home/ray/study/perl/inline/share/include -lmy_ctags";
b. 选择perl程序中C代码的导入方式. 这样有很多方式,最省事的就是用string,
use Inline C => <<'END_CODE';
// Your C source code here
END_CODE
还可以把perl内嵌c程序单独做成一个文件,然后把这个文件内容保存在一个变量中,然后再导入到inline中.
$source_code_string = read(file);
use Inline C => $source_code_string;
c. 编写C API的warpper函数,方便让perl调用.特别需要注意的地方就是合理的映射C和perl的数据结构.
如果某C函数A中需要返回指针, 那么A的wrapper函数就必须返回一个引用,这样perl中才可以直接调用.
如果A中指针的内容是数组,那么wrapper函数中对应就必须有一个数组HV.
如果A中指针的内容是定义的struct,wrapper中就可以映射成一个哈希表AV.
下面写个例子实际操作下,参考的C API就是我的一篇文章.
http://blog.chinaunix.net/u2/68938/showart_718163.html
工作路径如下
|-- MyCtags.pm
|-- ctags.pl
|-- include
| `-- my_ctags.h
|-- lib
| `-- libmy_ctags.so
`-- src
|-- my_ctags.c
`-- my_ctags.h
这里面src目录是souce code, 生成共享库, 然后放到lib目录下.
gcc -o libmy_ctags.so -shared my_ctags.c
此共享库中,有3个开放的API, 可以创建一个tag list,然后打印出来.
taglist* setup_taglist(char *filename, const char* pattern);
void output_taglist(taglist *list);
int cleanup_taglist(taglist **list);
ctags.pl就是一个测试脚本,简单的测试了Myctags.pm
MyCtags.pm就是此共享库的perl语言绑定, 让用户可以创建一个tag class, 然后打印出来.
列举几个简单的用法, 具体用法参见ctags.pl.
my $tags_ref = MyCtags->new($filename, $pattern);
$tags_ref->dump();
最后把所有代码打包出来,与大家共享.
|
文件: | taglist.zip |
大小: | 8KB |
下载: | 下载 |
|
[ray@localhost share]$ cat -n MyCtags.pm
1 package MyCtags;
2
3 use warnings;
4 use strict;
5
6 use Inline C => Config => MYEXTLIB => "/home/ray/study/perl/inline/share/lib/libmy_ctags.so",
7 INC => "-I/home/ray/study/perl/inline/share/include";
8 # LIBS => "-L/home/ray/study/perl/inline/share/include -lmy_ctags";
9
10 sub new
11 {
12 my ($class, $filename, $pattern) = @_;
13
14 my $self = {};
15 $self->{'taglist'} = __setup_taglist($filename, $pattern);
16
17 return bless $self, $class;
18 }
19
20 sub dump
21 {
22 my ($self) = @_;
23
24 __output_taglist($self->{'taglist'});
25
26 }
27
28 sub get_taglist
29 {
30 my ($self) = @_;
31
32 if ( $self->{'taglist'}) {
33
34 return $self->{'taglist'};
35 }
36 else {
37 return undef;
38 }
39
40 }
41
42 sub DESTROY
43 {
44 my ($self) = @_;
45
46 __cleanup_taglist($self->{'taglist'});
47 }
48
49 use Inline C => <<'END_CODE';
50
51 #include "my_ctags.h"
52
53 // List head
54 static taglist* local_taglist;
55
56 SV* __setup_taglist(char* filename, char* pattern)
57 {
58 taglist *loop;
59 AV* list = newAV();
60
61 printf("call setup_taglist in C API\n");
62 local_taglist = setup_taglist(filename, pattern);
63
64 //Convirt C structure to PERL data
65 int i = 0;
66 for(loop = local_taglist->tag_next; i < local_taglist->tag_num; i++)
67 {
68
69 //Get every tag info, like func name, line number.
70 char* funcname = loop->tag.func_name;
71 char* filename = loop->tag.file_name;
72 unsigned int linenum = loop->tag.line_num;
73 loop = loop->tag_next;
74
75 // Store the tag info into a hash of PERL, see perlapi for more help
76 HV* tag = newHV();
77 hv_stores(tag, "func_name", newSVpv(funcname, strlen(funcname)));
78 hv_stores(tag, "file_name", newSVpv(filename, strlen(filename)));
79 hv_stores(tag, "line_num", newSVuv(linenum));
80
81 // Append the above new tag HASH to an array of PERL
82 av_push(list, newRV_inc((SV*) tag));
83 }
84
85 return newRV_inc((SV*) list);
86 }
87
88 void __output_taglist(SV* list)
89 {
90 printf("call output_taglist in C API\n");
91
92 taglist* my_taglist = local_taglist;
93
94 output_taglist(my_taglist);
95
96 my_taglist = NULL;
97
98 }
99
100 void __cleanup_taglist(SV* list)
101 {
102 printf("call cleanup_taglist in C API\n");
103
104 taglist* my_taglist = local_taglist;
105
106 cleanup_taglist(&my_taglist);
107
108 }
109
110 END_CODE
111
112 1;
|